codeceptjs 4.0.0-beta.4 → 4.0.0-beta.6.esm-aria
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -119
- package/bin/codecept.js +53 -54
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +70 -102
- package/lib/ai.js +131 -121
- package/lib/assert/empty.js +11 -12
- package/lib/assert/equal.js +16 -21
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +11 -15
- package/lib/assert/throws.js +3 -5
- package/lib/assert/truth.js +10 -7
- package/lib/assert.js +18 -18
- package/lib/codecept.js +112 -101
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +13 -14
- package/lib/command/definitions.js +24 -36
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +38 -39
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +49 -15
- package/lib/command/init.js +41 -37
- package/lib/command/interactive.js +22 -13
- package/lib/command/list.js +11 -10
- package/lib/command/run-multiple/chunk.js +50 -47
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +27 -47
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +15 -66
- package/lib/command/run.js +8 -8
- package/lib/command/utils.js +22 -21
- package/lib/command/workers/runTests.js +131 -241
- package/lib/config.js +111 -49
- package/lib/container.js +589 -244
- package/lib/data/context.js +16 -18
- package/lib/data/dataScenarioConfig.js +9 -9
- package/lib/data/dataTableArgument.js +7 -7
- package/lib/data/table.js +6 -12
- package/lib/effects.js +307 -0
- package/lib/els.js +160 -0
- package/lib/event.js +24 -19
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -81
- package/lib/helper/AI.js +3 -2
- package/lib/helper/ApiDataFactory.js +19 -19
- package/lib/helper/Appium.js +47 -51
- package/lib/helper/FileSystem.js +35 -15
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +4 -4
- package/lib/helper/JSONResponse.js +72 -45
- package/lib/helper/Mochawesome.js +14 -11
- package/lib/helper/Playwright.js +832 -434
- package/lib/helper/Puppeteer.js +393 -292
- package/lib/helper/REST.js +32 -27
- package/lib/helper/WebDriver.js +320 -219
- package/lib/helper/errors/ConnectionRefused.js +6 -6
- package/lib/helper/errors/ElementAssertion.js +11 -16
- package/lib/helper/errors/ElementNotFound.js +5 -9
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
- package/lib/helper/extras/Console.js +11 -11
- package/lib/helper/extras/PlaywrightLocator.js +110 -0
- package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
- package/lib/helper/extras/PlaywrightRestartOpts.js +23 -23
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/extras/React.js +29 -30
- package/lib/helper/network/actions.js +33 -48
- package/lib/helper/network/utils.js +76 -83
- package/lib/helper/scripts/blurElement.js +6 -6
- package/lib/helper/scripts/focusElement.js +6 -6
- package/lib/helper/scripts/highlightElement.js +9 -9
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -1
- package/lib/history.js +23 -20
- package/lib/hooks.js +10 -10
- package/lib/html.js +90 -100
- package/lib/index.js +48 -21
- package/lib/listener/config.js +8 -9
- package/lib/listener/emptyRun.js +54 -0
- package/lib/listener/exit.js +10 -12
- package/lib/listener/{retry.js → globalRetry.js} +10 -10
- package/lib/listener/globalTimeout.js +166 -0
- package/lib/listener/helpers.js +43 -24
- package/lib/listener/mocha.js +4 -5
- package/lib/listener/result.js +11 -0
- package/lib/listener/steps.js +26 -23
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +264 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +160 -0
- package/lib/{interfaces → mocha}/featureConfig.js +33 -13
- package/lib/{interfaces → mocha}/gherkin.js +75 -45
- package/lib/mocha/hooks.js +121 -0
- package/lib/mocha/index.js +21 -0
- package/lib/mocha/inject.js +46 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +32 -8
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +178 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +229 -0
- package/lib/output.js +86 -64
- package/lib/parser.js +44 -44
- package/lib/pause.js +160 -139
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +137 -43
- package/lib/plugin/autoDelay.js +19 -15
- package/lib/plugin/coverage.js +22 -27
- package/lib/plugin/customLocator.js +5 -5
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/heal.js +49 -17
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +60 -19
- package/lib/plugin/screenshotOnFail.js +80 -83
- package/lib/plugin/stepByStepReport.js +70 -31
- package/lib/plugin/stepTimeout.js +7 -13
- package/lib/plugin/subtitles.js +10 -9
- package/lib/recorder.js +167 -126
- package/lib/rerun.js +94 -50
- package/lib/result.js +161 -0
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +239 -0
- package/lib/step/comment.js +10 -0
- package/lib/step/config.js +50 -0
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +50 -0
- package/lib/step/meta.js +99 -0
- package/lib/step/record.js +74 -0
- package/lib/step/retry.js +11 -0
- package/lib/step/section.js +55 -0
- package/lib/step.js +18 -332
- package/lib/steps.js +54 -0
- package/lib/store.js +37 -5
- package/lib/template/heal.js +2 -11
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils.js +354 -250
- package/lib/workerStorage.js +16 -16
- package/lib/workers.js +366 -282
- package/package.json +107 -95
- package/translations/de-DE.js +5 -4
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +23 -9
- package/translations/it-IT.js +5 -4
- package/translations/ja-JP.js +5 -4
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +5 -4
- package/translations/pt-BR.js +5 -4
- package/translations/ru-RU.js +5 -4
- package/translations/utils.js +18 -0
- package/translations/zh-CN.js +5 -4
- package/translations/zh-TW.js +5 -4
- package/typings/index.d.ts +177 -186
- package/typings/promiseBasedTypes.d.ts +3573 -5941
- package/typings/types.d.ts +4042 -6370
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/Nightmare.js +0 -1504
- package/lib/helper/Protractor.js +0 -1863
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1414
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -62
- package/lib/interfaces/bdd.js +0 -81
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -113
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/commentStep.js +0 -136
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/retryTo.js +0 -127
- package/lib/plugin/selenoid.js +0 -384
- package/lib/plugin/standardActingHelpers.js +0 -3
- package/lib/plugin/tryTo.js +0 -115
- package/lib/plugin/wdio.js +0 -249
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
- package/lib/within.js +0 -70
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
if (!window.codeceptjs) {
|
|
2
|
-
/**
|
|
3
|
-
* @alias CodeceptJS.browserCodecept
|
|
4
|
-
* @namespace
|
|
5
|
-
*/
|
|
6
|
-
const codeceptjs = {};
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* all found elements are stored here for reuse
|
|
10
|
-
* @inner
|
|
11
|
-
* @type {Node[]}
|
|
12
|
-
*/
|
|
13
|
-
codeceptjs.elements = [];
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* global context changer
|
|
17
|
-
* @inner
|
|
18
|
-
* @type {?Node}
|
|
19
|
-
*/
|
|
20
|
-
codeceptjs.within = null;
|
|
21
|
-
|
|
22
|
-
// save
|
|
23
|
-
const storeElement = function (el) {
|
|
24
|
-
if (!el) return;
|
|
25
|
-
return codeceptjs.elements.push(el) - 1; // return index
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const storeElements = function (els) {
|
|
29
|
-
return els.map(el => storeElement(el));
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* finders
|
|
34
|
-
* @param {number} id
|
|
35
|
-
* @return {Node}
|
|
36
|
-
*/
|
|
37
|
-
codeceptjs.fetchElement = function (id) {
|
|
38
|
-
if (!this.elements[id]) throw new Error(`Element (${id}) is not accessible`);
|
|
39
|
-
return this.elements[id];
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @param {string} by
|
|
44
|
-
* @param {CodeceptJS.ILocator} locator
|
|
45
|
-
* @param {*} [contextEl]
|
|
46
|
-
* @return {number[]}
|
|
47
|
-
*/
|
|
48
|
-
codeceptjs.findAndStoreElements = function (by, locator, contextEl) {
|
|
49
|
-
return storeElements(this.findElements(by, locator, contextEl));
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* @param {string} by
|
|
54
|
-
* @param {CodeceptJS.ILocator} locator
|
|
55
|
-
* @param {*} [contextEl]
|
|
56
|
-
* @return {number | undefined}
|
|
57
|
-
*/
|
|
58
|
-
codeceptjs.findAndStoreElement = function (by, locator, contextEl) {
|
|
59
|
-
return storeElement(this.findElement(by, locator, contextEl));
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @param {string} by
|
|
64
|
-
* @param {CodeceptJS.ILocator} locator
|
|
65
|
-
*/
|
|
66
|
-
codeceptjs.setWithin = function (by, locator) {
|
|
67
|
-
this.within = this.findElement(by, locator);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @param {string} by
|
|
72
|
-
* @param {CodeceptJS.ILocator} locator
|
|
73
|
-
* @param {*} [contextEl]
|
|
74
|
-
* @return {Node[]}
|
|
75
|
-
*/
|
|
76
|
-
codeceptjs.findElements = function (by, locator, contextEl) {
|
|
77
|
-
let context;
|
|
78
|
-
if (typeof contextEl !== 'number') {
|
|
79
|
-
context = this.within || document;
|
|
80
|
-
} else {
|
|
81
|
-
context = this.fetchElement(contextEl);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (by === 'name') {
|
|
85
|
-
by = 'css';
|
|
86
|
-
locator = `*[name="${locator}"]`;
|
|
87
|
-
}
|
|
88
|
-
if (by === 'id') {
|
|
89
|
-
by = 'css';
|
|
90
|
-
locator = `#${locator}`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (by === 'css') {
|
|
94
|
-
const found = context.querySelectorAll(locator);
|
|
95
|
-
const res = [];
|
|
96
|
-
for (let i = 0; i < found.length; i++) {
|
|
97
|
-
res.push(found[i]);
|
|
98
|
-
}
|
|
99
|
-
return res;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (by === 'xpath') {
|
|
103
|
-
const found = document.evaluate(locator, context, null, 5, null);
|
|
104
|
-
const res = [];
|
|
105
|
-
let current = null;
|
|
106
|
-
while (current = found.iterateNext()) {
|
|
107
|
-
res.push(current);
|
|
108
|
-
}
|
|
109
|
-
return res;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (by === 'frame') {
|
|
113
|
-
if (!Array.isArray(locator)) locator = [locator];
|
|
114
|
-
return [locator.reduce((parent, child) => parent.querySelector(child).contentDocument, window.document).querySelector('body')];
|
|
115
|
-
}
|
|
116
|
-
return [];
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @param {string} by
|
|
121
|
-
* @param {CodeceptJS.ILocator} locator
|
|
122
|
-
* @param {*} [context]
|
|
123
|
-
* @return {?Node}
|
|
124
|
-
*/
|
|
125
|
-
codeceptjs.findElement = function (by, locator, context) {
|
|
126
|
-
return this.findElements(by, locator, context)[0] || null;
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// actions
|
|
130
|
-
/**
|
|
131
|
-
* @param {number} el
|
|
132
|
-
* @return {boolean}
|
|
133
|
-
*/
|
|
134
|
-
codeceptjs.clickEl = function (el) {
|
|
135
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
136
|
-
document.activeElement.blur();
|
|
137
|
-
}
|
|
138
|
-
const event = document.createEvent('MouseEvent');
|
|
139
|
-
event.initEvent('click', true, true);
|
|
140
|
-
return this.fetchElement(el).dispatchEvent(event);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
/** @param {number} el */
|
|
144
|
-
codeceptjs.doubleClickEl = function (el) {
|
|
145
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
146
|
-
document.activeElement.blur();
|
|
147
|
-
}
|
|
148
|
-
const event = document.createEvent('MouseEvent');
|
|
149
|
-
event.initEvent('dblclick', true, true);
|
|
150
|
-
this.fetchElement(el).dispatchEvent(event);
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* @param {number} el
|
|
155
|
-
* @param {number | undefined} x
|
|
156
|
-
* @param {number | undefined} y
|
|
157
|
-
*/
|
|
158
|
-
codeceptjs.hoverEl = function (el, x, y) {
|
|
159
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
160
|
-
document.activeElement.blur();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const event = new MouseEvent('mouseover', {
|
|
164
|
-
bubbles: true,
|
|
165
|
-
cancelable: true,
|
|
166
|
-
screenX: x,
|
|
167
|
-
screenY: y,
|
|
168
|
-
clientX: x,
|
|
169
|
-
clientY: y,
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
this.fetchElement(el).dispatchEvent(event);
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
/** @param {number} el */
|
|
176
|
-
codeceptjs.rightClickEl = function (el) {
|
|
177
|
-
const event = new MouseEvent('contextmenu', {
|
|
178
|
-
bubbles: true,
|
|
179
|
-
cancelable: true,
|
|
180
|
-
view: window,
|
|
181
|
-
buttons: 2,
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
this.fetchElement(el).dispatchEvent(event);
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* @param {number} el
|
|
189
|
-
* @return {boolean | undefined}
|
|
190
|
-
*/
|
|
191
|
-
codeceptjs.checkEl = function (el) {
|
|
192
|
-
const element = this.fetchElement(el);
|
|
193
|
-
const event = document.createEvent('HTMLEvents');
|
|
194
|
-
if (element.checked) return;
|
|
195
|
-
element.checked = true;
|
|
196
|
-
event.initEvent('change', true, true);
|
|
197
|
-
return element.dispatchEvent(event);
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* @param {number} el
|
|
202
|
-
* @return {boolean}
|
|
203
|
-
*/
|
|
204
|
-
codeceptjs.unCheckEl = function (el) {
|
|
205
|
-
const element = this.fetchElement(el);
|
|
206
|
-
const event = document.createEvent('HTMLEvents');
|
|
207
|
-
element.checked = false;
|
|
208
|
-
event.initEvent('change', true, true);
|
|
209
|
-
return element.dispatchEvent(event);
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
window.codeceptjs = codeceptjs;
|
|
213
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
async function findReact(matcher, locator) {
|
|
2
|
-
let _locator = `_react=${locator.react}`;
|
|
3
|
-
let props = '';
|
|
4
|
-
|
|
5
|
-
if (locator.props) {
|
|
6
|
-
props += propBuilder(locator.props);
|
|
7
|
-
_locator += props;
|
|
8
|
-
}
|
|
9
|
-
return matcher.locator(_locator).all();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async function findVue(matcher, locator) {
|
|
13
|
-
let _locator = `_vue=${locator.vue}`;
|
|
14
|
-
let props = '';
|
|
15
|
-
|
|
16
|
-
if (locator.props) {
|
|
17
|
-
props += propBuilder(locator.props);
|
|
18
|
-
_locator += props;
|
|
19
|
-
}
|
|
20
|
-
return matcher.locator(_locator).all();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function findByPlaywrightLocator(matcher, locator) {
|
|
24
|
-
if (locator && locator.toString().includes(process.env.testIdAttribute)) return matcher.getByTestId(locator.pw.value.split('=')[1]);
|
|
25
|
-
return matcher.locator(locator.pw).all();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function propBuilder(props) {
|
|
29
|
-
let _props = '';
|
|
30
|
-
|
|
31
|
-
for (const [key, value] of Object.entries(props)) {
|
|
32
|
-
if (typeof value === 'object') {
|
|
33
|
-
for (const [k, v] of Object.entries(value)) {
|
|
34
|
-
_props += `[${key}.${k} = "${v}"]`;
|
|
35
|
-
}
|
|
36
|
-
} else {
|
|
37
|
-
_props += `[${key} = "${value}"]`;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return _props;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
module.exports = { findReact, findVue, findByPlaywrightLocator };
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const testControllerHolder = {
|
|
2
|
-
|
|
3
|
-
testController: undefined,
|
|
4
|
-
captureResolver: undefined,
|
|
5
|
-
getResolver: undefined,
|
|
6
|
-
|
|
7
|
-
capture(t) {
|
|
8
|
-
testControllerHolder.testController = t;
|
|
9
|
-
|
|
10
|
-
if (testControllerHolder.getResolver) {
|
|
11
|
-
// @ts-ignore
|
|
12
|
-
testControllerHolder.getResolver(t);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return new Promise((resolve) => {
|
|
16
|
-
// @ts-ignore
|
|
17
|
-
testControllerHolder.captureResolver = resolve;
|
|
18
|
-
});
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
free() {
|
|
22
|
-
testControllerHolder.testController = undefined;
|
|
23
|
-
|
|
24
|
-
if (testControllerHolder.captureResolver) {
|
|
25
|
-
// @ts-ignore
|
|
26
|
-
testControllerHolder.captureResolver();
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
get() {
|
|
31
|
-
return new Promise((resolve) => {
|
|
32
|
-
if (testControllerHolder.testController) {
|
|
33
|
-
resolve(testControllerHolder.testController);
|
|
34
|
-
} else {
|
|
35
|
-
// @ts-ignore
|
|
36
|
-
testControllerHolder.getResolver = resolve;
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
module.exports = testControllerHolder;
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
const { ClientFunction } = require('testcafe');
|
|
2
|
-
|
|
3
|
-
const assert = require('assert');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const { getParamNames } = require('../../utils');
|
|
7
|
-
|
|
8
|
-
const createTestFile = () => {
|
|
9
|
-
assert(global.output_dir, 'global.output_dir must be set');
|
|
10
|
-
|
|
11
|
-
const testFile = path.join(global.output_dir, `${Date.now()}_test.js`);
|
|
12
|
-
const testControllerHolderDir = __dirname.replace(/\\/g, '/');
|
|
13
|
-
|
|
14
|
-
fs.writeFileSync(
|
|
15
|
-
testFile,
|
|
16
|
-
`import testControllerHolder from "${testControllerHolderDir}/testControllerHolder.js";\n\n
|
|
17
|
-
fixture("fixture")\n
|
|
18
|
-
test\n
|
|
19
|
-
("test", testControllerHolder.capture)`,
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
return testFile;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// TODO Better error mapping (actual, expected)
|
|
26
|
-
const mapError = (testcafeError) => {
|
|
27
|
-
// console.log('TODO map error better', JSON.stringify(testcafeError, null, 2));
|
|
28
|
-
if (testcafeError.errMsg) {
|
|
29
|
-
throw new Error(testcafeError.errMsg);
|
|
30
|
-
}
|
|
31
|
-
const errorInfo = `${testcafeError.callsite ? JSON.stringify(testcafeError.callsite) : ''} ${testcafeError.apiFnChain || JSON.stringify(testcafeError)}`;
|
|
32
|
-
throw new Error(`TestCafe Error: ${errorInfo}`);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
function createClientFunction(func, args) {
|
|
36
|
-
if (!args || !args.length) {
|
|
37
|
-
return ClientFunction(func);
|
|
38
|
-
}
|
|
39
|
-
const paramNames = getParamNames(func);
|
|
40
|
-
const dependencies = {};
|
|
41
|
-
paramNames.forEach((param, i) => dependencies[param] = args[i]);
|
|
42
|
-
|
|
43
|
-
return ClientFunction(getFuncBody(func), { dependencies });
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function getFuncBody(func) {
|
|
47
|
-
let fnStr = func.toString();
|
|
48
|
-
const arrowIndex = fnStr.indexOf('=>');
|
|
49
|
-
if (arrowIndex >= 0) {
|
|
50
|
-
fnStr = fnStr.slice(arrowIndex + 2);
|
|
51
|
-
|
|
52
|
-
// eslint-disable-next-line no-eval
|
|
53
|
-
return eval(`() => ${fnStr}`);
|
|
54
|
-
}
|
|
55
|
-
// TODO: support general functions
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
module.exports = {
|
|
59
|
-
createTestFile,
|
|
60
|
-
mapError,
|
|
61
|
-
createClientFunction,
|
|
62
|
-
};
|
package/lib/interfaces/bdd.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
const { CucumberExpression, ParameterTypeRegistry, ParameterType } = require('@cucumber/cucumber-expressions')
|
|
2
|
-
const Config = require('../config')
|
|
3
|
-
|
|
4
|
-
let steps = {}
|
|
5
|
-
|
|
6
|
-
const STACK_POSITION = 2
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @param {*} step
|
|
10
|
-
* @param {*} fn
|
|
11
|
-
*/
|
|
12
|
-
const addStep = (step, fn) => {
|
|
13
|
-
const avoidDuplicateSteps = Config.get('gherkin', {}).avoidDuplicateSteps || false
|
|
14
|
-
const stack = new Error().stack
|
|
15
|
-
if (avoidDuplicateSteps && steps[step]) {
|
|
16
|
-
throw new Error(`Step '${step}' is already defined`)
|
|
17
|
-
}
|
|
18
|
-
steps[step] = fn
|
|
19
|
-
fn.line = stack && stack.split('\n')[STACK_POSITION]
|
|
20
|
-
if (fn.line) {
|
|
21
|
-
fn.line = fn.line
|
|
22
|
-
.trim()
|
|
23
|
-
.replace(/^at (.*?)\(/, '(')
|
|
24
|
-
.replace(codecept_dir, '.')
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const parameterTypeRegistry = new ParameterTypeRegistry()
|
|
29
|
-
|
|
30
|
-
const matchStep = (step) => {
|
|
31
|
-
for (const stepName in steps) {
|
|
32
|
-
if (stepName.indexOf('/') === 0) {
|
|
33
|
-
const regExpArr = stepName.match(/^\/(.*?)\/([gimy]*)$/) || []
|
|
34
|
-
const res = step.match(new RegExp(regExpArr[1], regExpArr[2]))
|
|
35
|
-
if (res) {
|
|
36
|
-
const fn = steps[stepName]
|
|
37
|
-
fn.params = res.slice(1)
|
|
38
|
-
return fn
|
|
39
|
-
}
|
|
40
|
-
continue
|
|
41
|
-
}
|
|
42
|
-
const expression = new CucumberExpression(stepName, parameterTypeRegistry)
|
|
43
|
-
const res = expression.match(step)
|
|
44
|
-
if (res) {
|
|
45
|
-
const fn = steps[stepName]
|
|
46
|
-
fn.params = res.map((arg) => arg.getValue())
|
|
47
|
-
return fn
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
throw new Error(`No steps matching "${step.toString()}"`)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const clearSteps = () => {
|
|
54
|
-
steps = {}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const getSteps = () => {
|
|
58
|
-
return steps
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const defineParameterType = (options) => {
|
|
62
|
-
const parameterType = buildParameterType(options)
|
|
63
|
-
parameterTypeRegistry.defineParameterType(parameterType)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const buildParameterType = ({ name, regexp, transformer, useForSnippets, preferForRegexpMatch }) => {
|
|
67
|
-
if (typeof useForSnippets !== 'boolean') useForSnippets = true
|
|
68
|
-
if (typeof preferForRegexpMatch !== 'boolean') preferForRegexpMatch = false
|
|
69
|
-
return new ParameterType(name, regexp, null, transformer, useForSnippets, preferForRegexpMatch)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
module.exports = {
|
|
73
|
-
Given: addStep,
|
|
74
|
-
When: addStep,
|
|
75
|
-
Then: addStep,
|
|
76
|
-
And: addStep,
|
|
77
|
-
matchStep,
|
|
78
|
-
getSteps,
|
|
79
|
-
clearSteps,
|
|
80
|
-
defineParameterType,
|
|
81
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const event = require('../event')
|
|
2
|
-
const recorder = require('../recorder')
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Create and clean up empty artifacts
|
|
6
|
-
*/
|
|
7
|
-
module.exports = function () {
|
|
8
|
-
event.dispatcher.on(event.test.before, (test) => {
|
|
9
|
-
test.artifacts = {}
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
event.dispatcher.on(event.test.after, (test) => {
|
|
13
|
-
recorder.add('clean up empty artifacts', () => {
|
|
14
|
-
for (const key in test.artifacts || {}) {
|
|
15
|
-
if (!test.artifacts[key]) delete test.artifacts[key]
|
|
16
|
-
}
|
|
17
|
-
})
|
|
18
|
-
})
|
|
19
|
-
}
|
package/lib/listener/timeout.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
const event = require('../event')
|
|
2
|
-
const output = require('../output')
|
|
3
|
-
const recorder = require('../recorder')
|
|
4
|
-
const Config = require('../config')
|
|
5
|
-
const { timeouts } = require('../store')
|
|
6
|
-
const TIMEOUT_ORDER = require('../step').TIMEOUT_ORDER
|
|
7
|
-
|
|
8
|
-
module.exports = function () {
|
|
9
|
-
let timeout
|
|
10
|
-
let suiteTimeout = []
|
|
11
|
-
let currentTest
|
|
12
|
-
let currentTimeout
|
|
13
|
-
|
|
14
|
-
if (!timeouts) {
|
|
15
|
-
console.log('Timeouts were disabled')
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
event.dispatcher.on(event.suite.before, (suite) => {
|
|
20
|
-
suiteTimeout = []
|
|
21
|
-
let timeoutConfig = Config.get('timeout')
|
|
22
|
-
|
|
23
|
-
if (timeoutConfig) {
|
|
24
|
-
if (!Number.isNaN(+timeoutConfig)) {
|
|
25
|
-
checkForSeconds(timeoutConfig)
|
|
26
|
-
suiteTimeout.push(timeoutConfig)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (!Array.isArray(timeoutConfig)) {
|
|
30
|
-
timeoutConfig = [timeoutConfig]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
for (const config of timeoutConfig.filter((c) => !!c.Feature)) {
|
|
34
|
-
if (config.grep) {
|
|
35
|
-
if (!suite.title.includes(config.grep)) continue
|
|
36
|
-
}
|
|
37
|
-
suiteTimeout.push(config.Feature)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (suite.totalTimeout) suiteTimeout.push(suite.totalTimeout)
|
|
42
|
-
output.log(`Timeouts: ${suiteTimeout}`)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
event.dispatcher.on(event.test.before, (test) => {
|
|
46
|
-
currentTest = test
|
|
47
|
-
let testTimeout = null
|
|
48
|
-
|
|
49
|
-
let timeoutConfig = Config.get('timeout')
|
|
50
|
-
|
|
51
|
-
if (typeof timeoutConfig === 'object' || Array.isArray(timeoutConfig)) {
|
|
52
|
-
if (!Array.isArray(timeoutConfig)) {
|
|
53
|
-
timeoutConfig = [timeoutConfig]
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
for (const config of timeoutConfig.filter((c) => !!c.Scenario)) {
|
|
57
|
-
console.log('Test Timeout', config, test.title.includes(config.grep))
|
|
58
|
-
if (config.grep) {
|
|
59
|
-
if (!test.title.includes(config.grep)) continue
|
|
60
|
-
}
|
|
61
|
-
testTimeout = config.Scenario
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
timeout = test.totalTimeout || testTimeout || suiteTimeout[suiteTimeout.length - 1]
|
|
66
|
-
if (!timeout) return
|
|
67
|
-
currentTimeout = timeout
|
|
68
|
-
output.debug(`Test Timeout: ${timeout}s`)
|
|
69
|
-
timeout *= 1000
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
event.dispatcher.on(event.test.passed, (test) => {
|
|
73
|
-
currentTest = null
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
event.dispatcher.on(event.test.failed, (test) => {
|
|
77
|
-
currentTest = null
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
event.dispatcher.on(event.step.before, (step) => {
|
|
81
|
-
if (typeof timeout !== 'number') return
|
|
82
|
-
|
|
83
|
-
if (timeout < 0) {
|
|
84
|
-
step.setTimeout(0.01, TIMEOUT_ORDER.testOrSuite)
|
|
85
|
-
} else {
|
|
86
|
-
step.setTimeout(timeout, TIMEOUT_ORDER.testOrSuite)
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
event.dispatcher.on(event.step.finished, (step) => {
|
|
91
|
-
if (typeof timeout === 'number' && !Number.isNaN(timeout)) timeout -= step.duration
|
|
92
|
-
|
|
93
|
-
if (typeof timeout === 'number' && timeout <= 0 && recorder.isRunning()) {
|
|
94
|
-
if (currentTest && currentTest.callback) {
|
|
95
|
-
recorder.reset()
|
|
96
|
-
// replace mocha timeout with custom timeout
|
|
97
|
-
currentTest.timeout(0)
|
|
98
|
-
currentTest.callback(new Error(`Timeout ${currentTimeout}s exceeded (with Before hook)`))
|
|
99
|
-
currentTest.timedOut = true
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function checkForSeconds(timeout) {
|
|
106
|
-
if (timeout >= 1000) {
|
|
107
|
-
console.log(`Warning: Timeout was set to ${timeout}secs.\nGlobal timeout should be specified in seconds.`)
|
|
108
|
-
}
|
|
109
|
-
}
|
package/lib/mochaFactory.js
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
const Mocha = require('mocha');
|
|
2
|
-
const fsPath = require('path');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const reporter = require('./cli');
|
|
5
|
-
const gherkinParser = require('./interfaces/gherkin');
|
|
6
|
-
const output = require('./output');
|
|
7
|
-
const { genTestId } = require('./utils');
|
|
8
|
-
const ConnectionRefused = require('./helper/errors/ConnectionRefused');
|
|
9
|
-
|
|
10
|
-
const scenarioUi = fsPath.join(__dirname, './ui.js');
|
|
11
|
-
|
|
12
|
-
let mocha;
|
|
13
|
-
|
|
14
|
-
class MochaFactory {
|
|
15
|
-
static create(config, opts) {
|
|
16
|
-
mocha = new Mocha(Object.assign(config, opts));
|
|
17
|
-
output.process(opts.child);
|
|
18
|
-
mocha.ui(scenarioUi);
|
|
19
|
-
|
|
20
|
-
Mocha.Runner.prototype.uncaught = function (err) {
|
|
21
|
-
if (err) {
|
|
22
|
-
if (err.toString().indexOf('ECONNREFUSED') >= 0) {
|
|
23
|
-
err = new ConnectionRefused(err);
|
|
24
|
-
}
|
|
25
|
-
output.error(err);
|
|
26
|
-
output.print(err.stack);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
output.error('Uncaught undefined exception');
|
|
30
|
-
process.exit(1);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
mocha.loadFiles = (fn) => {
|
|
34
|
-
// load features
|
|
35
|
-
if (mocha.suite.suites.length === 0) {
|
|
36
|
-
mocha.files
|
|
37
|
-
.filter(file => file.match(/\.feature$/))
|
|
38
|
-
.forEach(file => mocha.suite.addSuite(gherkinParser(fs.readFileSync(file, 'utf8'), file)));
|
|
39
|
-
|
|
40
|
-
// remove feature files
|
|
41
|
-
mocha.files = mocha.files.filter(file => !file.match(/\.feature$/));
|
|
42
|
-
|
|
43
|
-
Mocha.prototype.loadFiles.call(mocha, fn);
|
|
44
|
-
|
|
45
|
-
// add ids for each test and check uniqueness
|
|
46
|
-
const dupes = [];
|
|
47
|
-
let missingFeatureInFile = [];
|
|
48
|
-
const seenTests = [];
|
|
49
|
-
mocha.suite.eachTest(test => {
|
|
50
|
-
test.uid = genTestId(test);
|
|
51
|
-
|
|
52
|
-
const name = test.fullTitle();
|
|
53
|
-
if (seenTests.includes(test.uid)) {
|
|
54
|
-
dupes.push(name);
|
|
55
|
-
}
|
|
56
|
-
seenTests.push(test.uid);
|
|
57
|
-
|
|
58
|
-
if (name.slice(0, name.indexOf(':')) === '') {
|
|
59
|
-
missingFeatureInFile.push(test.file);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
if (dupes.length) {
|
|
63
|
-
// ideally this should be no-op and throw (breaking change)...
|
|
64
|
-
output.error(`Duplicate test names detected - Feature + Scenario name should be unique:\n${dupes.join('\n')}`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (missingFeatureInFile.length) {
|
|
68
|
-
missingFeatureInFile = [...new Set(missingFeatureInFile)];
|
|
69
|
-
output.error(`Missing Feature section in:\n${missingFeatureInFile.join('\n')}`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const presetReporter = opts.reporter || config.reporter;
|
|
75
|
-
// use standard reporter
|
|
76
|
-
if (!presetReporter) {
|
|
77
|
-
mocha.reporter(reporter, opts);
|
|
78
|
-
return mocha;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// load custom reporter with options
|
|
82
|
-
const reporterOptions = Object.assign(config.reporterOptions || {});
|
|
83
|
-
|
|
84
|
-
if (opts.reporterOptions !== undefined) {
|
|
85
|
-
opts.reporterOptions.split(',').forEach((opt) => {
|
|
86
|
-
const L = opt.split('=');
|
|
87
|
-
if (L.length > 2 || L.length === 0) {
|
|
88
|
-
throw new Error(`invalid reporter option '${opt}'`);
|
|
89
|
-
} else if (L.length === 2) {
|
|
90
|
-
reporterOptions[L[0]] = L[1];
|
|
91
|
-
} else {
|
|
92
|
-
reporterOptions[L[0]] = true;
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const attributes = Object.getOwnPropertyDescriptor(reporterOptions, 'codeceptjs-cli-reporter');
|
|
98
|
-
if (reporterOptions['codeceptjs-cli-reporter'] && attributes) {
|
|
99
|
-
Object.defineProperty(
|
|
100
|
-
reporterOptions,
|
|
101
|
-
'codeceptjs/lib/cli',
|
|
102
|
-
attributes,
|
|
103
|
-
);
|
|
104
|
-
delete reporterOptions['codeceptjs-cli-reporter'];
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// custom reporters
|
|
108
|
-
mocha.reporter(presetReporter, reporterOptions);
|
|
109
|
-
return mocha;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
module.exports = MochaFactory;
|
package/lib/plugin/allure.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
module.exports = () => {
|
|
2
|
-
console.log('Allure plugin was moved to @codeceptjs/allure-legacy. Please install it and update your config')
|
|
3
|
-
console.log()
|
|
4
|
-
console.log('npm install @codeceptjs/allure-legacy --save-dev')
|
|
5
|
-
console.log()
|
|
6
|
-
console.log('Then update your config to use it:')
|
|
7
|
-
console.log()
|
|
8
|
-
console.log('plugins: {')
|
|
9
|
-
console.log(' allure: {')
|
|
10
|
-
console.log(' enabled: true,')
|
|
11
|
-
console.log(" require: '@codeceptjs/allure-legacy',")
|
|
12
|
-
console.log(' }')
|
|
13
|
-
console.log('}')
|
|
14
|
-
console.log()
|
|
15
|
-
}
|