codeceptjs 3.5.0 → 3.5.1-2.beta.7
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 +24 -25
- package/lib/actor.js +6 -3
- package/lib/ai.js +12 -3
- package/lib/cli.js +12 -2
- package/lib/codecept.js +4 -0
- package/lib/colorUtils.js +10 -0
- package/lib/command/definitions.js +2 -7
- package/lib/command/dryRun.js +2 -1
- package/lib/command/info.js +24 -0
- package/lib/command/init.js +51 -5
- package/lib/command/run-multiple/collection.js +17 -5
- package/lib/command/run-multiple.js +4 -2
- package/lib/command/run-workers.js +66 -4
- package/lib/command/run.js +7 -0
- package/lib/command/workers/runTests.js +39 -0
- package/lib/data/context.js +14 -6
- package/lib/event.js +4 -0
- package/lib/helper/ApiDataFactory.js +2 -1
- package/lib/helper/Appium.js +73 -24
- package/lib/helper/Expect.js +422 -0
- package/lib/helper/FileSystem.js +1 -1
- package/lib/helper/GraphQL.js +25 -0
- package/lib/helper/Nightmare.js +9 -4
- package/lib/helper/OpenAI.js +14 -10
- package/lib/helper/Playwright.js +1205 -288
- package/lib/helper/Protractor.js +11 -6
- package/lib/helper/Puppeteer.js +173 -61
- package/lib/helper/TestCafe.js +44 -9
- package/lib/helper/WebDriver.js +231 -82
- package/lib/helper/errors/ElementNotFound.js +2 -1
- package/lib/helper/extras/PlaywrightReactVueLocator.js +38 -0
- package/lib/helper/scripts/blurElement.js +17 -0
- package/lib/helper/scripts/focusElement.js +17 -0
- package/lib/helper/scripts/highlightElement.js +2 -2
- package/lib/html.js +3 -3
- package/lib/interfaces/bdd.js +1 -1
- package/lib/interfaces/gherkin.js +37 -3
- package/lib/interfaces/scenarioConfig.js +1 -0
- package/lib/locator.js +17 -4
- package/lib/mochaFactory.js +2 -1
- package/lib/output.js +1 -1
- package/lib/pause.js +12 -9
- package/lib/plugin/autoLogin.js +45 -10
- package/lib/plugin/heal.js +47 -17
- package/lib/plugin/retryFailedStep.js +10 -1
- package/lib/plugin/retryTo.js +2 -4
- package/lib/plugin/selenoid.js +6 -1
- package/lib/plugin/standardActingHelpers.js +0 -2
- package/lib/plugin/stepByStepReport.js +2 -2
- package/lib/plugin/tryTo.js +5 -7
- package/lib/plugin/wdio.js +0 -1
- package/lib/recorder.js +20 -9
- package/lib/session.js +1 -1
- package/lib/step.js +30 -11
- package/lib/ui.js +1 -0
- package/lib/utils.js +18 -1
- package/lib/workers.js +28 -3
- package/package.json +108 -98
- package/translations/de-DE.js +5 -0
- package/translations/fr-FR.js +14 -1
- package/translations/it-IT.js +1 -0
- package/translations/ja-JP.js +5 -0
- package/translations/pl-PL.js +5 -0
- package/translations/pt-BR.js +1 -0
- package/translations/ru-RU.js +1 -0
- package/translations/zh-CN.js +5 -0
- package/translations/zh-TW.js +5 -0
- package/typings/index.d.ts +8 -6
- package/typings/promiseBasedTypes.d.ts +784 -822
- package/typings/types.d.ts +1214 -727
- package/CHANGELOG.md +0 -2492
- package/docs/advanced.md +0 -351
- package/docs/ai.md +0 -246
- package/docs/api.md +0 -323
- package/docs/basics.md +0 -980
- package/docs/bdd.md +0 -535
- package/docs/best.md +0 -237
- package/docs/books.md +0 -37
- package/docs/bootstrap.md +0 -135
- package/docs/build/ApiDataFactory.js +0 -409
- package/docs/build/Appium.js +0 -1978
- package/docs/build/FileSystem.js +0 -228
- package/docs/build/GraphQL.js +0 -204
- package/docs/build/GraphQLDataFactory.js +0 -309
- package/docs/build/JSONResponse.js +0 -338
- package/docs/build/Mochawesome.js +0 -71
- package/docs/build/Nightmare.js +0 -2147
- package/docs/build/OpenAI.js +0 -122
- package/docs/build/Playwright.js +0 -4134
- package/docs/build/Polly.js +0 -42
- package/docs/build/Protractor.js +0 -2701
- package/docs/build/Puppeteer.js +0 -3743
- package/docs/build/REST.js +0 -344
- package/docs/build/SeleniumWebdriver.js +0 -76
- package/docs/build/TestCafe.js +0 -2059
- package/docs/build/WebDriver.js +0 -4042
- package/docs/changelog.md +0 -2501
- package/docs/commands.md +0 -254
- package/docs/community-helpers.md +0 -58
- package/docs/configuration.md +0 -157
- package/docs/continuous-integration.md +0 -22
- package/docs/custom-helpers.md +0 -306
- package/docs/data.md +0 -375
- package/docs/detox.md +0 -235
- package/docs/docker.md +0 -137
- package/docs/email.md +0 -183
- package/docs/examples.md +0 -149
- package/docs/helpers/ApiDataFactory.md +0 -266
- package/docs/helpers/Appium.md +0 -1317
- package/docs/helpers/Detox.md +0 -586
- package/docs/helpers/FileSystem.md +0 -152
- package/docs/helpers/GraphQL.md +0 -130
- package/docs/helpers/GraphQLDataFactory.md +0 -226
- package/docs/helpers/JSONResponse.md +0 -254
- package/docs/helpers/Mochawesome.md +0 -8
- package/docs/helpers/MockRequest.md +0 -377
- package/docs/helpers/Nightmare.md +0 -1258
- package/docs/helpers/OpenAI.md +0 -70
- package/docs/helpers/Playwright.md +0 -2250
- package/docs/helpers/Polly.md +0 -44
- package/docs/helpers/Puppeteer-firefox.md +0 -86
- package/docs/helpers/Puppeteer.md +0 -2147
- package/docs/helpers/REST.md +0 -218
- package/docs/helpers/TestCafe.md +0 -1224
- package/docs/helpers/WebDriver.md +0 -2325
- package/docs/hooks.md +0 -340
- package/docs/index.md +0 -111
- package/docs/installation.md +0 -75
- package/docs/internal-api.md +0 -265
- package/docs/locators.md +0 -331
- package/docs/mobile-react-native-locators.md +0 -67
- package/docs/mobile.md +0 -344
- package/docs/nightmare.md +0 -223
- package/docs/pageobjects.md +0 -291
- package/docs/parallel.md +0 -288
- package/docs/playwright.md +0 -609
- package/docs/plugins.md +0 -1225
- package/docs/puppeteer.md +0 -316
- package/docs/quickstart.md +0 -163
- package/docs/react.md +0 -69
- package/docs/reports.md +0 -392
- package/docs/secrets.md +0 -36
- package/docs/shadow.md +0 -68
- package/docs/shared/keys.mustache +0 -31
- package/docs/shared/react.mustache +0 -1
- package/docs/testcafe.md +0 -174
- package/docs/translation.md +0 -247
- package/docs/tutorial.md +0 -271
- package/docs/typescript.md +0 -180
- package/docs/ui.md +0 -59
- package/docs/videos.md +0 -28
- package/docs/visual.md +0 -202
- package/docs/vue.md +0 -121
- package/docs/webapi/amOnPage.mustache +0 -11
- package/docs/webapi/appendField.mustache +0 -11
- package/docs/webapi/attachFile.mustache +0 -12
- package/docs/webapi/checkOption.mustache +0 -13
- package/docs/webapi/clearCookie.mustache +0 -10
- package/docs/webapi/clearField.mustache +0 -9
- package/docs/webapi/click.mustache +0 -25
- package/docs/webapi/clickLink.mustache +0 -8
- package/docs/webapi/closeCurrentTab.mustache +0 -7
- package/docs/webapi/closeOtherTabs.mustache +0 -8
- package/docs/webapi/dontSee.mustache +0 -11
- package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/dontSeeCookie.mustache +0 -8
- package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
- package/docs/webapi/dontSeeElement.mustache +0 -8
- package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
- package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
- package/docs/webapi/dontSeeInField.mustache +0 -11
- package/docs/webapi/dontSeeInSource.mustache +0 -8
- package/docs/webapi/dontSeeInTitle.mustache +0 -8
- package/docs/webapi/doubleClick.mustache +0 -13
- package/docs/webapi/downloadFile.mustache +0 -12
- package/docs/webapi/dragAndDrop.mustache +0 -9
- package/docs/webapi/dragSlider.mustache +0 -11
- package/docs/webapi/executeAsyncScript.mustache +0 -24
- package/docs/webapi/executeScript.mustache +0 -26
- package/docs/webapi/fillField.mustache +0 -16
- package/docs/webapi/forceClick.mustache +0 -28
- package/docs/webapi/forceRightClick.mustache +0 -18
- package/docs/webapi/grabAllWindowHandles.mustache +0 -7
- package/docs/webapi/grabAttributeFrom.mustache +0 -10
- package/docs/webapi/grabAttributeFromAll.mustache +0 -9
- package/docs/webapi/grabBrowserLogs.mustache +0 -9
- package/docs/webapi/grabCookie.mustache +0 -11
- package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
- package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
- package/docs/webapi/grabCurrentUrl.mustache +0 -9
- package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
- package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
- package/docs/webapi/grabElementBoundingRect.mustache +0 -20
- package/docs/webapi/grabGeoLocation.mustache +0 -8
- package/docs/webapi/grabHTMLFrom.mustache +0 -10
- package/docs/webapi/grabHTMLFromAll.mustache +0 -9
- package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
- package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
- package/docs/webapi/grabPageScrollPosition.mustache +0 -8
- package/docs/webapi/grabPopupText.mustache +0 -5
- package/docs/webapi/grabSource.mustache +0 -8
- package/docs/webapi/grabTextFrom.mustache +0 -10
- package/docs/webapi/grabTextFromAll.mustache +0 -9
- package/docs/webapi/grabTitle.mustache +0 -8
- package/docs/webapi/grabValueFrom.mustache +0 -9
- package/docs/webapi/grabValueFromAll.mustache +0 -8
- package/docs/webapi/moveCursorTo.mustache +0 -12
- package/docs/webapi/openNewTab.mustache +0 -7
- package/docs/webapi/pressKey.mustache +0 -12
- package/docs/webapi/pressKeyDown.mustache +0 -12
- package/docs/webapi/pressKeyUp.mustache +0 -12
- package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
- package/docs/webapi/refreshPage.mustache +0 -6
- package/docs/webapi/resizeWindow.mustache +0 -6
- package/docs/webapi/rightClick.mustache +0 -14
- package/docs/webapi/saveElementScreenshot.mustache +0 -10
- package/docs/webapi/saveScreenshot.mustache +0 -12
- package/docs/webapi/say.mustache +0 -10
- package/docs/webapi/scrollIntoView.mustache +0 -11
- package/docs/webapi/scrollPageToBottom.mustache +0 -6
- package/docs/webapi/scrollPageToTop.mustache +0 -6
- package/docs/webapi/scrollTo.mustache +0 -12
- package/docs/webapi/see.mustache +0 -11
- package/docs/webapi/seeAttributesOnElements.mustache +0 -9
- package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/seeCookie.mustache +0 -8
- package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
- package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
- package/docs/webapi/seeElement.mustache +0 -8
- package/docs/webapi/seeElementInDOM.mustache +0 -8
- package/docs/webapi/seeInCurrentUrl.mustache +0 -8
- package/docs/webapi/seeInField.mustache +0 -12
- package/docs/webapi/seeInPopup.mustache +0 -8
- package/docs/webapi/seeInSource.mustache +0 -7
- package/docs/webapi/seeInTitle.mustache +0 -8
- package/docs/webapi/seeNumberOfElements.mustache +0 -11
- package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/seeTextEquals.mustache +0 -9
- package/docs/webapi/seeTitleEquals.mustache +0 -8
- package/docs/webapi/selectOption.mustache +0 -21
- package/docs/webapi/setCookie.mustache +0 -16
- package/docs/webapi/setGeoLocation.mustache +0 -12
- package/docs/webapi/switchTo.mustache +0 -9
- package/docs/webapi/switchToNextTab.mustache +0 -10
- package/docs/webapi/switchToPreviousTab.mustache +0 -10
- package/docs/webapi/type.mustache +0 -21
- package/docs/webapi/uncheckOption.mustache +0 -13
- package/docs/webapi/wait.mustache +0 -8
- package/docs/webapi/waitForClickable.mustache +0 -11
- package/docs/webapi/waitForDetached.mustache +0 -10
- package/docs/webapi/waitForElement.mustache +0 -11
- package/docs/webapi/waitForEnabled.mustache +0 -6
- package/docs/webapi/waitForFunction.mustache +0 -17
- package/docs/webapi/waitForInvisible.mustache +0 -10
- package/docs/webapi/waitForText.mustache +0 -13
- package/docs/webapi/waitForValue.mustache +0 -10
- package/docs/webapi/waitForVisible.mustache +0 -10
- package/docs/webapi/waitInUrl.mustache +0 -9
- package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/waitToHide.mustache +0 -10
- package/docs/webapi/waitUrlEquals.mustache +0 -10
- package/docs/webdriver.md +0 -657
- package/docs/wiki/Books-&-Posts.md +0 -27
- package/docs/wiki/Community-Helpers-&-Plugins.md +0 -49
- package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +0 -29
- package/docs/wiki/Examples.md +0 -139
- package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +0 -68
- package/docs/wiki/Home.md +0 -16
- package/docs/wiki/Release-Process.md +0 -24
- package/docs/wiki/Roadmap.md +0 -23
- package/docs/wiki/Tests.md +0 -1393
- package/docs/wiki/Upgrading-to-CodeceptJS-3.md +0 -153
- package/docs/wiki/Videos.md +0 -19
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module.exports.focusElement = (element, context) => {
|
|
2
|
+
const clientSideFn = el => {
|
|
3
|
+
el.focus();
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
// Puppeteer
|
|
8
|
+
context.evaluate(clientSideFn, element);
|
|
9
|
+
} catch (e) {
|
|
10
|
+
// WebDriver
|
|
11
|
+
try {
|
|
12
|
+
context.execute(clientSideFn, element);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
// ignore
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
@@ -7,8 +7,8 @@ module.exports.highlightElement = (element, context) => {
|
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
try {
|
|
10
|
-
//
|
|
11
|
-
context.evaluate(clientSideHighlightFn, element);
|
|
10
|
+
// Puppeteer
|
|
11
|
+
context.evaluate(clientSideHighlightFn, element).catch(err => console.error(err));
|
|
12
12
|
} catch (e) {
|
|
13
13
|
// WebDriver
|
|
14
14
|
try {
|
package/lib/html.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { parse, serialize } = require('parse5');
|
|
2
|
-
const { minify } = require('html-minifier');
|
|
2
|
+
const { minify } = require('html-minifier-terser');
|
|
3
3
|
|
|
4
|
-
function minifyHtml(html) {
|
|
4
|
+
async function minifyHtml(html) {
|
|
5
5
|
return minify(html, {
|
|
6
6
|
collapseWhitespace: true,
|
|
7
7
|
removeComments: true,
|
|
@@ -11,7 +11,7 @@ function minifyHtml(html) {
|
|
|
11
11
|
removeStyleLinkTypeAttributes: true,
|
|
12
12
|
collapseBooleanAttributes: true,
|
|
13
13
|
useShortDoctype: true,
|
|
14
|
-
})
|
|
14
|
+
});
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const defaultHtmlOpts = {
|
package/lib/interfaces/bdd.js
CHANGED
|
@@ -30,7 +30,7 @@ const parameterTypeRegistry = new ParameterTypeRegistry();
|
|
|
30
30
|
const matchStep = (step) => {
|
|
31
31
|
for (const stepName in steps) {
|
|
32
32
|
if (stepName.indexOf('/') === 0) {
|
|
33
|
-
const regExpArr = stepName.match(
|
|
33
|
+
const regExpArr = stepName.match(/^\/(.*?)\/([gimy]*)$/) || [];
|
|
34
34
|
const res = step.match(new RegExp(regExpArr[1], regExpArr[2]));
|
|
35
35
|
if (res) {
|
|
36
36
|
const fn = steps[stepName];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const Gherkin = require('@cucumber/gherkin');
|
|
2
2
|
const Messages = require('@cucumber/messages');
|
|
3
3
|
const { Context, Suite, Test } = require('mocha');
|
|
4
|
+
const debug = require('debug')('codeceptjs:bdd');
|
|
4
5
|
|
|
5
6
|
const { matchStep } = require('./bdd');
|
|
6
7
|
const event = require('../event');
|
|
@@ -17,6 +18,11 @@ parser.stopAtFirstError = false;
|
|
|
17
18
|
|
|
18
19
|
module.exports = (text, file) => {
|
|
19
20
|
const ast = parser.parse(text);
|
|
21
|
+
let currentLanguage;
|
|
22
|
+
|
|
23
|
+
if (ast.feature) {
|
|
24
|
+
currentLanguage = getTranslation(ast.feature.language);
|
|
25
|
+
}
|
|
20
26
|
|
|
21
27
|
if (!ast.feature) {
|
|
22
28
|
throw new Error(`No 'Features' available in Gherkin '${file}' provided!`);
|
|
@@ -39,7 +45,9 @@ module.exports = (text, file) => {
|
|
|
39
45
|
for (const step of steps) {
|
|
40
46
|
const metaStep = new Step.MetaStep(null, step.text);
|
|
41
47
|
metaStep.actor = step.keyword.trim();
|
|
48
|
+
let helperStep;
|
|
42
49
|
const setMetaStep = (step) => {
|
|
50
|
+
helperStep = step;
|
|
43
51
|
if (step.metaStep) {
|
|
44
52
|
if (step.metaStep === metaStep) {
|
|
45
53
|
return;
|
|
@@ -67,11 +75,15 @@ module.exports = (text, file) => {
|
|
|
67
75
|
step.startTime = Date.now();
|
|
68
76
|
step.match = fn.line;
|
|
69
77
|
event.emit(event.bddStep.before, step);
|
|
78
|
+
event.emit(event.bddStep.started, metaStep);
|
|
70
79
|
event.dispatcher.prependListener(event.step.before, setMetaStep);
|
|
71
80
|
try {
|
|
81
|
+
debug(`Step '${step.text}' started...`);
|
|
72
82
|
await fn(...fn.params);
|
|
83
|
+
debug('Step passed');
|
|
73
84
|
step.status = 'passed';
|
|
74
85
|
} catch (err) {
|
|
86
|
+
debug(`Step failed: ${err?.message}`);
|
|
75
87
|
step.status = 'failed';
|
|
76
88
|
step.err = err;
|
|
77
89
|
throw err;
|
|
@@ -79,6 +91,7 @@ module.exports = (text, file) => {
|
|
|
79
91
|
step.endTime = Date.now();
|
|
80
92
|
event.dispatcher.removeListener(event.step.before, setMetaStep);
|
|
81
93
|
}
|
|
94
|
+
event.emit(event.bddStep.finished, metaStep);
|
|
82
95
|
event.emit(event.bddStep.after, step);
|
|
83
96
|
}
|
|
84
97
|
};
|
|
@@ -88,7 +101,7 @@ module.exports = (text, file) => {
|
|
|
88
101
|
suite.beforeEach('Before', scenario.injected(async () => runSteps(child.background.steps), suite, 'before'));
|
|
89
102
|
continue;
|
|
90
103
|
}
|
|
91
|
-
if (child.scenario && child.scenario.keyword === 'Scenario Outline') {
|
|
104
|
+
if (child.scenario && (currentLanguage ? child.scenario.keyword === currentLanguage.contexts.ScenarioOutline : child.scenario.keyword === 'Scenario Outline')) {
|
|
92
105
|
for (const examples of child.scenario.examples) {
|
|
93
106
|
const fields = examples.tableHeader.cells.map(c => c.value);
|
|
94
107
|
for (const example of examples.tableBody) {
|
|
@@ -106,7 +119,14 @@ module.exports = (text, file) => {
|
|
|
106
119
|
});
|
|
107
120
|
}
|
|
108
121
|
const tags = child.scenario.tags.map(t => t.name).concat(examples.tags.map(t => t.name));
|
|
109
|
-
|
|
122
|
+
let title = `${child.scenario.name} ${JSON.stringify(current)} ${tags.join(' ')}`.trim();
|
|
123
|
+
|
|
124
|
+
for (const [key, value] of Object.entries(current)) {
|
|
125
|
+
if (title.includes(`<${key}>`)) {
|
|
126
|
+
title = title.replace(JSON.stringify(current), '').replace(`<${key}>`, value);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
110
130
|
const test = new Test(title, async () => runSteps(addExampleInTable(exampleSteps, current)));
|
|
111
131
|
test.tags = suite.tags.concat(tags);
|
|
112
132
|
test.file = file;
|
|
@@ -133,7 +153,7 @@ function transformTable(table) {
|
|
|
133
153
|
let str = '';
|
|
134
154
|
for (const id in table.rows) {
|
|
135
155
|
const cells = table.rows[id].cells;
|
|
136
|
-
str += cells.map(c => c.value).map(c => c.
|
|
156
|
+
str += cells.map(c => c.value).map(c => c.padEnd(15)).join(' | ');
|
|
137
157
|
str += '\n';
|
|
138
158
|
}
|
|
139
159
|
return str;
|
|
@@ -154,3 +174,17 @@ function addExampleInTable(exampleSteps, placeholders) {
|
|
|
154
174
|
}
|
|
155
175
|
return steps;
|
|
156
176
|
}
|
|
177
|
+
|
|
178
|
+
function getTranslation(language) {
|
|
179
|
+
const translations = Object.keys(require('../../translations'));
|
|
180
|
+
|
|
181
|
+
for (const availableTranslation of translations) {
|
|
182
|
+
if (!language) {
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (availableTranslation.includes(language)) {
|
|
187
|
+
return require('../../translations')[availableTranslation];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
package/lib/locator.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const cssToXPath = require('
|
|
1
|
+
const cssToXPath = require('csstoxpath');
|
|
2
2
|
const { sprintf } = require('sprintf-js');
|
|
3
3
|
|
|
4
4
|
const { xpathLocator } = require('./utils');
|
|
@@ -158,11 +158,12 @@ class Locator {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
|
+
* @param {string} [pseudoSelector] CSS to XPath extension pseudo: https://www.npmjs.com/package/csstoxpath?activeTab=explore#extension-pseudos
|
|
161
162
|
* @returns {string}
|
|
162
163
|
*/
|
|
163
|
-
toXPath() {
|
|
164
|
+
toXPath(pseudoSelector = '') {
|
|
164
165
|
if (this.isXPath()) return this.value;
|
|
165
|
-
if (this.isCSS()) return cssToXPath(this.value);
|
|
166
|
+
if (this.isCSS()) return cssToXPath(`${this.value}${pseudoSelector}`);
|
|
166
167
|
|
|
167
168
|
throw new Error('Can\'t be converted to XPath');
|
|
168
169
|
}
|
|
@@ -243,12 +244,24 @@ class Locator {
|
|
|
243
244
|
}
|
|
244
245
|
|
|
245
246
|
/**
|
|
247
|
+
* Find an element containing a text
|
|
246
248
|
* @param {string} text
|
|
247
249
|
* @returns {Locator}
|
|
248
250
|
*/
|
|
249
251
|
withText(text) {
|
|
250
252
|
text = xpathLocator.literal(text);
|
|
251
|
-
const xpath =
|
|
253
|
+
const xpath = this.toXPath(`:text-contains-case(${text})`);
|
|
254
|
+
return new Locator({ xpath });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Find an element with exact text
|
|
259
|
+
* @param {string} text
|
|
260
|
+
* @returns {Locator}
|
|
261
|
+
*/
|
|
262
|
+
withTextEquals(text) {
|
|
263
|
+
text = xpathLocator.literal(text);
|
|
264
|
+
const xpath = this.toXPath(`:text-case(${text})`);
|
|
252
265
|
return new Locator({ xpath });
|
|
253
266
|
}
|
|
254
267
|
|
package/lib/mochaFactory.js
CHANGED
|
@@ -97,7 +97,8 @@ class MochaFactory {
|
|
|
97
97
|
const attributes = Object.getOwnPropertyDescriptor(reporterOptions, 'codeceptjs-cli-reporter');
|
|
98
98
|
if (reporterOptions['codeceptjs-cli-reporter'] && attributes) {
|
|
99
99
|
Object.defineProperty(
|
|
100
|
-
reporterOptions,
|
|
100
|
+
reporterOptions,
|
|
101
|
+
'codeceptjs/lib/cli',
|
|
101
102
|
attributes,
|
|
102
103
|
);
|
|
103
104
|
delete reporterOptions['codeceptjs-cli-reporter'];
|
package/lib/output.js
CHANGED
|
@@ -106,7 +106,7 @@ module.exports = {
|
|
|
106
106
|
if (!step) return;
|
|
107
107
|
// Avoid to print non-gherkin steps, when gherkin is running for --steps mode
|
|
108
108
|
if (outputLevel === 1) {
|
|
109
|
-
if (step.hasBDDAncestor()) {
|
|
109
|
+
if (typeof step === 'object' && step.hasBDDAncestor()) {
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
}
|
package/lib/pause.js
CHANGED
|
@@ -18,10 +18,10 @@ let nextStep;
|
|
|
18
18
|
let finish;
|
|
19
19
|
let next;
|
|
20
20
|
let registeredVariables = {};
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
let aiAssistant;
|
|
23
22
|
/**
|
|
24
23
|
* Pauses test execution and starts interactive shell
|
|
24
|
+
* @param {Object<string, *>} [passedObject]
|
|
25
25
|
*/
|
|
26
26
|
const pause = function (passedObject = {}) {
|
|
27
27
|
if (store.dryRun) return;
|
|
@@ -44,6 +44,8 @@ function pauseSession(passedObject = {}) {
|
|
|
44
44
|
let vars = Object.keys(registeredVariables).join(', ');
|
|
45
45
|
if (vars) vars = `(vars: ${vars})`;
|
|
46
46
|
|
|
47
|
+
aiAssistant = AiAssistant.getInstance();
|
|
48
|
+
|
|
47
49
|
output.print(colors.yellow(' Interactive shell started'));
|
|
48
50
|
output.print(colors.yellow(' Use JavaScript syntax to try steps in action'));
|
|
49
51
|
output.print(colors.yellow(` - Press ${colors.bold('ENTER')} to run the next step`));
|
|
@@ -67,6 +69,7 @@ function pauseSession(passedObject = {}) {
|
|
|
67
69
|
});
|
|
68
70
|
return new Promise(((resolve) => {
|
|
69
71
|
finish = resolve;
|
|
72
|
+
// eslint-disable-next-line
|
|
70
73
|
return askForStep();
|
|
71
74
|
}));
|
|
72
75
|
}
|
|
@@ -76,7 +79,6 @@ async function parseInput(cmd) {
|
|
|
76
79
|
rl.pause();
|
|
77
80
|
next = false;
|
|
78
81
|
recorder.session.start('pause');
|
|
79
|
-
store.debugMode = false;
|
|
80
82
|
if (cmd === '') next = true;
|
|
81
83
|
if (!cmd || cmd === 'resume' || cmd === 'exit') {
|
|
82
84
|
finish();
|
|
@@ -96,13 +98,14 @@ async function parseInput(cmd) {
|
|
|
96
98
|
return cmd;
|
|
97
99
|
};
|
|
98
100
|
|
|
99
|
-
store.debugMode = true;
|
|
100
101
|
let isCustomCommand = false;
|
|
101
102
|
let lastError = null;
|
|
102
103
|
let isAiCommand = false;
|
|
103
104
|
let $res;
|
|
104
105
|
try {
|
|
106
|
+
// eslint-disable-next-line
|
|
105
107
|
const locate = global.locate; // enable locate in this context
|
|
108
|
+
// eslint-disable-next-line
|
|
106
109
|
const I = container.support('I');
|
|
107
110
|
if (cmd.trim().startsWith('=>')) {
|
|
108
111
|
isCustomCommand = true;
|
|
@@ -114,13 +117,13 @@ async function parseInput(cmd) {
|
|
|
114
117
|
isAiCommand = true;
|
|
115
118
|
executeCommand = executeCommand.then(async () => {
|
|
116
119
|
try {
|
|
117
|
-
const html = await res;
|
|
118
|
-
aiAssistant.setHtmlContext(html);
|
|
120
|
+
const html = await res;
|
|
121
|
+
await aiAssistant.setHtmlContext(html);
|
|
119
122
|
} catch (err) {
|
|
120
123
|
output.print(output.styles.error(' ERROR '), 'Can\'t get HTML context', err.stack);
|
|
121
124
|
return;
|
|
122
125
|
} finally {
|
|
123
|
-
output.level(currentOutputLevel);
|
|
126
|
+
output.level(currentOutputLevel);
|
|
124
127
|
}
|
|
125
128
|
// aiAssistant.mockResponse("```js\nI.click('Sign in');\n```");
|
|
126
129
|
const spinner = ora("Processing OpenAI request...").start();
|
|
@@ -148,12 +151,12 @@ async function parseInput(cmd) {
|
|
|
148
151
|
})
|
|
149
152
|
|
|
150
153
|
const val = await executeCommand;
|
|
151
|
-
|
|
154
|
+
|
|
152
155
|
if (isCustomCommand) {
|
|
153
156
|
if (val !== undefined) console.log('Result', '$res=', val); // eslint-disable-line
|
|
154
157
|
$res = val;
|
|
155
158
|
}
|
|
156
|
-
|
|
159
|
+
|
|
157
160
|
if (cmd?.startsWith('I.see') || cmd?.startsWith('I.dontSee')) {
|
|
158
161
|
output.print(output.styles.success(' OK '), cmd);
|
|
159
162
|
}
|
package/lib/plugin/autoLogin.js
CHANGED
|
@@ -9,6 +9,7 @@ const isAsyncFunction = require('../utils').isAsyncFunction;
|
|
|
9
9
|
|
|
10
10
|
const defaultUser = {
|
|
11
11
|
fetch: I => I.grabCookie(),
|
|
12
|
+
check: () => {},
|
|
12
13
|
restore: (I, cookies) => {
|
|
13
14
|
I.amOnPage('/'); // open a page
|
|
14
15
|
I.setCookie(cookies);
|
|
@@ -37,12 +38,14 @@ const defaultConfig = {
|
|
|
37
38
|
* ```js
|
|
38
39
|
* // inside a test file
|
|
39
40
|
* // use login to inject auto-login function
|
|
41
|
+
* Feature('Login');
|
|
42
|
+
*
|
|
40
43
|
* Before(({ login }) => {
|
|
41
44
|
* login('user'); // login using user session
|
|
42
45
|
* });
|
|
43
46
|
*
|
|
44
|
-
* // Alternatively log in for one scenario
|
|
45
|
-
* Scenario('log me in', ( {I, login} ) => {
|
|
47
|
+
* // Alternatively log in for one scenario.
|
|
48
|
+
* Scenario('log me in', ( { I, login } ) => {
|
|
46
49
|
* login('admin');
|
|
47
50
|
* I.see('I am logged in');
|
|
48
51
|
* });
|
|
@@ -61,7 +64,7 @@ const defaultConfig = {
|
|
|
61
64
|
* #### How It Works
|
|
62
65
|
*
|
|
63
66
|
* 1. `restore` method is executed. It should open a page and set credentials.
|
|
64
|
-
* 2. `check` method is executed. It should reload a page (so cookies are applied) and check that this page belongs to logged
|
|
67
|
+
* 2. `check` method is executed. It should reload a page (so cookies are applied) and check that this page belongs to logged-in user. When you pass the second args `session`, you could perform the validation using passed session.
|
|
65
68
|
* 3. If `restore` and `check` were not successful, `login` is executed
|
|
66
69
|
* 4. `login` should fill in login form
|
|
67
70
|
* 5. After successful login, `fetch` is executed to save cookies into memory or file.
|
|
@@ -212,6 +215,38 @@ const defaultConfig = {
|
|
|
212
215
|
* })
|
|
213
216
|
* ```
|
|
214
217
|
*
|
|
218
|
+
* #### Tips: Using session to validate user
|
|
219
|
+
*
|
|
220
|
+
* Instead of asserting on page elements for the current user in `check`, you can use the `session` you saved in `fetch`
|
|
221
|
+
*
|
|
222
|
+
* ```js
|
|
223
|
+
* autoLogin: {
|
|
224
|
+
* enabled: true,
|
|
225
|
+
* saveToFile: true,
|
|
226
|
+
* inject: 'login',
|
|
227
|
+
* users: {
|
|
228
|
+
* admin: {
|
|
229
|
+
* login: async (I) => { // If you use async function in the autoLogin plugin
|
|
230
|
+
* const phrase = await I.grabTextFrom('#phrase')
|
|
231
|
+
* I.fillField('username', 'admin'),
|
|
232
|
+
* I.fillField('password', 'password')
|
|
233
|
+
* I.fillField('phrase', phrase)
|
|
234
|
+
* },
|
|
235
|
+
* check: (I, session) => {
|
|
236
|
+
* // Throwing an error in `check` will make CodeceptJS perform the login step for the user
|
|
237
|
+
* if (session.profile.email !== the.email.you.expect@some-mail.com) {
|
|
238
|
+
* throw new Error ('Wrong user signed in');
|
|
239
|
+
* }
|
|
240
|
+
* },
|
|
241
|
+
* }
|
|
242
|
+
* }
|
|
243
|
+
* }
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* ```js
|
|
247
|
+
* Scenario('login', async ( {I, login} ) => {
|
|
248
|
+
* await login('admin') // you should use `await`
|
|
249
|
+
* })
|
|
215
250
|
*
|
|
216
251
|
*
|
|
217
252
|
*/
|
|
@@ -251,27 +286,28 @@ module.exports = function (config) {
|
|
|
251
286
|
} else {
|
|
252
287
|
userSession.login(I);
|
|
253
288
|
}
|
|
254
|
-
|
|
289
|
+
|
|
255
290
|
const cookies = await userSession.fetch(I);
|
|
291
|
+
if (!cookies) {
|
|
292
|
+
debug('Cannot save user session with empty cookies from auto login\'s fetch method');
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
256
295
|
if (config.saveToFile) {
|
|
257
296
|
debug(`Saved user session into file for ${name}`);
|
|
258
297
|
fs.writeFileSync(path.join(global.output_dir, `${name}_session.json`), JSON.stringify(cookies));
|
|
259
298
|
}
|
|
260
299
|
store[`${name}_session`] = cookies;
|
|
261
|
-
store.debugMode = false;
|
|
262
300
|
};
|
|
263
301
|
|
|
264
302
|
if (!cookies) return loginAndSave();
|
|
265
303
|
|
|
266
|
-
store.debugMode = true;
|
|
267
|
-
|
|
268
304
|
recorder.session.start('check login');
|
|
269
305
|
if (shouldAwait) {
|
|
270
306
|
await userSession.restore(I, cookies);
|
|
271
|
-
await userSession.check(I);
|
|
307
|
+
await userSession.check(I, cookies);
|
|
272
308
|
} else {
|
|
273
309
|
userSession.restore(I, cookies);
|
|
274
|
-
userSession.check(I);
|
|
310
|
+
userSession.check(I, cookies);
|
|
275
311
|
}
|
|
276
312
|
recorder.session.catch((err) => {
|
|
277
313
|
debug(`Failed auto login for ${name} due to ${err}`);
|
|
@@ -287,7 +323,6 @@ module.exports = function (config) {
|
|
|
287
323
|
});
|
|
288
324
|
});
|
|
289
325
|
recorder.add(() => {
|
|
290
|
-
store.debugMode = false;
|
|
291
326
|
recorder.session.restore('check login');
|
|
292
327
|
});
|
|
293
328
|
|
package/lib/plugin/heal.js
CHANGED
|
@@ -8,6 +8,7 @@ const output = require('../output');
|
|
|
8
8
|
const supportedHelpers = require('./standardActingHelpers');
|
|
9
9
|
|
|
10
10
|
const defaultConfig = {
|
|
11
|
+
healTries: 1,
|
|
11
12
|
healLimit: 2,
|
|
12
13
|
healSteps: [
|
|
13
14
|
'click',
|
|
@@ -26,7 +27,7 @@ const defaultConfig = {
|
|
|
26
27
|
*
|
|
27
28
|
* This plugin is experimental and requires OpenAI API key.
|
|
28
29
|
*
|
|
29
|
-
* To use it you need to set OPENAI_API_KEY env variable and enable plugin inside
|
|
30
|
+
* To use it you need to set OPENAI_API_KEY env variable and enable plugin inside the config.
|
|
30
31
|
*
|
|
31
32
|
* ```js
|
|
32
33
|
* plugins: {
|
|
@@ -54,11 +55,14 @@ const defaultConfig = {
|
|
|
54
55
|
*
|
|
55
56
|
*/
|
|
56
57
|
module.exports = function (config = {}) {
|
|
57
|
-
const aiAssistant =
|
|
58
|
+
const aiAssistant = AiAssistant.getInstance();
|
|
58
59
|
|
|
59
60
|
let currentTest = null;
|
|
60
61
|
let currentStep = null;
|
|
61
62
|
let healedSteps = 0;
|
|
63
|
+
let caughtError;
|
|
64
|
+
let healTries = 0;
|
|
65
|
+
let isHealing = false;
|
|
62
66
|
|
|
63
67
|
const healSuggestions = [];
|
|
64
68
|
|
|
@@ -67,22 +71,37 @@ module.exports = function (config = {}) {
|
|
|
67
71
|
event.dispatcher.on(event.test.before, (test) => {
|
|
68
72
|
currentTest = test;
|
|
69
73
|
healedSteps = 0;
|
|
74
|
+
caughtError = null;
|
|
70
75
|
});
|
|
71
76
|
|
|
72
77
|
event.dispatcher.on(event.step.started, step => currentStep = step);
|
|
73
78
|
|
|
74
|
-
event.dispatcher.on(event.step.
|
|
79
|
+
event.dispatcher.on(event.step.after, (step) => {
|
|
80
|
+
if (isHealing) return;
|
|
75
81
|
const store = require('../store');
|
|
76
82
|
if (store.debugMode) return;
|
|
77
|
-
|
|
78
83
|
recorder.catchWithoutStop(async (err) => {
|
|
79
|
-
|
|
84
|
+
isHealing = true;
|
|
85
|
+
if (caughtError === err) throw err; // avoid double handling
|
|
86
|
+
caughtError = err;
|
|
87
|
+
if (!aiAssistant.isEnabled) {
|
|
88
|
+
output.print(colors.yellow('Heal plugin can\'t operate, AI assistant is disabled. Please set OPENAI_API_KEY env variable to enable it.'));
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
80
91
|
if (!currentStep) throw err;
|
|
81
92
|
if (!config.healSteps.includes(currentStep.name)) throw err;
|
|
82
93
|
const test = currentTest;
|
|
83
94
|
|
|
95
|
+
if (healTries >= config.healTries) {
|
|
96
|
+
output.print(colors.bold.red(`Healing failed for ${config.healTries} time(s)`));
|
|
97
|
+
output.print('AI couldn\'t identify the correct solution');
|
|
98
|
+
output.print('Probably the entire flow has changed and the test should be updated');
|
|
99
|
+
|
|
100
|
+
throw err;
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
if (healedSteps >= config.healLimit) {
|
|
85
|
-
output.print(colors.bold.red(`Can't heal more than ${config.healLimit}
|
|
104
|
+
output.print(colors.bold.red(`Can't heal more than ${config.healLimit} step(s) in a test`));
|
|
86
105
|
output.print('Entire flow can be broken, please check it manually');
|
|
87
106
|
output.print('or increase healing limit in heal plugin config');
|
|
88
107
|
|
|
@@ -111,9 +130,17 @@ module.exports = function (config = {}) {
|
|
|
111
130
|
|
|
112
131
|
if (!html) throw err;
|
|
113
132
|
|
|
114
|
-
|
|
133
|
+
healTries++;
|
|
134
|
+
await aiAssistant.setHtmlContext(html);
|
|
115
135
|
await tryToHeal(step, err);
|
|
116
|
-
|
|
136
|
+
|
|
137
|
+
recorder.add('close healing session', () => {
|
|
138
|
+
recorder.session.restore('heal');
|
|
139
|
+
recorder.ignoreErr(err);
|
|
140
|
+
});
|
|
141
|
+
await recorder.promise();
|
|
142
|
+
|
|
143
|
+
isHealing = false;
|
|
117
144
|
});
|
|
118
145
|
});
|
|
119
146
|
|
|
@@ -126,7 +153,7 @@ module.exports = function (config = {}) {
|
|
|
126
153
|
print('===================');
|
|
127
154
|
print(colors.bold.green('Self-Healing Report:'));
|
|
128
155
|
|
|
129
|
-
print(`${colors.bold(healSuggestions.length)}
|
|
156
|
+
print(`${colors.bold(healSuggestions.length)} step(s) were healed by AI`);
|
|
130
157
|
|
|
131
158
|
let i = 1;
|
|
132
159
|
print('');
|
|
@@ -145,19 +172,19 @@ module.exports = function (config = {}) {
|
|
|
145
172
|
});
|
|
146
173
|
|
|
147
174
|
async function tryToHeal(failedStep, err) {
|
|
148
|
-
output.debug(`Running
|
|
175
|
+
output.debug(`Running OpenAI to heal ${failedStep.toCode()} step`);
|
|
149
176
|
|
|
150
|
-
const
|
|
177
|
+
const codeSnippets = await aiAssistant.healFailedStep(failedStep, err, currentTest);
|
|
151
178
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
output.debug(`Received ${codeSnippets.length} proposals from OpenAI`);
|
|
179
|
+
output.debug(`Received ${codeSnippets.length} suggestions from OpenAI`);
|
|
180
|
+
const I = Container.support('I'); // eslint-disable-line
|
|
157
181
|
|
|
158
182
|
for (const codeSnippet of codeSnippets) {
|
|
159
183
|
try {
|
|
160
184
|
debug('Executing', codeSnippet);
|
|
185
|
+
recorder.catch((e) => {
|
|
186
|
+
console.log(e);
|
|
187
|
+
});
|
|
161
188
|
await eval(codeSnippet); // eslint-disable-line
|
|
162
189
|
|
|
163
190
|
healSuggestions.push({
|
|
@@ -166,14 +193,17 @@ module.exports = function (config = {}) {
|
|
|
166
193
|
snippet: codeSnippet,
|
|
167
194
|
});
|
|
168
195
|
|
|
169
|
-
output.print(colors.bold.green(' Code healed successfully'));
|
|
196
|
+
recorder.add('healed', () => output.print(colors.bold.green(' Code healed successfully')));
|
|
170
197
|
healedSteps++;
|
|
171
198
|
return;
|
|
172
199
|
} catch (err) {
|
|
173
200
|
debug('Failed to execute code', err);
|
|
201
|
+
recorder.ignoreErr(err); // healing ded not help
|
|
202
|
+
// recorder.catch(() => output.print(colors.bold.red(' Failed healing code')));
|
|
174
203
|
}
|
|
175
204
|
}
|
|
176
205
|
|
|
177
206
|
output.debug(`Couldn't heal the code for ${failedStep.toCode()}`);
|
|
178
207
|
}
|
|
208
|
+
return recorder.promise();
|
|
179
209
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const event = require('../event');
|
|
2
2
|
const recorder = require('../recorder');
|
|
3
|
+
const container = require('../container');
|
|
4
|
+
const { log } = require('../output');
|
|
3
5
|
|
|
4
6
|
const defaultConfig = {
|
|
5
7
|
retries: 3,
|
|
@@ -42,7 +44,7 @@ const defaultConfig = {
|
|
|
42
44
|
* * `factor` - The exponential factor to use. Default is 1.5.
|
|
43
45
|
* * `minTimeout` - The number of milliseconds before starting the first retry. Default is 1000.
|
|
44
46
|
* * `maxTimeout` - The maximum number of milliseconds between two retries. Default is Infinity.
|
|
45
|
-
* * `randomize` - Randomizes the timeouts by multiplying with a factor
|
|
47
|
+
* * `randomize` - Randomizes the timeouts by multiplying with a factor from 1 to 2. Default is false.
|
|
46
48
|
* * `defaultIgnoredSteps` - an array of steps to be ignored for retry. Includes:
|
|
47
49
|
* * `amOnPage`
|
|
48
50
|
* * `wait*`
|
|
@@ -98,6 +100,11 @@ module.exports = (config) => {
|
|
|
98
100
|
config.when = when;
|
|
99
101
|
|
|
100
102
|
event.dispatcher.on(event.step.started, (step) => {
|
|
103
|
+
if (process.env.TRY_TO === 'true') {
|
|
104
|
+
log('Info: RetryFailedStep plugin is disabled inside tryTo block');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
101
108
|
// if a step is ignored - return
|
|
102
109
|
for (const ignored of config.ignoredSteps) {
|
|
103
110
|
if (step.name === ignored) return;
|
|
@@ -114,6 +121,8 @@ module.exports = (config) => {
|
|
|
114
121
|
|
|
115
122
|
event.dispatcher.on(event.test.before, (test) => {
|
|
116
123
|
if (test && test.disableRetryFailedStep) return; // disable retry when a test is not active
|
|
124
|
+
// this env var is used to set the retries inside _before() block of helpers
|
|
125
|
+
process.env.FAILED_STEP_RETRIES = config.retries;
|
|
117
126
|
recorder.retry(config);
|
|
118
127
|
});
|
|
119
128
|
};
|
package/lib/plugin/retryTo.js
CHANGED
|
@@ -83,16 +83,15 @@ module.exports = function (config) {
|
|
|
83
83
|
return retryTo;
|
|
84
84
|
|
|
85
85
|
function retryTo(callback, maxTries, pollInterval = undefined) {
|
|
86
|
-
const mode = store.debugMode;
|
|
87
86
|
let tries = 1;
|
|
88
87
|
if (!pollInterval) pollInterval = config.pollInterval;
|
|
89
88
|
|
|
90
89
|
let err = null;
|
|
91
90
|
|
|
92
91
|
return new Promise((done) => {
|
|
93
|
-
const tryBlock = () => {
|
|
92
|
+
const tryBlock = async () => {
|
|
94
93
|
recorder.session.start(`retryTo ${tries}`);
|
|
95
|
-
callback(tries);
|
|
94
|
+
await callback(tries);
|
|
96
95
|
recorder.add(() => {
|
|
97
96
|
recorder.session.restore(`retryTo ${tries}`);
|
|
98
97
|
done(null);
|
|
@@ -113,7 +112,6 @@ module.exports = function (config) {
|
|
|
113
112
|
};
|
|
114
113
|
|
|
115
114
|
recorder.add('retryTo', async () => {
|
|
116
|
-
store.debugMode = true;
|
|
117
115
|
tryBlock();
|
|
118
116
|
});
|
|
119
117
|
}).then(() => {
|
package/lib/plugin/selenoid.js
CHANGED
|
@@ -58,7 +58,12 @@ let seleniumUrl = 'http://localhost:$port$';
|
|
|
58
58
|
const supportedHelpers = ['WebDriver'];
|
|
59
59
|
const SELENOID_START_TIMEOUT = 2000;
|
|
60
60
|
const SELENOID_STOP_TIMEOUT = 10000;
|
|
61
|
-
const wait = time => new Promise((res) =>
|
|
61
|
+
const wait = time => new Promise((res) => {
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
res();
|
|
65
|
+
}, time);
|
|
66
|
+
});
|
|
62
67
|
|
|
63
68
|
/**
|
|
64
69
|
* [Selenoid](https://aerokube.com/selenoid/) plugin automatically starts browsers and video recording.
|