codeceptjs
Advanced tools
Comparing version 3.7.0-rc.1 to 3.7.0
@@ -65,3 +65,3 @@ #!/usr/bin/env node | ||
.description('Checks configuration and environment before running tests') | ||
.option('-t, --timeout [ms]', 'timeout for checks in ms, 20000 by default') | ||
.option('-t, --timeout [ms]', 'timeout for checks in ms, 50000 by default') | ||
.action(errorHandler(require('../lib/command/check'))) | ||
@@ -68,0 +68,0 @@ |
@@ -28,2 +28,3 @@ const { getConfig, getTestRoot } = require('./utils') | ||
setup: false, | ||
teardown: false, | ||
tests: false, | ||
@@ -91,5 +92,5 @@ def: false, | ||
checks.ai = true | ||
printCheck('ai', checks['ai'], 'AI configuration is enabled, request function is set') | ||
printCheck('ai', checks['ai'], 'Configuration is enabled, request function is set') | ||
} else { | ||
printCheck('ai', checks['ai'], 'AI is disabled') | ||
printCheck('ai', checks['ai'], 'Disabled') | ||
} | ||
@@ -136,6 +137,19 @@ | ||
const test = createTest('test', () => {}) | ||
try { | ||
for (const helper of Object.values(helpers)) { | ||
checks.setup = true | ||
for (const helper of Object.values(helpers)) { | ||
try { | ||
if (helper._beforeSuite) await helper._beforeSuite(suite) | ||
if (helper._before) await helper._before(test) | ||
} catch (err) { | ||
err.message = `${helper.constructor.name} helper: ${err.message}` | ||
if (checks.setup instanceof Error) err.message = `${err.message}\n\n${checks.setup?.message || ''}`.trim() | ||
checks.setup = err | ||
} | ||
} | ||
printCheck('Helpers Before', checks['setup'], standardActingHelpers.some(h => Object.keys(helpers).includes(h)) ? 'Initializing browser' : '') | ||
checks.teardown = true | ||
for (const helper of Object.values(helpers).reverse()) { | ||
try { | ||
if (helper._passed) await helper._passed(test) | ||
@@ -145,11 +159,12 @@ if (helper._after) await helper._after(test) | ||
if (helper._afterSuite) await helper._afterSuite(suite) | ||
} catch (err) { | ||
err.message = `${helper.constructor.name} helper: ${err.message}` | ||
if (checks.teardown instanceof Error) err.message = `${err.message}\n\n${checks.teardown?.message || ''}`.trim() | ||
checks.teardown = err | ||
} | ||
checks.setup = true | ||
} catch (err) { | ||
checks.setup = err | ||
} | ||
printCheck('Helpers After', checks['teardown'], standardActingHelpers.some(h => Object.keys(helpers).includes(h)) ? 'Closing browser' : '') | ||
} | ||
printCheck('Helpers Before/After', checks['setup'], standardActingHelpers.some(h => Object.keys(helpers).includes(h)) ? 'Initializing and closing browser' : '') | ||
try { | ||
@@ -156,0 +171,0 @@ definitions(configFile, { dryRun: true }) |
@@ -5,2 +5,3 @@ const recorder = require('./recorder') | ||
const event = require('./event') | ||
const within = require('./within') | ||
@@ -182,2 +183,3 @@ /** | ||
let result = false | ||
let isAutoRetriesEnabled = store.autoRetries | ||
return recorder.add( | ||
@@ -187,3 +189,5 @@ sessionName, | ||
recorder.session.start(sessionName) | ||
store.tryTo = true | ||
isAutoRetriesEnabled = store.autoRetries | ||
if (isAutoRetriesEnabled) debug('Auto retries disabled inside tryTo effect') | ||
store.autoRetries = false | ||
callback() | ||
@@ -205,3 +209,3 @@ recorder.add(() => { | ||
() => { | ||
store.tryTo = undefined | ||
store.autoRetries = isAutoRetriesEnabled | ||
return result | ||
@@ -222,2 +226,3 @@ }, | ||
tryTo, | ||
within, | ||
} |
@@ -57,2 +57,4 @@ const debug = require('debug')('codeceptjs:event') | ||
* @property {'hook.passed'} passed | ||
* @property {'hook.failed'} failed | ||
* @property {'hook.finished'} finished | ||
*/ | ||
@@ -59,0 +61,0 @@ hook: { |
@@ -5,6 +5,6 @@ /** | ||
class Popup { | ||
constructor(popup, defaultAction) { | ||
this._popup = popup || null; | ||
this._actionType = ''; | ||
this._defaultAction = defaultAction || ''; | ||
constructor(popup = null, defaultAction = '') { | ||
this._popup = popup | ||
this._actionType = '' | ||
this._defaultAction = defaultAction | ||
} | ||
@@ -14,3 +14,3 @@ | ||
if (['accept', 'cancel'].indexOf(action) === -1) { | ||
throw new Error('Invalid Popup action type. Only "accept" or "cancel" actions are accepted'); | ||
throw new Error('Invalid Popup action type. Only "accept" or "cancel" actions are accepted') | ||
} | ||
@@ -20,12 +20,12 @@ } | ||
set defaultAction(action) { | ||
this._assertValidActionType(action); | ||
this._defaultAction = action; | ||
this._assertValidActionType(action) | ||
this._defaultAction = action | ||
} | ||
get defaultAction() { | ||
return this._defaultAction; | ||
return this._defaultAction | ||
} | ||
get popup() { | ||
return this._popup; | ||
return this._popup | ||
} | ||
@@ -35,19 +35,19 @@ | ||
if (this._popup) { | ||
console.error('Popup already exists and was not closed. Popups must always be closed by calling either I.acceptPopup() or I.cancelPopup()'); | ||
return | ||
} | ||
this._popup = popup; | ||
this._popup = popup | ||
} | ||
get actionType() { | ||
return this._actionType; | ||
return this._actionType | ||
} | ||
set actionType(action) { | ||
this._assertValidActionType(action); | ||
this._actionType = action; | ||
this._assertValidActionType(action) | ||
this._actionType = action | ||
} | ||
clear() { | ||
this._popup = null; | ||
this._actionType = ''; | ||
this._popup = null | ||
this._actionType = '' | ||
} | ||
@@ -57,3 +57,3 @@ | ||
if (!this._popup) { | ||
throw new Error('There is no Popup visible'); | ||
throw new Error('There is no Popup visible') | ||
} | ||
@@ -63,11 +63,11 @@ } | ||
assertPopupActionType(type) { | ||
this.assertPopupVisible(); | ||
const expectedAction = this._actionType || this._defaultAction; | ||
this.assertPopupVisible() | ||
const expectedAction = this._actionType || this._defaultAction | ||
if (expectedAction !== type) { | ||
throw new Error(`Popup action does not fit the expected action type. Expected popup action to be '${expectedAction}' not '${type}`); | ||
throw new Error(`Popup action does not fit the expected action type. Expected popup action to be '${expectedAction}' not '${type}`) | ||
} | ||
this.clear(); | ||
this.clear() | ||
} | ||
} | ||
module.exports = Popup; | ||
module.exports = Popup |
@@ -136,4 +136,6 @@ const Test = require('mocha/lib/test') | ||
function testToFileName(test) { | ||
let fileName = clearString(test.title) | ||
function testToFileName(test, suffix = '') { | ||
let fileName = test.title | ||
if (suffix) fileName = `${fileName}_${suffix}` | ||
// remove tags with empty string (disable for now) | ||
@@ -150,2 +152,3 @@ // fileName = fileName.replace(/\@\w+/g, '') | ||
// } | ||
fileName = clearString(fileName).slice(0, 100) | ||
return fileName | ||
@@ -152,0 +155,0 @@ } |
@@ -63,3 +63,3 @@ const debug = require('debug')('codeceptjs:analyze') | ||
Preserve error messages but cut them if they are too long. | ||
Respond clearly and directly, without introductory words or phrases like ‘Of course,’ ‘Here is the answer,’ etc. | ||
Respond clearly and directly, without introductory words or phrases like 'Of course,' 'Here is the answer,' etc. | ||
Do not list more than 3 errors in the group. | ||
@@ -164,5 +164,52 @@ If you identify that all tests in the group have the same tag, add this tag to the group report, otherwise ignore TAG section. | ||
/** | ||
* CodeceptJS Analyze Plugin - Uses AI to analyze test failures and provide insights | ||
* | ||
* @param {*} config | ||
* @returns | ||
* This plugin analyzes failed tests using AI to provide detailed explanations and group similar failures. | ||
* When enabled with --ai flag, it generates reports after test execution. | ||
* | ||
* #### Usage | ||
* | ||
* ```js | ||
* // in codecept.conf.js | ||
* exports.config = { | ||
* plugins: { | ||
* analyze: { | ||
* enabled: true, | ||
* clusterize: 5, | ||
* analyze: 2, | ||
* vision: false | ||
* } | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* #### Configuration | ||
* | ||
* * `clusterize` (number) - minimum number of failures to trigger clustering analysis. Default: 5 | ||
* * `analyze` (number) - maximum number of individual test failures to analyze in detail. Default: 2 | ||
* * `vision` (boolean) - enables visual analysis of test screenshots. Default: false | ||
* * `categories` (array) - list of failure categories for classification. Defaults to: | ||
* - Browser connection error / browser crash | ||
* - Network errors (server error, timeout, etc) | ||
* - HTML / page elements (not found, not visible, etc) | ||
* - Navigation errors (404, etc) | ||
* - Code errors (syntax error, JS errors, etc) | ||
* - Library & framework errors | ||
* - Data errors (password incorrect, invalid format, etc) | ||
* - Assertion failures | ||
* - Other errors | ||
* * `prompts` (object) - customize AI prompts for analysis | ||
* - `clusterize` - prompt for clustering analysis | ||
* - `analyze` - prompt for individual test analysis | ||
* | ||
* #### Features | ||
* | ||
* * Groups similar failures when number of failures >= clusterize value | ||
* * Provides detailed analysis of individual failures | ||
* * Analyzes screenshots if vision=true and screenshots are available | ||
* * Classifies failures into predefined categories | ||
* * Suggests possible causes and solutions | ||
* | ||
* @param {Object} config - Plugin configuration | ||
* @returns {void} | ||
*/ | ||
@@ -169,0 +216,0 @@ module.exports = function (config = {}) { |
const event = require('../event') | ||
const recorder = require('../recorder') | ||
const container = require('../container') | ||
const { log } = require('../output') | ||
const store = require('../store') | ||
const defaultConfig = { | ||
@@ -73,5 +71,5 @@ retries: 3, | ||
* ```js | ||
* Scenario('scenario tite', () => { | ||
* Scenario('scenario tite', { disableRetryFailedStep: true }, () => { | ||
* // test goes here | ||
* }).config(test => test.disableRetryFailedStep = true) | ||
* }) | ||
* ``` | ||
@@ -89,4 +87,4 @@ * | ||
if (!enableRetry) return | ||
const store = require('../store') | ||
if (store.debugMode) return false | ||
if (!store.autoRetries) return false | ||
if (customWhen) return customWhen(err) | ||
@@ -98,7 +96,2 @@ return true | ||
event.dispatcher.on(event.step.started, step => { | ||
if (process.env.TRY_TO === 'true') { | ||
log('Info: RetryFailedStep plugin is disabled inside tryTo block') | ||
return | ||
} | ||
// if a step is ignored - return | ||
@@ -119,7 +112,13 @@ for (const ignored of config.ignoredSteps) { | ||
event.dispatcher.on(event.test.before, test => { | ||
if (test && test.disableRetryFailedStep) return // disable retry when a test is not active | ||
// this env var is used to set the retries inside _before() block of helpers | ||
process.env.FAILED_STEP_RETRIES = config.retries | ||
// pass disableRetryFailedStep is a preferred way to disable retries | ||
// test.disableRetryFailedStep is used for backward compatibility | ||
if (test.opts.disableRetryFailedStep || test.disableRetryFailedStep) { | ||
store.autoRetries = false | ||
return // disable retry when a test is not active | ||
} | ||
// this option is used to set the retries inside _before() block of helpers | ||
store.autoRetries = true | ||
test.opts.conditionalRetries = config.retries | ||
recorder.retry(config) | ||
}) | ||
} |
@@ -9,3 +9,3 @@ const { retryTo } = require('../effects') | ||
config = Object.assign(defaultConfig, config) | ||
console.log(`Deprecation Warning: 'retryTo' has been moved to the 'codeceptjs/effects' module`) | ||
console.log(`Deprecation Warning: 'retryTo' has been moved to the 'codeceptjs/effects' module. Disable retryTo plugin to remove this warning.`) | ||
@@ -12,0 +12,0 @@ if (config.registerGlobal) { |
@@ -86,9 +86,8 @@ const fs = require('fs') | ||
// This prevents data driven to be included in the failed screenshot file name | ||
let fileName = testToFileName(test) | ||
let fileName | ||
if (options.uniqueScreenshotNames && test) { | ||
const uuid = _getUUID(test) | ||
fileName = `${fileName.substring(0, 10)}_${uuid}.failed.png` | ||
fileName = `${testToFileName(test, _getUUID(test))}.failed.png` | ||
} else { | ||
fileName += '.failed.png' | ||
fileName = `${testToFileName(test)}.failed.png` | ||
} | ||
@@ -95,0 +94,0 @@ output.plugin('screenshotOnFail', 'Test failed, try to save a screenshot') |
@@ -9,3 +9,3 @@ const { tryTo } = require('../effects') | ||
config = Object.assign(defaultConfig, config) | ||
console.log(`Deprecation Warning: 'tryTo' has been moved to the 'codeceptjs/effects' module`) | ||
console.log(`Deprecation Warning: 'tryTo' has been moved to the 'codeceptjs/effects' module. Disable tryTo plugin to remove this warning.`) | ||
@@ -12,0 +12,0 @@ if (config.registerGlobal) { |
@@ -195,2 +195,3 @@ const debug = require('debug')('codeceptjs:recorder') | ||
debug(`${currentQueue()} Running | ${taskName} | Timeout: ${timeout || 'None'}`) | ||
if (retryOpts) debug(`${currentQueue()} Retry opts`, JSON.stringify(retryOpts)) | ||
@@ -197,0 +198,0 @@ if (!retryOpts || !taskName || !retry) { |
@@ -6,13 +6,37 @@ /** | ||
const store = { | ||
/** @type {boolean} */ | ||
/** | ||
* If we are in --debug mode | ||
* @type {boolean} | ||
*/ | ||
debugMode: false, | ||
/** @type {boolean} */ | ||
/** | ||
* Is timeouts enabled | ||
* @type {boolean} | ||
*/ | ||
timeouts: true, | ||
/** @type {boolean} */ | ||
/** | ||
* If auto-retries are enabled by retryFailedStep plugin | ||
* tryTo effect disables them | ||
* @type {boolean} | ||
*/ | ||
autoRetries: false, | ||
/** | ||
* Tests are executed via dry-run | ||
* @type {boolean} | ||
*/ | ||
dryRun: false, | ||
/** @type {boolean} */ | ||
/** | ||
* If we are in pause mode | ||
* @type {boolean} | ||
*/ | ||
onPause: false, | ||
// current object states | ||
/** @type {CodeceptJS.Test | null} */ | ||
currentTest: null, | ||
/** @type {any} */ | ||
/** @type {CodeceptJS.Step | null} */ | ||
currentStep: null, | ||
@@ -19,0 +43,0 @@ /** @type {CodeceptJS.Suite | null} */ |
@@ -16,3 +16,3 @@ const fs = require('fs') | ||
module.exports.genTestId = test => { | ||
return require('crypto').createHash('sha256').update(test.fullTitle()).digest('base64').slice(0, -2) | ||
return this.clearString(require('crypto').createHash('sha256').update(test.fullTitle()).digest('base64').slice(0, -2)) | ||
} | ||
@@ -19,0 +19,0 @@ |
@@ -10,2 +10,4 @@ const output = require('./output') | ||
/** | ||
* TODO: move to effects | ||
* | ||
* @param {CodeceptJS.LocatorOrString} context | ||
@@ -12,0 +14,0 @@ * @param {Function} fn |
{ | ||
"name": "codeceptjs", | ||
"version": "3.7.0-rc.1", | ||
"version": "3.7.0", | ||
"description": "Supercharged End 2 End Testing Framework for NodeJS", | ||
@@ -101,3 +101,3 @@ "keywords": [ | ||
"html-minifier-terser": "7.2.0", | ||
"inquirer": "6.5.2", | ||
"inquirer": "8.2.6", | ||
"invisi-data": "^1.0.0", | ||
@@ -104,0 +104,0 @@ "joi": "17.13.3", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
2154801
58108
0
71
- Removedabbrev@3.0.0(transitive)
- Removedansi-escapes@3.2.0(transitive)
- Removedansi-regex@4.1.1(transitive)
- Removedansi-styles@3.2.1(transitive)
- Removedchalk@2.4.2(transitive)
- Removedcli-cursor@2.1.0(transitive)
- Removedcli-width@2.2.1(transitive)
- Removedcodeceptjs@3.7.2(transitive)
- Removedcolor-convert@1.9.3(transitive)
- Removedcolor-name@1.1.3(transitive)
- Removedfigures@2.0.0(transitive)
- Removedhas-flag@3.0.0(transitive)
- Removedinquirer@6.5.2(transitive)
- Removedis-fullwidth-code-point@2.0.0(transitive)
- Removedjs-beautify@1.15.2(transitive)
- Removedmimic-fn@1.2.0(transitive)
- Removedmonocart-coverage-reports@2.12.1(transitive)
- Removedmute-stream@0.0.7(transitive)
- Removednopt@8.1.0(transitive)
- Removedonetime@2.0.1(transitive)
- Removedrestore-cursor@2.0.0(transitive)
- Removedrxjs@6.6.7(transitive)
- Removedstring-width@2.1.1(transitive)
- Removedstrip-ansi@4.0.05.2.0(transitive)
- Removedsupports-color@5.5.0(transitive)
- Removedtslib@1.14.1(transitive)
Updatedinquirer@8.2.6