codeceptjs 4.0.0-beta.2 → 4.0.0-beta.21
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 +133 -120
- package/bin/codecept.js +107 -96
- package/bin/test-server.js +64 -0
- package/docs/webapi/clearCookie.mustache +1 -1
- package/docs/webapi/click.mustache +5 -1
- package/lib/actor.js +73 -103
- package/lib/ai.js +159 -188
- package/lib/assert/empty.js +22 -24
- package/lib/assert/equal.js +30 -37
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +43 -48
- package/lib/assert/throws.js +11 -11
- package/lib/assert/truth.js +22 -22
- package/lib/assert.js +20 -18
- package/lib/codecept.js +262 -162
- package/lib/colorUtils.js +50 -52
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +56 -51
- package/lib/command/definitions.js +96 -109
- package/lib/command/dryRun.js +77 -79
- package/lib/command/generate.js +234 -194
- package/lib/command/gherkin/init.js +42 -33
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +20 -17
- package/lib/command/info.js +74 -38
- package/lib/command/init.js +301 -290
- package/lib/command/interactive.js +41 -32
- package/lib/command/list.js +28 -27
- package/lib/command/run-multiple/chunk.js +51 -48
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +5 -1
- package/lib/command/run-multiple.js +97 -97
- package/lib/command/run-rerun.js +19 -25
- package/lib/command/run-workers.js +68 -92
- package/lib/command/run.js +39 -27
- package/lib/command/utils.js +80 -64
- package/lib/command/workers/runTests.js +388 -226
- package/lib/config.js +109 -50
- package/lib/container.js +765 -261
- package/lib/data/context.js +60 -61
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +32 -32
- package/lib/data/table.js +22 -22
- package/lib/effects.js +307 -0
- package/lib/element/WebElement.js +327 -0
- package/lib/els.js +160 -0
- package/lib/event.js +173 -163
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -85
- package/lib/helper/AI.js +131 -41
- package/lib/helper/ApiDataFactory.js +107 -75
- package/lib/helper/Appium.js +542 -404
- package/lib/helper/FileSystem.js +100 -79
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +52 -52
- package/lib/helper/JSONResponse.js +126 -88
- package/lib/helper/Mochawesome.js +54 -29
- package/lib/helper/Playwright.js +2547 -1316
- package/lib/helper/Puppeteer.js +1578 -1181
- package/lib/helper/REST.js +209 -68
- package/lib/helper/WebDriver.js +1482 -1342
- 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/PlaywrightReactVueLocator.js +17 -8
- package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/extras/React.js +27 -28
- package/lib/helper/network/actions.js +36 -42
- package/lib/helper/network/utils.js +78 -84
- package/lib/helper/scripts/blurElement.js +5 -5
- package/lib/helper/scripts/focusElement.js +5 -5
- package/lib/helper/scripts/highlightElement.js +8 -8
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -3
- package/lib/history.js +23 -19
- package/lib/hooks.js +8 -8
- package/lib/html.js +94 -104
- package/lib/index.js +38 -27
- package/lib/listener/config.js +30 -23
- package/lib/listener/emptyRun.js +54 -0
- package/lib/listener/enhancedGlobalRetry.js +110 -0
- package/lib/listener/exit.js +16 -18
- package/lib/listener/globalRetry.js +70 -0
- package/lib/listener/globalTimeout.js +181 -0
- package/lib/listener/helpers.js +76 -51
- package/lib/listener/mocha.js +10 -11
- package/lib/listener/result.js +11 -0
- package/lib/listener/retryEnhancer.js +85 -0
- package/lib/listener/steps.js +71 -59
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +214 -197
- package/lib/mocha/asyncWrapper.js +274 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +163 -0
- package/lib/mocha/featureConfig.js +89 -0
- package/lib/mocha/gherkin.js +231 -0
- 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 +58 -34
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +184 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +242 -0
- package/lib/output.js +141 -71
- package/lib/parser.js +54 -44
- package/lib/pause.js +173 -145
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +178 -79
- package/lib/plugin/autoDelay.js +36 -40
- package/lib/plugin/coverage.js +131 -78
- package/lib/plugin/customLocator.js +22 -21
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/enhancedRetryFailedStep.js +99 -0
- package/lib/plugin/heal.js +101 -110
- package/lib/plugin/htmlReporter.js +3648 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +12 -11
- package/lib/plugin/retryFailedStep.js +82 -47
- package/lib/plugin/screenshotOnFail.js +111 -92
- package/lib/plugin/stepByStepReport.js +159 -101
- package/lib/plugin/stepTimeout.js +20 -25
- package/lib/plugin/subtitles.js +38 -38
- package/lib/recorder.js +193 -130
- package/lib/rerun.js +94 -49
- package/lib/result.js +238 -0
- package/lib/retryCoordinator.js +207 -0
- package/lib/secret.js +20 -18
- 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 -329
- package/lib/steps.js +54 -0
- package/lib/store.js +38 -7
- package/lib/template/heal.js +3 -12
- package/lib/template/prompts/generatePageObject.js +31 -0
- package/lib/template/prompts/healStep.js +13 -0
- package/lib/template/prompts/writeStep.js +9 -0
- package/lib/test-server.js +334 -0
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +34 -21
- package/lib/utils/loaderCheck.js +124 -0
- package/lib/utils/mask_data.js +47 -0
- package/lib/utils/typescript.js +237 -0
- package/lib/utils.js +411 -228
- package/lib/workerStorage.js +37 -34
- package/lib/workers.js +532 -296
- package/package.json +124 -95
- package/translations/de-DE.js +5 -3
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +22 -12
- package/translations/it-IT.js +4 -3
- package/translations/ja-JP.js +4 -3
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +4 -3
- package/translations/pt-BR.js +4 -3
- package/translations/ru-RU.js +4 -3
- package/translations/utils.js +10 -0
- package/translations/zh-CN.js +4 -3
- package/translations/zh-TW.js +4 -3
- package/typings/index.d.ts +546 -185
- package/typings/promiseBasedTypes.d.ts +150 -875
- package/typings/types.d.ts +547 -992
- package/lib/cli.js +0 -249
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
- package/lib/helper/ExpectHelper.js +0 -399
- package/lib/helper/MockServer.js +0 -223
- package/lib/helper/Nightmare.js +0 -1411
- package/lib/helper/Protractor.js +0 -1835
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1410
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -63
- package/lib/interfaces/bdd.js +0 -98
- package/lib/interfaces/featureConfig.js +0 -69
- package/lib/interfaces/gherkin.js +0 -195
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/retry.js +0 -68
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -110
- 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 -121
- package/lib/plugin/selenoid.js +0 -371
- package/lib/plugin/standardActingHelpers.js +0 -9
- package/lib/plugin/tryTo.js +0 -105
- package/lib/plugin/wdio.js +0 -246
- package/lib/scenario.js +0 -222
- package/lib/ui.js +0 -238
- 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,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
|
-
export default testControllerHolder;
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { ClientFunction } from 'testcafe';
|
|
2
|
-
import assert from 'assert';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { getParamNames } from '../../utils.js';
|
|
6
|
-
|
|
7
|
-
const __dirname = path.resolve();
|
|
8
|
-
|
|
9
|
-
const createTestFile = () => {
|
|
10
|
-
assert(global.output_dir, 'global.output_dir must be set');
|
|
11
|
-
|
|
12
|
-
const testFile = path.join(global.output_dir, `${Date.now()}_test.js`);
|
|
13
|
-
const testControllerHolderDir = __dirname.replace(/\\/g, '/');
|
|
14
|
-
|
|
15
|
-
fs.writeFileSync(
|
|
16
|
-
testFile,
|
|
17
|
-
`import testControllerHolder from "${testControllerHolderDir}/testControllerHolder.js";\n\n
|
|
18
|
-
fixture("fixture")\n
|
|
19
|
-
test\n
|
|
20
|
-
("test", testControllerHolder.capture)`,
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
return testFile;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// TODO Better error mapping (actual, expected)
|
|
27
|
-
const mapError = (testcafeError) => {
|
|
28
|
-
// console.log('TODO map error better', JSON.stringify(testcafeError, null, 2));
|
|
29
|
-
if (testcafeError.errMsg) {
|
|
30
|
-
throw new Error(testcafeError.errMsg);
|
|
31
|
-
}
|
|
32
|
-
const errorInfo = `${testcafeError.callsite ? JSON.stringify(testcafeError.callsite) : ''} ${testcafeError.apiFnChain || JSON.stringify(testcafeError)}`;
|
|
33
|
-
throw new Error(`TestCafe Error: ${errorInfo}`);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
function createClientFunction(func, args) {
|
|
37
|
-
if (!args || !args.length) {
|
|
38
|
-
return ClientFunction(func);
|
|
39
|
-
}
|
|
40
|
-
const paramNames = getParamNames(func);
|
|
41
|
-
const dependencies = {};
|
|
42
|
-
paramNames.forEach((param, i) => dependencies[param] = args[i]);
|
|
43
|
-
|
|
44
|
-
return ClientFunction(getFuncBody(func), { dependencies });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getFuncBody(func) {
|
|
48
|
-
let fnStr = func.toString();
|
|
49
|
-
const arrowIndex = fnStr.indexOf('=>');
|
|
50
|
-
if (arrowIndex >= 0) {
|
|
51
|
-
fnStr = fnStr.slice(arrowIndex + 2);
|
|
52
|
-
// eslint-disable-next-line no-new-func
|
|
53
|
-
// eslint-disable-next-line no-eval
|
|
54
|
-
return eval(`() => ${fnStr}`);
|
|
55
|
-
}
|
|
56
|
-
// TODO: support general functions
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export default {
|
|
60
|
-
createTestFile,
|
|
61
|
-
mapError,
|
|
62
|
-
createClientFunction,
|
|
63
|
-
};
|
package/lib/interfaces/bdd.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { CucumberExpression, ParameterTypeRegistry, ParameterType } from '@cucumber/cucumber-expressions';
|
|
2
|
-
import Config from '../config.js';
|
|
3
|
-
|
|
4
|
-
let steps = {};
|
|
5
|
-
const codecept_dir = '';
|
|
6
|
-
|
|
7
|
-
const STACK_POSITION = 2;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {*} step
|
|
11
|
-
* @param {*} fn
|
|
12
|
-
*/
|
|
13
|
-
const addStep = (step, fn) => {
|
|
14
|
-
const avoidDuplicateSteps = Config.get('gherkin', {}).avoidDuplicateSteps || false;
|
|
15
|
-
const stack = (new Error()).stack;
|
|
16
|
-
if (avoidDuplicateSteps && steps[step]) {
|
|
17
|
-
throw new Error(`Step '${step}' is already defined`);
|
|
18
|
-
}
|
|
19
|
-
steps[step] = fn;
|
|
20
|
-
fn.line = stack && stack.split('\n')[STACK_POSITION];
|
|
21
|
-
if (fn.line) {
|
|
22
|
-
fn.line = fn.line
|
|
23
|
-
.trim()
|
|
24
|
-
.replace(/^at (.*?)\(/, '(')
|
|
25
|
-
.replace(codecept_dir, '.');
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const parameterTypeRegistry = new ParameterTypeRegistry();
|
|
30
|
-
|
|
31
|
-
export const matchStep = (step) => {
|
|
32
|
-
for (const stepName in steps) {
|
|
33
|
-
if (stepName.indexOf('/') === 0) {
|
|
34
|
-
const regExpArr = stepName.match(/^\/(.*?)\/([gimy]*)$/) || [];
|
|
35
|
-
const res = step.match(new RegExp(regExpArr[1], regExpArr[2]));
|
|
36
|
-
if (res) {
|
|
37
|
-
const fn = steps[stepName];
|
|
38
|
-
fn.params = res.slice(1);
|
|
39
|
-
return fn;
|
|
40
|
-
}
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
const expression = new CucumberExpression(stepName, parameterTypeRegistry);
|
|
44
|
-
// console.log(expression)
|
|
45
|
-
// console.log((step))
|
|
46
|
-
const res = expression.match(step);
|
|
47
|
-
if (res) {
|
|
48
|
-
const fn = steps[stepName];
|
|
49
|
-
fn.params = res.map(arg => arg.getValue());
|
|
50
|
-
return fn;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
throw new Error(`No steps matching "${step.toString()}"`);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const clearSteps = () => {
|
|
57
|
-
steps = {};
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const getSteps = () => {
|
|
61
|
-
return steps;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export const defineParameterType = (options) => {
|
|
65
|
-
const parameterType = buildParameterType(options);
|
|
66
|
-
parameterTypeRegistry.defineParameterType(parameterType);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export const buildParameterType = ({
|
|
70
|
-
name,
|
|
71
|
-
regexp,
|
|
72
|
-
transformer,
|
|
73
|
-
useForSnippets,
|
|
74
|
-
preferForRegexpMatch,
|
|
75
|
-
}) => {
|
|
76
|
-
if (typeof useForSnippets !== 'boolean') useForSnippets = true;
|
|
77
|
-
if (typeof preferForRegexpMatch !== 'boolean') preferForRegexpMatch = false;
|
|
78
|
-
return new ParameterType(
|
|
79
|
-
name,
|
|
80
|
-
regexp,
|
|
81
|
-
null,
|
|
82
|
-
transformer,
|
|
83
|
-
useForSnippets,
|
|
84
|
-
preferForRegexpMatch,
|
|
85
|
-
);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export { addStep as Given };
|
|
89
|
-
export { addStep as When };
|
|
90
|
-
export { addStep as Then };
|
|
91
|
-
export { addStep as And };
|
|
92
|
-
|
|
93
|
-
export default {
|
|
94
|
-
matchStep,
|
|
95
|
-
getSteps,
|
|
96
|
-
clearSteps,
|
|
97
|
-
defineParameterType,
|
|
98
|
-
};
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/** @class */
|
|
2
|
-
class FeatureConfig {
|
|
3
|
-
constructor(suite) {
|
|
4
|
-
this.suite = suite;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Retry this suite for x times
|
|
9
|
-
*
|
|
10
|
-
* @param {number} retries
|
|
11
|
-
* @returns {this}
|
|
12
|
-
*/
|
|
13
|
-
retry(retries) {
|
|
14
|
-
this.suite.retries(retries);
|
|
15
|
-
return this;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Set timeout for this suite
|
|
20
|
-
* @param {number} timeout
|
|
21
|
-
* @returns {this}
|
|
22
|
-
* @deprecated
|
|
23
|
-
*/
|
|
24
|
-
timeout(timeout) {
|
|
25
|
-
console.log(`Feature('${this.suite.title}').timeout(${timeout}) is deprecated!`);
|
|
26
|
-
console.log(`Please use Feature('${this.suite.title}', { timeout: ${timeout / 1000} }) instead`);
|
|
27
|
-
console.log('Timeout should be set in seconds');
|
|
28
|
-
this.suite.timeout(timeout);
|
|
29
|
-
return this;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Configures a helper.
|
|
34
|
-
* Helper name can be omitted and values will be applied to first helper.
|
|
35
|
-
* @param {string | Object<string, *>} helper
|
|
36
|
-
* @param {Object<string, *>} [obj]
|
|
37
|
-
* @returns {this}
|
|
38
|
-
*/
|
|
39
|
-
config(helper, obj) {
|
|
40
|
-
if (!obj) {
|
|
41
|
-
obj = helper;
|
|
42
|
-
helper = 0;
|
|
43
|
-
}
|
|
44
|
-
if (typeof obj === 'function') {
|
|
45
|
-
obj = obj(this.suite);
|
|
46
|
-
}
|
|
47
|
-
if (!this.suite.config) {
|
|
48
|
-
this.suite.config = {};
|
|
49
|
-
}
|
|
50
|
-
this.suite.config[helper] = obj;
|
|
51
|
-
return this;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Append a tag name to scenario title
|
|
56
|
-
* @param {string} tagName
|
|
57
|
-
* @returns {this}
|
|
58
|
-
*/
|
|
59
|
-
tag(tagName) {
|
|
60
|
-
if (tagName[0] !== '@') {
|
|
61
|
-
tagName = `@${tagName}`;
|
|
62
|
-
}
|
|
63
|
-
this.suite.tags.push(tagName);
|
|
64
|
-
this.suite.title = `${this.suite.title.trim()} ${tagName}`;
|
|
65
|
-
return this;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export default FeatureConfig;
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import * as Gherkin from '@cucumber/gherkin';
|
|
2
|
-
import * as Messages from '@cucumber/messages';
|
|
3
|
-
import { Context, Suite, Test } from 'mocha';
|
|
4
|
-
|
|
5
|
-
import debug from 'debug';
|
|
6
|
-
|
|
7
|
-
import { matchStep } from './bdd.js';
|
|
8
|
-
import * as event from '../event.js';
|
|
9
|
-
import * as scenario from '../scenario.js';
|
|
10
|
-
import * as Step from '../step.js';
|
|
11
|
-
import DataTableArgument from '../data/dataTableArgument.js';
|
|
12
|
-
import transform from '../transform.js';
|
|
13
|
-
|
|
14
|
-
import * as translations0 from '../../translations/index.js';
|
|
15
|
-
|
|
16
|
-
debug('codeceptjs:bdd');
|
|
17
|
-
|
|
18
|
-
const uuidFn = Messages.IdGenerator.uuid();
|
|
19
|
-
const builder = new Gherkin.AstBuilder(uuidFn);
|
|
20
|
-
const matcher = new Gherkin.GherkinClassicTokenMatcher();
|
|
21
|
-
const parser = new Gherkin.Parser(builder, matcher);
|
|
22
|
-
parser.stopAtFirstError = false;
|
|
23
|
-
|
|
24
|
-
export default (text, file) => {
|
|
25
|
-
const ast = parser.parse(text);
|
|
26
|
-
let currentLanguage;
|
|
27
|
-
|
|
28
|
-
if (ast.feature) {
|
|
29
|
-
currentLanguage = getTranslation(ast.feature.language);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (!ast.feature) {
|
|
33
|
-
throw new Error(`No 'Features' available in Gherkin '${file}' provided!`);
|
|
34
|
-
}
|
|
35
|
-
const suite = new Suite(ast.feature.name, new Context());
|
|
36
|
-
const tags = ast.feature.tags.map(t => t.name);
|
|
37
|
-
suite.title = `${suite.title} ${tags.join(' ')}`.trim();
|
|
38
|
-
suite.tags = tags || [];
|
|
39
|
-
suite.comment = ast.feature.description;
|
|
40
|
-
suite.feature = ast.feature;
|
|
41
|
-
suite.file = file;
|
|
42
|
-
suite.timeout(0);
|
|
43
|
-
|
|
44
|
-
suite.beforeEach('codeceptjs.before', () => scenario.setup(suite));
|
|
45
|
-
suite.afterEach('codeceptjs.after', () => scenario.teardown(suite));
|
|
46
|
-
suite.beforeAll('codeceptjs.beforeSuite', () => scenario.suiteSetup(suite));
|
|
47
|
-
suite.afterAll('codeceptjs.afterSuite', () => scenario.suiteTeardown(suite));
|
|
48
|
-
|
|
49
|
-
const runSteps = async (steps) => {
|
|
50
|
-
for (const step of steps) {
|
|
51
|
-
const metaStep = new Step.MetaStep(null, step.text);
|
|
52
|
-
metaStep.actor = step.keyword.trim();
|
|
53
|
-
let helperStep;
|
|
54
|
-
const setMetaStep = (step) => {
|
|
55
|
-
helperStep = step;
|
|
56
|
-
if (step.metaStep) {
|
|
57
|
-
if (step.metaStep === metaStep) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
setMetaStep(step.metaStep);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
step.metaStep = metaStep;
|
|
64
|
-
};
|
|
65
|
-
const fn = matchStep(step.text);
|
|
66
|
-
|
|
67
|
-
if (step.dataTable) {
|
|
68
|
-
fn.params.push({
|
|
69
|
-
...step.dataTable,
|
|
70
|
-
parse: () => new DataTableArgument(step.dataTable),
|
|
71
|
-
});
|
|
72
|
-
metaStep.comment = `\n${transformTable(step.dataTable)}`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (step.docString) {
|
|
76
|
-
fn.params.push(step.docString);
|
|
77
|
-
metaStep.comment = `\n"""\n${step.docString.content}\n"""`;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
step.startTime = Date.now();
|
|
81
|
-
step.match = fn.line;
|
|
82
|
-
event.emit(event.bddStep.before, step);
|
|
83
|
-
event.emit(event.bddStep.started, metaStep);
|
|
84
|
-
event.dispatcher.prependListener(event.step.before, setMetaStep);
|
|
85
|
-
try {
|
|
86
|
-
debug(`Step '${step.text}' started...`);
|
|
87
|
-
await fn(...fn.params);
|
|
88
|
-
debug('Step passed');
|
|
89
|
-
step.status = 'passed';
|
|
90
|
-
} catch (err) {
|
|
91
|
-
debug(`Step failed: ${err?.message}`);
|
|
92
|
-
step.status = 'failed';
|
|
93
|
-
step.err = err;
|
|
94
|
-
throw err;
|
|
95
|
-
} finally {
|
|
96
|
-
step.endTime = Date.now();
|
|
97
|
-
event.dispatcher.removeListener(event.step.before, setMetaStep);
|
|
98
|
-
}
|
|
99
|
-
event.emit(event.bddStep.finished, metaStep);
|
|
100
|
-
event.emit(event.bddStep.after, step);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
for (const child of ast.feature.children) {
|
|
105
|
-
if (child.background) {
|
|
106
|
-
suite.beforeEach('Before', scenario.injected(async () => runSteps(child.background.steps), suite, 'before'));
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (child.scenario && (currentLanguage ? child.scenario.keyword === currentLanguage.contexts.ScenarioOutline : child.scenario.keyword === 'Scenario Outline')) {
|
|
110
|
-
for (const examples of child.scenario.examples) {
|
|
111
|
-
const fields = examples.tableHeader.cells.map(c => c.value);
|
|
112
|
-
for (const example of examples.tableBody) {
|
|
113
|
-
let exampleSteps = [...child.scenario.steps];
|
|
114
|
-
const current = {};
|
|
115
|
-
for (const index in example.cells) {
|
|
116
|
-
const placeholder = fields[index];
|
|
117
|
-
const value = transform('gherkin.examples', example.cells[index].value);
|
|
118
|
-
example.cells[index].value = value;
|
|
119
|
-
current[placeholder] = value;
|
|
120
|
-
exampleSteps = exampleSteps.map((step) => {
|
|
121
|
-
step = { ...step };
|
|
122
|
-
step.text = step.text.split(`<${placeholder}>`).join(value);
|
|
123
|
-
return step;
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
const tags = child.scenario.tags.map(t => t.name).concat(examples.tags.map(t => t.name));
|
|
127
|
-
let title = `${child.scenario.name} ${JSON.stringify(current)} ${tags.join(' ')}`.trim();
|
|
128
|
-
|
|
129
|
-
for (const [key, value] of Object.entries(current)) {
|
|
130
|
-
if (title.includes(`<${key}>`)) {
|
|
131
|
-
title = title.replace(JSON.stringify(current), '').replace(`<${key}>`, value);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const test = new Test(title, async () => runSteps(addExampleInTable(exampleSteps, current)));
|
|
136
|
-
test.tags = suite.tags.concat(tags);
|
|
137
|
-
test.file = file;
|
|
138
|
-
suite.addTest(scenario.test(test));
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (child.scenario) {
|
|
145
|
-
const tags = child.scenario.tags.map(t => t.name);
|
|
146
|
-
const title = `${child.scenario.name} ${tags.join(' ')}`.trim();
|
|
147
|
-
const test = new Test(title, async () => runSteps(child.scenario.steps));
|
|
148
|
-
test.tags = suite.tags.concat(tags);
|
|
149
|
-
test.file = file;
|
|
150
|
-
suite.addTest(scenario.test(test));
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return suite;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
function transformTable(table) {
|
|
158
|
-
let str = '';
|
|
159
|
-
for (const id in table.rows) {
|
|
160
|
-
const cells = table.rows[id].cells;
|
|
161
|
-
str += cells.map(c => c.value).map(c => c.padEnd(15)).join(' | ');
|
|
162
|
-
str += '\n';
|
|
163
|
-
}
|
|
164
|
-
return str;
|
|
165
|
-
}
|
|
166
|
-
function addExampleInTable(exampleSteps, placeholders) {
|
|
167
|
-
const steps = JSON.parse(JSON.stringify(exampleSteps));
|
|
168
|
-
for (const placeholder in placeholders) {
|
|
169
|
-
steps.map((step) => {
|
|
170
|
-
step = { ...step };
|
|
171
|
-
if (step.dataTable) {
|
|
172
|
-
for (const id in step.dataTable.rows) {
|
|
173
|
-
const cells = step.dataTable.rows[id].cells;
|
|
174
|
-
cells.map(c => (c.value = c.value.replace(`<${placeholder}>`, placeholders[placeholder])));
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return step;
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
return steps;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function getTranslation(language) {
|
|
184
|
-
const translations = Object.keys(translations0);
|
|
185
|
-
|
|
186
|
-
for (const availableTranslation of translations) {
|
|
187
|
-
if (!language) {
|
|
188
|
-
break;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (availableTranslation.includes(language)) {
|
|
192
|
-
return translations0[availableTranslation];
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import * as event from '../event.js';
|
|
2
|
-
import recorder from '../recorder.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Create and clean up empty artifacts
|
|
6
|
-
*/
|
|
7
|
-
export default 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
|
-
}
|