codeceptjs 3.6.6 → 4.0.0-beta.1
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/bin/codecept.js +81 -84
- package/lib/actor.js +13 -13
- package/lib/ai.js +13 -10
- package/lib/assert/empty.js +21 -20
- package/lib/assert/equal.js +39 -37
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +47 -46
- package/lib/assert/throws.js +11 -13
- package/lib/assert/truth.js +22 -19
- package/lib/assert.js +2 -4
- package/lib/cli.js +49 -57
- package/lib/codecept.js +155 -142
- package/lib/colorUtils.js +3 -3
- package/lib/command/configMigrate.js +52 -58
- package/lib/command/definitions.js +89 -88
- package/lib/command/dryRun.js +68 -71
- package/lib/command/generate.js +188 -197
- package/lib/command/gherkin/init.js +16 -27
- package/lib/command/gherkin/snippets.js +20 -20
- package/lib/command/gherkin/steps.js +8 -8
- package/lib/command/info.js +38 -40
- package/lib/command/init.js +288 -290
- package/lib/command/interactive.js +32 -32
- package/lib/command/list.js +26 -26
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +3 -3
- package/lib/command/run-multiple/run.js +2 -6
- package/lib/command/run-multiple.js +93 -113
- package/lib/command/run-rerun.js +25 -20
- package/lib/command/run-workers.js +66 -64
- package/lib/command/run.js +29 -26
- package/lib/command/utils.js +65 -80
- package/lib/command/workers/runTests.js +10 -10
- package/lib/config.js +9 -10
- package/lib/container.js +48 -40
- package/lib/data/context.js +59 -60
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +29 -29
- package/lib/data/table.js +20 -26
- package/lib/dirname.js +5 -0
- package/lib/event.js +167 -163
- package/lib/heal.js +17 -13
- package/lib/helper/AI.js +41 -130
- package/lib/helper/ApiDataFactory.js +69 -73
- package/lib/helper/Appium.js +381 -412
- package/lib/helper/Expect.js +425 -0
- package/lib/helper/ExpectHelper.js +48 -40
- package/lib/helper/FileSystem.js +79 -80
- package/lib/helper/GraphQL.js +43 -44
- package/lib/helper/GraphQLDataFactory.js +50 -50
- package/lib/helper/JSONResponse.js +62 -65
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/MockServer.js +14 -12
- package/lib/helper/Nightmare.js +566 -662
- package/lib/helper/Playwright.js +1216 -1361
- package/lib/helper/Protractor.js +627 -663
- package/lib/helper/Puppeteer.js +1128 -1231
- package/lib/helper/REST.js +68 -159
- package/lib/helper/SoftExpectHelper.js +2 -2
- package/lib/helper/TestCafe.js +484 -490
- package/lib/helper/WebDriver.js +1156 -1297
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/helper/errors/ConnectionRefused.js +1 -1
- package/lib/helper/errors/ElementAssertion.js +2 -2
- package/lib/helper/errors/ElementNotFound.js +2 -2
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +1 -1
- package/lib/helper/extras/Console.js +1 -1
- package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
- package/lib/helper/extras/PlaywrightReactVueLocator.js +1 -1
- package/lib/helper/extras/PlaywrightRestartOpts.js +18 -21
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +3 -3
- package/lib/helper/network/actions.js +7 -14
- package/lib/helper/network/utils.js +2 -3
- package/lib/helper/scripts/blurElement.js +1 -1
- package/lib/helper/scripts/focusElement.js +1 -1
- package/lib/helper/scripts/highlightElement.js +1 -1
- package/lib/helper/scripts/isElementClickable.js +1 -1
- package/lib/helper/testcafe/testControllerHolder.js +1 -1
- package/lib/helper/testcafe/testcafe-utils.js +7 -6
- package/lib/helper.js +3 -1
- package/lib/history.js +5 -6
- package/lib/hooks.js +6 -6
- package/lib/html.js +7 -7
- package/lib/index.js +41 -25
- package/lib/interfaces/bdd.js +64 -47
- package/lib/interfaces/featureConfig.js +19 -19
- package/lib/interfaces/gherkin.js +118 -124
- package/lib/interfaces/scenarioConfig.js +29 -29
- package/lib/listener/artifacts.js +9 -9
- package/lib/listener/config.js +24 -24
- package/lib/listener/exit.js +12 -12
- package/lib/listener/helpers.js +42 -42
- package/lib/listener/mocha.js +11 -11
- package/lib/listener/retry.js +30 -32
- package/lib/listener/steps.js +53 -50
- package/lib/listener/timeout.js +54 -54
- package/lib/locator.js +10 -6
- package/lib/mochaFactory.js +15 -18
- package/lib/output.js +10 -6
- package/lib/parser.js +12 -15
- package/lib/pause.js +33 -40
- package/lib/plugin/allure.js +15 -15
- package/lib/plugin/autoDelay.js +37 -29
- package/lib/plugin/autoLogin.js +65 -70
- package/lib/plugin/commentStep.js +18 -18
- package/lib/plugin/coverage.js +67 -115
- package/lib/plugin/customLocator.js +20 -21
- package/lib/plugin/debugErrors.js +24 -24
- package/lib/plugin/eachElement.js +38 -38
- package/lib/plugin/fakerTransform.js +6 -6
- package/lib/plugin/heal.js +108 -67
- package/lib/plugin/pauseOnFail.js +11 -11
- package/lib/plugin/retryFailedStep.js +39 -32
- package/lib/plugin/retryTo.js +40 -46
- package/lib/plugin/screenshotOnFail.js +87 -109
- package/lib/plugin/selenoid.js +118 -131
- package/lib/plugin/standardActingHelpers.js +8 -2
- package/lib/plugin/stepByStepReport.js +91 -110
- package/lib/plugin/stepTimeout.js +23 -24
- package/lib/plugin/subtitles.js +35 -34
- package/lib/plugin/tryTo.js +30 -40
- package/lib/plugin/wdio.js +75 -78
- package/lib/recorder.js +17 -14
- package/lib/rerun.js +10 -11
- package/lib/scenario.js +23 -25
- package/lib/secret.js +2 -4
- package/lib/session.js +10 -10
- package/lib/step.js +9 -12
- package/lib/store.js +3 -2
- package/lib/transform.js +1 -1
- package/lib/translation.js +8 -7
- package/lib/ui.js +14 -12
- package/lib/utils.js +72 -70
- package/lib/within.js +10 -10
- package/lib/workerStorage.js +25 -27
- package/lib/workers.js +32 -29
- package/package.json +53 -51
- package/translations/de-DE.js +1 -1
- package/translations/fr-FR.js +1 -1
- package/translations/index.js +13 -9
- package/translations/it-IT.js +1 -1
- package/translations/ja-JP.js +1 -1
- package/translations/pl-PL.js +1 -1
- package/translations/pt-BR.js +1 -1
- package/translations/ru-RU.js +1 -1
- package/translations/zh-CN.js +1 -1
- package/translations/zh-TW.js +1 -1
- package/typings/index.d.ts +65 -415
- package/typings/promiseBasedTypes.d.ts +32 -0
- package/typings/types.d.ts +32 -0
package/lib/helper/Playwright.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const {
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import Helper from '@codeceptjs/helper';
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import assert from 'assert';
|
|
6
|
+
import promiseRetry from 'promise-retry';
|
|
7
|
+
import Locator from '../locator.js';
|
|
8
|
+
import recorder from '../recorder.js';
|
|
9
|
+
import { includes as stringIncludes } from '../assert/include.js';
|
|
10
|
+
import { urlEquals, equals } from '../assert/equal.js';
|
|
11
|
+
import { empty } from '../assert/empty.js';
|
|
12
|
+
import { truth } from '../assert/truth.js';
|
|
13
|
+
|
|
14
|
+
import {
|
|
16
15
|
xpathLocator,
|
|
17
16
|
ucfirst,
|
|
18
17
|
fileExists,
|
|
@@ -24,44 +23,40 @@ const {
|
|
|
24
23
|
clearString,
|
|
25
24
|
requireWithFallback,
|
|
26
25
|
normalizeSpacesInString,
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const {
|
|
44
|
-
setRestartStrategy,
|
|
45
|
-
restartsSession,
|
|
46
|
-
restartsContext,
|
|
47
|
-
restartsBrowser,
|
|
48
|
-
} = require('./extras/PlaywrightRestartOpts')
|
|
49
|
-
const { createValueEngine, createDisabledEngine } = require('./extras/PlaywrightPropEngine')
|
|
50
|
-
const {
|
|
26
|
+
} from '../utils.js';
|
|
27
|
+
|
|
28
|
+
import { isColorProperty, convertColorToRGBA } from '../colorUtils.js';
|
|
29
|
+
import ElementNotFound from './errors/ElementNotFound.js';
|
|
30
|
+
import RemoteBrowserConnectionRefused from './errors/RemoteBrowserConnectionRefused.js';
|
|
31
|
+
import Popup from './extras/Popup.js';
|
|
32
|
+
import Console from './extras/Console.js';
|
|
33
|
+
import { findByPlaywrightLocator, findReact, findVue } from './extras/PlaywrightReactVueLocator.js';
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
setRestartStrategy, restartsSession, restartsContext, restartsBrowser,
|
|
37
|
+
} from './extras/PlaywrightRestartOpts.js';
|
|
38
|
+
import { createValueEngine, createDisabledEngine } from './extras/PlaywrightPropEngine.js';
|
|
39
|
+
|
|
40
|
+
import {
|
|
51
41
|
seeElementError,
|
|
52
42
|
dontSeeElementError,
|
|
53
43
|
dontSeeElementInDOMError,
|
|
54
44
|
seeElementInDOMError,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
seeTraffic,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
45
|
+
} from './errors/ElementAssertion.js';
|
|
46
|
+
|
|
47
|
+
import {
|
|
48
|
+
dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics,
|
|
49
|
+
} from './network/actions.js';
|
|
50
|
+
|
|
51
|
+
let playwright;
|
|
52
|
+
let perfTiming;
|
|
53
|
+
let defaultSelectorEnginesInitialized = false;
|
|
63
54
|
|
|
64
|
-
const
|
|
55
|
+
const popupStore = new Popup();
|
|
56
|
+
const consoleLogStore = new Console();
|
|
57
|
+
const availableBrowsers = ['chromium', 'webkit', 'firefox', 'electron'];
|
|
58
|
+
|
|
59
|
+
const pathSeparator = path.sep;
|
|
65
60
|
|
|
66
61
|
/**
|
|
67
62
|
* ## Configuration
|
|
@@ -110,7 +105,7 @@ const pathSeparator = path.sep
|
|
|
110
105
|
* @prop {object} [recordHar] - record HAR and will be saved to `output/har`. See more of [HAR options](https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-har).
|
|
111
106
|
* @prop {string} [testIdAttribute=data-testid] - locate elements based on the testIdAttribute. See more of [locate by test id](https://playwright.dev/docs/locators#locate-by-test-id).
|
|
112
107
|
*/
|
|
113
|
-
const config = {}
|
|
108
|
+
const config = {};
|
|
114
109
|
|
|
115
110
|
/**
|
|
116
111
|
* Uses [Playwright](https://github.com/microsoft/playwright) library to run tests inside:
|
|
@@ -332,34 +327,34 @@ const config = {}
|
|
|
332
327
|
*/
|
|
333
328
|
class Playwright extends Helper {
|
|
334
329
|
constructor(config) {
|
|
335
|
-
super(config)
|
|
330
|
+
super(config);
|
|
336
331
|
|
|
337
|
-
playwright = requireWithFallback('playwright', 'playwright-core')
|
|
332
|
+
playwright = requireWithFallback('playwright', 'playwright-core');
|
|
338
333
|
|
|
339
334
|
// set defaults
|
|
340
|
-
this.isRemoteBrowser = false
|
|
341
|
-
this.isRunning = false
|
|
342
|
-
this.isAuthenticated = false
|
|
343
|
-
this.sessionPages = {}
|
|
344
|
-
this.activeSessionName = ''
|
|
345
|
-
this.isElectron = false
|
|
346
|
-
this.isCDPConnection = false
|
|
347
|
-
this.electronSessions = []
|
|
348
|
-
this.storageState = null
|
|
335
|
+
this.isRemoteBrowser = false;
|
|
336
|
+
this.isRunning = false;
|
|
337
|
+
this.isAuthenticated = false;
|
|
338
|
+
this.sessionPages = {};
|
|
339
|
+
this.activeSessionName = '';
|
|
340
|
+
this.isElectron = false;
|
|
341
|
+
this.isCDPConnection = false;
|
|
342
|
+
this.electronSessions = [];
|
|
343
|
+
this.storageState = null;
|
|
349
344
|
|
|
350
345
|
// for network stuff
|
|
351
|
-
this.requests = []
|
|
352
|
-
this.recording = false
|
|
353
|
-
this.recordedAtLeastOnce = false
|
|
346
|
+
this.requests = [];
|
|
347
|
+
this.recording = false;
|
|
348
|
+
this.recordedAtLeastOnce = false;
|
|
354
349
|
|
|
355
350
|
// for websocket messages
|
|
356
|
-
this.webSocketMessages = []
|
|
357
|
-
this.recordingWebSocketMessages = false
|
|
358
|
-
this.recordedWebSocketMessagesAtLeastOnce = false
|
|
359
|
-
this.cdpSession = null
|
|
351
|
+
this.webSocketMessages = [];
|
|
352
|
+
this.recordingWebSocketMessages = false;
|
|
353
|
+
this.recordedWebSocketMessagesAtLeastOnce = false;
|
|
354
|
+
this.cdpSession = null;
|
|
360
355
|
|
|
361
356
|
// override defaults with config
|
|
362
|
-
this._setConfig(config)
|
|
357
|
+
this._setConfig(config);
|
|
363
358
|
}
|
|
364
359
|
|
|
365
360
|
_validateConfig(config) {
|
|
@@ -386,65 +381,54 @@ class Playwright extends Helper {
|
|
|
386
381
|
use: { actionTimeout: 0 },
|
|
387
382
|
ignoreHTTPSErrors: false, // Adding it here o that context can be set up to ignore the SSL errors,
|
|
388
383
|
highlightElement: false,
|
|
389
|
-
}
|
|
384
|
+
};
|
|
390
385
|
|
|
391
|
-
process.env.testIdAttribute = 'data-testid'
|
|
392
|
-
config = Object.assign(defaults, config)
|
|
386
|
+
process.env.testIdAttribute = 'data-testid';
|
|
387
|
+
config = Object.assign(defaults, config);
|
|
393
388
|
|
|
394
389
|
if (availableBrowsers.indexOf(config.browser) < 0) {
|
|
395
|
-
throw new Error(
|
|
396
|
-
`Invalid config. Can't use browser "${config.browser}". Accepted values: ${availableBrowsers.join(', ')}`,
|
|
397
|
-
)
|
|
390
|
+
throw new Error(`Invalid config. Can't use browser "${config.browser}". Accepted values: ${availableBrowsers.join(', ')}`);
|
|
398
391
|
}
|
|
399
392
|
|
|
400
|
-
return config
|
|
393
|
+
return config;
|
|
401
394
|
}
|
|
402
395
|
|
|
403
396
|
_getOptionsForBrowser(config) {
|
|
404
397
|
if (config[config.browser]) {
|
|
405
398
|
if (config[config.browser].browserWSEndpoint && config[config.browser].browserWSEndpoint.wsEndpoint) {
|
|
406
|
-
config[config.browser].browserWSEndpoint = config[config.browser].browserWSEndpoint.wsEndpoint
|
|
399
|
+
config[config.browser].browserWSEndpoint = config[config.browser].browserWSEndpoint.wsEndpoint;
|
|
407
400
|
}
|
|
408
401
|
return {
|
|
409
402
|
...config[config.browser],
|
|
410
403
|
wsEndpoint: config[config.browser].browserWSEndpoint,
|
|
411
|
-
}
|
|
404
|
+
};
|
|
412
405
|
}
|
|
413
|
-
return {}
|
|
406
|
+
return {};
|
|
414
407
|
}
|
|
415
408
|
|
|
416
409
|
_setConfig(config) {
|
|
417
|
-
this.options = this._validateConfig(config)
|
|
418
|
-
setRestartStrategy(this.options)
|
|
410
|
+
this.options = this._validateConfig(config);
|
|
411
|
+
setRestartStrategy(this.options);
|
|
419
412
|
this.playwrightOptions = {
|
|
420
413
|
headless: !this.options.show,
|
|
421
414
|
...this._getOptionsForBrowser(config),
|
|
422
|
-
}
|
|
415
|
+
};
|
|
423
416
|
|
|
424
417
|
if (this.options.channel && this.options.browser === 'chromium') {
|
|
425
|
-
this.playwrightOptions.channel = this.options.channel
|
|
418
|
+
this.playwrightOptions.channel = this.options.channel;
|
|
426
419
|
}
|
|
427
420
|
|
|
428
421
|
if (this.options.video) {
|
|
429
|
-
|
|
430
|
-
let size = parseWindowSize(this.options.windowSize)
|
|
431
|
-
|
|
432
|
-
// if the video resolution is passed, set the record resoultion with that resolution
|
|
433
|
-
if (this.options.recordVideo && this.options.recordVideo.size) {
|
|
434
|
-
size = parseWindowSize(this.options.recordVideo.size)
|
|
435
|
-
}
|
|
436
|
-
this.options.recordVideo = { size }
|
|
422
|
+
this.options.recordVideo = { size: parseWindowSize(this.options.windowSize) };
|
|
437
423
|
}
|
|
438
424
|
if (this.options.recordVideo && !this.options.recordVideo.dir) {
|
|
439
|
-
this.options.recordVideo.dir = `${global.output_dir}/videos
|
|
425
|
+
this.options.recordVideo.dir = `${global.output_dir}/videos/`;
|
|
440
426
|
}
|
|
441
|
-
this.isRemoteBrowser = !!this.playwrightOptions.browserWSEndpoint
|
|
442
|
-
this.isElectron = this.options.browser === 'electron'
|
|
443
|
-
this.userDataDir = this.playwrightOptions.userDataDir
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
this.isCDPConnection = this.playwrightOptions.cdpConnection
|
|
447
|
-
popupStore.defaultAction = this.options.defaultPopupAction
|
|
427
|
+
this.isRemoteBrowser = !!this.playwrightOptions.browserWSEndpoint;
|
|
428
|
+
this.isElectron = this.options.browser === 'electron';
|
|
429
|
+
this.userDataDir = this.playwrightOptions.userDataDir ? `${this.playwrightOptions.userDataDir}_${Date.now().toString()}` : undefined;
|
|
430
|
+
this.isCDPConnection = this.playwrightOptions.cdpConnection;
|
|
431
|
+
popupStore.defaultAction = this.options.defaultPopupAction;
|
|
448
432
|
}
|
|
449
433
|
|
|
450
434
|
static _config() {
|
|
@@ -467,222 +451,216 @@ class Playwright extends Helper {
|
|
|
467
451
|
type: 'confirm',
|
|
468
452
|
when: (answers) => answers.Playwright_browser !== 'electron',
|
|
469
453
|
},
|
|
470
|
-
]
|
|
454
|
+
];
|
|
471
455
|
}
|
|
472
456
|
|
|
473
457
|
static _checkRequirements() {
|
|
474
458
|
try {
|
|
475
|
-
requireWithFallback('playwright', 'playwright-core')
|
|
459
|
+
requireWithFallback('playwright', 'playwright-core');
|
|
476
460
|
} catch (e) {
|
|
477
|
-
return ['playwright@^1.18']
|
|
461
|
+
return ['playwright@^1.18'];
|
|
478
462
|
}
|
|
479
463
|
}
|
|
480
464
|
|
|
481
465
|
async _init() {
|
|
482
466
|
// register an internal selector engine for reading value property of elements in a selector
|
|
483
|
-
if (defaultSelectorEnginesInitialized) return
|
|
484
|
-
defaultSelectorEnginesInitialized = true
|
|
467
|
+
if (defaultSelectorEnginesInitialized) return;
|
|
468
|
+
defaultSelectorEnginesInitialized = true;
|
|
485
469
|
try {
|
|
486
|
-
await playwright.selectors.register('__value', createValueEngine)
|
|
487
|
-
await playwright.selectors.register('__disabled', createDisabledEngine)
|
|
488
|
-
if (process.env.testIdAttribute) await playwright.selectors.setTestIdAttribute(process.env.testIdAttribute)
|
|
470
|
+
await playwright.selectors.register('__value', createValueEngine);
|
|
471
|
+
await playwright.selectors.register('__disabled', createDisabledEngine);
|
|
472
|
+
if (process.env.testIdAttribute) await playwright.selectors.setTestIdAttribute(process.env.testIdAttribute);
|
|
489
473
|
} catch (e) {
|
|
490
|
-
console.warn(e)
|
|
474
|
+
console.warn(e);
|
|
491
475
|
}
|
|
492
476
|
}
|
|
493
477
|
|
|
494
478
|
_beforeSuite() {
|
|
495
479
|
if ((restartsSession() || restartsContext()) && !this.options.manualStart && !this.isRunning) {
|
|
496
|
-
this.debugSection('Session', 'Starting singleton browser session')
|
|
497
|
-
return this._startBrowser()
|
|
480
|
+
this.debugSection('Session', 'Starting singleton browser session');
|
|
481
|
+
return this._startBrowser();
|
|
498
482
|
}
|
|
499
483
|
}
|
|
500
484
|
|
|
501
485
|
async _before(test) {
|
|
502
|
-
this.currentRunningTest = test
|
|
486
|
+
this.currentRunningTest = test;
|
|
503
487
|
recorder.retry({
|
|
504
488
|
retries: process.env.FAILED_STEP_RETRIES || 3,
|
|
505
|
-
when:
|
|
506
|
-
if (!err || typeof err.message !== 'string') {
|
|
507
|
-
return false
|
|
489
|
+
when: err => {
|
|
490
|
+
if (!err || typeof (err.message) !== 'string') {
|
|
491
|
+
return false;
|
|
508
492
|
}
|
|
509
493
|
// ignore context errors
|
|
510
|
-
return err.message.includes('context')
|
|
494
|
+
return err.message.includes('context');
|
|
511
495
|
},
|
|
512
|
-
})
|
|
496
|
+
});
|
|
513
497
|
|
|
514
|
-
if (restartsBrowser() && !this.options.manualStart) await this._startBrowser()
|
|
515
|
-
if (!this.isRunning && !this.options.manualStart) await this._startBrowser()
|
|
498
|
+
if (restartsBrowser() && !this.options.manualStart) await this._startBrowser();
|
|
499
|
+
if (!this.isRunning && !this.options.manualStart) await this._startBrowser();
|
|
516
500
|
|
|
517
|
-
this.isAuthenticated = false
|
|
501
|
+
this.isAuthenticated = false;
|
|
518
502
|
if (this.isElectron) {
|
|
519
|
-
this.browserContext = this.browser.context()
|
|
503
|
+
this.browserContext = this.browser.context();
|
|
520
504
|
} else if (this.playwrightOptions.userDataDir) {
|
|
521
|
-
this.browserContext = this.browser
|
|
505
|
+
this.browserContext = this.browser;
|
|
522
506
|
} else {
|
|
523
507
|
const contextOptions = {
|
|
524
508
|
ignoreHTTPSErrors: this.options.ignoreHTTPSErrors,
|
|
525
509
|
acceptDownloads: true,
|
|
526
510
|
...this.options.emulate,
|
|
527
|
-
}
|
|
511
|
+
};
|
|
528
512
|
if (this.options.basicAuth) {
|
|
529
|
-
contextOptions.httpCredentials = this.options.basicAuth
|
|
530
|
-
this.isAuthenticated = true
|
|
513
|
+
contextOptions.httpCredentials = this.options.basicAuth;
|
|
514
|
+
this.isAuthenticated = true;
|
|
531
515
|
}
|
|
532
|
-
if (this.options.bypassCSP) contextOptions.bypassCSP = this.options.bypassCSP
|
|
533
|
-
if (this.options.recordVideo) contextOptions.recordVideo = this.options.recordVideo
|
|
516
|
+
if (this.options.bypassCSP) contextOptions.bypassCSP = this.options.bypassCSP;
|
|
517
|
+
if (this.options.recordVideo) contextOptions.recordVideo = this.options.recordVideo;
|
|
534
518
|
if (this.options.recordHar) {
|
|
535
|
-
const harExt = this.options.recordHar.content && this.options.recordHar.content === 'attach' ? 'zip' : 'har'
|
|
536
|
-
const fileName = `${`${global.output_dir}${path.sep}har${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.${harExt}
|
|
537
|
-
const dir = path.dirname(fileName)
|
|
538
|
-
if (!fileExists(dir)) fs.mkdirSync(dir)
|
|
539
|
-
this.options.recordHar.path = fileName
|
|
540
|
-
this.currentRunningTest.artifacts.har = fileName
|
|
541
|
-
contextOptions.recordHar = this.options.recordHar
|
|
519
|
+
const harExt = this.options.recordHar.content && this.options.recordHar.content === 'attach' ? 'zip' : 'har';
|
|
520
|
+
const fileName = `${`${global.output_dir}${path.sep}har${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.${harExt}`;
|
|
521
|
+
const dir = path.dirname(fileName);
|
|
522
|
+
if (!fileExists(dir)) fs.mkdirSync(dir);
|
|
523
|
+
this.options.recordHar.path = fileName;
|
|
524
|
+
this.currentRunningTest.artifacts.har = fileName;
|
|
525
|
+
contextOptions.recordHar = this.options.recordHar;
|
|
542
526
|
}
|
|
543
|
-
if (this.storageState) contextOptions.storageState = this.storageState
|
|
544
|
-
if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent
|
|
545
|
-
if (this.options.locale) contextOptions.locale = this.options.locale
|
|
546
|
-
if (this.options.colorScheme) contextOptions.colorScheme = this.options.colorScheme
|
|
547
|
-
this.contextOptions = contextOptions
|
|
527
|
+
if (this.storageState) contextOptions.storageState = this.storageState;
|
|
528
|
+
if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent;
|
|
529
|
+
if (this.options.locale) contextOptions.locale = this.options.locale;
|
|
530
|
+
if (this.options.colorScheme) contextOptions.colorScheme = this.options.colorScheme;
|
|
531
|
+
this.contextOptions = contextOptions;
|
|
548
532
|
if (!this.browserContext || !restartsSession()) {
|
|
549
|
-
this.browserContext = await this.browser.newContext(this.contextOptions) // Adding the HTTPSError ignore in the context so that we can ignore those errors
|
|
533
|
+
this.browserContext = await this.browser.newContext(this.contextOptions); // Adding the HTTPSError ignore in the context so that we can ignore those errors
|
|
550
534
|
}
|
|
551
535
|
}
|
|
552
536
|
|
|
553
|
-
let mainPage
|
|
537
|
+
let mainPage;
|
|
554
538
|
if (this.isElectron) {
|
|
555
|
-
mainPage = await this.browser.firstWindow()
|
|
539
|
+
mainPage = await this.browser.firstWindow();
|
|
556
540
|
} else {
|
|
557
541
|
try {
|
|
558
|
-
const existingPages = await this.browserContext.pages()
|
|
559
|
-
mainPage = existingPages[0] || (await this.browserContext.newPage())
|
|
542
|
+
const existingPages = await this.browserContext.pages();
|
|
543
|
+
mainPage = existingPages[0] || (await this.browserContext.newPage());
|
|
560
544
|
} catch (e) {
|
|
561
545
|
if (this.playwrightOptions.userDataDir) {
|
|
562
|
-
this.browser = await playwright[this.options.browser].launchPersistentContext(
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
this.browserContext = this.browser
|
|
567
|
-
const existingPages = await this.browserContext.pages()
|
|
568
|
-
mainPage = existingPages[0]
|
|
546
|
+
this.browser = await playwright[this.options.browser].launchPersistentContext(this.userDataDir, this.playwrightOptions);
|
|
547
|
+
this.browserContext = this.browser;
|
|
548
|
+
const existingPages = await this.browserContext.pages();
|
|
549
|
+
mainPage = existingPages[0];
|
|
569
550
|
}
|
|
570
551
|
}
|
|
571
552
|
}
|
|
572
|
-
await targetCreatedHandler.call(this, mainPage)
|
|
553
|
+
await targetCreatedHandler.call(this, mainPage);
|
|
573
554
|
|
|
574
|
-
await this._setPage(mainPage)
|
|
555
|
+
await this._setPage(mainPage);
|
|
575
556
|
|
|
576
|
-
if (this.options.trace) await this.browserContext.tracing.start({ screenshots: true, snapshots: true })
|
|
557
|
+
if (this.options.trace) await this.browserContext.tracing.start({ screenshots: true, snapshots: true });
|
|
577
558
|
|
|
578
|
-
return this.browser
|
|
559
|
+
return this.browser;
|
|
579
560
|
}
|
|
580
561
|
|
|
581
562
|
async _after() {
|
|
582
|
-
if (!this.isRunning) return
|
|
563
|
+
if (!this.isRunning) return;
|
|
583
564
|
|
|
584
565
|
if (this.isElectron) {
|
|
585
|
-
this.browser.close()
|
|
586
|
-
this.electronSessions.forEach(
|
|
587
|
-
return
|
|
566
|
+
this.browser.close();
|
|
567
|
+
this.electronSessions.forEach(session => session.close());
|
|
568
|
+
return;
|
|
588
569
|
}
|
|
589
570
|
|
|
590
571
|
if (restartsSession()) {
|
|
591
|
-
return refreshContextSession.bind(this)()
|
|
572
|
+
return refreshContextSession.bind(this)();
|
|
592
573
|
}
|
|
593
574
|
|
|
594
575
|
if (restartsBrowser()) {
|
|
595
|
-
this.isRunning = false
|
|
596
|
-
return this._stopBrowser()
|
|
576
|
+
this.isRunning = false;
|
|
577
|
+
return this._stopBrowser();
|
|
597
578
|
}
|
|
598
579
|
|
|
599
580
|
// close other sessions
|
|
600
581
|
try {
|
|
601
582
|
if ((await this.browser)._type === 'Browser') {
|
|
602
|
-
const contexts = await this.browser.contexts()
|
|
603
|
-
const currentContext = contexts[0]
|
|
583
|
+
const contexts = await this.browser.contexts();
|
|
584
|
+
const currentContext = contexts[0];
|
|
604
585
|
if (currentContext && (this.options.keepCookies || this.options.keepBrowserState)) {
|
|
605
|
-
this.storageState = await currentContext.storageState()
|
|
586
|
+
this.storageState = await currentContext.storageState();
|
|
606
587
|
}
|
|
607
588
|
|
|
608
|
-
await Promise.all(contexts.map(
|
|
589
|
+
await Promise.all(contexts.map(c => c.close()));
|
|
609
590
|
}
|
|
610
591
|
} catch (e) {
|
|
611
|
-
console.log(e)
|
|
592
|
+
console.log(e);
|
|
612
593
|
}
|
|
613
594
|
|
|
614
595
|
// await this.closeOtherTabs();
|
|
615
|
-
return this.browser
|
|
596
|
+
return this.browser;
|
|
616
597
|
}
|
|
617
598
|
|
|
618
599
|
_afterSuite() {}
|
|
619
600
|
|
|
620
601
|
async _finishTest() {
|
|
621
|
-
if ((restartsSession() || restartsContext()) && this.isRunning) return this._stopBrowser()
|
|
602
|
+
if ((restartsSession() || restartsContext()) && this.isRunning) return this._stopBrowser();
|
|
622
603
|
}
|
|
623
604
|
|
|
624
605
|
_session() {
|
|
625
|
-
const defaultContext = this.browserContext
|
|
606
|
+
const defaultContext = this.browserContext;
|
|
626
607
|
return {
|
|
627
608
|
start: async (sessionName = '', config) => {
|
|
628
|
-
this.debugSection('New Context', config ? JSON.stringify(config) : 'opened')
|
|
629
|
-
this.activeSessionName = sessionName
|
|
609
|
+
this.debugSection('New Context', config ? JSON.stringify(config) : 'opened');
|
|
610
|
+
this.activeSessionName = sessionName;
|
|
630
611
|
|
|
631
|
-
let browserContext
|
|
632
|
-
let page
|
|
612
|
+
let browserContext;
|
|
613
|
+
let page;
|
|
633
614
|
if (this.isElectron) {
|
|
634
|
-
const browser = await playwright._electron.launch(this.playwrightOptions)
|
|
635
|
-
this.electronSessions.push(browser)
|
|
636
|
-
browserContext = browser.context()
|
|
637
|
-
page = await browser.firstWindow()
|
|
615
|
+
const browser = await playwright._electron.launch(this.playwrightOptions);
|
|
616
|
+
this.electronSessions.push(browser);
|
|
617
|
+
browserContext = browser.context();
|
|
618
|
+
page = await browser.firstWindow();
|
|
638
619
|
} else {
|
|
639
620
|
try {
|
|
640
|
-
browserContext = await this.browser.newContext(Object.assign(this.contextOptions, config))
|
|
641
|
-
page = await browserContext.newPage()
|
|
621
|
+
browserContext = await this.browser.newContext(Object.assign(this.contextOptions, config));
|
|
622
|
+
page = await browserContext.newPage();
|
|
642
623
|
} catch (e) {
|
|
643
624
|
if (this.playwrightOptions.userDataDir) {
|
|
644
|
-
browserContext = await playwright[this.options.browser].launchPersistentContext(
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
)
|
|
648
|
-
this.browser = browserContext
|
|
649
|
-
page = await browserContext.pages()[0]
|
|
625
|
+
browserContext = await playwright[this.options.browser].launchPersistentContext(`${this.userDataDir}_${this.activeSessionName}`, this.playwrightOptions);
|
|
626
|
+
this.browser = browserContext;
|
|
627
|
+
page = await browserContext.pages()[0];
|
|
650
628
|
}
|
|
651
629
|
}
|
|
652
630
|
}
|
|
653
631
|
|
|
654
|
-
if (this.options.trace) await browserContext.tracing.start({ screenshots: true, snapshots: true })
|
|
655
|
-
await targetCreatedHandler.call(this, page)
|
|
656
|
-
await this._setPage(page)
|
|
632
|
+
if (this.options.trace) await browserContext.tracing.start({ screenshots: true, snapshots: true });
|
|
633
|
+
await targetCreatedHandler.call(this, page);
|
|
634
|
+
await this._setPage(page);
|
|
657
635
|
// Create a new page inside context.
|
|
658
|
-
return browserContext
|
|
636
|
+
return browserContext;
|
|
659
637
|
},
|
|
660
638
|
stop: async () => {
|
|
661
639
|
// is closed by _after
|
|
662
640
|
},
|
|
663
641
|
loadVars: async (context) => {
|
|
664
642
|
if (context) {
|
|
665
|
-
this.browserContext = context
|
|
666
|
-
const existingPages = await context.pages()
|
|
667
|
-
this.sessionPages[this.activeSessionName] = existingPages[0]
|
|
668
|
-
return this._setPage(this.sessionPages[this.activeSessionName])
|
|
643
|
+
this.browserContext = context;
|
|
644
|
+
const existingPages = await context.pages();
|
|
645
|
+
this.sessionPages[this.activeSessionName] = existingPages[0];
|
|
646
|
+
return this._setPage(this.sessionPages[this.activeSessionName]);
|
|
669
647
|
}
|
|
670
648
|
},
|
|
671
649
|
restoreVars: async (session) => {
|
|
672
|
-
this.withinLocator = null
|
|
673
|
-
this.browserContext = defaultContext
|
|
650
|
+
this.withinLocator = null;
|
|
651
|
+
this.browserContext = defaultContext;
|
|
674
652
|
|
|
675
653
|
if (!session) {
|
|
676
|
-
this.activeSessionName = ''
|
|
654
|
+
this.activeSessionName = '';
|
|
677
655
|
} else {
|
|
678
|
-
this.activeSessionName = session
|
|
656
|
+
this.activeSessionName = session;
|
|
679
657
|
}
|
|
680
|
-
const existingPages = await this.browserContext.pages()
|
|
681
|
-
await this._setPage(existingPages[0])
|
|
658
|
+
const existingPages = await this.browserContext.pages();
|
|
659
|
+
await this._setPage(existingPages[0]);
|
|
682
660
|
|
|
683
|
-
return this._waitForAction()
|
|
661
|
+
return this._waitForAction();
|
|
684
662
|
},
|
|
685
|
-
}
|
|
663
|
+
};
|
|
686
664
|
}
|
|
687
665
|
|
|
688
666
|
/**
|
|
@@ -703,7 +681,7 @@ class Playwright extends Helper {
|
|
|
703
681
|
* @param {function} fn async function that executed with Playwright helper as arguments
|
|
704
682
|
*/
|
|
705
683
|
usePlaywrightTo(description, fn) {
|
|
706
|
-
return this._useTo(...arguments)
|
|
684
|
+
return this._useTo(...arguments);
|
|
707
685
|
}
|
|
708
686
|
|
|
709
687
|
/**
|
|
@@ -717,7 +695,7 @@ class Playwright extends Helper {
|
|
|
717
695
|
* ```
|
|
718
696
|
*/
|
|
719
697
|
amAcceptingPopups() {
|
|
720
|
-
popupStore.actionType = 'accept'
|
|
698
|
+
popupStore.actionType = 'accept';
|
|
721
699
|
}
|
|
722
700
|
|
|
723
701
|
/**
|
|
@@ -726,7 +704,7 @@ class Playwright extends Helper {
|
|
|
726
704
|
* libraries](http://jster.net/category/windows-modals-popups).
|
|
727
705
|
*/
|
|
728
706
|
acceptPopup() {
|
|
729
|
-
popupStore.assertPopupActionType('accept')
|
|
707
|
+
popupStore.assertPopupActionType('accept');
|
|
730
708
|
}
|
|
731
709
|
|
|
732
710
|
/**
|
|
@@ -740,23 +718,23 @@ class Playwright extends Helper {
|
|
|
740
718
|
* ```
|
|
741
719
|
*/
|
|
742
720
|
amCancellingPopups() {
|
|
743
|
-
popupStore.actionType = 'cancel'
|
|
721
|
+
popupStore.actionType = 'cancel';
|
|
744
722
|
}
|
|
745
723
|
|
|
746
724
|
/**
|
|
747
725
|
* Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt.
|
|
748
726
|
*/
|
|
749
727
|
cancelPopup() {
|
|
750
|
-
popupStore.assertPopupActionType('cancel')
|
|
728
|
+
popupStore.assertPopupActionType('cancel');
|
|
751
729
|
}
|
|
752
730
|
|
|
753
731
|
/**
|
|
754
732
|
* {{> seeInPopup }}
|
|
755
733
|
*/
|
|
756
734
|
async seeInPopup(text) {
|
|
757
|
-
popupStore.assertPopupVisible()
|
|
758
|
-
const popupText = await popupStore.popup.message()
|
|
759
|
-
stringIncludes('text in popup').assert(text, popupText)
|
|
735
|
+
popupStore.assertPopupVisible();
|
|
736
|
+
const popupText = await popupStore.popup.message();
|
|
737
|
+
stringIncludes('text in popup').assert(text, popupText);
|
|
760
738
|
}
|
|
761
739
|
|
|
762
740
|
/**
|
|
@@ -764,21 +742,21 @@ class Playwright extends Helper {
|
|
|
764
742
|
* @param {object} page page to set
|
|
765
743
|
*/
|
|
766
744
|
async _setPage(page) {
|
|
767
|
-
page = await page
|
|
768
|
-
this._addPopupListener(page)
|
|
769
|
-
this.page = page
|
|
770
|
-
if (!page) return
|
|
771
|
-
this.browserContext.setDefaultTimeout(0)
|
|
772
|
-
page.setDefaultNavigationTimeout(this.options.getPageTimeout)
|
|
773
|
-
page.setDefaultTimeout(this.options.timeout)
|
|
745
|
+
page = await page;
|
|
746
|
+
this._addPopupListener(page);
|
|
747
|
+
this.page = page;
|
|
748
|
+
if (!page) return;
|
|
749
|
+
this.browserContext.setDefaultTimeout(0);
|
|
750
|
+
page.setDefaultNavigationTimeout(this.options.getPageTimeout);
|
|
751
|
+
page.setDefaultTimeout(this.options.timeout);
|
|
774
752
|
|
|
775
753
|
page.on('crash', async () => {
|
|
776
|
-
console.log('ERROR: Page has crashed, closing page!')
|
|
777
|
-
await page.close()
|
|
778
|
-
})
|
|
779
|
-
this.context = await this.page
|
|
780
|
-
this.contextLocator = null
|
|
781
|
-
await page.bringToFront()
|
|
754
|
+
console.log('ERROR: Page has crashed, closing page!');
|
|
755
|
+
await page.close();
|
|
756
|
+
});
|
|
757
|
+
this.context = await this.page;
|
|
758
|
+
this.contextLocator = null;
|
|
759
|
+
await page.bringToFront();
|
|
782
760
|
}
|
|
783
761
|
|
|
784
762
|
/**
|
|
@@ -790,33 +768,33 @@ class Playwright extends Helper {
|
|
|
790
768
|
*/
|
|
791
769
|
_addPopupListener(page) {
|
|
792
770
|
if (!page) {
|
|
793
|
-
return
|
|
771
|
+
return;
|
|
794
772
|
}
|
|
795
|
-
page.removeAllListeners('dialog')
|
|
773
|
+
page.removeAllListeners('dialog');
|
|
796
774
|
page.on('dialog', async (dialog) => {
|
|
797
|
-
popupStore.popup = dialog
|
|
798
|
-
const action = popupStore.actionType || this.options.defaultPopupAction
|
|
799
|
-
await this._waitForAction()
|
|
775
|
+
popupStore.popup = dialog;
|
|
776
|
+
const action = popupStore.actionType || this.options.defaultPopupAction;
|
|
777
|
+
await this._waitForAction();
|
|
800
778
|
|
|
801
779
|
switch (action) {
|
|
802
780
|
case 'accept':
|
|
803
|
-
return dialog.accept()
|
|
781
|
+
return dialog.accept();
|
|
804
782
|
|
|
805
783
|
case 'cancel':
|
|
806
|
-
return dialog.dismiss()
|
|
784
|
+
return dialog.dismiss();
|
|
807
785
|
|
|
808
786
|
default: {
|
|
809
|
-
throw new Error('Unknown popup action type. Only "accept" or "cancel" are accepted')
|
|
787
|
+
throw new Error('Unknown popup action type. Only "accept" or "cancel" are accepted');
|
|
810
788
|
}
|
|
811
789
|
}
|
|
812
|
-
})
|
|
790
|
+
});
|
|
813
791
|
}
|
|
814
792
|
|
|
815
793
|
/**
|
|
816
794
|
* Gets page URL including hash.
|
|
817
795
|
*/
|
|
818
796
|
async _getPageUrl() {
|
|
819
|
-
return this.executeScript(() => window.location.href)
|
|
797
|
+
return this.executeScript(() => window.location.href);
|
|
820
798
|
}
|
|
821
799
|
|
|
822
800
|
/**
|
|
@@ -829,48 +807,45 @@ class Playwright extends Helper {
|
|
|
829
807
|
*/
|
|
830
808
|
async grabPopupText() {
|
|
831
809
|
if (popupStore.popup) {
|
|
832
|
-
return popupStore.popup.message()
|
|
810
|
+
return popupStore.popup.message();
|
|
833
811
|
}
|
|
834
|
-
return null
|
|
812
|
+
return null;
|
|
835
813
|
}
|
|
836
814
|
|
|
837
815
|
async _startBrowser() {
|
|
838
816
|
if (this.isElectron) {
|
|
839
|
-
this.browser = await playwright._electron.launch(this.playwrightOptions)
|
|
817
|
+
this.browser = await playwright._electron.launch(this.playwrightOptions);
|
|
840
818
|
} else if (this.isRemoteBrowser && this.isCDPConnection) {
|
|
841
819
|
try {
|
|
842
|
-
this.browser = await playwright[this.options.browser].connectOverCDP(this.playwrightOptions)
|
|
820
|
+
this.browser = await playwright[this.options.browser].connectOverCDP(this.playwrightOptions);
|
|
843
821
|
} catch (err) {
|
|
844
822
|
if (err.toString().indexOf('ECONNREFUSED')) {
|
|
845
|
-
throw new RemoteBrowserConnectionRefused(err)
|
|
823
|
+
throw new RemoteBrowserConnectionRefused(err);
|
|
846
824
|
}
|
|
847
|
-
throw err
|
|
825
|
+
throw err;
|
|
848
826
|
}
|
|
849
827
|
} else if (this.isRemoteBrowser) {
|
|
850
828
|
try {
|
|
851
|
-
this.browser = await playwright[this.options.browser].connect(this.playwrightOptions)
|
|
829
|
+
this.browser = await playwright[this.options.browser].connect(this.playwrightOptions);
|
|
852
830
|
} catch (err) {
|
|
853
831
|
if (err.toString().indexOf('ECONNREFUSED')) {
|
|
854
|
-
throw new RemoteBrowserConnectionRefused(err)
|
|
832
|
+
throw new RemoteBrowserConnectionRefused(err);
|
|
855
833
|
}
|
|
856
|
-
throw err
|
|
834
|
+
throw err;
|
|
857
835
|
}
|
|
858
836
|
} else if (this.playwrightOptions.userDataDir) {
|
|
859
|
-
this.browser = await playwright[this.options.browser].launchPersistentContext(
|
|
860
|
-
this.userDataDir,
|
|
861
|
-
this.playwrightOptions,
|
|
862
|
-
)
|
|
837
|
+
this.browser = await playwright[this.options.browser].launchPersistentContext(this.userDataDir, this.playwrightOptions);
|
|
863
838
|
} else {
|
|
864
|
-
this.browser = await playwright[this.options.browser].launch(this.playwrightOptions)
|
|
839
|
+
this.browser = await playwright[this.options.browser].launch(this.playwrightOptions);
|
|
865
840
|
}
|
|
866
841
|
|
|
867
842
|
// works only for Chromium
|
|
868
843
|
this.browser.on('targetchanged', (target) => {
|
|
869
|
-
this.debugSection('Url', target.url())
|
|
870
|
-
})
|
|
844
|
+
this.debugSection('Url', target.url());
|
|
845
|
+
});
|
|
871
846
|
|
|
872
|
-
this.isRunning = true
|
|
873
|
-
return this.browser
|
|
847
|
+
this.isRunning = true;
|
|
848
|
+
return this.browser;
|
|
874
849
|
}
|
|
875
850
|
|
|
876
851
|
/**
|
|
@@ -879,72 +854,72 @@ class Playwright extends Helper {
|
|
|
879
854
|
* @param {object} [contextOptions] See https://playwright.dev/docs/api/class-browser#browser-new-context
|
|
880
855
|
*/
|
|
881
856
|
async _createContextPage(contextOptions) {
|
|
882
|
-
this.browserContext = await this.browser.newContext(contextOptions)
|
|
883
|
-
const page = await this.browserContext.newPage()
|
|
884
|
-
targetCreatedHandler.call(this, page)
|
|
885
|
-
await this._setPage(page)
|
|
857
|
+
this.browserContext = await this.browser.newContext(contextOptions);
|
|
858
|
+
const page = await this.browserContext.newPage();
|
|
859
|
+
targetCreatedHandler.call(this, page);
|
|
860
|
+
await this._setPage(page);
|
|
886
861
|
}
|
|
887
862
|
|
|
888
863
|
_getType() {
|
|
889
|
-
return this.browser._type
|
|
864
|
+
return this.browser._type;
|
|
890
865
|
}
|
|
891
866
|
|
|
892
867
|
async _stopBrowser() {
|
|
893
|
-
this.withinLocator = null
|
|
894
|
-
await this._setPage(null)
|
|
895
|
-
this.context = null
|
|
896
|
-
this.frame = null
|
|
897
|
-
popupStore.clear()
|
|
898
|
-
if (this.options.recordHar) await this.browserContext.close()
|
|
899
|
-
await this.browser.close()
|
|
868
|
+
this.withinLocator = null;
|
|
869
|
+
await this._setPage(null);
|
|
870
|
+
this.context = null;
|
|
871
|
+
this.frame = null;
|
|
872
|
+
popupStore.clear();
|
|
873
|
+
if (this.options.recordHar) await this.browserContext.close();
|
|
874
|
+
await this.browser.close();
|
|
900
875
|
}
|
|
901
876
|
|
|
902
877
|
async _evaluateHandeInContext(...args) {
|
|
903
|
-
const context = await this._getContext()
|
|
904
|
-
return context.evaluateHandle(...args)
|
|
878
|
+
const context = await this._getContext();
|
|
879
|
+
return context.evaluateHandle(...args);
|
|
905
880
|
}
|
|
906
881
|
|
|
907
882
|
async _withinBegin(locator) {
|
|
908
883
|
if (this.withinLocator) {
|
|
909
|
-
throw new Error(
|
|
884
|
+
throw new Error('Can\'t start within block inside another within block');
|
|
910
885
|
}
|
|
911
886
|
|
|
912
|
-
const frame = isFrameLocator(locator)
|
|
887
|
+
const frame = isFrameLocator(locator);
|
|
913
888
|
|
|
914
889
|
if (frame) {
|
|
915
890
|
if (Array.isArray(frame)) {
|
|
916
|
-
await this.switchTo(null)
|
|
917
|
-
return frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve())
|
|
891
|
+
await this.switchTo(null);
|
|
892
|
+
return frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve());
|
|
918
893
|
}
|
|
919
|
-
await this.switchTo(frame)
|
|
920
|
-
this.withinLocator = new Locator(frame)
|
|
921
|
-
return
|
|
894
|
+
await this.switchTo(frame);
|
|
895
|
+
this.withinLocator = new Locator(frame);
|
|
896
|
+
return;
|
|
922
897
|
}
|
|
923
898
|
|
|
924
|
-
const el = await this._locateElement(locator)
|
|
925
|
-
assertElementExists(el, locator)
|
|
926
|
-
this.context = el
|
|
927
|
-
this.contextLocator = locator
|
|
899
|
+
const el = await this._locateElement(locator);
|
|
900
|
+
assertElementExists(el, locator);
|
|
901
|
+
this.context = el;
|
|
902
|
+
this.contextLocator = locator;
|
|
928
903
|
|
|
929
|
-
this.withinLocator = new Locator(locator)
|
|
904
|
+
this.withinLocator = new Locator(locator);
|
|
930
905
|
}
|
|
931
906
|
|
|
932
907
|
async _withinEnd() {
|
|
933
|
-
this.withinLocator = null
|
|
934
|
-
this.context = await this.page
|
|
935
|
-
this.contextLocator = null
|
|
936
|
-
this.frame = null
|
|
908
|
+
this.withinLocator = null;
|
|
909
|
+
this.context = await this.page;
|
|
910
|
+
this.contextLocator = null;
|
|
911
|
+
this.frame = null;
|
|
937
912
|
}
|
|
938
913
|
|
|
939
914
|
_extractDataFromPerformanceTiming(timing, ...dataNames) {
|
|
940
|
-
const navigationStart = timing.navigationStart
|
|
915
|
+
const navigationStart = timing.navigationStart;
|
|
941
916
|
|
|
942
|
-
const extractedData = {}
|
|
917
|
+
const extractedData = {};
|
|
943
918
|
dataNames.forEach((name) => {
|
|
944
|
-
extractedData[name] = timing[name] - navigationStart
|
|
945
|
-
})
|
|
919
|
+
extractedData[name] = timing[name] - navigationStart;
|
|
920
|
+
});
|
|
946
921
|
|
|
947
|
-
return extractedData
|
|
922
|
+
return extractedData;
|
|
948
923
|
}
|
|
949
924
|
|
|
950
925
|
/**
|
|
@@ -952,22 +927,22 @@ class Playwright extends Helper {
|
|
|
952
927
|
*/
|
|
953
928
|
async amOnPage(url) {
|
|
954
929
|
if (this.isElectron) {
|
|
955
|
-
throw new Error('Cannot open pages inside an Electron container')
|
|
930
|
+
throw new Error('Cannot open pages inside an Electron container');
|
|
956
931
|
}
|
|
957
|
-
if (
|
|
958
|
-
url = this.options.url + (url.startsWith('/') ? url : `/${url}`)
|
|
932
|
+
if (!(/^\w+\:(\/\/|.+)/.test(url))) {
|
|
933
|
+
url = this.options.url + (url.startsWith('/') ? url : `/${url}`);
|
|
959
934
|
}
|
|
960
935
|
|
|
961
|
-
if (this.options.basicAuth && this.isAuthenticated !== true) {
|
|
936
|
+
if (this.options.basicAuth && (this.isAuthenticated !== true)) {
|
|
962
937
|
if (url.includes(this.options.url)) {
|
|
963
|
-
await this.browserContext.setHTTPCredentials(this.options.basicAuth)
|
|
964
|
-
this.isAuthenticated = true
|
|
938
|
+
await this.browserContext.setHTTPCredentials(this.options.basicAuth);
|
|
939
|
+
this.isAuthenticated = true;
|
|
965
940
|
}
|
|
966
941
|
}
|
|
967
942
|
|
|
968
|
-
await this.page.goto(url, { waitUntil: this.options.waitForNavigation })
|
|
943
|
+
await this.page.goto(url, { waitUntil: this.options.waitForNavigation });
|
|
969
944
|
|
|
970
|
-
const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)))
|
|
945
|
+
const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)));
|
|
971
946
|
|
|
972
947
|
perfTiming = this._extractDataFromPerformanceTiming(
|
|
973
948
|
performanceTiming,
|
|
@@ -975,9 +950,9 @@ class Playwright extends Helper {
|
|
|
975
950
|
'domInteractive',
|
|
976
951
|
'domContentLoadedEventEnd',
|
|
977
952
|
'loadEventEnd',
|
|
978
|
-
)
|
|
953
|
+
);
|
|
979
954
|
|
|
980
|
-
return this._waitForAction()
|
|
955
|
+
return this._waitForAction();
|
|
981
956
|
}
|
|
982
957
|
|
|
983
958
|
/**
|
|
@@ -998,11 +973,11 @@ class Playwright extends Helper {
|
|
|
998
973
|
*/
|
|
999
974
|
async resizeWindow(width, height) {
|
|
1000
975
|
if (width === 'maximize') {
|
|
1001
|
-
throw new Error(
|
|
976
|
+
throw new Error('Playwright can\'t control windows, so it can\'t maximize it');
|
|
1002
977
|
}
|
|
1003
978
|
|
|
1004
|
-
await this.page.setViewportSize({ width, height })
|
|
1005
|
-
return this._waitForAction()
|
|
979
|
+
await this.page.setViewportSize({ width, height });
|
|
980
|
+
return this._waitForAction();
|
|
1006
981
|
}
|
|
1007
982
|
|
|
1008
983
|
/**
|
|
@@ -1018,9 +993,9 @@ class Playwright extends Helper {
|
|
|
1018
993
|
*/
|
|
1019
994
|
async setPlaywrightRequestHeaders(customHeaders) {
|
|
1020
995
|
if (!customHeaders) {
|
|
1021
|
-
throw new Error('Cannot send empty headers.')
|
|
996
|
+
throw new Error('Cannot send empty headers.');
|
|
1022
997
|
}
|
|
1023
|
-
return this.browserContext.setExtraHTTPHeaders(customHeaders)
|
|
998
|
+
return this.browserContext.setExtraHTTPHeaders(customHeaders);
|
|
1024
999
|
}
|
|
1025
1000
|
|
|
1026
1001
|
/**
|
|
@@ -1028,13 +1003,13 @@ class Playwright extends Helper {
|
|
|
1028
1003
|
*
|
|
1029
1004
|
*/
|
|
1030
1005
|
async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
|
|
1031
|
-
const el = await this._locateElement(locator)
|
|
1032
|
-
assertElementExists(el, locator)
|
|
1006
|
+
const el = await this._locateElement(locator);
|
|
1007
|
+
assertElementExists(el, locator);
|
|
1033
1008
|
|
|
1034
1009
|
// Use manual mouse.move instead of .hover() so the offset can be added to the coordinates
|
|
1035
|
-
const { x, y } = await clickablePoint(el)
|
|
1036
|
-
await this.page.mouse.move(x + offsetX, y + offsetY)
|
|
1037
|
-
return this._waitForAction()
|
|
1010
|
+
const { x, y } = await clickablePoint(el);
|
|
1011
|
+
await this.page.mouse.move(x + offsetX, y + offsetY);
|
|
1012
|
+
return this._waitForAction();
|
|
1038
1013
|
}
|
|
1039
1014
|
|
|
1040
1015
|
/**
|
|
@@ -1042,11 +1017,11 @@ class Playwright extends Helper {
|
|
|
1042
1017
|
*
|
|
1043
1018
|
*/
|
|
1044
1019
|
async focus(locator, options = {}) {
|
|
1045
|
-
const el = await this._locateElement(locator)
|
|
1046
|
-
assertElementExists(el, locator, 'Element to focus')
|
|
1020
|
+
const el = await this._locateElement(locator);
|
|
1021
|
+
assertElementExists(el, locator, 'Element to focus');
|
|
1047
1022
|
|
|
1048
|
-
await el.focus(options)
|
|
1049
|
-
return this._waitForAction()
|
|
1023
|
+
await el.focus(options);
|
|
1024
|
+
return this._waitForAction();
|
|
1050
1025
|
}
|
|
1051
1026
|
|
|
1052
1027
|
/**
|
|
@@ -1054,11 +1029,11 @@ class Playwright extends Helper {
|
|
|
1054
1029
|
*
|
|
1055
1030
|
*/
|
|
1056
1031
|
async blur(locator, options = {}) {
|
|
1057
|
-
const el = await this._locateElement(locator)
|
|
1058
|
-
assertElementExists(el, locator, 'Element to blur')
|
|
1032
|
+
const el = await this._locateElement(locator);
|
|
1033
|
+
assertElementExists(el, locator, 'Element to blur');
|
|
1059
1034
|
|
|
1060
|
-
await el.blur(options)
|
|
1061
|
-
return this._waitForAction()
|
|
1035
|
+
await el.blur(options);
|
|
1036
|
+
return this._waitForAction();
|
|
1062
1037
|
}
|
|
1063
1038
|
/**
|
|
1064
1039
|
* Return the checked status of given element.
|
|
@@ -1070,14 +1045,14 @@ class Playwright extends Helper {
|
|
|
1070
1045
|
*/
|
|
1071
1046
|
|
|
1072
1047
|
async grabCheckedElementStatus(locator, options = {}) {
|
|
1073
|
-
const supportedTypes = ['checkbox', 'radio']
|
|
1074
|
-
const el = await this._locateElement(locator)
|
|
1075
|
-
const type = await el.getAttribute('type')
|
|
1048
|
+
const supportedTypes = ['checkbox', 'radio'];
|
|
1049
|
+
const el = await this._locateElement(locator);
|
|
1050
|
+
const type = await el.getAttribute('type');
|
|
1076
1051
|
|
|
1077
1052
|
if (supportedTypes.includes(type)) {
|
|
1078
|
-
return el.isChecked(options)
|
|
1053
|
+
return el.isChecked(options);
|
|
1079
1054
|
}
|
|
1080
|
-
throw new Error(`Element is not a ${supportedTypes.join(' or ')} input`)
|
|
1055
|
+
throw new Error(`Element is not a ${supportedTypes.join(' or ')} input`);
|
|
1081
1056
|
}
|
|
1082
1057
|
/**
|
|
1083
1058
|
* Return the disabled status of given element.
|
|
@@ -1089,8 +1064,8 @@ class Playwright extends Helper {
|
|
|
1089
1064
|
*/
|
|
1090
1065
|
|
|
1091
1066
|
async grabDisabledElementStatus(locator, options = {}) {
|
|
1092
|
-
const el = await this._locateElement(locator)
|
|
1093
|
-
return el.isDisabled(options)
|
|
1067
|
+
const el = await this._locateElement(locator);
|
|
1068
|
+
return el.isDisabled(options);
|
|
1094
1069
|
}
|
|
1095
1070
|
|
|
1096
1071
|
/**
|
|
@@ -1107,24 +1082,24 @@ class Playwright extends Helper {
|
|
|
1107
1082
|
*
|
|
1108
1083
|
*/
|
|
1109
1084
|
async dragAndDrop(srcElement, destElement, options) {
|
|
1110
|
-
const src = new Locator(srcElement)
|
|
1111
|
-
const dst = new Locator(destElement)
|
|
1085
|
+
const src = new Locator(srcElement);
|
|
1086
|
+
const dst = new Locator(destElement);
|
|
1112
1087
|
|
|
1113
1088
|
if (options) {
|
|
1114
|
-
return this.page.dragAndDrop(buildLocatorString(src), buildLocatorString(dst), options)
|
|
1089
|
+
return this.page.dragAndDrop(buildLocatorString(src), buildLocatorString(dst), options);
|
|
1115
1090
|
}
|
|
1116
1091
|
|
|
1117
|
-
const _smallWaitInMs = 600
|
|
1118
|
-
await this.page.locator(buildLocatorString(src)).hover()
|
|
1119
|
-
await this.page.mouse.down()
|
|
1120
|
-
await this.page.waitForTimeout(_smallWaitInMs)
|
|
1092
|
+
const _smallWaitInMs = 600;
|
|
1093
|
+
await this.page.locator(buildLocatorString(src)).hover();
|
|
1094
|
+
await this.page.mouse.down();
|
|
1095
|
+
await this.page.waitForTimeout(_smallWaitInMs);
|
|
1121
1096
|
|
|
1122
|
-
const destElBox = await this.page.locator(buildLocatorString(dst)).boundingBox()
|
|
1097
|
+
const destElBox = await this.page.locator(buildLocatorString(dst)).boundingBox();
|
|
1123
1098
|
|
|
1124
|
-
await this.page.mouse.move(destElBox.x + destElBox.width / 2, destElBox.y + destElBox.height / 2)
|
|
1125
|
-
await this.page.locator(buildLocatorString(dst)).hover({ position: { x: 10, y: 10 } })
|
|
1126
|
-
await this.page.waitForTimeout(_smallWaitInMs)
|
|
1127
|
-
await this.page.mouse.up()
|
|
1099
|
+
await this.page.mouse.move(destElBox.x + destElBox.width / 2, destElBox.y + destElBox.height / 2);
|
|
1100
|
+
await this.page.locator(buildLocatorString(dst)).hover({ position: { x: 10, y: 10 } });
|
|
1101
|
+
await this.page.waitForTimeout(_smallWaitInMs);
|
|
1102
|
+
await this.page.mouse.up();
|
|
1128
1103
|
}
|
|
1129
1104
|
|
|
1130
1105
|
/**
|
|
@@ -1142,16 +1117,16 @@ class Playwright extends Helper {
|
|
|
1142
1117
|
* @param {object} [contextOptions] [Options for browser context](https://playwright.dev/docs/api/class-browser#browser-new-context) when starting new browser
|
|
1143
1118
|
*/
|
|
1144
1119
|
async restartBrowser(contextOptions) {
|
|
1145
|
-
await this._stopBrowser()
|
|
1146
|
-
await this._startBrowser()
|
|
1147
|
-
await this._createContextPage(contextOptions)
|
|
1120
|
+
await this._stopBrowser();
|
|
1121
|
+
await this._startBrowser();
|
|
1122
|
+
await this._createContextPage(contextOptions);
|
|
1148
1123
|
}
|
|
1149
1124
|
|
|
1150
1125
|
/**
|
|
1151
1126
|
* {{> refreshPage }}
|
|
1152
1127
|
*/
|
|
1153
1128
|
async refreshPage() {
|
|
1154
|
-
return this.page.reload({ timeout: this.options.getPageTimeout, waitUntil: this.options.waitForNavigation })
|
|
1129
|
+
return this.page.reload({ timeout: this.options.getPageTimeout, waitUntil: this.options.waitForNavigation });
|
|
1155
1130
|
}
|
|
1156
1131
|
|
|
1157
1132
|
/**
|
|
@@ -1172,13 +1147,13 @@ class Playwright extends Helper {
|
|
|
1172
1147
|
* @returns Promise<void>
|
|
1173
1148
|
*/
|
|
1174
1149
|
async replayFromHar(harFilePath, opts) {
|
|
1175
|
-
const file = path.join(global.codecept_dir, harFilePath)
|
|
1150
|
+
const file = path.join(global.codecept_dir, harFilePath);
|
|
1176
1151
|
|
|
1177
1152
|
if (!fileExists(file)) {
|
|
1178
|
-
throw new Error(`File at ${file} cannot be found on local system`)
|
|
1153
|
+
throw new Error(`File at ${file} cannot be found on local system`);
|
|
1179
1154
|
}
|
|
1180
1155
|
|
|
1181
|
-
await this.page.routeFromHAR(harFilePath, opts)
|
|
1156
|
+
await this.page.routeFromHAR(harFilePath, opts);
|
|
1182
1157
|
}
|
|
1183
1158
|
|
|
1184
1159
|
/**
|
|
@@ -1186,8 +1161,8 @@ class Playwright extends Helper {
|
|
|
1186
1161
|
*/
|
|
1187
1162
|
scrollPageToTop() {
|
|
1188
1163
|
return this.executeScript(() => {
|
|
1189
|
-
window.scrollTo(0, 0)
|
|
1190
|
-
})
|
|
1164
|
+
window.scrollTo(0, 0);
|
|
1165
|
+
});
|
|
1191
1166
|
}
|
|
1192
1167
|
|
|
1193
1168
|
/**
|
|
@@ -1195,13 +1170,16 @@ class Playwright extends Helper {
|
|
|
1195
1170
|
*/
|
|
1196
1171
|
async scrollPageToBottom() {
|
|
1197
1172
|
return this.executeScript(() => {
|
|
1198
|
-
const body = document.body
|
|
1199
|
-
const html = document.documentElement
|
|
1200
|
-
window.scrollTo(
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1173
|
+
const body = document.body;
|
|
1174
|
+
const html = document.documentElement;
|
|
1175
|
+
window.scrollTo(0, Math.max(
|
|
1176
|
+
body.scrollHeight,
|
|
1177
|
+
body.offsetHeight,
|
|
1178
|
+
html.clientHeight,
|
|
1179
|
+
html.scrollHeight,
|
|
1180
|
+
html.offsetHeight,
|
|
1181
|
+
));
|
|
1182
|
+
});
|
|
1205
1183
|
}
|
|
1206
1184
|
|
|
1207
1185
|
/**
|
|
@@ -1209,32 +1187,29 @@ class Playwright extends Helper {
|
|
|
1209
1187
|
*/
|
|
1210
1188
|
async scrollTo(locator, offsetX = 0, offsetY = 0) {
|
|
1211
1189
|
if (typeof locator === 'number' && typeof offsetX === 'number') {
|
|
1212
|
-
offsetY = offsetX
|
|
1213
|
-
offsetX = locator
|
|
1214
|
-
locator = null
|
|
1190
|
+
offsetY = offsetX;
|
|
1191
|
+
offsetX = locator;
|
|
1192
|
+
locator = null;
|
|
1215
1193
|
}
|
|
1216
1194
|
|
|
1217
1195
|
if (locator) {
|
|
1218
|
-
const el = await this._locateElement(locator)
|
|
1219
|
-
assertElementExists(el, locator, 'Element')
|
|
1220
|
-
await el.scrollIntoViewIfNeeded()
|
|
1221
|
-
const elementCoordinates = await clickablePoint(el)
|
|
1222
|
-
await this.executeScript((offsetX, offsetY) => window.scrollBy(offsetX, offsetY), {
|
|
1223
|
-
offsetX: elementCoordinates.x + offsetX,
|
|
1224
|
-
offsetY: elementCoordinates.y + offsetY,
|
|
1225
|
-
})
|
|
1196
|
+
const el = await this._locateElement(locator);
|
|
1197
|
+
assertElementExists(el, locator, 'Element');
|
|
1198
|
+
await el.scrollIntoViewIfNeeded();
|
|
1199
|
+
const elementCoordinates = await clickablePoint(el);
|
|
1200
|
+
await this.executeScript((offsetX, offsetY) => window.scrollBy(offsetX, offsetY), { offsetX: elementCoordinates.x + offsetX, offsetY: elementCoordinates.y + offsetY });
|
|
1226
1201
|
} else {
|
|
1227
|
-
await this.executeScript(({ offsetX, offsetY }) => window.scrollTo(offsetX, offsetY), { offsetX, offsetY })
|
|
1202
|
+
await this.executeScript(({ offsetX, offsetY }) => window.scrollTo(offsetX, offsetY), { offsetX, offsetY });
|
|
1228
1203
|
}
|
|
1229
|
-
return this._waitForAction()
|
|
1204
|
+
return this._waitForAction();
|
|
1230
1205
|
}
|
|
1231
1206
|
|
|
1232
1207
|
/**
|
|
1233
1208
|
* {{> seeInTitle }}
|
|
1234
1209
|
*/
|
|
1235
1210
|
async seeInTitle(text) {
|
|
1236
|
-
const title = await this.page.title()
|
|
1237
|
-
stringIncludes('web page title').assert(text, title)
|
|
1211
|
+
const title = await this.page.title();
|
|
1212
|
+
stringIncludes('web page title').assert(text, title);
|
|
1238
1213
|
}
|
|
1239
1214
|
|
|
1240
1215
|
/**
|
|
@@ -1245,34 +1220,34 @@ class Playwright extends Helper {
|
|
|
1245
1220
|
function getScrollPosition() {
|
|
1246
1221
|
return {
|
|
1247
1222
|
x: window.pageXOffset,
|
|
1248
|
-
y: window.pageYOffset
|
|
1249
|
-
}
|
|
1223
|
+
y: window.pageYOffset
|
|
1224
|
+
};
|
|
1250
1225
|
}
|
|
1251
1226
|
/* eslint-enable comma-dangle */
|
|
1252
|
-
return this.executeScript(getScrollPosition)
|
|
1227
|
+
return this.executeScript(getScrollPosition);
|
|
1253
1228
|
}
|
|
1254
1229
|
|
|
1255
1230
|
/**
|
|
1256
1231
|
* {{> seeTitleEquals }}
|
|
1257
1232
|
*/
|
|
1258
1233
|
async seeTitleEquals(text) {
|
|
1259
|
-
const title = await this.page.title()
|
|
1260
|
-
return equals('web page title').assert(title, text)
|
|
1234
|
+
const title = await this.page.title();
|
|
1235
|
+
return equals('web page title').assert(title, text);
|
|
1261
1236
|
}
|
|
1262
1237
|
|
|
1263
1238
|
/**
|
|
1264
1239
|
* {{> dontSeeInTitle }}
|
|
1265
1240
|
*/
|
|
1266
1241
|
async dontSeeInTitle(text) {
|
|
1267
|
-
const title = await this.page.title()
|
|
1268
|
-
stringIncludes('web page title').negate(text, title)
|
|
1242
|
+
const title = await this.page.title();
|
|
1243
|
+
stringIncludes('web page title').negate(text, title);
|
|
1269
1244
|
}
|
|
1270
1245
|
|
|
1271
1246
|
/**
|
|
1272
1247
|
* {{> grabTitle }}
|
|
1273
1248
|
*/
|
|
1274
1249
|
async grabTitle() {
|
|
1275
|
-
return this.page.title()
|
|
1250
|
+
return this.page.title();
|
|
1276
1251
|
}
|
|
1277
1252
|
|
|
1278
1253
|
/**
|
|
@@ -1284,11 +1259,11 @@ class Playwright extends Helper {
|
|
|
1284
1259
|
* ```
|
|
1285
1260
|
*/
|
|
1286
1261
|
async _locate(locator) {
|
|
1287
|
-
const context = (await this.context) || (await this._getContext())
|
|
1262
|
+
const context = (await this.context) || (await this._getContext());
|
|
1288
1263
|
|
|
1289
|
-
if (this.frame) return findElements(this.frame, locator)
|
|
1264
|
+
if (this.frame) return findElements(this.frame, locator);
|
|
1290
1265
|
|
|
1291
|
-
return findElements(context, locator)
|
|
1266
|
+
return findElements(context, locator);
|
|
1292
1267
|
}
|
|
1293
1268
|
|
|
1294
1269
|
/**
|
|
@@ -1300,8 +1275,8 @@ class Playwright extends Helper {
|
|
|
1300
1275
|
* ```
|
|
1301
1276
|
*/
|
|
1302
1277
|
async _locateElement(locator) {
|
|
1303
|
-
const context = (await this.context) || (await this._getContext())
|
|
1304
|
-
return findElement(context, locator)
|
|
1278
|
+
const context = (await this.context) || (await this._getContext());
|
|
1279
|
+
return findElement(context, locator);
|
|
1305
1280
|
}
|
|
1306
1281
|
|
|
1307
1282
|
/**
|
|
@@ -1313,10 +1288,10 @@ class Playwright extends Helper {
|
|
|
1313
1288
|
* ```
|
|
1314
1289
|
*/
|
|
1315
1290
|
async _locateCheckable(locator, providedContext = null) {
|
|
1316
|
-
const context = providedContext || (await this._getContext())
|
|
1317
|
-
const els = await findCheckable.call(this, locator, context)
|
|
1318
|
-
assertElementExists(els[0], locator, 'Checkbox or radio')
|
|
1319
|
-
return els[0]
|
|
1291
|
+
const context = providedContext || (await this._getContext());
|
|
1292
|
+
const els = await findCheckable.call(this, locator, context);
|
|
1293
|
+
assertElementExists(els[0], locator, 'Checkbox or radio');
|
|
1294
|
+
return els[0];
|
|
1320
1295
|
}
|
|
1321
1296
|
|
|
1322
1297
|
/**
|
|
@@ -1327,8 +1302,8 @@ class Playwright extends Helper {
|
|
|
1327
1302
|
* ```
|
|
1328
1303
|
*/
|
|
1329
1304
|
async _locateClickable(locator) {
|
|
1330
|
-
const context = await this._getContext()
|
|
1331
|
-
return findClickable.call(this, context, locator)
|
|
1305
|
+
const context = await this._getContext();
|
|
1306
|
+
return findClickable.call(this, context, locator);
|
|
1332
1307
|
}
|
|
1333
1308
|
|
|
1334
1309
|
/**
|
|
@@ -1339,7 +1314,7 @@ class Playwright extends Helper {
|
|
|
1339
1314
|
* ```
|
|
1340
1315
|
*/
|
|
1341
1316
|
async _locateFields(locator) {
|
|
1342
|
-
return findFields.call(this, locator)
|
|
1317
|
+
return findFields.call(this, locator);
|
|
1343
1318
|
}
|
|
1344
1319
|
|
|
1345
1320
|
/**
|
|
@@ -1347,7 +1322,7 @@ class Playwright extends Helper {
|
|
|
1347
1322
|
*
|
|
1348
1323
|
*/
|
|
1349
1324
|
async grabWebElements(locator) {
|
|
1350
|
-
return this._locate(locator)
|
|
1325
|
+
return this._locate(locator);
|
|
1351
1326
|
}
|
|
1352
1327
|
|
|
1353
1328
|
/**
|
|
@@ -1355,7 +1330,7 @@ class Playwright extends Helper {
|
|
|
1355
1330
|
*
|
|
1356
1331
|
*/
|
|
1357
1332
|
async grabWebElement(locator) {
|
|
1358
|
-
return this._locateElement(locator)
|
|
1333
|
+
return this._locateElement(locator);
|
|
1359
1334
|
}
|
|
1360
1335
|
|
|
1361
1336
|
/**
|
|
@@ -1370,20 +1345,20 @@ class Playwright extends Helper {
|
|
|
1370
1345
|
*/
|
|
1371
1346
|
async switchToNextTab(num = 1) {
|
|
1372
1347
|
if (this.isElectron) {
|
|
1373
|
-
throw new Error('Cannot switch tabs inside an Electron container')
|
|
1348
|
+
throw new Error('Cannot switch tabs inside an Electron container');
|
|
1374
1349
|
}
|
|
1375
|
-
const pages = await this.browserContext.pages()
|
|
1350
|
+
const pages = await this.browserContext.pages();
|
|
1376
1351
|
|
|
1377
|
-
const index = pages.indexOf(this.page)
|
|
1378
|
-
this.withinLocator = null
|
|
1379
|
-
const page = pages[index + num]
|
|
1352
|
+
const index = pages.indexOf(this.page);
|
|
1353
|
+
this.withinLocator = null;
|
|
1354
|
+
const page = pages[index + num];
|
|
1380
1355
|
|
|
1381
1356
|
if (!page) {
|
|
1382
|
-
throw new Error(`There is no ability to switch to next tab with offset ${num}`)
|
|
1357
|
+
throw new Error(`There is no ability to switch to next tab with offset ${num}`);
|
|
1383
1358
|
}
|
|
1384
|
-
await targetCreatedHandler.call(this, page)
|
|
1385
|
-
await this._setPage(page)
|
|
1386
|
-
return this._waitForAction()
|
|
1359
|
+
await targetCreatedHandler.call(this, page);
|
|
1360
|
+
await this._setPage(page);
|
|
1361
|
+
return this._waitForAction();
|
|
1387
1362
|
}
|
|
1388
1363
|
|
|
1389
1364
|
/**
|
|
@@ -1397,19 +1372,19 @@ class Playwright extends Helper {
|
|
|
1397
1372
|
*/
|
|
1398
1373
|
async switchToPreviousTab(num = 1) {
|
|
1399
1374
|
if (this.isElectron) {
|
|
1400
|
-
throw new Error('Cannot switch tabs inside an Electron container')
|
|
1375
|
+
throw new Error('Cannot switch tabs inside an Electron container');
|
|
1401
1376
|
}
|
|
1402
|
-
const pages = await this.browserContext.pages()
|
|
1403
|
-
const index = pages.indexOf(this.page)
|
|
1404
|
-
this.withinLocator = null
|
|
1405
|
-
const page = pages[index - num]
|
|
1377
|
+
const pages = await this.browserContext.pages();
|
|
1378
|
+
const index = pages.indexOf(this.page);
|
|
1379
|
+
this.withinLocator = null;
|
|
1380
|
+
const page = pages[index - num];
|
|
1406
1381
|
|
|
1407
1382
|
if (!page) {
|
|
1408
|
-
throw new Error(`There is no ability to switch to previous tab with offset ${num}`)
|
|
1383
|
+
throw new Error(`There is no ability to switch to previous tab with offset ${num}`);
|
|
1409
1384
|
}
|
|
1410
1385
|
|
|
1411
|
-
await this._setPage(page)
|
|
1412
|
-
return this._waitForAction()
|
|
1386
|
+
await this._setPage(page);
|
|
1387
|
+
return this._waitForAction();
|
|
1413
1388
|
}
|
|
1414
1389
|
|
|
1415
1390
|
/**
|
|
@@ -1421,12 +1396,12 @@ class Playwright extends Helper {
|
|
|
1421
1396
|
*/
|
|
1422
1397
|
async closeCurrentTab() {
|
|
1423
1398
|
if (this.isElectron) {
|
|
1424
|
-
throw new Error('Cannot close current tab inside an Electron container')
|
|
1399
|
+
throw new Error('Cannot close current tab inside an Electron container');
|
|
1425
1400
|
}
|
|
1426
|
-
const oldPage = this.page
|
|
1427
|
-
await this.switchToPreviousTab()
|
|
1428
|
-
await oldPage.close()
|
|
1429
|
-
return this._waitForAction()
|
|
1401
|
+
const oldPage = this.page;
|
|
1402
|
+
await this.switchToPreviousTab();
|
|
1403
|
+
await oldPage.close();
|
|
1404
|
+
return this._waitForAction();
|
|
1430
1405
|
}
|
|
1431
1406
|
|
|
1432
1407
|
/**
|
|
@@ -1437,13 +1412,13 @@ class Playwright extends Helper {
|
|
|
1437
1412
|
* ```
|
|
1438
1413
|
*/
|
|
1439
1414
|
async closeOtherTabs() {
|
|
1440
|
-
const pages = await this.browserContext.pages()
|
|
1441
|
-
const otherPages = pages.filter(
|
|
1415
|
+
const pages = await this.browserContext.pages();
|
|
1416
|
+
const otherPages = pages.filter(page => page !== this.page);
|
|
1442
1417
|
if (otherPages.length) {
|
|
1443
|
-
this.debug(`Closing ${otherPages.length} tabs`)
|
|
1444
|
-
return Promise.all(otherPages.map(
|
|
1418
|
+
this.debug(`Closing ${otherPages.length} tabs`);
|
|
1419
|
+
return Promise.all(otherPages.map(p => p.close()));
|
|
1445
1420
|
}
|
|
1446
|
-
return Promise.resolve()
|
|
1421
|
+
return Promise.resolve();
|
|
1447
1422
|
}
|
|
1448
1423
|
|
|
1449
1424
|
/**
|
|
@@ -1462,20 +1437,20 @@ class Playwright extends Helper {
|
|
|
1462
1437
|
*/
|
|
1463
1438
|
async openNewTab(options) {
|
|
1464
1439
|
if (this.isElectron) {
|
|
1465
|
-
throw new Error('Cannot open new tabs inside an Electron container')
|
|
1440
|
+
throw new Error('Cannot open new tabs inside an Electron container');
|
|
1466
1441
|
}
|
|
1467
|
-
const page = await this.browserContext.newPage(options)
|
|
1468
|
-
await targetCreatedHandler.call(this, page)
|
|
1469
|
-
await this._setPage(page)
|
|
1470
|
-
return this._waitForAction()
|
|
1442
|
+
const page = await this.browserContext.newPage(options);
|
|
1443
|
+
await targetCreatedHandler.call(this, page);
|
|
1444
|
+
await this._setPage(page);
|
|
1445
|
+
return this._waitForAction();
|
|
1471
1446
|
}
|
|
1472
1447
|
|
|
1473
1448
|
/**
|
|
1474
1449
|
* {{> grabNumberOfOpenTabs }}
|
|
1475
1450
|
*/
|
|
1476
1451
|
async grabNumberOfOpenTabs() {
|
|
1477
|
-
const pages = await this.browserContext.pages()
|
|
1478
|
-
return pages.length
|
|
1452
|
+
const pages = await this.browserContext.pages();
|
|
1453
|
+
return pages.length;
|
|
1479
1454
|
}
|
|
1480
1455
|
|
|
1481
1456
|
/**
|
|
@@ -1483,12 +1458,12 @@ class Playwright extends Helper {
|
|
|
1483
1458
|
*
|
|
1484
1459
|
*/
|
|
1485
1460
|
async seeElement(locator) {
|
|
1486
|
-
let els = await this._locate(locator)
|
|
1487
|
-
els = await Promise.all(els.map(
|
|
1461
|
+
let els = await this._locate(locator);
|
|
1462
|
+
els = await Promise.all(els.map(el => el.isVisible()));
|
|
1488
1463
|
try {
|
|
1489
|
-
return empty('visible elements').negate(els.filter(
|
|
1464
|
+
return empty('visible elements').negate(els.filter(v => v).fill('ELEMENT'));
|
|
1490
1465
|
} catch (e) {
|
|
1491
|
-
dontSeeElementError(locator)
|
|
1466
|
+
dontSeeElementError(locator);
|
|
1492
1467
|
}
|
|
1493
1468
|
}
|
|
1494
1469
|
|
|
@@ -1497,12 +1472,12 @@ class Playwright extends Helper {
|
|
|
1497
1472
|
*
|
|
1498
1473
|
*/
|
|
1499
1474
|
async dontSeeElement(locator) {
|
|
1500
|
-
let els = await this._locate(locator)
|
|
1501
|
-
els = await Promise.all(els.map(
|
|
1475
|
+
let els = await this._locate(locator);
|
|
1476
|
+
els = await Promise.all(els.map(el => el.isVisible()));
|
|
1502
1477
|
try {
|
|
1503
|
-
return empty('visible elements').assert(els.filter(
|
|
1478
|
+
return empty('visible elements').assert(els.filter(v => v).fill('ELEMENT'));
|
|
1504
1479
|
} catch (e) {
|
|
1505
|
-
seeElementError(locator)
|
|
1480
|
+
seeElementError(locator);
|
|
1506
1481
|
}
|
|
1507
1482
|
}
|
|
1508
1483
|
|
|
@@ -1510,11 +1485,11 @@ class Playwright extends Helper {
|
|
|
1510
1485
|
* {{> seeElementInDOM }}
|
|
1511
1486
|
*/
|
|
1512
1487
|
async seeElementInDOM(locator) {
|
|
1513
|
-
const els = await this._locate(locator)
|
|
1488
|
+
const els = await this._locate(locator);
|
|
1514
1489
|
try {
|
|
1515
|
-
return empty('elements on page').negate(els.filter(
|
|
1490
|
+
return empty('elements on page').negate(els.filter(v => v).fill('ELEMENT'));
|
|
1516
1491
|
} catch (e) {
|
|
1517
|
-
dontSeeElementInDOMError(locator)
|
|
1492
|
+
dontSeeElementInDOMError(locator);
|
|
1518
1493
|
}
|
|
1519
1494
|
}
|
|
1520
1495
|
|
|
@@ -1522,11 +1497,11 @@ class Playwright extends Helper {
|
|
|
1522
1497
|
* {{> dontSeeElementInDOM }}
|
|
1523
1498
|
*/
|
|
1524
1499
|
async dontSeeElementInDOM(locator) {
|
|
1525
|
-
const els = await this._locate(locator)
|
|
1500
|
+
const els = await this._locate(locator);
|
|
1526
1501
|
try {
|
|
1527
|
-
return empty('elements on a page').assert(els.filter(
|
|
1502
|
+
return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'));
|
|
1528
1503
|
} catch (e) {
|
|
1529
|
-
seeElementInDOMError(locator)
|
|
1504
|
+
seeElementInDOMError(locator);
|
|
1530
1505
|
}
|
|
1531
1506
|
}
|
|
1532
1507
|
|
|
@@ -1549,18 +1524,18 @@ class Playwright extends Helper {
|
|
|
1549
1524
|
*/
|
|
1550
1525
|
async handleDownloads(fileName) {
|
|
1551
1526
|
this.page.waitForEvent('download').then(async (download) => {
|
|
1552
|
-
const filePath = await download.path()
|
|
1553
|
-
fileName = fileName || `downloads/${path.basename(filePath)}
|
|
1527
|
+
const filePath = await download.path();
|
|
1528
|
+
fileName = fileName || `downloads/${path.basename(filePath)}`;
|
|
1554
1529
|
|
|
1555
|
-
const downloadPath = path.join(global.output_dir, fileName)
|
|
1530
|
+
const downloadPath = path.join(global.output_dir, fileName);
|
|
1556
1531
|
if (!fs.existsSync(path.dirname(downloadPath))) {
|
|
1557
|
-
fs.mkdirSync(path.dirname(downloadPath), '0777')
|
|
1532
|
+
fs.mkdirSync(path.dirname(downloadPath), '0777');
|
|
1558
1533
|
}
|
|
1559
|
-
fs.copyFileSync(filePath, downloadPath)
|
|
1560
|
-
this.debug('Download completed')
|
|
1561
|
-
this.debugSection('Downloaded From', await download.url())
|
|
1562
|
-
this.debugSection('Downloaded To', downloadPath)
|
|
1563
|
-
})
|
|
1534
|
+
fs.copyFileSync(filePath, downloadPath);
|
|
1535
|
+
this.debug('Download completed');
|
|
1536
|
+
this.debugSection('Downloaded From', await download.url());
|
|
1537
|
+
this.debugSection('Downloaded To', downloadPath);
|
|
1538
|
+
});
|
|
1564
1539
|
}
|
|
1565
1540
|
|
|
1566
1541
|
/**
|
|
@@ -1580,37 +1555,37 @@ class Playwright extends Helper {
|
|
|
1580
1555
|
*
|
|
1581
1556
|
*/
|
|
1582
1557
|
async click(locator, context = null, options = {}) {
|
|
1583
|
-
return proceedClick.call(this, locator, context, options)
|
|
1558
|
+
return proceedClick.call(this, locator, context, options);
|
|
1584
1559
|
}
|
|
1585
1560
|
|
|
1586
1561
|
/**
|
|
1587
1562
|
* Clicks link and waits for navigation (deprecated)
|
|
1588
1563
|
*/
|
|
1589
1564
|
async clickLink(locator, context = null) {
|
|
1590
|
-
console.log('clickLink deprecated: Playwright automatically waits for navigation to happen.')
|
|
1591
|
-
console.log('Replace I.clickLink with I.click')
|
|
1592
|
-
return this.click(locator, context)
|
|
1565
|
+
console.log('clickLink deprecated: Playwright automatically waits for navigation to happen.');
|
|
1566
|
+
console.log('Replace I.clickLink with I.click');
|
|
1567
|
+
return this.click(locator, context);
|
|
1593
1568
|
}
|
|
1594
1569
|
|
|
1595
1570
|
/**
|
|
1596
1571
|
* {{> forceClick }}
|
|
1597
1572
|
*/
|
|
1598
1573
|
async forceClick(locator, context = null) {
|
|
1599
|
-
return proceedClick.call(this, locator, context, { force: true })
|
|
1574
|
+
return proceedClick.call(this, locator, context, { force: true });
|
|
1600
1575
|
}
|
|
1601
1576
|
|
|
1602
1577
|
/**
|
|
1603
1578
|
* {{> doubleClick }}
|
|
1604
1579
|
*/
|
|
1605
1580
|
async doubleClick(locator, context = null) {
|
|
1606
|
-
return proceedClick.call(this, locator, context, { clickCount: 2 })
|
|
1581
|
+
return proceedClick.call(this, locator, context, { clickCount: 2 });
|
|
1607
1582
|
}
|
|
1608
1583
|
|
|
1609
1584
|
/**
|
|
1610
1585
|
* {{> rightClick }}
|
|
1611
1586
|
*/
|
|
1612
1587
|
async rightClick(locator, context = null) {
|
|
1613
|
-
return proceedClick.call(this, locator, context, { button: 'right' })
|
|
1588
|
+
return proceedClick.call(this, locator, context, { button: 'right' });
|
|
1614
1589
|
}
|
|
1615
1590
|
|
|
1616
1591
|
/**
|
|
@@ -1629,9 +1604,9 @@ class Playwright extends Helper {
|
|
|
1629
1604
|
*
|
|
1630
1605
|
*/
|
|
1631
1606
|
async checkOption(field, context = null, options = { force: true }) {
|
|
1632
|
-
const elm = await this._locateCheckable(field, context)
|
|
1633
|
-
await elm.check(options)
|
|
1634
|
-
return this._waitForAction()
|
|
1607
|
+
const elm = await this._locateCheckable(field, context);
|
|
1608
|
+
await elm.check(options);
|
|
1609
|
+
return this._waitForAction();
|
|
1635
1610
|
}
|
|
1636
1611
|
|
|
1637
1612
|
/**
|
|
@@ -1649,41 +1624,41 @@ class Playwright extends Helper {
|
|
|
1649
1624
|
* {{> uncheckOption }}
|
|
1650
1625
|
*/
|
|
1651
1626
|
async uncheckOption(field, context = null, options = { force: true }) {
|
|
1652
|
-
const elm = await this._locateCheckable(field, context)
|
|
1653
|
-
await elm.uncheck(options)
|
|
1654
|
-
return this._waitForAction()
|
|
1627
|
+
const elm = await this._locateCheckable(field, context);
|
|
1628
|
+
await elm.uncheck(options);
|
|
1629
|
+
return this._waitForAction();
|
|
1655
1630
|
}
|
|
1656
1631
|
|
|
1657
1632
|
/**
|
|
1658
1633
|
* {{> seeCheckboxIsChecked }}
|
|
1659
1634
|
*/
|
|
1660
1635
|
async seeCheckboxIsChecked(field) {
|
|
1661
|
-
return proceedIsChecked.call(this, 'assert', field)
|
|
1636
|
+
return proceedIsChecked.call(this, 'assert', field);
|
|
1662
1637
|
}
|
|
1663
1638
|
|
|
1664
1639
|
/**
|
|
1665
1640
|
* {{> dontSeeCheckboxIsChecked }}
|
|
1666
1641
|
*/
|
|
1667
1642
|
async dontSeeCheckboxIsChecked(field) {
|
|
1668
|
-
return proceedIsChecked.call(this, 'negate', field)
|
|
1643
|
+
return proceedIsChecked.call(this, 'negate', field);
|
|
1669
1644
|
}
|
|
1670
1645
|
|
|
1671
1646
|
/**
|
|
1672
1647
|
* {{> pressKeyDown }}
|
|
1673
1648
|
*/
|
|
1674
1649
|
async pressKeyDown(key) {
|
|
1675
|
-
key = getNormalizedKey.call(this, key)
|
|
1676
|
-
await this.page.keyboard.down(key)
|
|
1677
|
-
return this._waitForAction()
|
|
1650
|
+
key = getNormalizedKey.call(this, key);
|
|
1651
|
+
await this.page.keyboard.down(key);
|
|
1652
|
+
return this._waitForAction();
|
|
1678
1653
|
}
|
|
1679
1654
|
|
|
1680
1655
|
/**
|
|
1681
1656
|
* {{> pressKeyUp }}
|
|
1682
1657
|
*/
|
|
1683
1658
|
async pressKeyUp(key) {
|
|
1684
|
-
key = getNormalizedKey.call(this, key)
|
|
1685
|
-
await this.page.keyboard.up(key)
|
|
1686
|
-
return this._waitForAction()
|
|
1659
|
+
key = getNormalizedKey.call(this, key);
|
|
1660
|
+
await this.page.keyboard.up(key);
|
|
1661
|
+
return this._waitForAction();
|
|
1687
1662
|
}
|
|
1688
1663
|
|
|
1689
1664
|
/**
|
|
@@ -1693,28 +1668,28 @@ class Playwright extends Helper {
|
|
|
1693
1668
|
* {{> pressKeyWithKeyNormalization }}
|
|
1694
1669
|
*/
|
|
1695
1670
|
async pressKey(key) {
|
|
1696
|
-
const modifiers = []
|
|
1671
|
+
const modifiers = [];
|
|
1697
1672
|
if (Array.isArray(key)) {
|
|
1698
1673
|
for (let k of key) {
|
|
1699
|
-
k = getNormalizedKey.call(this, k)
|
|
1674
|
+
k = getNormalizedKey.call(this, k);
|
|
1700
1675
|
if (isModifierKey(k)) {
|
|
1701
|
-
modifiers.push(k)
|
|
1676
|
+
modifiers.push(k);
|
|
1702
1677
|
} else {
|
|
1703
|
-
key = k
|
|
1704
|
-
break
|
|
1678
|
+
key = k;
|
|
1679
|
+
break;
|
|
1705
1680
|
}
|
|
1706
1681
|
}
|
|
1707
1682
|
} else {
|
|
1708
|
-
key = getNormalizedKey.call(this, key)
|
|
1683
|
+
key = getNormalizedKey.call(this, key);
|
|
1709
1684
|
}
|
|
1710
1685
|
for (const modifier of modifiers) {
|
|
1711
|
-
await this.page.keyboard.down(modifier)
|
|
1686
|
+
await this.page.keyboard.down(modifier);
|
|
1712
1687
|
}
|
|
1713
|
-
await this.page.keyboard.press(key)
|
|
1688
|
+
await this.page.keyboard.press(key);
|
|
1714
1689
|
for (const modifier of modifiers) {
|
|
1715
|
-
await this.page.keyboard.up(modifier)
|
|
1690
|
+
await this.page.keyboard.up(modifier);
|
|
1716
1691
|
}
|
|
1717
|
-
return this._waitForAction()
|
|
1692
|
+
return this._waitForAction();
|
|
1718
1693
|
}
|
|
1719
1694
|
|
|
1720
1695
|
/**
|
|
@@ -1722,13 +1697,13 @@ class Playwright extends Helper {
|
|
|
1722
1697
|
*/
|
|
1723
1698
|
async type(keys, delay = null) {
|
|
1724
1699
|
if (!Array.isArray(keys)) {
|
|
1725
|
-
keys = keys.toString()
|
|
1726
|
-
keys = keys.split('')
|
|
1700
|
+
keys = keys.toString();
|
|
1701
|
+
keys = keys.split('');
|
|
1727
1702
|
}
|
|
1728
1703
|
|
|
1729
1704
|
for (const key of keys) {
|
|
1730
|
-
await this.page.keyboard.press(key)
|
|
1731
|
-
if (delay) await this.wait(delay / 1000)
|
|
1705
|
+
await this.page.keyboard.press(key);
|
|
1706
|
+
if (delay) await this.wait(delay / 1000);
|
|
1732
1707
|
}
|
|
1733
1708
|
}
|
|
1734
1709
|
|
|
@@ -1737,75 +1712,75 @@ class Playwright extends Helper {
|
|
|
1737
1712
|
*
|
|
1738
1713
|
*/
|
|
1739
1714
|
async fillField(field, value) {
|
|
1740
|
-
const els = await findFields.call(this, field)
|
|
1741
|
-
assertElementExists(els, field, 'Field')
|
|
1742
|
-
const el = els[0]
|
|
1715
|
+
const els = await findFields.call(this, field);
|
|
1716
|
+
assertElementExists(els, field, 'Field');
|
|
1717
|
+
const el = els[0];
|
|
1743
1718
|
|
|
1744
|
-
await el.clear()
|
|
1719
|
+
await el.clear();
|
|
1745
1720
|
|
|
1746
|
-
await highlightActiveElement.call(this, el)
|
|
1721
|
+
await highlightActiveElement.call(this, el);
|
|
1747
1722
|
|
|
1748
|
-
await el.type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1723
|
+
await el.type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1749
1724
|
|
|
1750
|
-
return this._waitForAction()
|
|
1725
|
+
return this._waitForAction();
|
|
1751
1726
|
}
|
|
1752
1727
|
|
|
1753
1728
|
/**
|
|
1754
1729
|
* Clears the text input element: `<input>`, `<textarea>` or `[contenteditable]` .
|
|
1755
1730
|
*
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1731
|
+
*
|
|
1732
|
+
* Examples:
|
|
1733
|
+
*
|
|
1734
|
+
* ```js
|
|
1735
|
+
* I.clearField('.text-area')
|
|
1736
|
+
*
|
|
1737
|
+
* // if this doesn't work use force option
|
|
1738
|
+
* I.clearField('#submit', { force: true })
|
|
1739
|
+
* ```
|
|
1740
|
+
* Use `force` to bypass the [actionability](https://playwright.dev/docs/actionability) checks.
|
|
1741
|
+
*
|
|
1767
1742
|
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
1768
1743
|
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-locator#locator-clear) for available options object as 2nd argument.
|
|
1769
1744
|
*/
|
|
1770
1745
|
async clearField(locator, options = {}) {
|
|
1771
|
-
const els = await findFields.call(this, locator)
|
|
1772
|
-
assertElementExists(els, locator, 'Field to clear')
|
|
1746
|
+
const els = await findFields.call(this, locator);
|
|
1747
|
+
assertElementExists(els, locator, 'Field to clear');
|
|
1773
1748
|
|
|
1774
|
-
const el = els[0]
|
|
1749
|
+
const el = els[0];
|
|
1775
1750
|
|
|
1776
|
-
await highlightActiveElement.call(this, el)
|
|
1751
|
+
await highlightActiveElement.call(this, el);
|
|
1777
1752
|
|
|
1778
|
-
await el.clear()
|
|
1753
|
+
await el.clear();
|
|
1779
1754
|
|
|
1780
|
-
return this._waitForAction()
|
|
1755
|
+
return this._waitForAction();
|
|
1781
1756
|
}
|
|
1782
1757
|
|
|
1783
1758
|
/**
|
|
1784
1759
|
* {{> appendField }}
|
|
1785
1760
|
*/
|
|
1786
1761
|
async appendField(field, value) {
|
|
1787
|
-
const els = await findFields.call(this, field)
|
|
1788
|
-
assertElementExists(els, field, 'Field')
|
|
1789
|
-
await highlightActiveElement.call(this, els[0])
|
|
1790
|
-
await els[0].press('End')
|
|
1791
|
-
await els[0].type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1792
|
-
return this._waitForAction()
|
|
1762
|
+
const els = await findFields.call(this, field);
|
|
1763
|
+
assertElementExists(els, field, 'Field');
|
|
1764
|
+
await highlightActiveElement.call(this, els[0]);
|
|
1765
|
+
await els[0].press('End');
|
|
1766
|
+
await els[0].type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1767
|
+
return this._waitForAction();
|
|
1793
1768
|
}
|
|
1794
1769
|
|
|
1795
1770
|
/**
|
|
1796
1771
|
* {{> seeInField }}
|
|
1797
1772
|
*/
|
|
1798
1773
|
async seeInField(field, value) {
|
|
1799
|
-
const _value = typeof value === 'boolean' ? value : value.toString()
|
|
1800
|
-
return proceedSeeInField.call(this, 'assert', field, _value)
|
|
1774
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1775
|
+
return proceedSeeInField.call(this, 'assert', field, _value);
|
|
1801
1776
|
}
|
|
1802
1777
|
|
|
1803
1778
|
/**
|
|
1804
1779
|
* {{> dontSeeInField }}
|
|
1805
1780
|
*/
|
|
1806
1781
|
async dontSeeInField(field, value) {
|
|
1807
|
-
const _value = typeof value === 'boolean' ? value : value.toString()
|
|
1808
|
-
return proceedSeeInField.call(this, 'negate', field, _value)
|
|
1782
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1783
|
+
return proceedSeeInField.call(this, 'negate', field, _value);
|
|
1809
1784
|
}
|
|
1810
1785
|
|
|
1811
1786
|
/**
|
|
@@ -1813,38 +1788,38 @@ class Playwright extends Helper {
|
|
|
1813
1788
|
*
|
|
1814
1789
|
*/
|
|
1815
1790
|
async attachFile(locator, pathToFile) {
|
|
1816
|
-
const file = path.join(global.codecept_dir, pathToFile)
|
|
1791
|
+
const file = path.join(global.codecept_dir, pathToFile);
|
|
1817
1792
|
|
|
1818
1793
|
if (!fileExists(file)) {
|
|
1819
|
-
throw new Error(`File at ${file} can not be found on local system`)
|
|
1794
|
+
throw new Error(`File at ${file} can not be found on local system`);
|
|
1820
1795
|
}
|
|
1821
|
-
const els = await findFields.call(this, locator)
|
|
1822
|
-
assertElementExists(els, locator, 'Field')
|
|
1823
|
-
await els[0].setInputFiles(file)
|
|
1824
|
-
return this._waitForAction()
|
|
1796
|
+
const els = await findFields.call(this, locator);
|
|
1797
|
+
assertElementExists(els, locator, 'Field');
|
|
1798
|
+
await els[0].setInputFiles(file);
|
|
1799
|
+
return this._waitForAction();
|
|
1825
1800
|
}
|
|
1826
1801
|
|
|
1827
1802
|
/**
|
|
1828
1803
|
* {{> selectOption }}
|
|
1829
1804
|
*/
|
|
1830
1805
|
async selectOption(select, option) {
|
|
1831
|
-
const els = await findFields.call(this, select)
|
|
1832
|
-
assertElementExists(els, select, 'Selectable field')
|
|
1833
|
-
const el = els[0]
|
|
1806
|
+
const els = await findFields.call(this, select);
|
|
1807
|
+
assertElementExists(els, select, 'Selectable field');
|
|
1808
|
+
const el = els[0];
|
|
1834
1809
|
|
|
1835
|
-
await highlightActiveElement.call(this, el)
|
|
1836
|
-
let optionToSelect = ''
|
|
1810
|
+
await highlightActiveElement.call(this, el);
|
|
1811
|
+
let optionToSelect = '';
|
|
1837
1812
|
|
|
1838
1813
|
try {
|
|
1839
|
-
optionToSelect = (await el.locator('option', { hasText: option }).textContent()).trim()
|
|
1814
|
+
optionToSelect = (await el.locator('option', { hasText: option }).textContent()).trim();
|
|
1840
1815
|
} catch (e) {
|
|
1841
|
-
optionToSelect = option
|
|
1816
|
+
optionToSelect = option;
|
|
1842
1817
|
}
|
|
1843
1818
|
|
|
1844
|
-
if (!Array.isArray(option)) option = [optionToSelect]
|
|
1819
|
+
if (!Array.isArray(option)) option = [optionToSelect];
|
|
1845
1820
|
|
|
1846
|
-
await el.selectOption(option)
|
|
1847
|
-
return this._waitForAction()
|
|
1821
|
+
await el.selectOption(option);
|
|
1822
|
+
return this._waitForAction();
|
|
1848
1823
|
}
|
|
1849
1824
|
|
|
1850
1825
|
/**
|
|
@@ -1852,37 +1827,37 @@ class Playwright extends Helper {
|
|
|
1852
1827
|
*
|
|
1853
1828
|
*/
|
|
1854
1829
|
async grabNumberOfVisibleElements(locator) {
|
|
1855
|
-
let els = await this._locate(locator)
|
|
1856
|
-
els = await Promise.all(els.map(
|
|
1857
|
-
return els.filter(
|
|
1830
|
+
let els = await this._locate(locator);
|
|
1831
|
+
els = await Promise.all(els.map(el => el.isVisible()));
|
|
1832
|
+
return els.filter(v => v).length;
|
|
1858
1833
|
}
|
|
1859
1834
|
|
|
1860
1835
|
/**
|
|
1861
1836
|
* {{> seeInCurrentUrl }}
|
|
1862
1837
|
*/
|
|
1863
1838
|
async seeInCurrentUrl(url) {
|
|
1864
|
-
stringIncludes('url').assert(url, await this._getPageUrl())
|
|
1839
|
+
stringIncludes('url').assert(url, await this._getPageUrl());
|
|
1865
1840
|
}
|
|
1866
1841
|
|
|
1867
1842
|
/**
|
|
1868
1843
|
* {{> dontSeeInCurrentUrl }}
|
|
1869
1844
|
*/
|
|
1870
1845
|
async dontSeeInCurrentUrl(url) {
|
|
1871
|
-
stringIncludes('url').negate(url, await this._getPageUrl())
|
|
1846
|
+
stringIncludes('url').negate(url, await this._getPageUrl());
|
|
1872
1847
|
}
|
|
1873
1848
|
|
|
1874
1849
|
/**
|
|
1875
1850
|
* {{> seeCurrentUrlEquals }}
|
|
1876
1851
|
*/
|
|
1877
1852
|
async seeCurrentUrlEquals(url) {
|
|
1878
|
-
urlEquals(this.options.url).assert(url, await this._getPageUrl())
|
|
1853
|
+
urlEquals(this.options.url).assert(url, await this._getPageUrl());
|
|
1879
1854
|
}
|
|
1880
1855
|
|
|
1881
1856
|
/**
|
|
1882
1857
|
* {{> dontSeeCurrentUrlEquals }}
|
|
1883
1858
|
*/
|
|
1884
1859
|
async dontSeeCurrentUrlEquals(url) {
|
|
1885
|
-
urlEquals(this.options.url).negate(url, await this._getPageUrl())
|
|
1860
|
+
urlEquals(this.options.url).negate(url, await this._getPageUrl());
|
|
1886
1861
|
}
|
|
1887
1862
|
|
|
1888
1863
|
/**
|
|
@@ -1891,14 +1866,14 @@ class Playwright extends Helper {
|
|
|
1891
1866
|
*
|
|
1892
1867
|
*/
|
|
1893
1868
|
async see(text, context = null) {
|
|
1894
|
-
return proceedSee.call(this, 'assert', text, context)
|
|
1869
|
+
return proceedSee.call(this, 'assert', text, context);
|
|
1895
1870
|
}
|
|
1896
1871
|
|
|
1897
1872
|
/**
|
|
1898
1873
|
* {{> seeTextEquals }}
|
|
1899
1874
|
*/
|
|
1900
1875
|
async seeTextEquals(text, context = null) {
|
|
1901
|
-
return proceedSee.call(this, 'assert', text, context, true)
|
|
1876
|
+
return proceedSee.call(this, 'assert', text, context, true);
|
|
1902
1877
|
}
|
|
1903
1878
|
|
|
1904
1879
|
/**
|
|
@@ -1907,14 +1882,14 @@ class Playwright extends Helper {
|
|
|
1907
1882
|
*
|
|
1908
1883
|
*/
|
|
1909
1884
|
async dontSee(text, context = null) {
|
|
1910
|
-
return proceedSee.call(this, 'negate', text, context)
|
|
1885
|
+
return proceedSee.call(this, 'negate', text, context);
|
|
1911
1886
|
}
|
|
1912
1887
|
|
|
1913
1888
|
/**
|
|
1914
1889
|
* {{> grabSource }}
|
|
1915
1890
|
*/
|
|
1916
1891
|
async grabSource() {
|
|
1917
|
-
return this.page.content()
|
|
1892
|
+
return this.page.content();
|
|
1918
1893
|
}
|
|
1919
1894
|
|
|
1920
1895
|
/**
|
|
@@ -1929,32 +1904,32 @@ class Playwright extends Helper {
|
|
|
1929
1904
|
* @return {Promise<any[]>}
|
|
1930
1905
|
*/
|
|
1931
1906
|
async grabBrowserLogs() {
|
|
1932
|
-
const logs = consoleLogStore.entries
|
|
1933
|
-
consoleLogStore.clear()
|
|
1934
|
-
return logs
|
|
1907
|
+
const logs = consoleLogStore.entries;
|
|
1908
|
+
consoleLogStore.clear();
|
|
1909
|
+
return logs;
|
|
1935
1910
|
}
|
|
1936
1911
|
|
|
1937
1912
|
/**
|
|
1938
1913
|
* {{> grabCurrentUrl }}
|
|
1939
1914
|
*/
|
|
1940
1915
|
async grabCurrentUrl() {
|
|
1941
|
-
return this._getPageUrl()
|
|
1916
|
+
return this._getPageUrl();
|
|
1942
1917
|
}
|
|
1943
1918
|
|
|
1944
1919
|
/**
|
|
1945
1920
|
* {{> seeInSource }}
|
|
1946
1921
|
*/
|
|
1947
1922
|
async seeInSource(text) {
|
|
1948
|
-
const source = await this.page.content()
|
|
1949
|
-
stringIncludes('HTML source of a page').assert(text, source)
|
|
1923
|
+
const source = await this.page.content();
|
|
1924
|
+
stringIncludes('HTML source of a page').assert(text, source);
|
|
1950
1925
|
}
|
|
1951
1926
|
|
|
1952
1927
|
/**
|
|
1953
1928
|
* {{> dontSeeInSource }}
|
|
1954
1929
|
*/
|
|
1955
1930
|
async dontSeeInSource(text) {
|
|
1956
|
-
const source = await this.page.content()
|
|
1957
|
-
stringIncludes('HTML source of a page').negate(text, source)
|
|
1931
|
+
const source = await this.page.content();
|
|
1932
|
+
stringIncludes('HTML source of a page').negate(text, source);
|
|
1958
1933
|
}
|
|
1959
1934
|
|
|
1960
1935
|
/**
|
|
@@ -1963,10 +1938,8 @@ class Playwright extends Helper {
|
|
|
1963
1938
|
*
|
|
1964
1939
|
*/
|
|
1965
1940
|
async seeNumberOfElements(locator, num) {
|
|
1966
|
-
const elements = await this._locate(locator)
|
|
1967
|
-
return equals(
|
|
1968
|
-
`expected number of elements (${new Locator(locator)}) is ${num}, but found ${elements.length}`,
|
|
1969
|
-
).assert(elements.length, num)
|
|
1941
|
+
const elements = await this._locate(locator);
|
|
1942
|
+
return equals(`expected number of elements (${(new Locator(locator))}) is ${num}, but found ${elements.length}`).assert(elements.length, num);
|
|
1970
1943
|
}
|
|
1971
1944
|
|
|
1972
1945
|
/**
|
|
@@ -1975,11 +1948,8 @@ class Playwright extends Helper {
|
|
|
1975
1948
|
*
|
|
1976
1949
|
*/
|
|
1977
1950
|
async seeNumberOfVisibleElements(locator, num) {
|
|
1978
|
-
const res = await this.grabNumberOfVisibleElements(locator)
|
|
1979
|
-
return equals(`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`).assert(
|
|
1980
|
-
res,
|
|
1981
|
-
num,
|
|
1982
|
-
)
|
|
1951
|
+
const res = await this.grabNumberOfVisibleElements(locator);
|
|
1952
|
+
return equals(`expected number of visible elements (${(new Locator(locator))}) is ${num}, but found ${res}`).assert(res, num);
|
|
1983
1953
|
}
|
|
1984
1954
|
|
|
1985
1955
|
/**
|
|
@@ -1987,9 +1957,9 @@ class Playwright extends Helper {
|
|
|
1987
1957
|
*/
|
|
1988
1958
|
async setCookie(cookie) {
|
|
1989
1959
|
if (Array.isArray(cookie)) {
|
|
1990
|
-
return this.browserContext.addCookies(cookie)
|
|
1960
|
+
return this.browserContext.addCookies(cookie);
|
|
1991
1961
|
}
|
|
1992
|
-
return this.browserContext.addCookies([cookie])
|
|
1962
|
+
return this.browserContext.addCookies([cookie]);
|
|
1993
1963
|
}
|
|
1994
1964
|
|
|
1995
1965
|
/**
|
|
@@ -1997,16 +1967,16 @@ class Playwright extends Helper {
|
|
|
1997
1967
|
*
|
|
1998
1968
|
*/
|
|
1999
1969
|
async seeCookie(name) {
|
|
2000
|
-
const cookies = await this.browserContext.cookies()
|
|
2001
|
-
empty(`cookie ${name} to be set`).negate(cookies.filter(
|
|
1970
|
+
const cookies = await this.browserContext.cookies();
|
|
1971
|
+
empty(`cookie ${name} to be set`).negate(cookies.filter(c => c.name === name));
|
|
2002
1972
|
}
|
|
2003
1973
|
|
|
2004
1974
|
/**
|
|
2005
1975
|
* {{> dontSeeCookie }}
|
|
2006
1976
|
*/
|
|
2007
1977
|
async dontSeeCookie(name) {
|
|
2008
|
-
const cookies = await this.browserContext.cookies()
|
|
2009
|
-
empty(`cookie ${name}
|
|
1978
|
+
const cookies = await this.browserContext.cookies();
|
|
1979
|
+
empty(`cookie ${name} to be set`).assert(cookies.filter(c => c.name === name));
|
|
2010
1980
|
}
|
|
2011
1981
|
|
|
2012
1982
|
/**
|
|
@@ -2015,10 +1985,10 @@ class Playwright extends Helper {
|
|
|
2015
1985
|
* {{> grabCookie }}
|
|
2016
1986
|
*/
|
|
2017
1987
|
async grabCookie(name) {
|
|
2018
|
-
const cookies = await this.browserContext.cookies()
|
|
2019
|
-
if (!name) return cookies
|
|
2020
|
-
const cookie = cookies.filter(
|
|
2021
|
-
if (cookie[0]) return cookie[0]
|
|
1988
|
+
const cookies = await this.browserContext.cookies();
|
|
1989
|
+
if (!name) return cookies;
|
|
1990
|
+
const cookie = cookies.filter(c => c.name === name);
|
|
1991
|
+
if (cookie[0]) return cookie[0];
|
|
2022
1992
|
}
|
|
2023
1993
|
|
|
2024
1994
|
/**
|
|
@@ -2027,8 +1997,8 @@ class Playwright extends Helper {
|
|
|
2027
1997
|
async clearCookie() {
|
|
2028
1998
|
// Playwright currently doesn't support to delete a certain cookie
|
|
2029
1999
|
// https://github.com/microsoft/playwright/blob/main/docs/src/api/class-browsercontext.md#async-method-browsercontextclearcookies
|
|
2030
|
-
if (!this.browserContext) return
|
|
2031
|
-
return this.browserContext.clearCookies()
|
|
2000
|
+
if (!this.browserContext) return;
|
|
2001
|
+
return this.browserContext.clearCookies();
|
|
2032
2002
|
}
|
|
2033
2003
|
|
|
2034
2004
|
/**
|
|
@@ -2058,9 +2028,9 @@ class Playwright extends Helper {
|
|
|
2058
2028
|
async executeScript(fn, arg) {
|
|
2059
2029
|
if (this.context && this.context.constructor.name === 'FrameLocator') {
|
|
2060
2030
|
// switching to iframe context
|
|
2061
|
-
return this.context.locator(':root').evaluate(fn, arg)
|
|
2031
|
+
return this.context.locator(':root').evaluate(fn, arg);
|
|
2062
2032
|
}
|
|
2063
|
-
return this.page.evaluate.apply(this.page, [fn, arg])
|
|
2033
|
+
return this.page.evaluate.apply(this.page, [fn, arg]);
|
|
2064
2034
|
}
|
|
2065
2035
|
|
|
2066
2036
|
/**
|
|
@@ -2069,14 +2039,14 @@ class Playwright extends Helper {
|
|
|
2069
2039
|
* @param {*} locator
|
|
2070
2040
|
*/
|
|
2071
2041
|
_contextLocator(locator) {
|
|
2072
|
-
locator = buildLocatorString(new Locator(locator, 'css'))
|
|
2042
|
+
locator = buildLocatorString(new Locator(locator, 'css'));
|
|
2073
2043
|
|
|
2074
2044
|
if (this.contextLocator) {
|
|
2075
|
-
const contextLocator = buildLocatorString(new Locator(this.contextLocator, 'css'))
|
|
2076
|
-
locator = `${contextLocator} >> ${locator}
|
|
2045
|
+
const contextLocator = buildLocatorString(new Locator(this.contextLocator, 'css'));
|
|
2046
|
+
locator = `${contextLocator} >> ${locator}`;
|
|
2077
2047
|
}
|
|
2078
2048
|
|
|
2079
|
-
return locator
|
|
2049
|
+
return locator;
|
|
2080
2050
|
}
|
|
2081
2051
|
|
|
2082
2052
|
/**
|
|
@@ -2084,11 +2054,11 @@ class Playwright extends Helper {
|
|
|
2084
2054
|
*
|
|
2085
2055
|
*/
|
|
2086
2056
|
async grabTextFrom(locator) {
|
|
2087
|
-
locator = this._contextLocator(locator)
|
|
2088
|
-
const text = await this.page.textContent(locator)
|
|
2089
|
-
assertElementExists(text, locator)
|
|
2090
|
-
this.debugSection('Text', text)
|
|
2091
|
-
return text
|
|
2057
|
+
locator = this._contextLocator(locator);
|
|
2058
|
+
const text = await this.page.textContent(locator);
|
|
2059
|
+
assertElementExists(text, locator);
|
|
2060
|
+
this.debugSection('Text', text);
|
|
2061
|
+
return text;
|
|
2092
2062
|
}
|
|
2093
2063
|
|
|
2094
2064
|
/**
|
|
@@ -2096,51 +2066,51 @@ class Playwright extends Helper {
|
|
|
2096
2066
|
*
|
|
2097
2067
|
*/
|
|
2098
2068
|
async grabTextFromAll(locator) {
|
|
2099
|
-
const els = await this._locate(locator)
|
|
2100
|
-
const texts = []
|
|
2069
|
+
const els = await this._locate(locator);
|
|
2070
|
+
const texts = [];
|
|
2101
2071
|
for (const el of els) {
|
|
2102
|
-
texts.push(await await el.innerText())
|
|
2072
|
+
texts.push(await (await el.innerText()));
|
|
2103
2073
|
}
|
|
2104
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2105
|
-
return texts
|
|
2074
|
+
this.debug(`Matched ${els.length} elements`);
|
|
2075
|
+
return texts;
|
|
2106
2076
|
}
|
|
2107
2077
|
|
|
2108
2078
|
/**
|
|
2109
2079
|
* {{> grabValueFrom }}
|
|
2110
2080
|
*/
|
|
2111
2081
|
async grabValueFrom(locator) {
|
|
2112
|
-
const values = await this.grabValueFromAll(locator)
|
|
2113
|
-
assertElementExists(values, locator)
|
|
2114
|
-
this.debugSection('Value', values[0])
|
|
2115
|
-
return values[0]
|
|
2082
|
+
const values = await this.grabValueFromAll(locator);
|
|
2083
|
+
assertElementExists(values, locator);
|
|
2084
|
+
this.debugSection('Value', values[0]);
|
|
2085
|
+
return values[0];
|
|
2116
2086
|
}
|
|
2117
2087
|
|
|
2118
2088
|
/**
|
|
2119
2089
|
* {{> grabValueFromAll }}
|
|
2120
2090
|
*/
|
|
2121
2091
|
async grabValueFromAll(locator) {
|
|
2122
|
-
const els = await findFields.call(this, locator)
|
|
2123
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2124
|
-
return Promise.all(els.map(
|
|
2092
|
+
const els = await findFields.call(this, locator);
|
|
2093
|
+
this.debug(`Matched ${els.length} elements`);
|
|
2094
|
+
return Promise.all(els.map(el => el.inputValue()));
|
|
2125
2095
|
}
|
|
2126
2096
|
|
|
2127
2097
|
/**
|
|
2128
2098
|
* {{> grabHTMLFrom }}
|
|
2129
2099
|
*/
|
|
2130
2100
|
async grabHTMLFrom(locator) {
|
|
2131
|
-
const html = await this.grabHTMLFromAll(locator)
|
|
2132
|
-
assertElementExists(html, locator)
|
|
2133
|
-
this.debugSection('HTML', html[0])
|
|
2134
|
-
return html[0]
|
|
2101
|
+
const html = await this.grabHTMLFromAll(locator);
|
|
2102
|
+
assertElementExists(html, locator);
|
|
2103
|
+
this.debugSection('HTML', html[0]);
|
|
2104
|
+
return html[0];
|
|
2135
2105
|
}
|
|
2136
2106
|
|
|
2137
2107
|
/**
|
|
2138
2108
|
* {{> grabHTMLFromAll }}
|
|
2139
2109
|
*/
|
|
2140
2110
|
async grabHTMLFromAll(locator) {
|
|
2141
|
-
const els = await this._locate(locator)
|
|
2142
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2143
|
-
return Promise.all(els.map(
|
|
2111
|
+
const els = await this._locate(locator);
|
|
2112
|
+
this.debug(`Matched ${els.length} elements`);
|
|
2113
|
+
return Promise.all(els.map(el => el.innerHTML()));
|
|
2144
2114
|
}
|
|
2145
2115
|
|
|
2146
2116
|
/**
|
|
@@ -2148,10 +2118,10 @@ class Playwright extends Helper {
|
|
|
2148
2118
|
*
|
|
2149
2119
|
*/
|
|
2150
2120
|
async grabCssPropertyFrom(locator, cssProperty) {
|
|
2151
|
-
const cssValues = await this.grabCssPropertyFromAll(locator, cssProperty)
|
|
2152
|
-
assertElementExists(cssValues, locator)
|
|
2153
|
-
this.debugSection('CSS', cssValues[0])
|
|
2154
|
-
return cssValues[0]
|
|
2121
|
+
const cssValues = await this.grabCssPropertyFromAll(locator, cssProperty);
|
|
2122
|
+
assertElementExists(cssValues, locator);
|
|
2123
|
+
this.debugSection('CSS', cssValues[0]);
|
|
2124
|
+
return cssValues[0];
|
|
2155
2125
|
}
|
|
2156
2126
|
|
|
2157
2127
|
/**
|
|
@@ -2159,15 +2129,11 @@ class Playwright extends Helper {
|
|
|
2159
2129
|
*
|
|
2160
2130
|
*/
|
|
2161
2131
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
2162
|
-
const els = await this._locate(locator)
|
|
2163
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2164
|
-
const cssValues = await Promise.all(
|
|
2165
|
-
els.map((el) =>
|
|
2166
|
-
el.evaluate((el, cssProperty) => getComputedStyle(el).getPropertyValue(cssProperty), cssProperty),
|
|
2167
|
-
),
|
|
2168
|
-
)
|
|
2132
|
+
const els = await this._locate(locator);
|
|
2133
|
+
this.debug(`Matched ${els.length} elements`);
|
|
2134
|
+
const cssValues = await Promise.all(els.map(el => el.evaluate((el, cssProperty) => getComputedStyle(el).getPropertyValue(cssProperty), cssProperty)));
|
|
2169
2135
|
|
|
2170
|
-
return cssValues
|
|
2136
|
+
return cssValues;
|
|
2171
2137
|
}
|
|
2172
2138
|
|
|
2173
2139
|
/**
|
|
@@ -2175,37 +2141,35 @@ class Playwright extends Helper {
|
|
|
2175
2141
|
*
|
|
2176
2142
|
*/
|
|
2177
2143
|
async seeCssPropertiesOnElements(locator, cssProperties) {
|
|
2178
|
-
const res = await this._locate(locator)
|
|
2179
|
-
assertElementExists(res, locator)
|
|
2144
|
+
const res = await this._locate(locator);
|
|
2145
|
+
assertElementExists(res, locator);
|
|
2180
2146
|
|
|
2181
|
-
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties)
|
|
2182
|
-
const elemAmount = res.length
|
|
2183
|
-
let props = []
|
|
2147
|
+
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
2148
|
+
const elemAmount = res.length;
|
|
2149
|
+
let props = [];
|
|
2184
2150
|
|
|
2185
2151
|
for (const element of res) {
|
|
2186
2152
|
for (const prop of Object.keys(cssProperties)) {
|
|
2187
|
-
const cssProp = await this.grabCssPropertyFrom(locator, prop)
|
|
2153
|
+
const cssProp = await this.grabCssPropertyFrom(locator, prop);
|
|
2188
2154
|
if (isColorProperty(prop)) {
|
|
2189
|
-
props.push(convertColorToRGBA(cssProp))
|
|
2155
|
+
props.push(convertColorToRGBA(cssProp));
|
|
2190
2156
|
} else {
|
|
2191
|
-
props.push(cssProp)
|
|
2157
|
+
props.push(cssProp);
|
|
2192
2158
|
}
|
|
2193
2159
|
}
|
|
2194
2160
|
}
|
|
2195
2161
|
|
|
2196
|
-
const values = Object.keys(cssPropertiesCamelCase).map(
|
|
2197
|
-
if (!Array.isArray(props)) props = [props]
|
|
2198
|
-
let chunked = chunkArray(props, values.length)
|
|
2162
|
+
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
|
|
2163
|
+
if (!Array.isArray(props)) props = [props];
|
|
2164
|
+
let chunked = chunkArray(props, values.length);
|
|
2199
2165
|
chunked = chunked.filter((val) => {
|
|
2200
2166
|
for (let i = 0; i < val.length; ++i) {
|
|
2201
2167
|
// eslint-disable-next-line eqeqeq
|
|
2202
|
-
if (val[i] != values[i]) return false
|
|
2168
|
+
if (val[i] != values[i]) return false;
|
|
2203
2169
|
}
|
|
2204
|
-
return true
|
|
2205
|
-
})
|
|
2206
|
-
return equals(
|
|
2207
|
-
`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`,
|
|
2208
|
-
).assert(chunked.length, elemAmount)
|
|
2170
|
+
return true;
|
|
2171
|
+
});
|
|
2172
|
+
return equals(`all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount);
|
|
2209
2173
|
}
|
|
2210
2174
|
|
|
2211
2175
|
/**
|
|
@@ -2213,33 +2177,31 @@ class Playwright extends Helper {
|
|
|
2213
2177
|
*
|
|
2214
2178
|
*/
|
|
2215
2179
|
async seeAttributesOnElements(locator, attributes) {
|
|
2216
|
-
const res = await this._locate(locator)
|
|
2217
|
-
assertElementExists(res, locator)
|
|
2180
|
+
const res = await this._locate(locator);
|
|
2181
|
+
assertElementExists(res, locator);
|
|
2218
2182
|
|
|
2219
|
-
const elemAmount = res.length
|
|
2220
|
-
const commands = []
|
|
2183
|
+
const elemAmount = res.length;
|
|
2184
|
+
const commands = [];
|
|
2221
2185
|
res.forEach((el) => {
|
|
2222
2186
|
Object.keys(attributes).forEach((prop) => {
|
|
2223
|
-
commands.push(el
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2187
|
+
commands.push(el
|
|
2188
|
+
.evaluate((el, attr) => el[attr] || el.getAttribute(attr), prop));
|
|
2189
|
+
});
|
|
2190
|
+
});
|
|
2191
|
+
let attrs = await Promise.all(commands);
|
|
2192
|
+
const values = Object.keys(attributes).map(key => attributes[key]);
|
|
2193
|
+
if (!Array.isArray(attrs)) attrs = [attrs];
|
|
2194
|
+
let chunked = chunkArray(attrs, values.length);
|
|
2230
2195
|
chunked = chunked.filter((val) => {
|
|
2231
2196
|
for (let i = 0; i < val.length; ++i) {
|
|
2232
2197
|
// the attribute could be a boolean
|
|
2233
|
-
if (typeof val[i] === 'boolean') return val[i] === values[i]
|
|
2198
|
+
if (typeof val[i] === 'boolean') return val[i] === values[i];
|
|
2234
2199
|
// if the attribute doesn't exist, returns false as well
|
|
2235
|
-
if (!val[i] || !val[i].includes(values[i])) return false
|
|
2200
|
+
if (!val[i] || !val[i].includes(values[i])) return false;
|
|
2236
2201
|
}
|
|
2237
|
-
return true
|
|
2238
|
-
})
|
|
2239
|
-
return equals(`all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`).assert(
|
|
2240
|
-
chunked.length,
|
|
2241
|
-
elemAmount,
|
|
2242
|
-
)
|
|
2202
|
+
return true;
|
|
2203
|
+
});
|
|
2204
|
+
return equals(`all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`).assert(chunked.length, elemAmount);
|
|
2243
2205
|
}
|
|
2244
2206
|
|
|
2245
2207
|
/**
|
|
@@ -2247,21 +2209,21 @@ class Playwright extends Helper {
|
|
|
2247
2209
|
*
|
|
2248
2210
|
*/
|
|
2249
2211
|
async dragSlider(locator, offsetX = 0) {
|
|
2250
|
-
const src = await this._locateElement(locator)
|
|
2251
|
-
assertElementExists(src, locator, 'Slider Element')
|
|
2212
|
+
const src = await this._locateElement(locator);
|
|
2213
|
+
assertElementExists(src, locator, 'Slider Element');
|
|
2252
2214
|
|
|
2253
2215
|
// Note: Using clickablePoint private api because the .BoundingBox does not take into account iframe offsets!
|
|
2254
|
-
const sliderSource = await clickablePoint(src)
|
|
2216
|
+
const sliderSource = await clickablePoint(src);
|
|
2255
2217
|
|
|
2256
2218
|
// Drag start point
|
|
2257
|
-
await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 })
|
|
2258
|
-
await this.page.mouse.down()
|
|
2219
|
+
await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 });
|
|
2220
|
+
await this.page.mouse.down();
|
|
2259
2221
|
|
|
2260
2222
|
// Drag destination
|
|
2261
|
-
await this.page.mouse.move(sliderSource.x + offsetX, sliderSource.y, { steps: 5 })
|
|
2262
|
-
await this.page.mouse.up()
|
|
2223
|
+
await this.page.mouse.move(sliderSource.x + offsetX, sliderSource.y, { steps: 5 });
|
|
2224
|
+
await this.page.mouse.up();
|
|
2263
2225
|
|
|
2264
|
-
return this._waitForAction()
|
|
2226
|
+
return this._waitForAction();
|
|
2265
2227
|
}
|
|
2266
2228
|
|
|
2267
2229
|
/**
|
|
@@ -2269,10 +2231,10 @@ class Playwright extends Helper {
|
|
|
2269
2231
|
*
|
|
2270
2232
|
*/
|
|
2271
2233
|
async grabAttributeFrom(locator, attr) {
|
|
2272
|
-
const attrs = await this.grabAttributeFromAll(locator, attr)
|
|
2273
|
-
assertElementExists(attrs, locator)
|
|
2274
|
-
this.debugSection('Attribute', attrs[0])
|
|
2275
|
-
return attrs[0]
|
|
2234
|
+
const attrs = await this.grabAttributeFromAll(locator, attr);
|
|
2235
|
+
assertElementExists(attrs, locator);
|
|
2236
|
+
this.debugSection('Attribute', attrs[0]);
|
|
2237
|
+
return attrs[0];
|
|
2276
2238
|
}
|
|
2277
2239
|
|
|
2278
2240
|
/**
|
|
@@ -2280,15 +2242,15 @@ class Playwright extends Helper {
|
|
|
2280
2242
|
*
|
|
2281
2243
|
*/
|
|
2282
2244
|
async grabAttributeFromAll(locator, attr) {
|
|
2283
|
-
const els = await this._locate(locator)
|
|
2284
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2285
|
-
const array = []
|
|
2245
|
+
const els = await this._locate(locator);
|
|
2246
|
+
this.debug(`Matched ${els.length} elements`);
|
|
2247
|
+
const array = [];
|
|
2286
2248
|
|
|
2287
2249
|
for (let index = 0; index < els.length; index++) {
|
|
2288
|
-
array.push(await els[index].getAttribute(attr))
|
|
2250
|
+
array.push(await els[index].getAttribute(attr));
|
|
2289
2251
|
}
|
|
2290
2252
|
|
|
2291
|
-
return array
|
|
2253
|
+
return array;
|
|
2292
2254
|
}
|
|
2293
2255
|
|
|
2294
2256
|
/**
|
|
@@ -2296,43 +2258,43 @@ class Playwright extends Helper {
|
|
|
2296
2258
|
*
|
|
2297
2259
|
*/
|
|
2298
2260
|
async saveElementScreenshot(locator, fileName) {
|
|
2299
|
-
const outputFile = screenshotOutputFolder(fileName)
|
|
2261
|
+
const outputFile = screenshotOutputFolder(fileName);
|
|
2300
2262
|
|
|
2301
|
-
const res = await this._locateElement(locator)
|
|
2302
|
-
assertElementExists(res, locator)
|
|
2303
|
-
const elem = res
|
|
2304
|
-
this.debug(`Screenshot of ${new Locator(locator)} element has been saved to ${outputFile}`)
|
|
2305
|
-
return elem.screenshot({ path: outputFile, type: 'png' })
|
|
2263
|
+
const res = await this._locateElement(locator);
|
|
2264
|
+
assertElementExists(res, locator);
|
|
2265
|
+
const elem = res;
|
|
2266
|
+
this.debug(`Screenshot of ${(new Locator(locator))} element has been saved to ${outputFile}`);
|
|
2267
|
+
return elem.screenshot({ path: outputFile, type: 'png' });
|
|
2306
2268
|
}
|
|
2307
2269
|
|
|
2308
2270
|
/**
|
|
2309
2271
|
* {{> saveScreenshot }}
|
|
2310
2272
|
*/
|
|
2311
2273
|
async saveScreenshot(fileName, fullPage) {
|
|
2312
|
-
const fullPageOption = fullPage || this.options.fullPageScreenshots
|
|
2313
|
-
let outputFile = screenshotOutputFolder(fileName)
|
|
2274
|
+
const fullPageOption = fullPage || this.options.fullPageScreenshots;
|
|
2275
|
+
let outputFile = screenshotOutputFolder(fileName);
|
|
2314
2276
|
|
|
2315
|
-
this.debug(`Screenshot is saving to ${outputFile}`)
|
|
2277
|
+
this.debug(`Screenshot is saving to ${outputFile}`);
|
|
2316
2278
|
|
|
2317
2279
|
await this.page.screenshot({
|
|
2318
2280
|
path: outputFile,
|
|
2319
2281
|
fullPage: fullPageOption,
|
|
2320
2282
|
type: 'png',
|
|
2321
|
-
})
|
|
2283
|
+
});
|
|
2322
2284
|
|
|
2323
2285
|
if (this.activeSessionName) {
|
|
2324
2286
|
for (const sessionName in this.sessionPages) {
|
|
2325
|
-
const activeSessionPage = this.sessionPages[sessionName]
|
|
2326
|
-
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`)
|
|
2287
|
+
const activeSessionPage = this.sessionPages[sessionName];
|
|
2288
|
+
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`);
|
|
2327
2289
|
|
|
2328
|
-
this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`)
|
|
2290
|
+
this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`);
|
|
2329
2291
|
|
|
2330
2292
|
if (activeSessionPage) {
|
|
2331
2293
|
await activeSessionPage.screenshot({
|
|
2332
2294
|
path: outputFile,
|
|
2333
2295
|
fullPage: fullPageOption,
|
|
2334
2296
|
type: 'png',
|
|
2335
|
-
})
|
|
2297
|
+
});
|
|
2336
2298
|
}
|
|
2337
2299
|
}
|
|
2338
2300
|
}
|
|
@@ -2356,23 +2318,20 @@ class Playwright extends Helper {
|
|
|
2356
2318
|
* @returns {Promise<object>} response
|
|
2357
2319
|
*/
|
|
2358
2320
|
async makeApiRequest(method, url, options) {
|
|
2359
|
-
method = method.toLowerCase()
|
|
2360
|
-
const allowedMethods = ['get', 'post', 'patch', 'head', 'fetch', 'delete']
|
|
2321
|
+
method = method.toLowerCase();
|
|
2322
|
+
const allowedMethods = ['get', 'post', 'patch', 'head', 'fetch', 'delete'];
|
|
2361
2323
|
if (!allowedMethods.includes(method)) {
|
|
2362
|
-
throw new Error(
|
|
2363
|
-
`Method ${method} is not allowed, use the one from a list ${allowedMethods} or switch to using REST helper`,
|
|
2364
|
-
)
|
|
2324
|
+
throw new Error(`Method ${method} is not allowed, use the one from a list ${allowedMethods} or switch to using REST helper`);
|
|
2365
2325
|
}
|
|
2366
2326
|
|
|
2367
|
-
if (url.startsWith('/')) {
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
this.debugSection('URL', url)
|
|
2327
|
+
if (url.startsWith('/')) { // local url
|
|
2328
|
+
url = this.options.url + url;
|
|
2329
|
+
this.debugSection('URL', url);
|
|
2371
2330
|
}
|
|
2372
2331
|
|
|
2373
|
-
const response = await this.page.request[method](url, options)
|
|
2374
|
-
this.debugSection('Status', response.status())
|
|
2375
|
-
this.debugSection('Response', await response.text())
|
|
2332
|
+
const response = await this.page.request[method](url, options);
|
|
2333
|
+
this.debugSection('Status', response.status());
|
|
2334
|
+
this.debugSection('Response', await response.text());
|
|
2376
2335
|
|
|
2377
2336
|
// hook to allow JSON response handle this
|
|
2378
2337
|
if (this.config.onResponse) {
|
|
@@ -2381,83 +2340,68 @@ class Playwright extends Helper {
|
|
|
2381
2340
|
status: response.status(),
|
|
2382
2341
|
statusText: response.statusText(),
|
|
2383
2342
|
headers: response.headers(),
|
|
2384
|
-
}
|
|
2385
|
-
this.config.onResponse(axiosResponse)
|
|
2343
|
+
};
|
|
2344
|
+
this.config.onResponse(axiosResponse);
|
|
2386
2345
|
}
|
|
2387
2346
|
|
|
2388
|
-
return response
|
|
2347
|
+
return response;
|
|
2389
2348
|
}
|
|
2390
2349
|
|
|
2391
2350
|
async _failed(test) {
|
|
2392
|
-
await this._withinEnd()
|
|
2351
|
+
await this._withinEnd();
|
|
2393
2352
|
|
|
2394
2353
|
if (!test.artifacts) {
|
|
2395
|
-
test.artifacts = {}
|
|
2354
|
+
test.artifacts = {};
|
|
2396
2355
|
}
|
|
2397
2356
|
|
|
2398
2357
|
if (this.options.recordVideo && this.page && this.page.video()) {
|
|
2399
|
-
test.artifacts.video = saveVideoForPage(this.page, `${test.title}.failed`)
|
|
2358
|
+
test.artifacts.video = await saveVideoForPage(this.page, `${test.title}.failed`);
|
|
2400
2359
|
for (const sessionName in this.sessionPages) {
|
|
2401
|
-
test.artifacts[`video_${sessionName}`] = saveVideoForPage(
|
|
2402
|
-
this.sessionPages[sessionName],
|
|
2403
|
-
`${test.title}_${sessionName}.failed`,
|
|
2404
|
-
)
|
|
2360
|
+
test.artifacts[`video_${sessionName}`] = await saveVideoForPage(this.sessionPages[sessionName], `${test.title}_${sessionName}.failed`);
|
|
2405
2361
|
}
|
|
2406
2362
|
}
|
|
2407
2363
|
|
|
2408
2364
|
if (this.options.trace) {
|
|
2409
|
-
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.failed`)
|
|
2365
|
+
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.failed`);
|
|
2410
2366
|
for (const sessionName in this.sessionPages) {
|
|
2411
|
-
if (!this.sessionPages[sessionName].context) continue
|
|
2412
|
-
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2413
|
-
this.sessionPages[sessionName].context,
|
|
2414
|
-
`${test.title}_${sessionName}.failed`,
|
|
2415
|
-
)
|
|
2367
|
+
if (!this.sessionPages[sessionName].context) continue;
|
|
2368
|
+
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(this.sessionPages[sessionName].context, `${test.title}_${sessionName}.failed`);
|
|
2416
2369
|
}
|
|
2417
2370
|
}
|
|
2418
2371
|
|
|
2419
2372
|
if (this.options.recordHar) {
|
|
2420
|
-
test.artifacts.har = this.currentRunningTest.artifacts.har
|
|
2373
|
+
test.artifacts.har = this.currentRunningTest.artifacts.har;
|
|
2421
2374
|
}
|
|
2422
2375
|
}
|
|
2423
2376
|
|
|
2424
2377
|
async _passed(test) {
|
|
2425
2378
|
if (this.options.recordVideo && this.page && this.page.video()) {
|
|
2426
2379
|
if (this.options.keepVideoForPassedTests) {
|
|
2427
|
-
test.artifacts.video = saveVideoForPage(this.page, `${test.title}.passed`)
|
|
2380
|
+
test.artifacts.video = await saveVideoForPage(this.page, `${test.title}.passed`);
|
|
2428
2381
|
for (const sessionName of Object.keys(this.sessionPages)) {
|
|
2429
|
-
test.artifacts[`video_${sessionName}`] = saveVideoForPage(
|
|
2430
|
-
this.sessionPages[sessionName],
|
|
2431
|
-
`${test.title}_${sessionName}.passed`,
|
|
2432
|
-
)
|
|
2382
|
+
test.artifacts[`video_${sessionName}`] = await saveVideoForPage(this.sessionPages[sessionName], `${test.title}_${sessionName}.passed`);
|
|
2433
2383
|
}
|
|
2434
2384
|
} else {
|
|
2435
|
-
this.page
|
|
2436
|
-
.video()
|
|
2437
|
-
.delete()
|
|
2438
|
-
.catch((e) => {})
|
|
2385
|
+
this.page.video().delete().catch(e => {});
|
|
2439
2386
|
}
|
|
2440
2387
|
}
|
|
2441
2388
|
|
|
2442
2389
|
if (this.options.trace) {
|
|
2443
2390
|
if (this.options.keepTraceForPassedTests) {
|
|
2444
2391
|
if (this.options.trace) {
|
|
2445
|
-
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.passed`)
|
|
2392
|
+
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.passed`);
|
|
2446
2393
|
for (const sessionName in this.sessionPages) {
|
|
2447
|
-
if (!this.sessionPages[sessionName].context) continue
|
|
2448
|
-
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2449
|
-
this.sessionPages[sessionName].context,
|
|
2450
|
-
`${test.title}_${sessionName}.passed`,
|
|
2451
|
-
)
|
|
2394
|
+
if (!this.sessionPages[sessionName].context) continue;
|
|
2395
|
+
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(this.sessionPages[sessionName].context, `${test.title}_${sessionName}.passed`);
|
|
2452
2396
|
}
|
|
2453
2397
|
}
|
|
2454
2398
|
} else {
|
|
2455
|
-
await this.browserContext.tracing.stop()
|
|
2399
|
+
await this.browserContext.tracing.stop();
|
|
2456
2400
|
}
|
|
2457
2401
|
}
|
|
2458
2402
|
|
|
2459
2403
|
if (this.options.recordHar) {
|
|
2460
|
-
test.artifacts.har = this.currentRunningTest.artifacts.har
|
|
2404
|
+
test.artifacts.har = this.currentRunningTest.artifacts.har;
|
|
2461
2405
|
}
|
|
2462
2406
|
}
|
|
2463
2407
|
|
|
@@ -2465,99 +2409,62 @@ class Playwright extends Helper {
|
|
|
2465
2409
|
* {{> wait }}
|
|
2466
2410
|
*/
|
|
2467
2411
|
async wait(sec) {
|
|
2468
|
-
return new Promise((done) => {
|
|
2469
|
-
setTimeout(done, sec * 1000)
|
|
2470
|
-
})
|
|
2412
|
+
return new Promise(((done) => {
|
|
2413
|
+
setTimeout(done, sec * 1000);
|
|
2414
|
+
}));
|
|
2471
2415
|
}
|
|
2472
2416
|
|
|
2473
2417
|
/**
|
|
2474
2418
|
* {{> waitForEnabled }}
|
|
2475
2419
|
*/
|
|
2476
2420
|
async waitForEnabled(locator, sec) {
|
|
2477
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2478
|
-
locator = new Locator(locator, 'css')
|
|
2479
|
-
|
|
2480
|
-
let waiter
|
|
2481
|
-
const context = await this._getContext()
|
|
2421
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2422
|
+
locator = new Locator(locator, 'css');
|
|
2423
|
+
const matcher = await this.context;
|
|
2424
|
+
let waiter;
|
|
2425
|
+
const context = await this._getContext();
|
|
2482
2426
|
if (!locator.isXPath()) {
|
|
2483
2427
|
const valueFn = function ([locator]) {
|
|
2484
|
-
return Array.from(document.querySelectorAll(locator)).filter(
|
|
2485
|
-
}
|
|
2486
|
-
waiter = context.waitForFunction(valueFn, [locator.value], { timeout: waitTimeout })
|
|
2428
|
+
return Array.from(document.querySelectorAll(locator)).filter(el => !el.disabled).length > 0;
|
|
2429
|
+
};
|
|
2430
|
+
waiter = context.waitForFunction(valueFn, [locator.value], { timeout: waitTimeout });
|
|
2487
2431
|
} else {
|
|
2488
2432
|
const enabledFn = function ([locator, $XPath]) {
|
|
2489
|
-
eval($XPath) // eslint-disable-line no-eval
|
|
2490
|
-
return $XPath(null, locator).filter(
|
|
2491
|
-
}
|
|
2492
|
-
waiter = context.waitForFunction(enabledFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2493
|
-
}
|
|
2494
|
-
return waiter.catch((err) => {
|
|
2495
|
-
throw new Error(
|
|
2496
|
-
`element (${locator.toString()}) still not enabled after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2497
|
-
)
|
|
2498
|
-
})
|
|
2499
|
-
}
|
|
2500
|
-
|
|
2501
|
-
/**
|
|
2502
|
-
* {{> waitForDisabled }}
|
|
2503
|
-
*/
|
|
2504
|
-
async waitForDisabled(locator, sec) {
|
|
2505
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2506
|
-
locator = new Locator(locator, 'css')
|
|
2507
|
-
|
|
2508
|
-
let waiter
|
|
2509
|
-
const context = await this._getContext()
|
|
2510
|
-
if (!locator.isXPath()) {
|
|
2511
|
-
const valueFn = function ([locator]) {
|
|
2512
|
-
return Array.from(document.querySelectorAll(locator)).filter((el) => el.disabled).length > 0
|
|
2513
|
-
}
|
|
2514
|
-
waiter = context.waitForFunction(valueFn, [locator.value], { timeout: waitTimeout })
|
|
2515
|
-
} else {
|
|
2516
|
-
const disabledFn = function ([locator, $XPath]) {
|
|
2517
|
-
eval($XPath) // eslint-disable-line no-eval
|
|
2518
|
-
return $XPath(null, locator).filter((el) => el.disabled).length > 0
|
|
2519
|
-
}
|
|
2520
|
-
waiter = context.waitForFunction(disabledFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2433
|
+
eval($XPath); // eslint-disable-line no-eval
|
|
2434
|
+
return $XPath(null, locator).filter(el => !el.disabled).length > 0;
|
|
2435
|
+
};
|
|
2436
|
+
waiter = context.waitForFunction(enabledFn, [locator.value, $XPath.toString()], { timeout: waitTimeout });
|
|
2521
2437
|
}
|
|
2522
2438
|
return waiter.catch((err) => {
|
|
2523
|
-
throw new Error(
|
|
2524
|
-
|
|
2525
|
-
)
|
|
2526
|
-
})
|
|
2439
|
+
throw new Error(`element (${locator.toString()}) still not enabled after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2440
|
+
});
|
|
2527
2441
|
}
|
|
2528
2442
|
|
|
2529
2443
|
/**
|
|
2530
2444
|
* {{> waitForValue }}
|
|
2531
2445
|
*/
|
|
2532
2446
|
async waitForValue(field, value, sec) {
|
|
2533
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2534
|
-
const locator = new Locator(field, 'css')
|
|
2535
|
-
const matcher = await this.context
|
|
2536
|
-
let waiter
|
|
2537
|
-
const context = await this._getContext()
|
|
2447
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2448
|
+
const locator = new Locator(field, 'css');
|
|
2449
|
+
const matcher = await this.context;
|
|
2450
|
+
let waiter;
|
|
2451
|
+
const context = await this._getContext();
|
|
2538
2452
|
if (!locator.isXPath()) {
|
|
2539
2453
|
const valueFn = function ([locator, value]) {
|
|
2540
|
-
return (
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
)
|
|
2544
|
-
}
|
|
2545
|
-
waiter = context.waitForFunction(valueFn, [locator.value, value], { timeout: waitTimeout })
|
|
2454
|
+
return Array.from(document.querySelectorAll(locator)).filter(el => (el.value || '').indexOf(value) !== -1).length > 0;
|
|
2455
|
+
};
|
|
2456
|
+
waiter = context.waitForFunction(valueFn, [locator.value, value], { timeout: waitTimeout });
|
|
2546
2457
|
} else {
|
|
2547
2458
|
const valueFn = function ([locator, $XPath, value]) {
|
|
2548
|
-
eval($XPath) // eslint-disable-line no-eval
|
|
2549
|
-
return $XPath(null, locator).filter(
|
|
2550
|
-
}
|
|
2551
|
-
waiter = context.waitForFunction(valueFn, [locator.value, $XPath.toString(), value], {
|
|
2552
|
-
timeout: waitTimeout,
|
|
2553
|
-
})
|
|
2459
|
+
eval($XPath); // eslint-disable-line no-eval
|
|
2460
|
+
return $XPath(null, locator).filter(el => (el.value || '').indexOf(value) !== -1).length > 0;
|
|
2461
|
+
};
|
|
2462
|
+
waiter = context.waitForFunction(valueFn, [locator.value, $XPath.toString(), value], { timeout: waitTimeout });
|
|
2554
2463
|
}
|
|
2555
2464
|
return waiter.catch((err) => {
|
|
2556
|
-
const loc = locator.toString()
|
|
2557
|
-
throw new Error(
|
|
2558
|
-
|
|
2559
|
-
)
|
|
2560
|
-
})
|
|
2465
|
+
const loc = locator.toString();
|
|
2466
|
+
throw new Error(`element (${loc}) is not in DOM or there is no element(${loc}) with value "${value}" after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2467
|
+
});
|
|
2561
2468
|
}
|
|
2562
2469
|
|
|
2563
2470
|
/**
|
|
@@ -2565,44 +2472,38 @@ class Playwright extends Helper {
|
|
|
2565
2472
|
*
|
|
2566
2473
|
*/
|
|
2567
2474
|
async waitNumberOfVisibleElements(locator, num, sec) {
|
|
2568
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2569
|
-
locator = new Locator(locator, 'css')
|
|
2475
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2476
|
+
locator = new Locator(locator, 'css');
|
|
2570
2477
|
|
|
2571
|
-
let waiter
|
|
2572
|
-
const context = await this._getContext()
|
|
2478
|
+
let waiter;
|
|
2479
|
+
const context = await this._getContext();
|
|
2573
2480
|
if (locator.isCSS()) {
|
|
2574
2481
|
const visibleFn = function ([locator, num]) {
|
|
2575
|
-
const els = document.querySelectorAll(locator)
|
|
2482
|
+
const els = document.querySelectorAll(locator);
|
|
2576
2483
|
if (!els || els.length === 0) {
|
|
2577
|
-
return false
|
|
2484
|
+
return false;
|
|
2578
2485
|
}
|
|
2579
|
-
return Array.prototype.filter.call(els,
|
|
2580
|
-
}
|
|
2581
|
-
waiter = context.waitForFunction(visibleFn, [locator.value, num], { timeout: waitTimeout })
|
|
2486
|
+
return Array.prototype.filter.call(els, el => el.offsetParent !== null).length === num;
|
|
2487
|
+
};
|
|
2488
|
+
waiter = context.waitForFunction(visibleFn, [locator.value, num], { timeout: waitTimeout });
|
|
2582
2489
|
} else {
|
|
2583
2490
|
const visibleFn = function ([locator, $XPath, num]) {
|
|
2584
|
-
eval($XPath) // eslint-disable-line no-eval
|
|
2585
|
-
return $XPath(null, locator).filter(
|
|
2586
|
-
}
|
|
2587
|
-
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString(), num], {
|
|
2588
|
-
timeout: waitTimeout,
|
|
2589
|
-
})
|
|
2491
|
+
eval($XPath); // eslint-disable-line no-eval
|
|
2492
|
+
return $XPath(null, locator).filter(el => el.offsetParent !== null).length === num;
|
|
2493
|
+
};
|
|
2494
|
+
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString(), num], { timeout: waitTimeout });
|
|
2590
2495
|
}
|
|
2591
2496
|
return waiter.catch((err) => {
|
|
2592
|
-
throw new Error(
|
|
2593
|
-
|
|
2594
|
-
)
|
|
2595
|
-
})
|
|
2497
|
+
throw new Error(`The number of elements (${locator.toString()}) is not ${num} after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2498
|
+
});
|
|
2596
2499
|
}
|
|
2597
2500
|
|
|
2598
2501
|
/**
|
|
2599
2502
|
* {{> waitForClickable }}
|
|
2600
2503
|
*/
|
|
2601
2504
|
async waitForClickable(locator, waitTimeout) {
|
|
2602
|
-
console.log(
|
|
2603
|
-
|
|
2604
|
-
)
|
|
2605
|
-
console.log('Remove usage of this function')
|
|
2505
|
+
console.log('I.waitForClickable is DEPRECATED: This is no longer needed, Playwright automatically waits for element to be clickable');
|
|
2506
|
+
console.log('Remove usage of this function');
|
|
2606
2507
|
}
|
|
2607
2508
|
|
|
2608
2509
|
/**
|
|
@@ -2610,16 +2511,14 @@ class Playwright extends Helper {
|
|
|
2610
2511
|
*
|
|
2611
2512
|
*/
|
|
2612
2513
|
async waitForElement(locator, sec) {
|
|
2613
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2614
|
-
locator = new Locator(locator, 'css')
|
|
2514
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2515
|
+
locator = new Locator(locator, 'css');
|
|
2615
2516
|
|
|
2616
|
-
const context = await this._getContext()
|
|
2517
|
+
const context = await this._getContext();
|
|
2617
2518
|
try {
|
|
2618
|
-
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'attached' })
|
|
2519
|
+
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'attached' });
|
|
2619
2520
|
} catch (e) {
|
|
2620
|
-
throw new Error(
|
|
2621
|
-
`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${e.message}`,
|
|
2622
|
-
)
|
|
2521
|
+
throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${e.message}`);
|
|
2623
2522
|
}
|
|
2624
2523
|
}
|
|
2625
2524
|
|
|
@@ -2629,28 +2528,28 @@ class Playwright extends Helper {
|
|
|
2629
2528
|
* {{> waitForVisible }}
|
|
2630
2529
|
*/
|
|
2631
2530
|
async waitForVisible(locator, sec) {
|
|
2632
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2633
|
-
locator = new Locator(locator, 'css')
|
|
2634
|
-
const context = await this._getContext()
|
|
2635
|
-
let count = 0
|
|
2531
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2532
|
+
locator = new Locator(locator, 'css');
|
|
2533
|
+
const context = await this._getContext();
|
|
2534
|
+
let count = 0;
|
|
2636
2535
|
|
|
2637
2536
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2638
|
-
let waiter
|
|
2537
|
+
let waiter;
|
|
2639
2538
|
if (this.frame) {
|
|
2640
2539
|
do {
|
|
2641
|
-
waiter = await this.frame.locator(buildLocatorString(locator)).first().isVisible()
|
|
2642
|
-
await this.wait(1)
|
|
2643
|
-
count += 1000
|
|
2644
|
-
if (waiter) break
|
|
2645
|
-
} while (count <= waitTimeout)
|
|
2540
|
+
waiter = await this.frame.locator(buildLocatorString(locator)).first().isVisible();
|
|
2541
|
+
await this.wait(1);
|
|
2542
|
+
count += 1000;
|
|
2543
|
+
if (waiter) break;
|
|
2544
|
+
} while (count <= waitTimeout);
|
|
2646
2545
|
|
|
2647
|
-
if (!waiter) throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec.`)
|
|
2546
|
+
if (!waiter) throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec.`);
|
|
2648
2547
|
}
|
|
2649
2548
|
|
|
2650
2549
|
try {
|
|
2651
|
-
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'visible' })
|
|
2550
|
+
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'visible' });
|
|
2652
2551
|
} catch (e) {
|
|
2653
|
-
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2552
|
+
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${e.message}`);
|
|
2654
2553
|
}
|
|
2655
2554
|
}
|
|
2656
2555
|
|
|
@@ -2658,29 +2557,29 @@ class Playwright extends Helper {
|
|
|
2658
2557
|
* {{> waitForInvisible }}
|
|
2659
2558
|
*/
|
|
2660
2559
|
async waitForInvisible(locator, sec) {
|
|
2661
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2662
|
-
locator = new Locator(locator, 'css')
|
|
2663
|
-
const context = await this._getContext()
|
|
2664
|
-
let waiter
|
|
2665
|
-
let count = 0
|
|
2560
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2561
|
+
locator = new Locator(locator, 'css');
|
|
2562
|
+
const context = await this._getContext();
|
|
2563
|
+
let waiter;
|
|
2564
|
+
let count = 0;
|
|
2666
2565
|
|
|
2667
2566
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2668
2567
|
if (this.frame) {
|
|
2669
2568
|
do {
|
|
2670
|
-
waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden()
|
|
2671
|
-
await this.wait(1)
|
|
2672
|
-
count += 1000
|
|
2673
|
-
if (waiter) break
|
|
2674
|
-
} while (count <= waitTimeout)
|
|
2569
|
+
waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden();
|
|
2570
|
+
await this.wait(1);
|
|
2571
|
+
count += 1000;
|
|
2572
|
+
if (waiter) break;
|
|
2573
|
+
} while (count <= waitTimeout);
|
|
2675
2574
|
|
|
2676
|
-
if (!waiter) throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec.`)
|
|
2677
|
-
return
|
|
2575
|
+
if (!waiter) throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec.`);
|
|
2576
|
+
return;
|
|
2678
2577
|
}
|
|
2679
2578
|
|
|
2680
2579
|
try {
|
|
2681
|
-
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'hidden' })
|
|
2580
|
+
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'hidden' });
|
|
2682
2581
|
} catch (e) {
|
|
2683
|
-
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2582
|
+
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${e.message}`);
|
|
2684
2583
|
}
|
|
2685
2584
|
}
|
|
2686
2585
|
|
|
@@ -2688,168 +2587,143 @@ class Playwright extends Helper {
|
|
|
2688
2587
|
* {{> waitToHide }}
|
|
2689
2588
|
*/
|
|
2690
2589
|
async waitToHide(locator, sec) {
|
|
2691
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2692
|
-
locator = new Locator(locator, 'css')
|
|
2693
|
-
const context = await this._getContext()
|
|
2694
|
-
let waiter
|
|
2695
|
-
let count = 0
|
|
2590
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2591
|
+
locator = new Locator(locator, 'css');
|
|
2592
|
+
const context = await this._getContext();
|
|
2593
|
+
let waiter;
|
|
2594
|
+
let count = 0;
|
|
2696
2595
|
|
|
2697
2596
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2698
2597
|
if (this.frame) {
|
|
2699
2598
|
do {
|
|
2700
|
-
waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden()
|
|
2701
|
-
await this.wait(1)
|
|
2702
|
-
count += 1000
|
|
2703
|
-
if (waiter) break
|
|
2704
|
-
} while (count <= waitTimeout)
|
|
2599
|
+
waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden();
|
|
2600
|
+
await this.wait(1);
|
|
2601
|
+
count += 1000;
|
|
2602
|
+
if (waiter) break;
|
|
2603
|
+
} while (count <= waitTimeout);
|
|
2705
2604
|
|
|
2706
|
-
if (!waiter) throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec.`)
|
|
2707
|
-
return
|
|
2605
|
+
if (!waiter) throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec.`);
|
|
2606
|
+
return;
|
|
2708
2607
|
}
|
|
2709
2608
|
|
|
2710
|
-
return context
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
.waitFor({ timeout: waitTimeout, state: 'hidden' })
|
|
2714
|
-
.catch((err) => {
|
|
2715
|
-
throw new Error(
|
|
2716
|
-
`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2717
|
-
)
|
|
2718
|
-
})
|
|
2609
|
+
return context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'hidden' }).catch((err) => {
|
|
2610
|
+
throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2611
|
+
});
|
|
2719
2612
|
}
|
|
2720
2613
|
|
|
2721
2614
|
/**
|
|
2722
2615
|
* {{> waitForNumberOfTabs }}
|
|
2723
2616
|
*/
|
|
2724
2617
|
async waitForNumberOfTabs(expectedTabs, sec) {
|
|
2725
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2726
|
-
let currentTabs
|
|
2727
|
-
let count = 0
|
|
2618
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2619
|
+
let currentTabs;
|
|
2620
|
+
let count = 0;
|
|
2728
2621
|
|
|
2729
2622
|
do {
|
|
2730
|
-
currentTabs = await this.grabNumberOfOpenTabs()
|
|
2731
|
-
await this.wait(1)
|
|
2732
|
-
count += 1000
|
|
2733
|
-
if (currentTabs >= expectedTabs) return
|
|
2734
|
-
} while (count <= waitTimeout)
|
|
2623
|
+
currentTabs = await this.grabNumberOfOpenTabs();
|
|
2624
|
+
await this.wait(1);
|
|
2625
|
+
count += 1000;
|
|
2626
|
+
if (currentTabs >= expectedTabs) return;
|
|
2627
|
+
} while (count <= waitTimeout);
|
|
2735
2628
|
|
|
2736
|
-
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`)
|
|
2629
|
+
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`);
|
|
2737
2630
|
}
|
|
2738
2631
|
|
|
2739
2632
|
async _getContext() {
|
|
2740
2633
|
if (this.context && this.context.constructor.name === 'FrameLocator') {
|
|
2741
|
-
return this.context
|
|
2634
|
+
return this.context;
|
|
2742
2635
|
}
|
|
2743
|
-
return this.page
|
|
2636
|
+
return this.page;
|
|
2744
2637
|
}
|
|
2745
2638
|
|
|
2746
2639
|
/**
|
|
2747
2640
|
* {{> waitInUrl }}
|
|
2748
2641
|
*/
|
|
2749
2642
|
async waitInUrl(urlPart, sec = null) {
|
|
2750
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2751
|
-
|
|
2752
|
-
return this.page
|
|
2753
|
-
.
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
urlPart,
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
if (/Timeout/i.test(e.message)) {
|
|
2764
|
-
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`)
|
|
2765
|
-
} else {
|
|
2766
|
-
throw e
|
|
2767
|
-
}
|
|
2768
|
-
})
|
|
2643
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2644
|
+
|
|
2645
|
+
return this.page.waitForFunction((urlPart) => {
|
|
2646
|
+
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)));
|
|
2647
|
+
return currUrl.indexOf(urlPart) > -1;
|
|
2648
|
+
}, urlPart, { timeout: waitTimeout }).catch(async (e) => {
|
|
2649
|
+
const currUrl = await this._getPageUrl(); // Required because the waitForFunction can't return data.
|
|
2650
|
+
if (/Timeout/i.test(e.message)) {
|
|
2651
|
+
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`);
|
|
2652
|
+
} else {
|
|
2653
|
+
throw e;
|
|
2654
|
+
}
|
|
2655
|
+
});
|
|
2769
2656
|
}
|
|
2770
2657
|
|
|
2771
2658
|
/**
|
|
2772
2659
|
* {{> waitUrlEquals }}
|
|
2773
2660
|
*/
|
|
2774
2661
|
async waitUrlEquals(urlPart, sec = null) {
|
|
2775
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2662
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2776
2663
|
|
|
2777
|
-
const baseUrl = this.options.url
|
|
2664
|
+
const baseUrl = this.options.url;
|
|
2778
2665
|
if (urlPart.indexOf('http') < 0) {
|
|
2779
|
-
urlPart = baseUrl + urlPart
|
|
2780
|
-
}
|
|
2781
|
-
|
|
2782
|
-
return this.page
|
|
2783
|
-
.
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
urlPart,
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
if (/Timeout/i.test(e.message)) {
|
|
2794
|
-
throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`)
|
|
2795
|
-
} else {
|
|
2796
|
-
throw e
|
|
2797
|
-
}
|
|
2798
|
-
})
|
|
2666
|
+
urlPart = baseUrl + urlPart;
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
return this.page.waitForFunction((urlPart) => {
|
|
2670
|
+
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)));
|
|
2671
|
+
return currUrl.indexOf(urlPart) > -1;
|
|
2672
|
+
}, urlPart, { timeout: waitTimeout }).catch(async (e) => {
|
|
2673
|
+
const currUrl = await this._getPageUrl(); // Required because the waitForFunction can't return data.
|
|
2674
|
+
if (/Timeout/i.test(e.message)) {
|
|
2675
|
+
throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`);
|
|
2676
|
+
} else {
|
|
2677
|
+
throw e;
|
|
2678
|
+
}
|
|
2679
|
+
});
|
|
2799
2680
|
}
|
|
2800
2681
|
|
|
2801
2682
|
/**
|
|
2802
2683
|
* {{> waitForText }}
|
|
2803
2684
|
*/
|
|
2804
2685
|
async waitForText(text, sec = null, context = null) {
|
|
2805
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2806
|
-
const errorMessage = `Text "${text}" was not found on page after ${waitTimeout / 1000} sec
|
|
2807
|
-
let waiter
|
|
2686
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2687
|
+
const errorMessage = `Text "${text}" was not found on page after ${waitTimeout / 1000} sec.`;
|
|
2688
|
+
let waiter;
|
|
2808
2689
|
|
|
2809
|
-
const contextObject = await this._getContext()
|
|
2690
|
+
const contextObject = await this._getContext();
|
|
2810
2691
|
|
|
2811
2692
|
if (context) {
|
|
2812
|
-
const locator = new Locator(context, 'css')
|
|
2693
|
+
const locator = new Locator(context, 'css');
|
|
2813
2694
|
if (!locator.isXPath()) {
|
|
2814
2695
|
try {
|
|
2815
|
-
await contextObject
|
|
2816
|
-
.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> text=${text}`)
|
|
2817
|
-
.first()
|
|
2818
|
-
.waitFor({ timeout: waitTimeout, state: 'visible' })
|
|
2696
|
+
await contextObject.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> text=${text}`).first().waitFor({ timeout: waitTimeout, state: 'visible' });
|
|
2819
2697
|
} catch (e) {
|
|
2820
|
-
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2698
|
+
throw new Error(`${errorMessage}\n${e.message}`);
|
|
2821
2699
|
}
|
|
2822
2700
|
}
|
|
2823
2701
|
|
|
2824
2702
|
if (locator.isXPath()) {
|
|
2825
2703
|
try {
|
|
2826
|
-
await contextObject.waitForFunction(
|
|
2827
|
-
(
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
},
|
|
2833
|
-
[locator.value, text, $XPath.toString()],
|
|
2834
|
-
{ timeout: waitTimeout },
|
|
2835
|
-
)
|
|
2704
|
+
await contextObject.waitForFunction(([locator, text, $XPath]) => {
|
|
2705
|
+
eval($XPath); // eslint-disable-line no-eval
|
|
2706
|
+
const el = $XPath(null, locator);
|
|
2707
|
+
if (!el.length) return false;
|
|
2708
|
+
return el[0].innerText.indexOf(text) > -1;
|
|
2709
|
+
}, [locator.value, text, $XPath.toString()], { timeout: waitTimeout });
|
|
2836
2710
|
} catch (e) {
|
|
2837
|
-
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2711
|
+
throw new Error(`${errorMessage}\n${e.message}`);
|
|
2838
2712
|
}
|
|
2839
2713
|
}
|
|
2840
2714
|
} else {
|
|
2841
2715
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2842
2716
|
// eslint-disable-next-line no-lonely-if
|
|
2843
|
-
const _contextObject = this.frame ? this.frame : contextObject
|
|
2844
|
-
let count = 0
|
|
2717
|
+
const _contextObject = this.frame ? this.frame : contextObject;
|
|
2718
|
+
let count = 0;
|
|
2845
2719
|
do {
|
|
2846
|
-
waiter = await _contextObject.locator(`:has-text("${text}")`).first().isVisible()
|
|
2847
|
-
if (waiter) break
|
|
2848
|
-
await this.wait(1)
|
|
2849
|
-
count += 1000
|
|
2850
|
-
} while (count <= waitTimeout)
|
|
2720
|
+
waiter = await _contextObject.locator(`:has-text("${text}")`).first().isVisible();
|
|
2721
|
+
if (waiter) break;
|
|
2722
|
+
await this.wait(1);
|
|
2723
|
+
count += 1000;
|
|
2724
|
+
} while (count <= waitTimeout);
|
|
2851
2725
|
|
|
2852
|
-
if (!waiter) throw new Error(`${errorMessage}`)
|
|
2726
|
+
if (!waiter) throw new Error(`${errorMessage}`);
|
|
2853
2727
|
}
|
|
2854
2728
|
}
|
|
2855
2729
|
|
|
@@ -2865,8 +2739,8 @@ class Playwright extends Helper {
|
|
|
2865
2739
|
* @param {?number} [sec=null] seconds to wait
|
|
2866
2740
|
*/
|
|
2867
2741
|
async waitForRequest(urlOrPredicate, sec = null) {
|
|
2868
|
-
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2869
|
-
return this.page.waitForRequest(urlOrPredicate, { timeout })
|
|
2742
|
+
const timeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2743
|
+
return this.page.waitForRequest(urlOrPredicate, { timeout });
|
|
2870
2744
|
}
|
|
2871
2745
|
|
|
2872
2746
|
/**
|
|
@@ -2881,8 +2755,8 @@ class Playwright extends Helper {
|
|
|
2881
2755
|
* @param {?number} [sec=null] number of seconds to wait
|
|
2882
2756
|
*/
|
|
2883
2757
|
async waitForResponse(urlOrPredicate, sec = null) {
|
|
2884
|
-
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2885
|
-
return this.page.waitForResponse(urlOrPredicate, { timeout })
|
|
2758
|
+
const timeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2759
|
+
return this.page.waitForResponse(urlOrPredicate, { timeout });
|
|
2886
2760
|
}
|
|
2887
2761
|
|
|
2888
2762
|
/**
|
|
@@ -2892,51 +2766,51 @@ class Playwright extends Helper {
|
|
|
2892
2766
|
if (Number.isInteger(locator)) {
|
|
2893
2767
|
// Select by frame index of current context
|
|
2894
2768
|
|
|
2895
|
-
let childFrames = null
|
|
2769
|
+
let childFrames = null;
|
|
2896
2770
|
if (this.context && typeof this.context.childFrames === 'function') {
|
|
2897
|
-
childFrames = this.context.childFrames()
|
|
2771
|
+
childFrames = this.context.childFrames();
|
|
2898
2772
|
} else {
|
|
2899
|
-
childFrames = this.page.mainFrame().childFrames()
|
|
2773
|
+
childFrames = this.page.mainFrame().childFrames();
|
|
2900
2774
|
}
|
|
2901
2775
|
|
|
2902
2776
|
if (locator >= 0 && locator < childFrames.length) {
|
|
2903
|
-
this.context = await this.page.frameLocator('iframe').nth(locator)
|
|
2904
|
-
this.contextLocator = locator
|
|
2777
|
+
this.context = await this.page.frameLocator('iframe').nth(locator);
|
|
2778
|
+
this.contextLocator = locator;
|
|
2905
2779
|
} else {
|
|
2906
|
-
throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath')
|
|
2780
|
+
throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath');
|
|
2907
2781
|
}
|
|
2908
|
-
return
|
|
2782
|
+
return;
|
|
2909
2783
|
}
|
|
2910
2784
|
|
|
2911
2785
|
if (!locator) {
|
|
2912
|
-
this.context = this.page
|
|
2913
|
-
this.contextLocator = null
|
|
2914
|
-
this.frame = null
|
|
2915
|
-
return
|
|
2786
|
+
this.context = this.page;
|
|
2787
|
+
this.contextLocator = null;
|
|
2788
|
+
this.frame = null;
|
|
2789
|
+
return;
|
|
2916
2790
|
}
|
|
2917
2791
|
|
|
2918
2792
|
// iframe by selector
|
|
2919
|
-
locator = buildLocatorString(new Locator(locator, 'css'))
|
|
2920
|
-
const frame = await this._locateElement(locator)
|
|
2793
|
+
locator = buildLocatorString(new Locator(locator, 'css'));
|
|
2794
|
+
const frame = await this._locateElement(locator);
|
|
2921
2795
|
|
|
2922
2796
|
if (!frame) {
|
|
2923
|
-
throw new Error(`Frame ${JSON.stringify(locator)} was not found by text|CSS|XPath`)
|
|
2797
|
+
throw new Error(`Frame ${JSON.stringify(locator)} was not found by text|CSS|XPath`);
|
|
2924
2798
|
}
|
|
2925
2799
|
|
|
2926
2800
|
if (this.frame) {
|
|
2927
|
-
this.frame = await this.frame.frameLocator(locator)
|
|
2801
|
+
this.frame = await this.frame.frameLocator(locator);
|
|
2928
2802
|
} else {
|
|
2929
|
-
this.frame = await this.page.frameLocator(locator)
|
|
2803
|
+
this.frame = await this.page.frameLocator(locator);
|
|
2930
2804
|
}
|
|
2931
2805
|
|
|
2932
|
-
const contentFrame = this.frame
|
|
2806
|
+
const contentFrame = this.frame;
|
|
2933
2807
|
|
|
2934
2808
|
if (contentFrame) {
|
|
2935
|
-
this.context = contentFrame
|
|
2936
|
-
this.contextLocator = null
|
|
2809
|
+
this.context = contentFrame;
|
|
2810
|
+
this.contextLocator = null;
|
|
2937
2811
|
} else {
|
|
2938
|
-
this.context = this.page.frame(this.page.frames()[1].name())
|
|
2939
|
-
this.contextLocator = locator
|
|
2812
|
+
this.context = this.page.frame(this.page.frames()[1].name());
|
|
2813
|
+
this.contextLocator = locator;
|
|
2940
2814
|
}
|
|
2941
2815
|
}
|
|
2942
2816
|
|
|
@@ -2944,17 +2818,17 @@ class Playwright extends Helper {
|
|
|
2944
2818
|
* {{> waitForFunction }}
|
|
2945
2819
|
*/
|
|
2946
2820
|
async waitForFunction(fn, argsOrSec = null, sec = null) {
|
|
2947
|
-
let args = []
|
|
2821
|
+
let args = [];
|
|
2948
2822
|
if (argsOrSec) {
|
|
2949
2823
|
if (Array.isArray(argsOrSec)) {
|
|
2950
|
-
args = argsOrSec
|
|
2824
|
+
args = argsOrSec;
|
|
2951
2825
|
} else if (typeof argsOrSec === 'number') {
|
|
2952
|
-
sec = argsOrSec
|
|
2826
|
+
sec = argsOrSec;
|
|
2953
2827
|
}
|
|
2954
2828
|
}
|
|
2955
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2956
|
-
const context = await this._getContext()
|
|
2957
|
-
return context.waitForFunction(fn, args, { timeout: waitTimeout })
|
|
2829
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2830
|
+
const context = await this._getContext();
|
|
2831
|
+
return context.waitForFunction(fn, args, { timeout: waitTimeout });
|
|
2958
2832
|
}
|
|
2959
2833
|
|
|
2960
2834
|
/**
|
|
@@ -2966,13 +2840,13 @@ class Playwright extends Helper {
|
|
|
2966
2840
|
*/
|
|
2967
2841
|
async waitForNavigation(options = {}) {
|
|
2968
2842
|
console.log(`waitForNavigation deprecated:
|
|
2969
|
-
* This method is inherently racy, please use 'waitForURL' instead.`)
|
|
2843
|
+
* This method is inherently racy, please use 'waitForURL' instead.`);
|
|
2970
2844
|
options = {
|
|
2971
2845
|
timeout: this.options.getPageTimeout,
|
|
2972
2846
|
waitUntil: this.options.waitForNavigation,
|
|
2973
2847
|
...options,
|
|
2974
|
-
}
|
|
2975
|
-
return this.page.waitForNavigation(options)
|
|
2848
|
+
};
|
|
2849
|
+
return this.page.waitForNavigation(options);
|
|
2976
2850
|
}
|
|
2977
2851
|
|
|
2978
2852
|
/**
|
|
@@ -2988,44 +2862,41 @@ class Playwright extends Helper {
|
|
|
2988
2862
|
timeout: this.options.getPageTimeout,
|
|
2989
2863
|
waitUntil: this.options.waitForNavigation,
|
|
2990
2864
|
...options,
|
|
2991
|
-
}
|
|
2992
|
-
return this.page.waitForURL(url, options)
|
|
2865
|
+
};
|
|
2866
|
+
return this.page.waitForURL(url, options);
|
|
2993
2867
|
}
|
|
2994
2868
|
|
|
2995
2869
|
async waitUntilExists(locator, sec) {
|
|
2996
2870
|
console.log(`waitUntilExists deprecated:
|
|
2997
2871
|
* use 'waitForElement' to wait for element to be attached
|
|
2998
|
-
* use 'waitForDetached to wait for element to be removed'`)
|
|
2999
|
-
return this.waitForDetached(locator, sec)
|
|
2872
|
+
* use 'waitForDetached to wait for element to be removed'`);
|
|
2873
|
+
return this.waitForDetached(locator, sec);
|
|
3000
2874
|
}
|
|
3001
2875
|
|
|
3002
2876
|
/**
|
|
3003
2877
|
* {{> waitForDetached }}
|
|
3004
2878
|
*/
|
|
3005
2879
|
async waitForDetached(locator, sec) {
|
|
3006
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
3007
|
-
locator = new Locator(locator, 'css')
|
|
2880
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2881
|
+
locator = new Locator(locator, 'css');
|
|
3008
2882
|
|
|
3009
|
-
let waiter
|
|
3010
|
-
const context = await this._getContext()
|
|
2883
|
+
let waiter;
|
|
2884
|
+
const context = await this._getContext();
|
|
3011
2885
|
if (!locator.isXPath()) {
|
|
3012
2886
|
try {
|
|
3013
|
-
await context
|
|
3014
|
-
.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()}`)
|
|
3015
|
-
.first()
|
|
3016
|
-
.waitFor({ timeout: waitTimeout, state: 'detached' })
|
|
2887
|
+
await context.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()}`).first().waitFor({ timeout: waitTimeout, state: 'detached' });
|
|
3017
2888
|
} catch (e) {
|
|
3018
|
-
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2889
|
+
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${e.message}`);
|
|
3019
2890
|
}
|
|
3020
2891
|
} else {
|
|
3021
2892
|
const visibleFn = function ([locator, $XPath]) {
|
|
3022
|
-
eval($XPath) // eslint-disable-line no-eval
|
|
3023
|
-
return $XPath(null, locator).length === 0
|
|
3024
|
-
}
|
|
3025
|
-
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2893
|
+
eval($XPath); // eslint-disable-line no-eval
|
|
2894
|
+
return $XPath(null, locator).length === 0;
|
|
2895
|
+
};
|
|
2896
|
+
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString()], { timeout: waitTimeout });
|
|
3026
2897
|
return waiter.catch((err) => {
|
|
3027
|
-
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
3028
|
-
})
|
|
2898
|
+
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2899
|
+
});
|
|
3029
2900
|
}
|
|
3030
2901
|
}
|
|
3031
2902
|
|
|
@@ -3034,56 +2905,53 @@ class Playwright extends Helper {
|
|
|
3034
2905
|
*/
|
|
3035
2906
|
async waitForCookie(name, sec) {
|
|
3036
2907
|
// by default, we will retry 3 times
|
|
3037
|
-
let retries = 3
|
|
3038
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2908
|
+
let retries = 3;
|
|
2909
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
3039
2910
|
|
|
3040
2911
|
if (sec) {
|
|
3041
|
-
retries = sec
|
|
2912
|
+
retries = sec;
|
|
3042
2913
|
} else {
|
|
3043
|
-
retries = Math.ceil(waitTimeout / 1000) - 1
|
|
2914
|
+
retries = Math.ceil(waitTimeout / 1000) - 1;
|
|
3044
2915
|
}
|
|
3045
2916
|
|
|
3046
|
-
return promiseRetry(
|
|
3047
|
-
async (
|
|
3048
|
-
const
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
}
|
|
2917
|
+
return promiseRetry(async (retry, number) => {
|
|
2918
|
+
const _grabCookie = async (name) => {
|
|
2919
|
+
const cookies = await this.browserContext.cookies();
|
|
2920
|
+
const cookie = cookies.filter(c => c.name === name);
|
|
2921
|
+
if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`);
|
|
2922
|
+
};
|
|
3053
2923
|
|
|
3054
|
-
|
|
3055
|
-
|
|
2924
|
+
this.debugSection('Wait for cookie: ', name);
|
|
2925
|
+
if (number > 1) this.debugSection('Retrying... Attempt #', number);
|
|
3056
2926
|
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
{ retries, maxTimeout: 1000 },
|
|
3064
|
-
)
|
|
2927
|
+
try {
|
|
2928
|
+
await _grabCookie(name);
|
|
2929
|
+
} catch (e) {
|
|
2930
|
+
retry(e);
|
|
2931
|
+
}
|
|
2932
|
+
}, { retries, maxTimeout: 1000 });
|
|
3065
2933
|
}
|
|
3066
2934
|
|
|
3067
2935
|
async _waitForAction() {
|
|
3068
|
-
return this.wait(this.options.waitForAction / 1000)
|
|
2936
|
+
return this.wait(this.options.waitForAction / 1000);
|
|
3069
2937
|
}
|
|
3070
2938
|
|
|
3071
2939
|
/**
|
|
3072
2940
|
* {{> grabDataFromPerformanceTiming }}
|
|
3073
2941
|
*/
|
|
3074
2942
|
async grabDataFromPerformanceTiming() {
|
|
3075
|
-
return perfTiming
|
|
2943
|
+
return perfTiming;
|
|
3076
2944
|
}
|
|
3077
2945
|
|
|
3078
2946
|
/**
|
|
3079
2947
|
* {{> grabElementBoundingRect }}
|
|
3080
2948
|
*/
|
|
3081
2949
|
async grabElementBoundingRect(locator, prop) {
|
|
3082
|
-
const el = await this._locateElement(locator)
|
|
3083
|
-
assertElementExists(el, locator)
|
|
3084
|
-
const rect = await el.boundingBox()
|
|
3085
|
-
if (prop) return rect[prop]
|
|
3086
|
-
return rect
|
|
2950
|
+
const el = await this._locateElement(locator);
|
|
2951
|
+
assertElementExists(el, locator);
|
|
2952
|
+
const rect = await el.boundingBox();
|
|
2953
|
+
if (prop) return rect[prop];
|
|
2954
|
+
return rect;
|
|
3087
2955
|
}
|
|
3088
2956
|
|
|
3089
2957
|
/**
|
|
@@ -3098,7 +2966,7 @@ class Playwright extends Helper {
|
|
|
3098
2966
|
* @param {function} [handler] a function to process request
|
|
3099
2967
|
*/
|
|
3100
2968
|
async mockRoute(url, handler) {
|
|
3101
|
-
return this.browserContext.route(...arguments)
|
|
2969
|
+
return this.browserContext.route(...arguments);
|
|
3102
2970
|
}
|
|
3103
2971
|
|
|
3104
2972
|
/**
|
|
@@ -3114,17 +2982,17 @@ class Playwright extends Helper {
|
|
|
3114
2982
|
* @param {function} [handler] a function to process request
|
|
3115
2983
|
*/
|
|
3116
2984
|
async stopMockingRoute(url, handler) {
|
|
3117
|
-
return this.browserContext.unroute(...arguments)
|
|
2985
|
+
return this.browserContext.unroute(...arguments);
|
|
3118
2986
|
}
|
|
3119
2987
|
|
|
3120
2988
|
/**
|
|
3121
|
-
* {{>
|
|
2989
|
+
* {{> flushNetworkTraffics }}
|
|
3122
2990
|
*
|
|
3123
2991
|
*/
|
|
3124
2992
|
startRecordingTraffic() {
|
|
3125
|
-
this.flushNetworkTraffics()
|
|
3126
|
-
this.recording = true
|
|
3127
|
-
this.recordedAtLeastOnce = true
|
|
2993
|
+
this.flushNetworkTraffics();
|
|
2994
|
+
this.recording = true;
|
|
2995
|
+
this.recordedAtLeastOnce = true;
|
|
3128
2996
|
|
|
3129
2997
|
this.page.on('requestfinished', async (request) => {
|
|
3130
2998
|
const information = {
|
|
@@ -3133,16 +3001,16 @@ class Playwright extends Helper {
|
|
|
3133
3001
|
requestHeaders: request.headers(),
|
|
3134
3002
|
requestPostData: request.postData(),
|
|
3135
3003
|
response: request.response(),
|
|
3136
|
-
}
|
|
3004
|
+
};
|
|
3137
3005
|
|
|
3138
|
-
this.debugSection('REQUEST: ', JSON.stringify(information))
|
|
3006
|
+
this.debugSection('REQUEST: ', JSON.stringify(information));
|
|
3139
3007
|
|
|
3140
3008
|
if (typeof information.requestPostData === 'object') {
|
|
3141
|
-
information.requestPostData = JSON.parse(information.requestPostData)
|
|
3009
|
+
information.requestPostData = JSON.parse(information.requestPostData);
|
|
3142
3010
|
}
|
|
3143
3011
|
|
|
3144
|
-
this.requests.push(information)
|
|
3145
|
-
})
|
|
3012
|
+
this.requests.push(information);
|
|
3013
|
+
});
|
|
3146
3014
|
}
|
|
3147
3015
|
|
|
3148
3016
|
/**
|
|
@@ -3165,21 +3033,21 @@ class Playwright extends Helper {
|
|
|
3165
3033
|
*/
|
|
3166
3034
|
blockTraffic(urls) {
|
|
3167
3035
|
if (Array.isArray(urls)) {
|
|
3168
|
-
urls.forEach(
|
|
3036
|
+
urls.forEach(url => {
|
|
3169
3037
|
this.page.route(url, (route) => {
|
|
3170
3038
|
route
|
|
3171
3039
|
.abort()
|
|
3172
3040
|
// Sometimes it happens that browser has been closed in the meantime. It is ok to ignore error then.
|
|
3173
|
-
.catch((e) => {})
|
|
3174
|
-
})
|
|
3175
|
-
})
|
|
3041
|
+
.catch((e) => {});
|
|
3042
|
+
});
|
|
3043
|
+
});
|
|
3176
3044
|
} else {
|
|
3177
3045
|
this.page.route(urls, (route) => {
|
|
3178
3046
|
route
|
|
3179
3047
|
.abort()
|
|
3180
3048
|
// Sometimes it happens that browser has been closed in the meantime. It is ok to ignore error then.
|
|
3181
|
-
.catch((e) => {})
|
|
3182
|
-
})
|
|
3049
|
+
.catch((e) => {});
|
|
3050
|
+
});
|
|
3183
3051
|
}
|
|
3184
3052
|
}
|
|
3185
3053
|
|
|
@@ -3201,10 +3069,10 @@ class Playwright extends Helper {
|
|
|
3201
3069
|
*/
|
|
3202
3070
|
mockTraffic(urls, responseString, contentType = 'application/json') {
|
|
3203
3071
|
// Required to mock cross-domain requests
|
|
3204
|
-
const headers = { 'access-control-allow-origin': '*' }
|
|
3072
|
+
const headers = { 'access-control-allow-origin': '*' };
|
|
3205
3073
|
|
|
3206
3074
|
if (typeof urls === 'string') {
|
|
3207
|
-
urls = [urls]
|
|
3075
|
+
urls = [urls];
|
|
3208
3076
|
}
|
|
3209
3077
|
|
|
3210
3078
|
urls.forEach((url) => {
|
|
@@ -3212,15 +3080,15 @@ class Playwright extends Helper {
|
|
|
3212
3080
|
if (this.page.isClosed()) {
|
|
3213
3081
|
// Sometimes it happens that browser has been closed in the meantime.
|
|
3214
3082
|
// In this case we just don't fulfill to prevent error in test scenario.
|
|
3215
|
-
return
|
|
3083
|
+
return;
|
|
3216
3084
|
}
|
|
3217
3085
|
route.fulfill({
|
|
3218
3086
|
contentType,
|
|
3219
3087
|
headers,
|
|
3220
3088
|
body: responseString,
|
|
3221
|
-
})
|
|
3222
|
-
})
|
|
3223
|
-
})
|
|
3089
|
+
});
|
|
3090
|
+
});
|
|
3091
|
+
});
|
|
3224
3092
|
}
|
|
3225
3093
|
|
|
3226
3094
|
/**
|
|
@@ -3228,7 +3096,7 @@ class Playwright extends Helper {
|
|
|
3228
3096
|
* {{> flushNetworkTraffics }}
|
|
3229
3097
|
*/
|
|
3230
3098
|
flushNetworkTraffics() {
|
|
3231
|
-
flushNetworkTraffics.call(this)
|
|
3099
|
+
flushNetworkTraffics.call(this);
|
|
3232
3100
|
}
|
|
3233
3101
|
|
|
3234
3102
|
/**
|
|
@@ -3236,12 +3104,14 @@ class Playwright extends Helper {
|
|
|
3236
3104
|
* {{> stopRecordingTraffic }}
|
|
3237
3105
|
*/
|
|
3238
3106
|
stopRecordingTraffic() {
|
|
3239
|
-
stopRecordingTraffic.call(this)
|
|
3107
|
+
stopRecordingTraffic.call(this);
|
|
3240
3108
|
}
|
|
3241
3109
|
|
|
3242
3110
|
/**
|
|
3243
3111
|
* Returns full URL of request matching parameter "urlMatch".
|
|
3244
3112
|
*
|
|
3113
|
+
* @param {string|RegExp} urlMatch Expected URL of request in network traffic. Can be a string or a regular expression.
|
|
3114
|
+
*
|
|
3245
3115
|
* Examples:
|
|
3246
3116
|
*
|
|
3247
3117
|
* ```js
|
|
@@ -3249,28 +3119,25 @@ class Playwright extends Helper {
|
|
|
3249
3119
|
* I.grabTrafficUrl(/session.*start/);
|
|
3250
3120
|
* ```
|
|
3251
3121
|
*
|
|
3252
|
-
* @param {string|RegExp} urlMatch Expected URL of request in network traffic. Can be a string or a regular expression.
|
|
3253
3122
|
* @return {Promise<*>}
|
|
3254
3123
|
*/
|
|
3255
3124
|
grabTrafficUrl(urlMatch) {
|
|
3256
3125
|
if (!this.recordedAtLeastOnce) {
|
|
3257
|
-
throw new Error(
|
|
3258
|
-
'Failure in test automation. You use "I.grabTrafficUrl", but "I.startRecordingTraffic" was never called before.',
|
|
3259
|
-
)
|
|
3126
|
+
throw new Error('Failure in test automation. You use "I.grabTrafficUrl", but "I.startRecordingTraffic" was never called before.');
|
|
3260
3127
|
}
|
|
3261
3128
|
|
|
3262
3129
|
for (const i in this.requests) {
|
|
3263
3130
|
// eslint-disable-next-line no-prototype-builtins
|
|
3264
3131
|
if (this.requests.hasOwnProperty(i)) {
|
|
3265
|
-
const request = this.requests[i]
|
|
3132
|
+
const request = this.requests[i];
|
|
3266
3133
|
|
|
3267
3134
|
if (request.url && request.url.match(new RegExp(urlMatch))) {
|
|
3268
|
-
return request.url
|
|
3135
|
+
return request.url;
|
|
3269
3136
|
}
|
|
3270
3137
|
}
|
|
3271
3138
|
}
|
|
3272
3139
|
|
|
3273
|
-
assert.fail(`Method "getTrafficUrl" failed: No request found in traffic that matches ${urlMatch}`)
|
|
3140
|
+
assert.fail(`Method "getTrafficUrl" failed: No request found in traffic that matches ${urlMatch}`);
|
|
3274
3141
|
}
|
|
3275
3142
|
|
|
3276
3143
|
/**
|
|
@@ -3278,15 +3145,17 @@ class Playwright extends Helper {
|
|
|
3278
3145
|
* {{> grabRecordedNetworkTraffics }}
|
|
3279
3146
|
*/
|
|
3280
3147
|
async grabRecordedNetworkTraffics() {
|
|
3281
|
-
return grabRecordedNetworkTraffics.call(this)
|
|
3148
|
+
return grabRecordedNetworkTraffics.call(this);
|
|
3282
3149
|
}
|
|
3283
3150
|
|
|
3284
3151
|
/**
|
|
3285
3152
|
*
|
|
3286
3153
|
* {{> seeTraffic }}
|
|
3287
3154
|
*/
|
|
3288
|
-
async seeTraffic({
|
|
3289
|
-
|
|
3155
|
+
async seeTraffic({
|
|
3156
|
+
name, url, parameters, requestPostData, timeout = 10,
|
|
3157
|
+
}) {
|
|
3158
|
+
await seeTraffic.call(this, ...arguments);
|
|
3290
3159
|
}
|
|
3291
3160
|
|
|
3292
3161
|
/**
|
|
@@ -3295,42 +3164,51 @@ class Playwright extends Helper {
|
|
|
3295
3164
|
*
|
|
3296
3165
|
*/
|
|
3297
3166
|
dontSeeTraffic({ name, url }) {
|
|
3298
|
-
dontSeeTraffic.call(this, ...arguments)
|
|
3167
|
+
dontSeeTraffic.call(this, ...arguments);
|
|
3299
3168
|
}
|
|
3300
3169
|
|
|
3301
3170
|
/**
|
|
3302
3171
|
* {{> startRecordingWebSocketMessages }}
|
|
3303
3172
|
*/
|
|
3304
3173
|
async startRecordingWebSocketMessages() {
|
|
3305
|
-
this.flushWebSocketMessages()
|
|
3306
|
-
this.recordingWebSocketMessages = true
|
|
3307
|
-
this.recordedWebSocketMessagesAtLeastOnce = true
|
|
3308
|
-
|
|
3309
|
-
this.cdpSession = await this.getNewCDPSession()
|
|
3310
|
-
await this.cdpSession.send('Network.enable')
|
|
3311
|
-
await this.cdpSession.send('Page.enable')
|
|
3312
|
-
|
|
3313
|
-
this.cdpSession.on(
|
|
3314
|
-
|
|
3315
|
-
|
|
3174
|
+
this.flushWebSocketMessages();
|
|
3175
|
+
this.recordingWebSocketMessages = true;
|
|
3176
|
+
this.recordedWebSocketMessagesAtLeastOnce = true;
|
|
3177
|
+
|
|
3178
|
+
this.cdpSession = await this.getNewCDPSession();
|
|
3179
|
+
await this.cdpSession.send('Network.enable');
|
|
3180
|
+
await this.cdpSession.send('Page.enable');
|
|
3181
|
+
|
|
3182
|
+
this.cdpSession.on(
|
|
3183
|
+
'Network.webSocketFrameReceived',
|
|
3184
|
+
(payload) => {
|
|
3185
|
+
this._logWebsocketMessages(this._getWebSocketLog('RECEIVED', payload));
|
|
3186
|
+
},
|
|
3187
|
+
);
|
|
3316
3188
|
|
|
3317
|
-
this.cdpSession.on(
|
|
3318
|
-
|
|
3319
|
-
|
|
3189
|
+
this.cdpSession.on(
|
|
3190
|
+
'Network.webSocketFrameSent',
|
|
3191
|
+
(payload) => {
|
|
3192
|
+
this._logWebsocketMessages(this._getWebSocketLog('SENT', payload));
|
|
3193
|
+
},
|
|
3194
|
+
);
|
|
3320
3195
|
|
|
3321
|
-
this.cdpSession.on(
|
|
3322
|
-
|
|
3323
|
-
|
|
3196
|
+
this.cdpSession.on(
|
|
3197
|
+
'Network.webSocketFrameError',
|
|
3198
|
+
(payload) => {
|
|
3199
|
+
this._logWebsocketMessages(this._getWebSocketLog('ERROR', payload));
|
|
3200
|
+
},
|
|
3201
|
+
);
|
|
3324
3202
|
}
|
|
3325
3203
|
|
|
3326
3204
|
/**
|
|
3327
3205
|
* {{> stopRecordingWebSocketMessages }}
|
|
3328
3206
|
*/
|
|
3329
3207
|
async stopRecordingWebSocketMessages() {
|
|
3330
|
-
await this.cdpSession.send('Network.disable')
|
|
3331
|
-
await this.cdpSession.send('Page.disable')
|
|
3332
|
-
this.page.removeAllListeners('Network')
|
|
3333
|
-
this.recordingWebSocketMessages = false
|
|
3208
|
+
await this.cdpSession.send('Network.disable');
|
|
3209
|
+
await this.cdpSession.send('Page.disable');
|
|
3210
|
+
this.page.removeAllListeners('Network');
|
|
3211
|
+
this.recordingWebSocketMessages = false;
|
|
3334
3212
|
}
|
|
3335
3213
|
|
|
3336
3214
|
/**
|
|
@@ -3342,19 +3220,17 @@ class Playwright extends Helper {
|
|
|
3342
3220
|
grabWebSocketMessages() {
|
|
3343
3221
|
if (!this.recordingWebSocketMessages) {
|
|
3344
3222
|
if (!this.recordedWebSocketMessagesAtLeastOnce) {
|
|
3345
|
-
throw new Error(
|
|
3346
|
-
'Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.',
|
|
3347
|
-
)
|
|
3223
|
+
throw new Error('Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.');
|
|
3348
3224
|
}
|
|
3349
3225
|
}
|
|
3350
|
-
return this.webSocketMessages
|
|
3226
|
+
return this.webSocketMessages;
|
|
3351
3227
|
}
|
|
3352
3228
|
|
|
3353
3229
|
/**
|
|
3354
3230
|
* Resets all recorded WS messages.
|
|
3355
3231
|
*/
|
|
3356
3232
|
flushWebSocketMessages() {
|
|
3357
|
-
this.webSocketMessages = []
|
|
3233
|
+
this.webSocketMessages = [];
|
|
3358
3234
|
}
|
|
3359
3235
|
|
|
3360
3236
|
/**
|
|
@@ -3412,444 +3288,426 @@ class Playwright extends Helper {
|
|
|
3412
3288
|
* @return {Promise<Array<Object>>}
|
|
3413
3289
|
*/
|
|
3414
3290
|
async grabMetrics() {
|
|
3415
|
-
const client = await this.page.context().newCDPSession(this.page)
|
|
3416
|
-
await client.send('Performance.enable')
|
|
3417
|
-
const perfMetricObject = await client.send('Performance.getMetrics')
|
|
3418
|
-
return perfMetricObject?.metrics
|
|
3291
|
+
const client = await this.page.context().newCDPSession(this.page);
|
|
3292
|
+
await client.send('Performance.enable');
|
|
3293
|
+
const perfMetricObject = await client.send('Performance.getMetrics');
|
|
3294
|
+
return perfMetricObject?.metrics;
|
|
3419
3295
|
}
|
|
3420
3296
|
|
|
3421
3297
|
_getWebSocketMessage(payload) {
|
|
3422
3298
|
if (payload.errorMessage) {
|
|
3423
|
-
return payload.errorMessage
|
|
3299
|
+
return payload.errorMessage;
|
|
3424
3300
|
}
|
|
3425
3301
|
|
|
3426
|
-
return payload.response.payloadData
|
|
3302
|
+
return payload.response.payloadData;
|
|
3427
3303
|
}
|
|
3428
3304
|
|
|
3429
3305
|
_getWebSocketLog(prefix, payload) {
|
|
3430
|
-
return `${prefix} ID: ${payload.requestId} TIMESTAMP: ${payload.timestamp} (${new Date().toISOString()})\n\n${this._getWebSocketMessage(payload)}\n\n
|
|
3306
|
+
return `${prefix} ID: ${payload.requestId} TIMESTAMP: ${payload.timestamp} (${new Date().toISOString()})\n\n${this._getWebSocketMessage(payload)}\n\n`;
|
|
3431
3307
|
}
|
|
3432
3308
|
|
|
3433
3309
|
async getNewCDPSession() {
|
|
3434
|
-
return this.page.context().newCDPSession(this.page)
|
|
3310
|
+
return this.page.context().newCDPSession(this.page);
|
|
3435
3311
|
}
|
|
3436
3312
|
|
|
3437
3313
|
_logWebsocketMessages(message) {
|
|
3438
|
-
this.webSocketMessages += message
|
|
3314
|
+
this.webSocketMessages += message;
|
|
3439
3315
|
}
|
|
3440
3316
|
}
|
|
3441
3317
|
|
|
3442
|
-
|
|
3318
|
+
export default Playwright;
|
|
3443
3319
|
|
|
3444
3320
|
function buildLocatorString(locator) {
|
|
3445
3321
|
if (locator.isCustom()) {
|
|
3446
|
-
return `${locator.type}=${locator.value}
|
|
3447
|
-
}
|
|
3448
|
-
|
|
3449
|
-
return `xpath=${locator.value}`
|
|
3322
|
+
return `${locator.type}=${locator.value}`;
|
|
3323
|
+
} if (locator.isXPath()) {
|
|
3324
|
+
return `xpath=${locator.value}`;
|
|
3450
3325
|
}
|
|
3451
|
-
return locator.simplify()
|
|
3326
|
+
return locator.simplify();
|
|
3452
3327
|
}
|
|
3453
3328
|
|
|
3454
3329
|
async function findElements(matcher, locator) {
|
|
3455
|
-
if (locator.react) return findReact(matcher, locator)
|
|
3456
|
-
if (locator.vue) return findVue(matcher, locator)
|
|
3457
|
-
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3458
|
-
locator = new Locator(locator, 'css')
|
|
3330
|
+
if (locator.react) return findReact(matcher, locator);
|
|
3331
|
+
if (locator.vue) return findVue(matcher, locator);
|
|
3332
|
+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator);
|
|
3333
|
+
locator = new Locator(locator, 'css');
|
|
3459
3334
|
|
|
3460
|
-
return matcher.locator(buildLocatorString(locator)).all()
|
|
3335
|
+
return matcher.locator(buildLocatorString(locator)).all();
|
|
3461
3336
|
}
|
|
3462
3337
|
|
|
3463
3338
|
async function findElement(matcher, locator) {
|
|
3464
|
-
if (locator.react) return findReact(matcher, locator)
|
|
3465
|
-
if (locator.vue) return findVue(matcher, locator)
|
|
3466
|
-
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3467
|
-
locator = new Locator(locator, 'css')
|
|
3339
|
+
if (locator.react) return findReact(matcher, locator);
|
|
3340
|
+
if (locator.vue) return findVue(matcher, locator);
|
|
3341
|
+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator);
|
|
3342
|
+
locator = new Locator(locator, 'css');
|
|
3468
3343
|
|
|
3469
|
-
return matcher.locator(buildLocatorString(locator)).first()
|
|
3344
|
+
return matcher.locator(buildLocatorString(locator)).first();
|
|
3470
3345
|
}
|
|
3471
3346
|
|
|
3472
3347
|
async function getVisibleElements(elements) {
|
|
3473
|
-
const visibleElements = []
|
|
3348
|
+
const visibleElements = [];
|
|
3474
3349
|
for (const element of elements) {
|
|
3475
3350
|
if (await element.isVisible()) {
|
|
3476
|
-
visibleElements.push(element)
|
|
3351
|
+
visibleElements.push(element);
|
|
3477
3352
|
}
|
|
3478
3353
|
}
|
|
3479
3354
|
if (visibleElements.length === 0) {
|
|
3480
|
-
return elements
|
|
3355
|
+
return elements;
|
|
3481
3356
|
}
|
|
3482
|
-
return visibleElements
|
|
3357
|
+
return visibleElements;
|
|
3483
3358
|
}
|
|
3484
3359
|
|
|
3485
3360
|
async function proceedClick(locator, context = null, options = {}) {
|
|
3486
|
-
let matcher = await this._getContext()
|
|
3361
|
+
let matcher = await this._getContext();
|
|
3487
3362
|
if (context) {
|
|
3488
|
-
const els = await this._locate(context)
|
|
3489
|
-
assertElementExists(els, context)
|
|
3490
|
-
matcher = els[0]
|
|
3363
|
+
const els = await this._locate(context);
|
|
3364
|
+
assertElementExists(els, context);
|
|
3365
|
+
matcher = els[0];
|
|
3491
3366
|
}
|
|
3492
|
-
const els = await findClickable.call(this, matcher, locator)
|
|
3367
|
+
const els = await findClickable.call(this, matcher, locator);
|
|
3493
3368
|
if (context) {
|
|
3494
|
-
assertElementExists(
|
|
3495
|
-
els,
|
|
3496
|
-
locator,
|
|
3497
|
-
'Clickable element',
|
|
3498
|
-
`was not found inside element ${new Locator(context).toString()}`,
|
|
3499
|
-
)
|
|
3369
|
+
assertElementExists(els, locator, 'Clickable element', `was not found inside element ${new Locator(context).toString()}`);
|
|
3500
3370
|
} else {
|
|
3501
|
-
assertElementExists(els, locator, 'Clickable element')
|
|
3371
|
+
assertElementExists(els, locator, 'Clickable element');
|
|
3502
3372
|
}
|
|
3503
3373
|
|
|
3504
|
-
await highlightActiveElement.call(this, els[0])
|
|
3374
|
+
await highlightActiveElement.call(this, els[0]);
|
|
3505
3375
|
|
|
3506
3376
|
/*
|
|
3507
3377
|
using the force true options itself but instead dispatching a click
|
|
3508
3378
|
*/
|
|
3509
3379
|
if (options.force) {
|
|
3510
|
-
await els[0].dispatchEvent('click')
|
|
3380
|
+
await els[0].dispatchEvent('click');
|
|
3511
3381
|
} else {
|
|
3512
|
-
const element = els.length > 1 ? (await getVisibleElements(els))[0] : els[0]
|
|
3513
|
-
await element.click(options)
|
|
3382
|
+
const element = els.length > 1 ? (await getVisibleElements(els))[0] : els[0];
|
|
3383
|
+
await element.click(options);
|
|
3514
3384
|
}
|
|
3515
|
-
const promises = []
|
|
3385
|
+
const promises = [];
|
|
3516
3386
|
if (options.waitForNavigation) {
|
|
3517
|
-
promises.push(this.waitForURL(/.*/, { waitUntil: options.waitForNavigation }))
|
|
3387
|
+
promises.push(this.waitForURL(/.*/, { waitUntil: options.waitForNavigation }));
|
|
3518
3388
|
}
|
|
3519
|
-
promises.push(this._waitForAction())
|
|
3389
|
+
promises.push(this._waitForAction());
|
|
3520
3390
|
|
|
3521
|
-
return Promise.all(promises)
|
|
3391
|
+
return Promise.all(promises);
|
|
3522
3392
|
}
|
|
3523
3393
|
|
|
3524
3394
|
async function findClickable(matcher, locator) {
|
|
3525
|
-
if (locator.react) return findReact(matcher, locator)
|
|
3526
|
-
if (locator.vue) return findVue(matcher, locator)
|
|
3527
|
-
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3395
|
+
if (locator.react) return findReact(matcher, locator);
|
|
3396
|
+
if (locator.vue) return findVue(matcher, locator);
|
|
3397
|
+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator);
|
|
3528
3398
|
|
|
3529
|
-
locator = new Locator(locator)
|
|
3530
|
-
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator)
|
|
3399
|
+
locator = new Locator(locator);
|
|
3400
|
+
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator);
|
|
3531
3401
|
|
|
3532
|
-
let els
|
|
3533
|
-
const literal = xpathLocator.literal(locator.value)
|
|
3402
|
+
let els;
|
|
3403
|
+
const literal = xpathLocator.literal(locator.value);
|
|
3534
3404
|
|
|
3535
|
-
els = await findElements.call(this, matcher, Locator.clickable.narrow(literal))
|
|
3536
|
-
if (els.length) return els
|
|
3405
|
+
els = await findElements.call(this, matcher, Locator.clickable.narrow(literal));
|
|
3406
|
+
if (els.length) return els;
|
|
3537
3407
|
|
|
3538
|
-
els = await findElements.call(this, matcher, Locator.clickable.wide(literal))
|
|
3539
|
-
if (els.length) return els
|
|
3408
|
+
els = await findElements.call(this, matcher, Locator.clickable.wide(literal));
|
|
3409
|
+
if (els.length) return els;
|
|
3540
3410
|
|
|
3541
3411
|
try {
|
|
3542
|
-
els = await findElements.call(this, matcher, Locator.clickable.self(literal))
|
|
3543
|
-
if (els.length) return els
|
|
3412
|
+
els = await findElements.call(this, matcher, Locator.clickable.self(literal));
|
|
3413
|
+
if (els.length) return els;
|
|
3544
3414
|
} catch (err) {
|
|
3545
3415
|
// Do nothing
|
|
3546
3416
|
}
|
|
3547
3417
|
|
|
3548
|
-
return findElements.call(this, matcher, locator.value) // by css or xpath
|
|
3418
|
+
return findElements.call(this, matcher, locator.value); // by css or xpath
|
|
3549
3419
|
}
|
|
3550
3420
|
|
|
3551
3421
|
async function proceedSee(assertType, text, context, strict = false) {
|
|
3552
|
-
let description
|
|
3553
|
-
let allText
|
|
3422
|
+
let description;
|
|
3423
|
+
let allText;
|
|
3554
3424
|
|
|
3555
3425
|
if (!context) {
|
|
3556
|
-
const el = await this.context
|
|
3426
|
+
const el = await this.context;
|
|
3557
3427
|
|
|
3558
|
-
allText = el.constructor.name !== 'Locator' ? [await el.locator('body').innerText()] : [await el.innerText()]
|
|
3428
|
+
allText = el.constructor.name !== 'Locator' ? [await el.locator('body').innerText()] : [await el.innerText()];
|
|
3559
3429
|
|
|
3560
|
-
description = 'web application'
|
|
3430
|
+
description = 'web application';
|
|
3561
3431
|
} else {
|
|
3562
|
-
const locator = new Locator(context, 'css')
|
|
3563
|
-
description = `element ${locator.toString()}
|
|
3564
|
-
const els = await this._locate(locator)
|
|
3565
|
-
assertElementExists(els, locator.toString())
|
|
3566
|
-
allText = await Promise.all(els.map(
|
|
3432
|
+
const locator = new Locator(context, 'css');
|
|
3433
|
+
description = `element ${locator.toString()}`;
|
|
3434
|
+
const els = await this._locate(locator);
|
|
3435
|
+
assertElementExists(els, locator.toString());
|
|
3436
|
+
allText = await Promise.all(els.map(el => el.innerText()));
|
|
3567
3437
|
}
|
|
3568
3438
|
|
|
3569
3439
|
if (strict) {
|
|
3570
|
-
return allText.map(
|
|
3440
|
+
return allText.map(elText => equals(description)[assertType](text, elText));
|
|
3571
3441
|
}
|
|
3572
|
-
return stringIncludes(description)[assertType](
|
|
3573
|
-
normalizeSpacesInString(text),
|
|
3574
|
-
normalizeSpacesInString(allText.join(' | ')),
|
|
3575
|
-
)
|
|
3442
|
+
return stringIncludes(description)[assertType](normalizeSpacesInString(text), normalizeSpacesInString(allText.join(' | ')));
|
|
3576
3443
|
}
|
|
3577
3444
|
|
|
3578
3445
|
async function findCheckable(locator, context) {
|
|
3579
|
-
let contextEl = await this.context
|
|
3446
|
+
let contextEl = await this.context;
|
|
3580
3447
|
if (typeof context === 'string') {
|
|
3581
|
-
contextEl = await findElements.call(this, contextEl, new Locator(context, 'css').simplify())
|
|
3582
|
-
contextEl = contextEl[0]
|
|
3448
|
+
contextEl = await findElements.call(this, contextEl, (new Locator(context, 'css')).simplify());
|
|
3449
|
+
contextEl = contextEl[0];
|
|
3583
3450
|
}
|
|
3584
3451
|
|
|
3585
|
-
const matchedLocator = new Locator(locator)
|
|
3452
|
+
const matchedLocator = new Locator(locator);
|
|
3586
3453
|
if (!matchedLocator.isFuzzy()) {
|
|
3587
|
-
return findElements.call(this, contextEl, matchedLocator.simplify())
|
|
3454
|
+
return findElements.call(this, contextEl, matchedLocator.simplify());
|
|
3588
3455
|
}
|
|
3589
3456
|
|
|
3590
|
-
const literal = xpathLocator.literal(locator)
|
|
3591
|
-
let els = await findElements.call(this, contextEl, Locator.checkable.byText(literal))
|
|
3457
|
+
const literal = xpathLocator.literal(locator);
|
|
3458
|
+
let els = await findElements.call(this, contextEl, Locator.checkable.byText(literal));
|
|
3592
3459
|
if (els.length) {
|
|
3593
|
-
return els
|
|
3460
|
+
return els;
|
|
3594
3461
|
}
|
|
3595
|
-
els = await findElements.call(this, contextEl, Locator.checkable.byName(literal))
|
|
3462
|
+
els = await findElements.call(this, contextEl, Locator.checkable.byName(literal));
|
|
3596
3463
|
if (els.length) {
|
|
3597
|
-
return els
|
|
3464
|
+
return els;
|
|
3598
3465
|
}
|
|
3599
|
-
return findElements.call(this, contextEl, locator)
|
|
3466
|
+
return findElements.call(this, contextEl, locator);
|
|
3600
3467
|
}
|
|
3601
3468
|
|
|
3602
3469
|
async function proceedIsChecked(assertType, option) {
|
|
3603
|
-
let els = await findCheckable.call(this, option)
|
|
3604
|
-
assertElementExists(els, option, 'Checkable')
|
|
3605
|
-
els = await Promise.all(els.map(
|
|
3606
|
-
const selected = els.reduce((prev, cur) => prev || cur)
|
|
3607
|
-
return truth(`checkable ${option}`, 'to be checked')[assertType](selected)
|
|
3470
|
+
let els = await findCheckable.call(this, option);
|
|
3471
|
+
assertElementExists(els, option, 'Checkable');
|
|
3472
|
+
els = await Promise.all(els.map(el => el.isChecked()));
|
|
3473
|
+
const selected = els.reduce((prev, cur) => prev || cur);
|
|
3474
|
+
return truth(`checkable ${option}`, 'to be checked')[assertType](selected);
|
|
3608
3475
|
}
|
|
3609
3476
|
|
|
3610
3477
|
async function findFields(locator) {
|
|
3611
|
-
const matchedLocator = new Locator(locator)
|
|
3478
|
+
const matchedLocator = new Locator(locator);
|
|
3612
3479
|
if (!matchedLocator.isFuzzy()) {
|
|
3613
|
-
return this._locate(matchedLocator)
|
|
3480
|
+
return this._locate(matchedLocator);
|
|
3614
3481
|
}
|
|
3615
|
-
const literal = xpathLocator.literal(locator)
|
|
3482
|
+
const literal = xpathLocator.literal(locator);
|
|
3616
3483
|
|
|
3617
|
-
let els = await this._locate({ xpath: Locator.field.labelEquals(literal) })
|
|
3484
|
+
let els = await this._locate({ xpath: Locator.field.labelEquals(literal) });
|
|
3618
3485
|
if (els.length) {
|
|
3619
|
-
return els
|
|
3486
|
+
return els;
|
|
3620
3487
|
}
|
|
3621
3488
|
|
|
3622
|
-
els = await this._locate({ xpath: Locator.field.labelContains(literal) })
|
|
3489
|
+
els = await this._locate({ xpath: Locator.field.labelContains(literal) });
|
|
3623
3490
|
if (els.length) {
|
|
3624
|
-
return els
|
|
3491
|
+
return els;
|
|
3625
3492
|
}
|
|
3626
|
-
els = await this._locate({ xpath: Locator.field.byName(literal) })
|
|
3493
|
+
els = await this._locate({ xpath: Locator.field.byName(literal) });
|
|
3627
3494
|
if (els.length) {
|
|
3628
|
-
return els
|
|
3495
|
+
return els;
|
|
3629
3496
|
}
|
|
3630
|
-
return this._locate({ css: locator })
|
|
3497
|
+
return this._locate({ css: locator });
|
|
3631
3498
|
}
|
|
3632
3499
|
|
|
3633
3500
|
async function proceedSeeInField(assertType, field, value) {
|
|
3634
|
-
const els = await findFields.call(this, field)
|
|
3635
|
-
assertElementExists(els, field, 'Field')
|
|
3636
|
-
const el = els[0]
|
|
3637
|
-
const tag = await el.evaluate(
|
|
3638
|
-
const fieldType = await el.getAttribute('type')
|
|
3501
|
+
const els = await findFields.call(this, field);
|
|
3502
|
+
assertElementExists(els, field, 'Field');
|
|
3503
|
+
const el = els[0];
|
|
3504
|
+
const tag = await el.evaluate(e => e.tagName);
|
|
3505
|
+
const fieldType = await el.getAttribute('type');
|
|
3639
3506
|
|
|
3640
3507
|
const proceedMultiple = async (elements) => {
|
|
3641
|
-
const fields = Array.isArray(elements) ? elements : [elements]
|
|
3508
|
+
const fields = Array.isArray(elements) ? elements : [elements];
|
|
3642
3509
|
|
|
3643
|
-
const elementValues = []
|
|
3510
|
+
const elementValues = [];
|
|
3644
3511
|
for (const element of fields) {
|
|
3645
|
-
elementValues.push(await element.inputValue())
|
|
3512
|
+
elementValues.push(await element.inputValue());
|
|
3646
3513
|
}
|
|
3647
3514
|
|
|
3648
3515
|
if (typeof value === 'boolean') {
|
|
3649
|
-
equals(`no. of items matching > 0: ${field}`)[assertType](value, !!elementValues.length)
|
|
3516
|
+
equals(`no. of items matching > 0: ${field}`)[assertType](value, !!elementValues.length);
|
|
3650
3517
|
} else {
|
|
3651
3518
|
if (assertType === 'assert') {
|
|
3652
|
-
equals(`select option by ${field}`)[assertType](true, elementValues.length > 0)
|
|
3519
|
+
equals(`select option by ${field}`)[assertType](true, elementValues.length > 0);
|
|
3653
3520
|
}
|
|
3654
|
-
elementValues.forEach(
|
|
3521
|
+
elementValues.forEach(val => stringIncludes(`fields by ${field}`)[assertType](value, val));
|
|
3655
3522
|
}
|
|
3656
|
-
}
|
|
3523
|
+
};
|
|
3657
3524
|
|
|
3658
3525
|
if (tag === 'SELECT') {
|
|
3659
3526
|
if (await el.getAttribute('multiple')) {
|
|
3660
|
-
const selectedOptions = await el.all('option:checked')
|
|
3661
|
-
if (!selectedOptions.length) return null
|
|
3527
|
+
const selectedOptions = await el.all('option:checked');
|
|
3528
|
+
if (!selectedOptions.length) return null;
|
|
3662
3529
|
|
|
3663
|
-
const options = await filterFieldsByValue(selectedOptions, value, true)
|
|
3664
|
-
return proceedMultiple(options)
|
|
3530
|
+
const options = await filterFieldsByValue(selectedOptions, value, true);
|
|
3531
|
+
return proceedMultiple(options);
|
|
3665
3532
|
}
|
|
3666
3533
|
|
|
3667
|
-
return el.inputValue()
|
|
3534
|
+
return el.inputValue();
|
|
3668
3535
|
}
|
|
3669
3536
|
|
|
3670
3537
|
if (tag === 'INPUT') {
|
|
3671
3538
|
if (fieldType === 'checkbox' || fieldType === 'radio') {
|
|
3672
3539
|
if (typeof value === 'boolean') {
|
|
3673
3540
|
// Filter by values
|
|
3674
|
-
const options = await filterFieldsBySelectionState(els, true)
|
|
3675
|
-
return proceedMultiple(options)
|
|
3541
|
+
const options = await filterFieldsBySelectionState(els, true);
|
|
3542
|
+
return proceedMultiple(options);
|
|
3676
3543
|
}
|
|
3677
3544
|
|
|
3678
|
-
const options = await filterFieldsByValue(els, value, true)
|
|
3679
|
-
return proceedMultiple(options)
|
|
3545
|
+
const options = await filterFieldsByValue(els, value, true);
|
|
3546
|
+
return proceedMultiple(options);
|
|
3680
3547
|
}
|
|
3681
|
-
return proceedMultiple(els[0])
|
|
3548
|
+
return proceedMultiple(els[0]);
|
|
3682
3549
|
}
|
|
3683
3550
|
|
|
3684
|
-
let fieldVal
|
|
3551
|
+
let fieldVal;
|
|
3685
3552
|
|
|
3686
3553
|
try {
|
|
3687
|
-
fieldVal = await el.inputValue()
|
|
3554
|
+
fieldVal = await el.inputValue();
|
|
3688
3555
|
} catch (e) {
|
|
3689
3556
|
if (e.message.includes('Error: Node is not an <input>, <textarea> or <select> element')) {
|
|
3690
|
-
fieldVal = await el.innerText()
|
|
3557
|
+
fieldVal = await el.innerText();
|
|
3691
3558
|
}
|
|
3692
3559
|
}
|
|
3693
3560
|
|
|
3694
|
-
return stringIncludes(`fields by ${field}`)[assertType](value, fieldVal)
|
|
3561
|
+
return stringIncludes(`fields by ${field}`)[assertType](value, fieldVal);
|
|
3695
3562
|
}
|
|
3696
3563
|
|
|
3697
3564
|
async function filterFieldsByValue(elements, value, onlySelected) {
|
|
3698
|
-
const matches = []
|
|
3565
|
+
const matches = [];
|
|
3699
3566
|
for (const element of elements) {
|
|
3700
|
-
const val = await element.getAttribute('value')
|
|
3701
|
-
let isSelected = true
|
|
3567
|
+
const val = await element.getAttribute('value');
|
|
3568
|
+
let isSelected = true;
|
|
3702
3569
|
if (onlySelected) {
|
|
3703
|
-
isSelected = await elementSelected(element)
|
|
3570
|
+
isSelected = await elementSelected(element);
|
|
3704
3571
|
}
|
|
3705
3572
|
if ((value == null || val.indexOf(value) > -1) && isSelected) {
|
|
3706
|
-
matches.push(element)
|
|
3573
|
+
matches.push(element);
|
|
3707
3574
|
}
|
|
3708
3575
|
}
|
|
3709
|
-
return matches
|
|
3576
|
+
return matches;
|
|
3710
3577
|
}
|
|
3711
3578
|
|
|
3712
3579
|
async function filterFieldsBySelectionState(elements, state) {
|
|
3713
|
-
const matches = []
|
|
3580
|
+
const matches = [];
|
|
3714
3581
|
for (const element of elements) {
|
|
3715
|
-
const isSelected = await elementSelected(element)
|
|
3582
|
+
const isSelected = await elementSelected(element);
|
|
3716
3583
|
if (isSelected === state) {
|
|
3717
|
-
matches.push(element)
|
|
3584
|
+
matches.push(element);
|
|
3718
3585
|
}
|
|
3719
3586
|
}
|
|
3720
|
-
return matches
|
|
3587
|
+
return matches;
|
|
3721
3588
|
}
|
|
3722
3589
|
|
|
3723
3590
|
async function elementSelected(element) {
|
|
3724
|
-
const type = await element.getAttribute('type')
|
|
3591
|
+
const type = await element.getAttribute('type');
|
|
3725
3592
|
|
|
3726
3593
|
if (type === 'checkbox' || type === 'radio') {
|
|
3727
|
-
return element.isChecked()
|
|
3594
|
+
return element.isChecked();
|
|
3728
3595
|
}
|
|
3729
|
-
return element.getAttribute('selected')
|
|
3596
|
+
return element.getAttribute('selected');
|
|
3730
3597
|
}
|
|
3731
3598
|
|
|
3732
3599
|
function isFrameLocator(locator) {
|
|
3733
|
-
locator = new Locator(locator)
|
|
3600
|
+
locator = new Locator(locator);
|
|
3734
3601
|
if (locator.isFrame()) {
|
|
3735
|
-
return locator.value
|
|
3602
|
+
return locator.value;
|
|
3736
3603
|
}
|
|
3737
|
-
return false
|
|
3604
|
+
return false;
|
|
3738
3605
|
}
|
|
3739
3606
|
|
|
3740
3607
|
function assertElementExists(res, locator, prefix, suffix) {
|
|
3741
3608
|
if (!res || res.length === 0) {
|
|
3742
|
-
throw new ElementNotFound(locator, prefix, suffix)
|
|
3609
|
+
throw new ElementNotFound(locator, prefix, suffix);
|
|
3743
3610
|
}
|
|
3744
3611
|
}
|
|
3745
3612
|
|
|
3746
3613
|
function $XPath(element, selector) {
|
|
3747
|
-
const found = document.evaluate(selector, element || document.body, null, 5, null)
|
|
3748
|
-
const res = []
|
|
3749
|
-
let current = null
|
|
3750
|
-
while (
|
|
3751
|
-
res.push(current)
|
|
3614
|
+
const found = document.evaluate(selector, element || document.body, null, 5, null);
|
|
3615
|
+
const res = [];
|
|
3616
|
+
let current = null;
|
|
3617
|
+
while (current = found.iterateNext()) {
|
|
3618
|
+
res.push(current);
|
|
3752
3619
|
}
|
|
3753
|
-
return res
|
|
3620
|
+
return res;
|
|
3754
3621
|
}
|
|
3755
3622
|
|
|
3756
3623
|
async function targetCreatedHandler(page) {
|
|
3757
|
-
if (!page) return
|
|
3758
|
-
this.withinLocator = null
|
|
3624
|
+
if (!page) return;
|
|
3625
|
+
this.withinLocator = null;
|
|
3759
3626
|
page.on('load', () => {
|
|
3760
|
-
page
|
|
3761
|
-
.$('body')
|
|
3627
|
+
page.$('body')
|
|
3762
3628
|
.catch(() => null)
|
|
3763
3629
|
.then(async () => {
|
|
3764
3630
|
if (this.context && this.context._type === 'Frame') {
|
|
3765
3631
|
// we are inside iframe?
|
|
3766
|
-
const frameEl = await this.context.frameElement()
|
|
3767
|
-
this.context = await frameEl.contentFrame()
|
|
3768
|
-
this.contextLocator = null
|
|
3769
|
-
return
|
|
3632
|
+
const frameEl = await this.context.frameElement();
|
|
3633
|
+
this.context = await frameEl.contentFrame();
|
|
3634
|
+
this.contextLocator = null;
|
|
3635
|
+
return;
|
|
3770
3636
|
}
|
|
3771
3637
|
// if context element was in iframe - keep it
|
|
3772
3638
|
// if (await this.context.ownerFrame()) return;
|
|
3773
|
-
this.context = page
|
|
3774
|
-
this.contextLocator = null
|
|
3775
|
-
})
|
|
3776
|
-
})
|
|
3639
|
+
this.context = page;
|
|
3640
|
+
this.contextLocator = null;
|
|
3641
|
+
});
|
|
3642
|
+
});
|
|
3777
3643
|
page.on('console', (msg) => {
|
|
3778
3644
|
if (!consoleLogStore.includes(msg) && this.options.ignoreLog && !this.options.ignoreLog.includes(msg.type())) {
|
|
3779
|
-
this.debugSection(
|
|
3780
|
-
`Browser:${ucfirst(msg.type())}`,
|
|
3781
|
-
((msg.text && msg.text()) || msg._text || '') + msg.args().join(' '),
|
|
3782
|
-
)
|
|
3645
|
+
this.debugSection(`Browser:${ucfirst(msg.type())}`, (msg.text && msg.text() || msg._text || '') + msg.args().join(' '));
|
|
3783
3646
|
}
|
|
3784
|
-
consoleLogStore.add(msg)
|
|
3785
|
-
})
|
|
3647
|
+
consoleLogStore.add(msg);
|
|
3648
|
+
});
|
|
3786
3649
|
|
|
3787
3650
|
if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0 && this._getType() === 'Browser') {
|
|
3788
3651
|
try {
|
|
3789
|
-
await page.setViewportSize(parseWindowSize(this.options.windowSize))
|
|
3652
|
+
await page.setViewportSize(parseWindowSize(this.options.windowSize));
|
|
3790
3653
|
} catch (err) {
|
|
3791
|
-
this.debug('Target can be already closed, ignoring...')
|
|
3654
|
+
this.debug('Target can be already closed, ignoring...');
|
|
3792
3655
|
}
|
|
3793
3656
|
}
|
|
3794
3657
|
}
|
|
3795
3658
|
|
|
3796
3659
|
function parseWindowSize(windowSize) {
|
|
3797
|
-
if (!windowSize) return { width: 800, height: 600 }
|
|
3798
|
-
|
|
3799
|
-
if (windowSize.width && windowSize.height) {
|
|
3800
|
-
return { width: parseInt(windowSize.width, 10), height: parseInt(windowSize.height, 10) }
|
|
3801
|
-
}
|
|
3802
|
-
|
|
3803
|
-
const dimensions = windowSize.split('x')
|
|
3660
|
+
if (!windowSize) return { width: 800, height: 600 };
|
|
3661
|
+
const dimensions = windowSize.split('x');
|
|
3804
3662
|
if (dimensions.length < 2 || windowSize === 'maximize') {
|
|
3805
|
-
console.log('Invalid window size, setting window to default values')
|
|
3806
|
-
return { width: 800, height: 600 } // invalid size
|
|
3663
|
+
console.log('Invalid window size, setting window to default values');
|
|
3664
|
+
return { width: 800, height: 600 }; // invalid size
|
|
3807
3665
|
}
|
|
3808
|
-
const width = parseInt(dimensions[0], 10)
|
|
3809
|
-
const height = parseInt(dimensions[1], 10)
|
|
3810
|
-
return { width, height }
|
|
3666
|
+
const width = parseInt(dimensions[0], 10);
|
|
3667
|
+
const height = parseInt(dimensions[1], 10);
|
|
3668
|
+
return { width, height };
|
|
3811
3669
|
}
|
|
3812
3670
|
|
|
3813
3671
|
// List of key values to key definitions
|
|
3814
3672
|
// https://github.com/puppeteer/puppeteer/blob/v1.20.0/lib/USKeyboardLayout.js
|
|
3815
3673
|
const keyDefinitionMap = {
|
|
3816
3674
|
/* eslint-disable quote-props */
|
|
3817
|
-
0: 'Digit0',
|
|
3818
|
-
1: 'Digit1',
|
|
3819
|
-
2: 'Digit2',
|
|
3820
|
-
3: 'Digit3',
|
|
3821
|
-
4: 'Digit4',
|
|
3822
|
-
5: 'Digit5',
|
|
3823
|
-
6: 'Digit6',
|
|
3824
|
-
7: 'Digit7',
|
|
3825
|
-
8: 'Digit8',
|
|
3826
|
-
9: 'Digit9',
|
|
3827
|
-
a: 'KeyA',
|
|
3828
|
-
b: 'KeyB',
|
|
3829
|
-
c: 'KeyC',
|
|
3830
|
-
d: 'KeyD',
|
|
3831
|
-
e: 'KeyE',
|
|
3832
|
-
f: 'KeyF',
|
|
3833
|
-
g: 'KeyG',
|
|
3834
|
-
h: 'KeyH',
|
|
3835
|
-
i: 'KeyI',
|
|
3836
|
-
j: 'KeyJ',
|
|
3837
|
-
k: 'KeyK',
|
|
3838
|
-
l: 'KeyL',
|
|
3839
|
-
m: 'KeyM',
|
|
3840
|
-
n: 'KeyN',
|
|
3841
|
-
o: 'KeyO',
|
|
3842
|
-
p: 'KeyP',
|
|
3843
|
-
q: 'KeyQ',
|
|
3844
|
-
r: 'KeyR',
|
|
3845
|
-
s: 'KeyS',
|
|
3846
|
-
t: 'KeyT',
|
|
3847
|
-
u: 'KeyU',
|
|
3848
|
-
v: 'KeyV',
|
|
3849
|
-
w: 'KeyW',
|
|
3850
|
-
x: 'KeyX',
|
|
3851
|
-
y: 'KeyY',
|
|
3852
|
-
z: 'KeyZ',
|
|
3675
|
+
'0': 'Digit0',
|
|
3676
|
+
'1': 'Digit1',
|
|
3677
|
+
'2': 'Digit2',
|
|
3678
|
+
'3': 'Digit3',
|
|
3679
|
+
'4': 'Digit4',
|
|
3680
|
+
'5': 'Digit5',
|
|
3681
|
+
'6': 'Digit6',
|
|
3682
|
+
'7': 'Digit7',
|
|
3683
|
+
'8': 'Digit8',
|
|
3684
|
+
'9': 'Digit9',
|
|
3685
|
+
'a': 'KeyA',
|
|
3686
|
+
'b': 'KeyB',
|
|
3687
|
+
'c': 'KeyC',
|
|
3688
|
+
'd': 'KeyD',
|
|
3689
|
+
'e': 'KeyE',
|
|
3690
|
+
'f': 'KeyF',
|
|
3691
|
+
'g': 'KeyG',
|
|
3692
|
+
'h': 'KeyH',
|
|
3693
|
+
'i': 'KeyI',
|
|
3694
|
+
'j': 'KeyJ',
|
|
3695
|
+
'k': 'KeyK',
|
|
3696
|
+
'l': 'KeyL',
|
|
3697
|
+
'm': 'KeyM',
|
|
3698
|
+
'n': 'KeyN',
|
|
3699
|
+
'o': 'KeyO',
|
|
3700
|
+
'p': 'KeyP',
|
|
3701
|
+
'q': 'KeyQ',
|
|
3702
|
+
'r': 'KeyR',
|
|
3703
|
+
's': 'KeyS',
|
|
3704
|
+
't': 'KeyT',
|
|
3705
|
+
'u': 'KeyU',
|
|
3706
|
+
'v': 'KeyV',
|
|
3707
|
+
'w': 'KeyW',
|
|
3708
|
+
'x': 'KeyX',
|
|
3709
|
+
'y': 'KeyY',
|
|
3710
|
+
'z': 'KeyZ',
|
|
3853
3711
|
';': 'Semicolon',
|
|
3854
3712
|
'=': 'Equal',
|
|
3855
3713
|
',': 'Comma',
|
|
@@ -3860,92 +3718,89 @@ const keyDefinitionMap = {
|
|
|
3860
3718
|
'[': 'BracketLeft',
|
|
3861
3719
|
'\\': 'Backslash',
|
|
3862
3720
|
']': 'BracketRight',
|
|
3863
|
-
|
|
3721
|
+
'\'': 'Quote',
|
|
3864
3722
|
/* eslint-enable quote-props */
|
|
3865
|
-
}
|
|
3723
|
+
};
|
|
3866
3724
|
|
|
3867
3725
|
function getNormalizedKey(key) {
|
|
3868
|
-
const normalizedKey = getNormalizedKeyAttributeValue(key)
|
|
3726
|
+
const normalizedKey = getNormalizedKeyAttributeValue(key);
|
|
3869
3727
|
if (key !== normalizedKey) {
|
|
3870
|
-
this.debugSection('Input', `Mapping key '${key}' to '${normalizedKey}'`)
|
|
3728
|
+
this.debugSection('Input', `Mapping key '${key}' to '${normalizedKey}'`);
|
|
3871
3729
|
}
|
|
3872
3730
|
// Use key definition to ensure correct key is displayed when Shift modifier is active
|
|
3873
3731
|
if (Object.prototype.hasOwnProperty.call(keyDefinitionMap, normalizedKey)) {
|
|
3874
|
-
return keyDefinitionMap[normalizedKey]
|
|
3732
|
+
return keyDefinitionMap[normalizedKey];
|
|
3875
3733
|
}
|
|
3876
|
-
return normalizedKey
|
|
3734
|
+
return normalizedKey;
|
|
3877
3735
|
}
|
|
3878
3736
|
|
|
3879
3737
|
async function clickablePoint(el) {
|
|
3880
|
-
const rect = await el.boundingBox()
|
|
3881
|
-
if (!rect) throw new ElementNotFound(el)
|
|
3882
|
-
const {
|
|
3883
|
-
|
|
3738
|
+
const rect = await el.boundingBox();
|
|
3739
|
+
if (!rect) throw new ElementNotFound(el);
|
|
3740
|
+
const {
|
|
3741
|
+
x, y, width, height,
|
|
3742
|
+
} = rect;
|
|
3743
|
+
return { x: x + width / 2, y: y + height / 2 };
|
|
3884
3744
|
}
|
|
3885
3745
|
|
|
3886
3746
|
async function refreshContextSession() {
|
|
3887
3747
|
// close other sessions
|
|
3888
3748
|
try {
|
|
3889
|
-
const contexts = await this.browser.contexts()
|
|
3890
|
-
contexts.shift()
|
|
3749
|
+
const contexts = await this.browser.contexts();
|
|
3750
|
+
contexts.shift();
|
|
3891
3751
|
|
|
3892
|
-
await Promise.all(contexts.map(
|
|
3752
|
+
await Promise.all(contexts.map(c => c.close()));
|
|
3893
3753
|
} catch (e) {
|
|
3894
|
-
console.log(e)
|
|
3754
|
+
console.log(e);
|
|
3895
3755
|
}
|
|
3896
3756
|
|
|
3897
3757
|
if (this.page) {
|
|
3898
|
-
const existingPages = await this.browserContext.pages()
|
|
3899
|
-
await this._setPage(existingPages[0])
|
|
3758
|
+
const existingPages = await this.browserContext.pages();
|
|
3759
|
+
await this._setPage(existingPages[0]);
|
|
3900
3760
|
}
|
|
3901
3761
|
|
|
3902
|
-
if (this.options.keepBrowserState) return
|
|
3762
|
+
if (this.options.keepBrowserState) return;
|
|
3903
3763
|
|
|
3904
3764
|
if (!this.options.keepCookies) {
|
|
3905
|
-
this.debugSection('Session', 'cleaning cookies and localStorage')
|
|
3906
|
-
await this.clearCookie()
|
|
3765
|
+
this.debugSection('Session', 'cleaning cookies and localStorage');
|
|
3766
|
+
await this.clearCookie();
|
|
3907
3767
|
}
|
|
3908
|
-
const currentUrl = await this.grabCurrentUrl()
|
|
3768
|
+
const currentUrl = await this.grabCurrentUrl();
|
|
3909
3769
|
|
|
3910
3770
|
if (currentUrl.startsWith('http')) {
|
|
3911
3771
|
await this.executeScript('localStorage.clear();').catch((err) => {
|
|
3912
|
-
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
3913
|
-
})
|
|
3772
|
+
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
|
|
3773
|
+
});
|
|
3914
3774
|
await this.executeScript('sessionStorage.clear();').catch((err) => {
|
|
3915
|
-
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
3916
|
-
})
|
|
3775
|
+
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
|
|
3776
|
+
});
|
|
3917
3777
|
}
|
|
3918
3778
|
}
|
|
3919
3779
|
|
|
3920
|
-
function saveVideoForPage(page, name) {
|
|
3921
|
-
if (!page.video()) return null
|
|
3922
|
-
const fileName = `${`${global.output_dir}${pathSeparator}videos${pathSeparator}${uuidv4()}_${clearString(name)}`.slice(0, 245)}.webm
|
|
3923
|
-
page
|
|
3924
|
-
|
|
3925
|
-
.
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
page
|
|
3929
|
-
.video()
|
|
3930
|
-
.delete()
|
|
3931
|
-
.catch(() => {})
|
|
3932
|
-
})
|
|
3933
|
-
return fileName
|
|
3780
|
+
async function saveVideoForPage(page, name) {
|
|
3781
|
+
if (!page.video()) return null;
|
|
3782
|
+
const fileName = `${`${global.output_dir}${pathSeparator}videos${pathSeparator}${uuidv4()}_${clearString(name)}`.slice(0, 245)}.webm`;
|
|
3783
|
+
page.video().saveAs(fileName).then(() => {
|
|
3784
|
+
if (!page) return;
|
|
3785
|
+
page.video().delete().catch(e => {});
|
|
3786
|
+
});
|
|
3787
|
+
return fileName;
|
|
3934
3788
|
}
|
|
3789
|
+
|
|
3935
3790
|
async function saveTraceForContext(context, name) {
|
|
3936
|
-
if (!context) return
|
|
3937
|
-
if (!context.tracing) return
|
|
3938
|
-
const fileName = `${`${global.output_dir}${pathSeparator}trace${pathSeparator}${uuidv4()}_${clearString(name)}`.slice(0, 245)}.zip
|
|
3939
|
-
await context.tracing.stop({ path: fileName })
|
|
3940
|
-
return fileName
|
|
3791
|
+
if (!context) return;
|
|
3792
|
+
if (!context.tracing) return;
|
|
3793
|
+
const fileName = `${`${global.output_dir}${pathSeparator}trace${pathSeparator}${uuidv4()}_${clearString(name)}`.slice(0, 245)}.zip`;
|
|
3794
|
+
await context.tracing.stop({ path: fileName });
|
|
3795
|
+
return fileName;
|
|
3941
3796
|
}
|
|
3942
3797
|
|
|
3943
3798
|
async function highlightActiveElement(element) {
|
|
3944
3799
|
if (this.options.highlightElement && global.debugMode) {
|
|
3945
|
-
await element.evaluate(
|
|
3946
|
-
const prevStyle = el.style.boxShadow
|
|
3947
|
-
el.style.boxShadow = '0px 0px 4px 3px rgba(255, 0, 0, 0.7)'
|
|
3948
|
-
setTimeout(() =>
|
|
3949
|
-
})
|
|
3800
|
+
await element.evaluate(el => {
|
|
3801
|
+
const prevStyle = el.style.boxShadow;
|
|
3802
|
+
el.style.boxShadow = '0px 0px 4px 3px rgba(255, 0, 0, 0.7)';
|
|
3803
|
+
setTimeout(() => el.style.boxShadow = prevStyle, 2000);
|
|
3804
|
+
});
|
|
3950
3805
|
}
|
|
3951
3806
|
}
|