codeceptjs 3.5.15 → 3.6.0-beta.1.ai-healers
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 +2 -2
- package/bin/codecept.js +66 -30
- package/docs/advanced.md +351 -0
- package/docs/ai.md +365 -0
- package/docs/api.md +323 -0
- package/docs/basics.md +979 -0
- package/docs/bdd.md +539 -0
- package/docs/best.md +237 -0
- package/docs/books.md +37 -0
- package/docs/bootstrap.md +135 -0
- package/docs/build/AI.js +124 -0
- package/docs/build/ApiDataFactory.js +410 -0
- package/docs/build/Appium.js +2027 -0
- package/docs/build/Expect.js +422 -0
- package/docs/build/FileSystem.js +228 -0
- package/docs/build/GraphQL.js +229 -0
- package/docs/build/GraphQLDataFactory.js +309 -0
- package/docs/build/JSONResponse.js +338 -0
- package/docs/build/Mochawesome.js +71 -0
- package/docs/build/Nightmare.js +2152 -0
- package/docs/build/Playwright.js +5110 -0
- package/docs/build/Protractor.js +2706 -0
- package/docs/build/Puppeteer.js +3905 -0
- package/docs/build/REST.js +344 -0
- package/docs/build/TestCafe.js +2125 -0
- package/docs/build/WebDriver.js +4240 -0
- package/docs/changelog.md +2572 -0
- package/docs/commands.md +266 -0
- package/docs/community-helpers.md +58 -0
- package/docs/configuration.md +157 -0
- package/docs/continuous-integration.md +22 -0
- package/docs/custom-helpers.md +306 -0
- package/docs/data.md +379 -0
- package/docs/detox.md +235 -0
- package/docs/docker.md +136 -0
- package/docs/email.md +183 -0
- package/docs/examples.md +149 -0
- package/docs/heal.md +186 -0
- package/docs/helpers/ApiDataFactory.md +266 -0
- package/docs/helpers/Appium.md +1374 -0
- package/docs/helpers/Detox.md +586 -0
- package/docs/helpers/Expect.md +275 -0
- package/docs/helpers/FileSystem.md +152 -0
- package/docs/helpers/GraphQL.md +151 -0
- package/docs/helpers/GraphQLDataFactory.md +226 -0
- package/docs/helpers/JSONResponse.md +254 -0
- package/docs/helpers/Mochawesome.md +8 -0
- package/docs/helpers/MockRequest.md +377 -0
- package/docs/helpers/Nightmare.md +1305 -0
- package/docs/helpers/OpenAI.md +70 -0
- package/docs/helpers/Playwright.md +2759 -0
- package/docs/helpers/Polly.md +44 -0
- package/docs/helpers/Protractor.md +1769 -0
- package/docs/helpers/Puppeteer-firefox.md +86 -0
- package/docs/helpers/Puppeteer.md +2317 -0
- package/docs/helpers/REST.md +218 -0
- package/docs/helpers/TestCafe.md +1321 -0
- package/docs/helpers/WebDriver.md +2547 -0
- package/docs/hooks.md +340 -0
- package/docs/index.md +111 -0
- package/docs/installation.md +75 -0
- package/docs/internal-api.md +266 -0
- package/docs/locators.md +339 -0
- package/docs/mobile-react-native-locators.md +67 -0
- package/docs/mobile.md +338 -0
- package/docs/pageobjects.md +291 -0
- package/docs/parallel.md +400 -0
- package/docs/playwright.md +632 -0
- package/docs/plugins.md +1247 -0
- package/docs/puppeteer.md +316 -0
- package/docs/quickstart.md +162 -0
- package/docs/react.md +70 -0
- package/docs/reports.md +392 -0
- package/docs/secrets.md +36 -0
- package/docs/shadow.md +68 -0
- package/docs/shared/keys.mustache +31 -0
- package/docs/shared/react.mustache +1 -0
- package/docs/testcafe.md +174 -0
- package/docs/translation.md +247 -0
- package/docs/tutorial.md +271 -0
- package/docs/typescript.md +180 -0
- package/docs/ui.md +59 -0
- package/docs/videos.md +28 -0
- package/docs/visual.md +202 -0
- package/docs/vue.md +143 -0
- package/docs/webdriver.md +701 -0
- package/docs/wiki/Books-&-Posts.md +27 -0
- package/docs/wiki/Community-Helpers-&-Plugins.md +53 -0
- package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +61 -0
- package/docs/wiki/Examples.md +145 -0
- package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +68 -0
- package/docs/wiki/Home.md +16 -0
- package/docs/wiki/Migration-to-Appium-v2---CodeceptJS.md +83 -0
- package/docs/wiki/Release-Process.md +24 -0
- package/docs/wiki/Roadmap.md +23 -0
- package/docs/wiki/Tests.md +1393 -0
- package/docs/wiki/Upgrading-to-CodeceptJS-3.md +153 -0
- package/docs/wiki/Videos.md +19 -0
- package/lib/actor.js +3 -6
- package/lib/ai.js +152 -80
- package/lib/cli.js +1 -0
- package/lib/command/dryRun.js +13 -44
- package/lib/command/generate.js +34 -0
- package/lib/command/run-workers.js +3 -0
- package/lib/command/run.js +3 -0
- package/lib/container.js +2 -0
- package/lib/heal.js +172 -0
- package/lib/helper/AI.js +124 -0
- package/lib/helper/Appium.js +12 -36
- package/lib/helper/Expect.js +8 -11
- package/lib/helper/JSONResponse.js +8 -8
- package/lib/helper/Playwright.js +240 -100
- package/lib/helper/Puppeteer.js +68 -182
- package/lib/helper/REST.js +1 -4
- package/lib/helper/WebDriver.js +10 -324
- package/lib/index.js +3 -0
- package/lib/listener/steps.js +0 -2
- package/lib/locator.js +4 -13
- package/lib/plugin/coverage.js +99 -112
- package/lib/plugin/heal.js +26 -117
- package/lib/recorder.js +11 -5
- package/lib/step.js +1 -3
- package/lib/store.js +2 -0
- package/lib/template/heal.js +39 -0
- package/package.json +35 -47
- package/typings/index.d.ts +0 -17
- package/typings/promiseBasedTypes.d.ts +57 -340
- package/typings/types.d.ts +73 -433
- package/docs/webapi/dontSeeTraffic.mustache +0 -13
- package/docs/webapi/flushNetworkTraffics.mustache +0 -5
- package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
- package/docs/webapi/seeTraffic.mustache +0 -36
- package/docs/webapi/startRecordingTraffic.mustache +0 -8
- package/docs/webapi/stopRecordingTraffic.mustache +0 -5
- package/docs/webapi/waitForCookie.mustache +0 -9
- package/lib/helper/MockServer.js +0 -221
- package/lib/helper/errors/ElementAssertion.js +0 -38
- package/lib/helper/networkTraffics/utils.js +0 -137
- /package/{lib/helper → docs/build}/OpenAI.js +0 -0
package/lib/helper/WebDriver.js
CHANGED
|
@@ -6,7 +6,6 @@ const fs = require('fs');
|
|
|
6
6
|
|
|
7
7
|
const Helper = require('@codeceptjs/helper');
|
|
8
8
|
const crypto = require('crypto');
|
|
9
|
-
const promiseRetry = require('promise-retry');
|
|
10
9
|
const stringIncludes = require('../assert/include').includes;
|
|
11
10
|
const { urlEquals, equals } = require('../assert/equal');
|
|
12
11
|
const { debug } = require('../output');
|
|
@@ -33,10 +32,6 @@ const { highlightElement } = require('./scripts/highlightElement');
|
|
|
33
32
|
const store = require('../store');
|
|
34
33
|
const { focusElement } = require('./scripts/focusElement');
|
|
35
34
|
const { blurElement } = require('./scripts/blurElement');
|
|
36
|
-
const {
|
|
37
|
-
dontSeeElementError, seeElementError, seeElementInDOMError, dontSeeElementInDOMError,
|
|
38
|
-
} = require('./errors/ElementAssertion');
|
|
39
|
-
const { allParameterValuePairsMatchExtreme, extractQueryObjects, createAdvancedTestResults } = require('./networkTraffics/utils');
|
|
40
35
|
|
|
41
36
|
const SHADOW = 'shadow';
|
|
42
37
|
const webRoot = 'body';
|
|
@@ -453,11 +448,6 @@ class WebDriver extends Helper {
|
|
|
453
448
|
this.activeSessionName = '';
|
|
454
449
|
this.customLocatorStrategies = config.customLocatorStrategies;
|
|
455
450
|
|
|
456
|
-
// for network stuff
|
|
457
|
-
this.requests = [];
|
|
458
|
-
this.recording = false;
|
|
459
|
-
this.recordedAtLeastOnce = false;
|
|
460
|
-
|
|
461
451
|
this._setConfig(config);
|
|
462
452
|
|
|
463
453
|
Locator.addFilter((locator, result) => {
|
|
@@ -497,9 +487,8 @@ class WebDriver extends Helper {
|
|
|
497
487
|
if (config.host) {
|
|
498
488
|
// webdriverio spec
|
|
499
489
|
config.hostname = config.host;
|
|
500
|
-
config.path =
|
|
490
|
+
config.path = '/wd/hub';
|
|
501
491
|
}
|
|
502
|
-
|
|
503
492
|
config.baseUrl = config.url || config.baseUrl;
|
|
504
493
|
if (config.desiredCapabilities && Object.keys(config.desiredCapabilities).length) {
|
|
505
494
|
config.capabilities = config.desiredCapabilities;
|
|
@@ -640,11 +629,6 @@ class WebDriver extends Helper {
|
|
|
640
629
|
if (this.browser.capabilities && this.browser.capabilities.platformName) {
|
|
641
630
|
this.browser.capabilities.platformName = this.browser.capabilities.platformName.toLowerCase();
|
|
642
631
|
}
|
|
643
|
-
|
|
644
|
-
if (this.options.automationProtocol) {
|
|
645
|
-
this.puppeteerBrowser = await this.browser.getPuppeteer();
|
|
646
|
-
}
|
|
647
|
-
|
|
648
632
|
return this.browser;
|
|
649
633
|
}
|
|
650
634
|
|
|
@@ -977,12 +961,12 @@ class WebDriver extends Helper {
|
|
|
977
961
|
*/
|
|
978
962
|
amOnPage(url) {
|
|
979
963
|
let split_url;
|
|
980
|
-
if (this.
|
|
964
|
+
if (this.config.basicAuth) {
|
|
981
965
|
if (url.startsWith('/')) {
|
|
982
|
-
url = this.
|
|
966
|
+
url = this.config.url + url;
|
|
983
967
|
}
|
|
984
968
|
split_url = url.split('//');
|
|
985
|
-
url = `${split_url[0]}//${this.
|
|
969
|
+
url = `${split_url[0]}//${this.config.basicAuth.username}:${this.config.basicAuth.password}@${split_url[1]}`;
|
|
986
970
|
}
|
|
987
971
|
return this.browser.url(url);
|
|
988
972
|
}
|
|
@@ -1477,11 +1461,7 @@ class WebDriver extends Helper {
|
|
|
1477
1461
|
const res = await this._locate(locator, true);
|
|
1478
1462
|
assertElementExists(res, locator);
|
|
1479
1463
|
const selected = await forEachAsync(res, async el => el.isDisplayed());
|
|
1480
|
-
|
|
1481
|
-
return truth(`elements of ${(new Locator(locator))}`, 'to be seen').assert(selected);
|
|
1482
|
-
} catch (e) {
|
|
1483
|
-
dontSeeElementError(locator);
|
|
1484
|
-
}
|
|
1464
|
+
return truth(`elements of ${(new Locator(locator))}`, 'to be seen').assert(selected);
|
|
1485
1465
|
}
|
|
1486
1466
|
|
|
1487
1467
|
/**
|
|
@@ -1494,11 +1474,7 @@ class WebDriver extends Helper {
|
|
|
1494
1474
|
return truth(`elements of ${(new Locator(locator))}`, 'to be seen').negate(false);
|
|
1495
1475
|
}
|
|
1496
1476
|
const selected = await forEachAsync(res, async el => el.isDisplayed());
|
|
1497
|
-
|
|
1498
|
-
return truth(`elements of ${(new Locator(locator))}`, 'to be seen').negate(selected);
|
|
1499
|
-
} catch (e) {
|
|
1500
|
-
seeElementError(locator);
|
|
1501
|
-
}
|
|
1477
|
+
return truth(`elements of ${(new Locator(locator))}`, 'to be seen').negate(selected);
|
|
1502
1478
|
}
|
|
1503
1479
|
|
|
1504
1480
|
/**
|
|
@@ -1507,11 +1483,7 @@ class WebDriver extends Helper {
|
|
|
1507
1483
|
*/
|
|
1508
1484
|
async seeElementInDOM(locator) {
|
|
1509
1485
|
const res = await this._res(locator);
|
|
1510
|
-
|
|
1511
|
-
return empty('elements').negate(res);
|
|
1512
|
-
} catch (e) {
|
|
1513
|
-
dontSeeElementInDOMError(locator);
|
|
1514
|
-
}
|
|
1486
|
+
return empty('elements').negate(res);
|
|
1515
1487
|
}
|
|
1516
1488
|
|
|
1517
1489
|
/**
|
|
@@ -1520,11 +1492,7 @@ class WebDriver extends Helper {
|
|
|
1520
1492
|
*/
|
|
1521
1493
|
async dontSeeElementInDOM(locator) {
|
|
1522
1494
|
const res = await this._res(locator);
|
|
1523
|
-
|
|
1524
|
-
return empty('elements').assert(res);
|
|
1525
|
-
} catch (e) {
|
|
1526
|
-
seeElementInDOMError(locator);
|
|
1527
|
-
}
|
|
1495
|
+
return empty('elements').assert(res);
|
|
1528
1496
|
}
|
|
1529
1497
|
|
|
1530
1498
|
/**
|
|
@@ -1644,8 +1612,6 @@ class WebDriver extends Helper {
|
|
|
1644
1612
|
for (let i = 0; i < val.length; ++i) {
|
|
1645
1613
|
const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(val[i], 10);
|
|
1646
1614
|
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
|
|
1647
|
-
// the attribute could be a boolean
|
|
1648
|
-
if (typeof _actual === 'boolean') return _actual === _expected;
|
|
1649
1615
|
if (_actual !== _expected) return false;
|
|
1650
1616
|
}
|
|
1651
1617
|
return true;
|
|
@@ -1873,37 +1839,6 @@ class WebDriver extends Helper {
|
|
|
1873
1839
|
return cookie[0];
|
|
1874
1840
|
}
|
|
1875
1841
|
|
|
1876
|
-
/**
|
|
1877
|
-
* {{> waitForCookie }}
|
|
1878
|
-
*/
|
|
1879
|
-
async waitForCookie(name, sec) {
|
|
1880
|
-
// by default, we will retry 3 times
|
|
1881
|
-
let retries = 3;
|
|
1882
|
-
const waitTimeout = sec || this.options.waitForTimeoutInSeconds;
|
|
1883
|
-
|
|
1884
|
-
if (sec) {
|
|
1885
|
-
retries = sec;
|
|
1886
|
-
} else {
|
|
1887
|
-
retries = waitTimeout - 1;
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
|
-
return promiseRetry(async (retry, number) => {
|
|
1891
|
-
const _grabCookie = async (name) => {
|
|
1892
|
-
const cookie = await this.browser.getCookies([name]);
|
|
1893
|
-
if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`);
|
|
1894
|
-
};
|
|
1895
|
-
|
|
1896
|
-
this.debugSection('Wait for cookie: ', name);
|
|
1897
|
-
if (number > 1) this.debugSection('Retrying... Attempt #', number);
|
|
1898
|
-
|
|
1899
|
-
try {
|
|
1900
|
-
await _grabCookie(name);
|
|
1901
|
-
} catch (e) {
|
|
1902
|
-
retry(e);
|
|
1903
|
-
}
|
|
1904
|
-
}, { retries, maxTimeout: 1000 });
|
|
1905
|
-
}
|
|
1906
|
-
|
|
1907
1842
|
/**
|
|
1908
1843
|
* Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt.
|
|
1909
1844
|
* Don't confuse popups with modal windows, as created by [various
|
|
@@ -2615,9 +2550,9 @@ class WebDriver extends Helper {
|
|
|
2615
2550
|
return;
|
|
2616
2551
|
}
|
|
2617
2552
|
this.geoLocation = { latitude, longitude };
|
|
2618
|
-
|
|
2553
|
+
const puppeteerBrowser = await this.browser.getPuppeteer();
|
|
2619
2554
|
await this.browser.call(async () => {
|
|
2620
|
-
const pages = await
|
|
2555
|
+
const pages = await puppeteerBrowser.pages();
|
|
2621
2556
|
await pages[0].setGeolocation({ latitude, longitude });
|
|
2622
2557
|
});
|
|
2623
2558
|
}
|
|
@@ -2679,255 +2614,6 @@ class WebDriver extends Helper {
|
|
|
2679
2614
|
runInWeb(fn) {
|
|
2680
2615
|
return fn();
|
|
2681
2616
|
}
|
|
2682
|
-
|
|
2683
|
-
/**
|
|
2684
|
-
*
|
|
2685
|
-
* _Note:_ Only works when devtoolsProtocol is enabled.
|
|
2686
|
-
*
|
|
2687
|
-
* {{> flushNetworkTraffics }}
|
|
2688
|
-
*/
|
|
2689
|
-
flushNetworkTraffics() {
|
|
2690
|
-
if (!this.options.automationProtocol) {
|
|
2691
|
-
console.log('* Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration');
|
|
2692
|
-
return;
|
|
2693
|
-
}
|
|
2694
|
-
this.requests = [];
|
|
2695
|
-
}
|
|
2696
|
-
|
|
2697
|
-
/**
|
|
2698
|
-
*
|
|
2699
|
-
* _Note:_ Only works when devtoolsProtocol is enabled.
|
|
2700
|
-
*
|
|
2701
|
-
* {{> stopRecordingTraffic }}
|
|
2702
|
-
*/
|
|
2703
|
-
stopRecordingTraffic() {
|
|
2704
|
-
if (!this.options.automationProtocol) {
|
|
2705
|
-
console.log('* Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration');
|
|
2706
|
-
return;
|
|
2707
|
-
}
|
|
2708
|
-
this.page.removeAllListeners('request');
|
|
2709
|
-
this.recording = false;
|
|
2710
|
-
}
|
|
2711
|
-
|
|
2712
|
-
/**
|
|
2713
|
-
*
|
|
2714
|
-
* _Note:_ Only works when devtoolsProtocol is enabled.
|
|
2715
|
-
*
|
|
2716
|
-
* {{> startRecordingTraffic }}
|
|
2717
|
-
*
|
|
2718
|
-
*/
|
|
2719
|
-
async startRecordingTraffic() {
|
|
2720
|
-
if (!this.options.automationProtocol) {
|
|
2721
|
-
console.log('* Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration');
|
|
2722
|
-
return;
|
|
2723
|
-
}
|
|
2724
|
-
this.flushNetworkTraffics();
|
|
2725
|
-
this.recording = true;
|
|
2726
|
-
this.recordedAtLeastOnce = true;
|
|
2727
|
-
|
|
2728
|
-
this.page = (await this.puppeteerBrowser.pages())[0];
|
|
2729
|
-
await this.page.setRequestInterception(true);
|
|
2730
|
-
|
|
2731
|
-
this.page.on('request', (request) => {
|
|
2732
|
-
const information = {
|
|
2733
|
-
url: request.url(),
|
|
2734
|
-
method: request.method(),
|
|
2735
|
-
requestHeaders: request.headers(),
|
|
2736
|
-
requestPostData: request.postData(),
|
|
2737
|
-
response: request.response(),
|
|
2738
|
-
};
|
|
2739
|
-
|
|
2740
|
-
this.debugSection('REQUEST: ', JSON.stringify(information));
|
|
2741
|
-
|
|
2742
|
-
if (typeof information.requestPostData === 'object') {
|
|
2743
|
-
information.requestPostData = JSON.parse(information.requestPostData);
|
|
2744
|
-
}
|
|
2745
|
-
request.continue();
|
|
2746
|
-
this.requests.push(information);
|
|
2747
|
-
});
|
|
2748
|
-
}
|
|
2749
|
-
|
|
2750
|
-
/**
|
|
2751
|
-
*
|
|
2752
|
-
* _Note:_ Only works when devtoolsProtocol is enabled.
|
|
2753
|
-
*
|
|
2754
|
-
* {{> grabRecordedNetworkTraffics }}
|
|
2755
|
-
*/
|
|
2756
|
-
async grabRecordedNetworkTraffics() {
|
|
2757
|
-
if (!this.options.automationProtocol) {
|
|
2758
|
-
console.log('* Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration');
|
|
2759
|
-
return;
|
|
2760
|
-
}
|
|
2761
|
-
if (!this.recording || !this.recordedAtLeastOnce) {
|
|
2762
|
-
throw new Error('Failure in test automation. You use "I.grabRecordedNetworkTraffics", but "I.startRecordingTraffic" was never called before.');
|
|
2763
|
-
}
|
|
2764
|
-
|
|
2765
|
-
const promises = this.requests.map(async (request) => {
|
|
2766
|
-
const resp = await request.response;
|
|
2767
|
-
|
|
2768
|
-
if (!resp) {
|
|
2769
|
-
return {
|
|
2770
|
-
url: '',
|
|
2771
|
-
response: {
|
|
2772
|
-
status: '',
|
|
2773
|
-
statusText: '',
|
|
2774
|
-
body: '',
|
|
2775
|
-
},
|
|
2776
|
-
};
|
|
2777
|
-
}
|
|
2778
|
-
|
|
2779
|
-
let body;
|
|
2780
|
-
try {
|
|
2781
|
-
// There's no 'body' for some requests (redirect etc...)
|
|
2782
|
-
body = JSON.parse((await resp.body()).toString());
|
|
2783
|
-
} catch (e) {
|
|
2784
|
-
// only interested in JSON, not HTML responses.
|
|
2785
|
-
}
|
|
2786
|
-
|
|
2787
|
-
return {
|
|
2788
|
-
url: resp.url(),
|
|
2789
|
-
response: {
|
|
2790
|
-
status: resp.status(),
|
|
2791
|
-
statusText: resp.statusText(),
|
|
2792
|
-
body,
|
|
2793
|
-
},
|
|
2794
|
-
};
|
|
2795
|
-
});
|
|
2796
|
-
return Promise.all(promises);
|
|
2797
|
-
}
|
|
2798
|
-
|
|
2799
|
-
/**
|
|
2800
|
-
*
|
|
2801
|
-
* _Note:_ Only works when devtoolsProtocol is enabled.
|
|
2802
|
-
*
|
|
2803
|
-
* {{> seeTraffic }}
|
|
2804
|
-
*/
|
|
2805
|
-
async seeTraffic({
|
|
2806
|
-
name, url, parameters, requestPostData, timeout = 10,
|
|
2807
|
-
}) {
|
|
2808
|
-
if (!this.options.automationProtocol) {
|
|
2809
|
-
console.log('* Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration');
|
|
2810
|
-
return;
|
|
2811
|
-
}
|
|
2812
|
-
if (!name) {
|
|
2813
|
-
throw new Error('Missing required key "name" in object given to "I.seeTraffic".');
|
|
2814
|
-
}
|
|
2815
|
-
|
|
2816
|
-
if (!url) {
|
|
2817
|
-
throw new Error('Missing required key "url" in object given to "I.seeTraffic".');
|
|
2818
|
-
}
|
|
2819
|
-
|
|
2820
|
-
if (!this.recording || !this.recordedAtLeastOnce) {
|
|
2821
|
-
throw new Error('Failure in test automation. You use "I.seeTraffic", but "I.startRecordingTraffic" was never called before.');
|
|
2822
|
-
}
|
|
2823
|
-
|
|
2824
|
-
for (let i = 0; i <= timeout * 2; i++) {
|
|
2825
|
-
const found = this._isInTraffic(url, parameters);
|
|
2826
|
-
if (found) {
|
|
2827
|
-
return true;
|
|
2828
|
-
}
|
|
2829
|
-
await new Promise((done) => {
|
|
2830
|
-
setTimeout(done, 1000);
|
|
2831
|
-
});
|
|
2832
|
-
}
|
|
2833
|
-
|
|
2834
|
-
// check request post data
|
|
2835
|
-
if (requestPostData && this._isInTraffic(url)) {
|
|
2836
|
-
const advancedTestResults = createAdvancedTestResults(url, requestPostData, this.requests);
|
|
2837
|
-
|
|
2838
|
-
assert.equal(advancedTestResults, true, `Traffic named "${name}" found correct URL ${url}, BUT the post data did not match:\n ${advancedTestResults}`);
|
|
2839
|
-
} else if (parameters && this._isInTraffic(url)) {
|
|
2840
|
-
const advancedTestResults = createAdvancedTestResults(url, parameters, this.requests);
|
|
2841
|
-
|
|
2842
|
-
assert.fail(
|
|
2843
|
-
`Traffic named "${name}" found correct URL ${url}, BUT the query parameters did not match:\n`
|
|
2844
|
-
+ `${advancedTestResults}`,
|
|
2845
|
-
);
|
|
2846
|
-
} else {
|
|
2847
|
-
assert.fail(
|
|
2848
|
-
`Traffic named "${name}" not found in recorded traffic within ${timeout} seconds.\n`
|
|
2849
|
-
+ `Expected url: ${url}.\n`
|
|
2850
|
-
+ `Recorded traffic:\n${this._getTrafficDump()}`,
|
|
2851
|
-
);
|
|
2852
|
-
}
|
|
2853
|
-
}
|
|
2854
|
-
|
|
2855
|
-
/**
|
|
2856
|
-
*
|
|
2857
|
-
* _Note:_ Only works when devtoolsProtocol is enabled.
|
|
2858
|
-
*
|
|
2859
|
-
* {{> dontSeeTraffic }}
|
|
2860
|
-
*
|
|
2861
|
-
*/
|
|
2862
|
-
dontSeeTraffic({ name, url }) {
|
|
2863
|
-
if (!this.options.automationProtocol) {
|
|
2864
|
-
console.log('* Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration');
|
|
2865
|
-
return;
|
|
2866
|
-
}
|
|
2867
|
-
if (!this.recordedAtLeastOnce) {
|
|
2868
|
-
throw new Error('Failure in test automation. You use "I.dontSeeTraffic", but "I.startRecordingTraffic" was never called before.');
|
|
2869
|
-
}
|
|
2870
|
-
|
|
2871
|
-
if (!name) {
|
|
2872
|
-
throw new Error('Missing required key "name" in object given to "I.dontSeeTraffic".');
|
|
2873
|
-
}
|
|
2874
|
-
|
|
2875
|
-
if (!url) {
|
|
2876
|
-
throw new Error('Missing required key "url" in object given to "I.dontSeeTraffic".');
|
|
2877
|
-
}
|
|
2878
|
-
|
|
2879
|
-
if (this._isInTraffic(url)) {
|
|
2880
|
-
assert.fail(`Traffic with name "${name}" (URL: "${url}') found, but was not expected to be found.`);
|
|
2881
|
-
}
|
|
2882
|
-
}
|
|
2883
|
-
|
|
2884
|
-
/**
|
|
2885
|
-
* Checks if URL with parameters is part of network traffic. Returns true or false. Internal method for this helper.
|
|
2886
|
-
*
|
|
2887
|
-
* @param url URL to look for.
|
|
2888
|
-
* @param [parameters] Parameters that this URL needs to contain
|
|
2889
|
-
* @return {boolean} Whether or not URL with parameters is part of network traffic.
|
|
2890
|
-
* @private
|
|
2891
|
-
*/
|
|
2892
|
-
_isInTraffic(url, parameters) {
|
|
2893
|
-
let isInTraffic = false;
|
|
2894
|
-
this.requests.forEach((request) => {
|
|
2895
|
-
if (isInTraffic) {
|
|
2896
|
-
return; // We already found traffic. Continue with next request
|
|
2897
|
-
}
|
|
2898
|
-
|
|
2899
|
-
if (!request.url.match(new RegExp(url))) {
|
|
2900
|
-
return; // url not found in this request. continue with next request
|
|
2901
|
-
}
|
|
2902
|
-
|
|
2903
|
-
// URL has matched. Now we check the parameters
|
|
2904
|
-
|
|
2905
|
-
if (parameters) {
|
|
2906
|
-
const advancedReport = allParameterValuePairsMatchExtreme(extractQueryObjects(request.url), parameters);
|
|
2907
|
-
if (advancedReport === true) {
|
|
2908
|
-
isInTraffic = true;
|
|
2909
|
-
}
|
|
2910
|
-
} else {
|
|
2911
|
-
isInTraffic = true;
|
|
2912
|
-
}
|
|
2913
|
-
});
|
|
2914
|
-
|
|
2915
|
-
return isInTraffic;
|
|
2916
|
-
}
|
|
2917
|
-
|
|
2918
|
-
/**
|
|
2919
|
-
* Returns all URLs of all network requests recorded so far during execution of test scenario.
|
|
2920
|
-
*
|
|
2921
|
-
* @return {string} List of URLs recorded as a string, separated by new lines after each URL
|
|
2922
|
-
* @private
|
|
2923
|
-
*/
|
|
2924
|
-
_getTrafficDump() {
|
|
2925
|
-
let dumpedTraffic = '';
|
|
2926
|
-
this.requests.forEach((request) => {
|
|
2927
|
-
dumpedTraffic += `${request.method} - ${request.url}\n`;
|
|
2928
|
-
});
|
|
2929
|
-
return dumpedTraffic;
|
|
2930
|
-
}
|
|
2931
2617
|
}
|
|
2932
2618
|
|
|
2933
2619
|
async function proceedSee(assertType, text, context, strict = false) {
|
package/lib/index.js
CHANGED
package/lib/listener/steps.js
CHANGED
|
@@ -67,7 +67,6 @@ module.exports = function () {
|
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
event.dispatcher.on(event.step.started, (step) => {
|
|
70
|
-
if (store.debugMode) return;
|
|
71
70
|
step.startedAt = +new Date();
|
|
72
71
|
step.test = currentTest;
|
|
73
72
|
if (currentHook && Array.isArray(currentHook.steps)) {
|
|
@@ -78,7 +77,6 @@ module.exports = function () {
|
|
|
78
77
|
});
|
|
79
78
|
|
|
80
79
|
event.dispatcher.on(event.step.finished, (step) => {
|
|
81
|
-
if (store.debugMode) return;
|
|
82
80
|
step.finishedAt = +new Date();
|
|
83
81
|
if (step.startedAt) step.duration = step.finishedAt - step.startedAt;
|
|
84
82
|
debug(`Step '${step}' finished; Duration: ${step.duration || 0}ms`);
|
package/lib/locator.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
const cssToXPath = require('csstoxpath');
|
|
2
2
|
const { sprintf } = require('sprintf-js');
|
|
3
3
|
|
|
4
4
|
const { xpathLocator } = require('./utils');
|
|
@@ -162,17 +162,8 @@ class Locator {
|
|
|
162
162
|
* @returns {string}
|
|
163
163
|
*/
|
|
164
164
|
toXPath(pseudoSelector = '') {
|
|
165
|
-
const locator = `${this.value}${pseudoSelector}`;
|
|
166
|
-
const limitation = [':nth-of-type', ':first-of-type', ':last-of-type', ':nth-last-child', ':nth-last-of-type', ':checked', ':disabled', ':enabled', ':required', ':lang', ':nth-child'];
|
|
167
|
-
|
|
168
|
-
if (limitation.some(item => locator.includes(item))) {
|
|
169
|
-
cssToXPath = require('css-to-xpath');
|
|
170
|
-
} else {
|
|
171
|
-
cssToXPath = require('csstoxpath');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
165
|
if (this.isXPath()) return this.value;
|
|
175
|
-
if (this.isCSS()) return cssToXPath(
|
|
166
|
+
if (this.isCSS()) return cssToXPath(`${this.value}${pseudoSelector}`);
|
|
176
167
|
|
|
177
168
|
throw new Error('Can\'t be converted to XPath');
|
|
178
169
|
}
|
|
@@ -259,7 +250,7 @@ class Locator {
|
|
|
259
250
|
*/
|
|
260
251
|
withText(text) {
|
|
261
252
|
text = xpathLocator.literal(text);
|
|
262
|
-
const xpath =
|
|
253
|
+
const xpath = this.toXPath(`:text-contains-case(${text})`);
|
|
263
254
|
return new Locator({ xpath });
|
|
264
255
|
}
|
|
265
256
|
|
|
@@ -270,7 +261,7 @@ class Locator {
|
|
|
270
261
|
*/
|
|
271
262
|
withTextEquals(text) {
|
|
272
263
|
text = xpathLocator.literal(text);
|
|
273
|
-
const xpath =
|
|
264
|
+
const xpath = this.toXPath(`:text-case(${text})`);
|
|
274
265
|
return new Locator({ xpath });
|
|
275
266
|
}
|
|
276
267
|
|