Skip to content

Commit ff19db7

Browse files
DavertMikDavertMik
andauthored
fixed pause finishing, added suggest for empty run, included fuse.js … (#4713)
* fixed pause finishing, added suggest for empty run, included fuse.js for better search * fixed test * updated plugins docs * improved unit tests setup * fixed interface test for CI mode * fixed on ci * added env variable config --------- Co-authored-by: DavertMik <[email protected]>
1 parent 18966a9 commit ff19db7

File tree

11 files changed

+280
-153
lines changed

11 files changed

+280
-153
lines changed

.github/workflows/test.yml

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,42 @@ on:
99
- '**'
1010

1111
jobs:
12-
build:
12+
unit-tests:
13+
name: Unit tests
14+
runs-on: ubuntu-22.04
15+
16+
strategy:
17+
matrix:
18+
node-version: [20.x, 22.x]
19+
20+
steps:
21+
- uses: actions/checkout@v4
22+
- name: Use Node.js ${{ matrix.node-version }}
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: ${{ matrix.node-version }}
26+
- run: npm i
27+
env:
28+
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: true
29+
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
30+
- run: npm run test:unit
1331

32+
runner-tests:
33+
name: Runner tests
1434
runs-on: ubuntu-22.04
1535

1636
strategy:
1737
matrix:
18-
node-version: [20.x]
38+
node-version: [20.x, 22.x]
1939

2040
steps:
21-
- uses: actions/checkout@v4
22-
- name: Use Node.js ${{ matrix.node-version }}
23-
uses: actions/setup-node@v4
24-
with:
25-
node-version: ${{ matrix.node-version }}
26-
- run: npm i --force
27-
env:
28-
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: true
29-
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
30-
- uses: nick-fields/retry@v3
31-
with:
32-
timeout_minutes: 6
33-
max_attempts: 3
34-
retry_on: error
35-
command: npm test
41+
- uses: actions/checkout@v4
42+
- name: Use Node.js ${{ matrix.node-version }}
43+
uses: actions/setup-node@v4
44+
with:
45+
node-version: ${{ matrix.node-version }}
46+
- run: npm i
47+
env:
48+
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: true
49+
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
50+
- run: npm run test:runner

lib/codecept.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class Codecept {
111111
runHook(require('./listener/globalTimeout'))
112112
runHook(require('./listener/globalRetry'))
113113
runHook(require('./listener/exit'))
114+
runHook(require('./listener/emptyRun'))
114115

115116
// custom hooks (previous iteration of plugins)
116117
this.config.hooks.forEach(hook => runHook(hook))

lib/listener/emptyRun.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const figures = require('figures')
2+
const Container = require('../container')
3+
const event = require('../event')
4+
const output = require('../output')
5+
6+
module.exports = function () {
7+
let isEmptyRun = true
8+
9+
event.dispatcher.on(event.test.before, test => {
10+
isEmptyRun = false
11+
})
12+
13+
event.dispatcher.on(event.all.result, () => {
14+
if (isEmptyRun) {
15+
const mocha = Container.mocha()
16+
17+
if (mocha.options.grep) {
18+
const Fuse = require('fuse.js')
19+
20+
output.print()
21+
output.print('No tests found by pattern: ' + mocha.options.grep)
22+
23+
const allTests = []
24+
mocha.suite.suites.forEach(suite => {
25+
suite.tests.forEach(test => {
26+
allTests.push(test.fullTitle())
27+
})
28+
})
29+
30+
const fuse = new Fuse(allTests, {
31+
includeScore: true,
32+
threshold: 0.6,
33+
caseSensitive: false,
34+
})
35+
36+
const results = fuse.search(mocha.options.grep.toString())
37+
38+
if (results.length > 0) {
39+
output.print()
40+
output.print('Maybe you wanted to run one of these tests?')
41+
results.forEach(result => {
42+
output.print(figures.checkboxOff, output.styles.log(result.item))
43+
})
44+
45+
output.print()
46+
output.print(output.styles.debug('To run the first test use the following command:'))
47+
output.print(output.styles.bold('npx codeceptjs run --debug --grep "' + results[0].item + '"'))
48+
}
49+
}
50+
if (process.env.CI && !process.env.DONT_FAIL_ON_EMPTY_RUN) {
51+
output.print()
52+
output.error('No tests were executed. Failing on CI to avoid false positives')
53+
output.error('To disable this check, set `DONT_FAIL_ON_EMPTY_RUN` environment variable to true in CI config')
54+
process.exitCode = 1
55+
}
56+
}
57+
})
58+
}

lib/mocha/factory.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ const fs = require('fs')
44
const reporter = require('./cli')
55
const gherkinParser = require('./gherkin')
66
const output = require('../output')
7-
const { genTestId } = require('../utils')
87
const ConnectionRefused = require('../helper/errors/ConnectionRefused')
98

109
const scenarioUi = fsPath.join(__dirname, './ui.js')
@@ -45,8 +44,6 @@ class MochaFactory {
4544
let missingFeatureInFile = []
4645
const seenTests = []
4746
mocha.suite.eachTest(test => {
48-
test.uid = genTestId(test)
49-
5047
const name = test.fullTitle()
5148
if (seenTests.includes(test.uid)) {
5249
dupes.push(name)

lib/mocha/test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const Test = require('mocha/lib/test')
22
const { test: testWrapper } = require('./asyncWrapper')
33
const { enhanceMochaSuite } = require('./suite')
4+
const { genTestId } = require('../utils')
45

56
/**
67
* Factory function to create enhanced tests
@@ -40,6 +41,7 @@ function enhanceMochaTest(test) {
4041
suite.addTest(testWrapper(this))
4142
test.tags = [...(test.tags || []), ...(suite.tags || [])]
4243
test.fullTitle = () => `${suite.title}: ${test.title}`
44+
test.uid = genTestId(test)
4345
}
4446

4547
test.applyOptions = function (opts) {

lib/mocha/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Test as MochaTest, Suite as MochaSuite } from 'mocha'
33
declare global {
44
namespace CodeceptJS {
55
interface Test extends MochaTest {
6+
uid: string
67
title: string
78
tags: string[]
89
steps: string[]

lib/pause.js

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const colors = require('chalk')
22
const readline = require('readline')
33
const ora = require('ora-classic')
44
const debug = require('debug')('codeceptjs:pause')
5+
const Fuse = require('fuse.js')
56

67
const container = require('./container')
78
const history = require('./history')
@@ -29,10 +30,20 @@ const pause = function (passedObject = {}) {
2930
// add listener to all next steps to provide next() functionality
3031
event.dispatcher.on(event.step.after, () => {
3132
recorder.add('Start next pause session', () => {
33+
// test already finished, nothing to pause
34+
if (!store.currentTest) return
3235
if (!next) return
3336
return pauseSession()
3437
})
3538
})
39+
40+
event.dispatcher.on(event.test.finished, () => {
41+
finish()
42+
recorder.session.restore('pause')
43+
rl.close()
44+
history.save()
45+
})
46+
3647
recorder.add('Start new session', () => pauseSession(passedObject))
3748
}
3849

@@ -74,18 +85,20 @@ function pauseSession(passedObject = {}) {
7485
})
7586
return new Promise(resolve => {
7687
finish = resolve
88+
// eslint-disable-next-line
7789
return askForStep()
7890
})
7991
}
8092

93+
/* eslint-disable */
8194
async function parseInput(cmd) {
8295
rl.pause()
8396
next = false
8497
recorder.session.start('pause')
8598
if (cmd === '') next = true
8699
if (!cmd || cmd === 'resume' || cmd === 'exit') {
87100
finish()
88-
recorder.session.restore()
101+
recorder.session.restore('pause')
89102
rl.close()
90103
history.save()
91104
return nextStep()
@@ -186,6 +199,7 @@ async function parseInput(cmd) {
186199
recorder.add('ask for next step', askForStep)
187200
nextStep()
188201
}
202+
/* eslint-enable */
189203

190204
function askForStep() {
191205
return new Promise(resolve => {
@@ -199,13 +213,23 @@ function askForStep() {
199213
function completer(line) {
200214
const I = container.support('I')
201215
const completions = methodsOfObject(I)
202-
const hits = completions.filter(c => {
203-
if (c.indexOf(line) === 0) {
204-
return c
205-
}
206-
return null
216+
// If no input, return all completions
217+
if (!line) {
218+
return [completions, line]
219+
}
220+
221+
// Initialize Fuse with completions
222+
const fuse = new Fuse(completions, {
223+
threshold: 0.3,
224+
distance: 100,
225+
minMatchCharLength: 1,
207226
})
208-
return [hits && hits.length ? hits : completions, line]
227+
228+
// Search using Fuse.js
229+
const searchResults = fuse.search(line)
230+
const hits = searchResults.map(result => result.item)
231+
232+
return [hits, line]
209233
}
210234

211235
function registerVariable(name, value) {

0 commit comments

Comments
 (0)