codeceptjs 4.0.0-beta.2 → 4.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +133 -120
  2. package/bin/codecept.js +107 -96
  3. package/bin/test-server.js +64 -0
  4. package/docs/webapi/clearCookie.mustache +1 -1
  5. package/docs/webapi/click.mustache +5 -1
  6. package/lib/actor.js +73 -103
  7. package/lib/ai.js +159 -188
  8. package/lib/assert/empty.js +22 -24
  9. package/lib/assert/equal.js +30 -37
  10. package/lib/assert/error.js +14 -14
  11. package/lib/assert/include.js +43 -48
  12. package/lib/assert/throws.js +11 -11
  13. package/lib/assert/truth.js +22 -22
  14. package/lib/assert.js +20 -18
  15. package/lib/codecept.js +262 -162
  16. package/lib/colorUtils.js +50 -52
  17. package/lib/command/check.js +206 -0
  18. package/lib/command/configMigrate.js +56 -51
  19. package/lib/command/definitions.js +96 -109
  20. package/lib/command/dryRun.js +77 -79
  21. package/lib/command/generate.js +234 -194
  22. package/lib/command/gherkin/init.js +42 -33
  23. package/lib/command/gherkin/snippets.js +76 -74
  24. package/lib/command/gherkin/steps.js +20 -17
  25. package/lib/command/info.js +74 -38
  26. package/lib/command/init.js +301 -290
  27. package/lib/command/interactive.js +41 -32
  28. package/lib/command/list.js +28 -27
  29. package/lib/command/run-multiple/chunk.js +51 -48
  30. package/lib/command/run-multiple/collection.js +5 -5
  31. package/lib/command/run-multiple/run.js +5 -1
  32. package/lib/command/run-multiple.js +97 -97
  33. package/lib/command/run-rerun.js +19 -25
  34. package/lib/command/run-workers.js +68 -92
  35. package/lib/command/run.js +39 -27
  36. package/lib/command/utils.js +80 -64
  37. package/lib/command/workers/runTests.js +388 -226
  38. package/lib/config.js +109 -50
  39. package/lib/container.js +765 -261
  40. package/lib/data/context.js +60 -61
  41. package/lib/data/dataScenarioConfig.js +47 -47
  42. package/lib/data/dataTableArgument.js +32 -32
  43. package/lib/data/table.js +22 -22
  44. package/lib/effects.js +307 -0
  45. package/lib/element/WebElement.js +327 -0
  46. package/lib/els.js +160 -0
  47. package/lib/event.js +173 -163
  48. package/lib/globals.js +141 -0
  49. package/lib/heal.js +89 -85
  50. package/lib/helper/AI.js +131 -41
  51. package/lib/helper/ApiDataFactory.js +107 -75
  52. package/lib/helper/Appium.js +542 -404
  53. package/lib/helper/FileSystem.js +100 -79
  54. package/lib/helper/GraphQL.js +44 -43
  55. package/lib/helper/GraphQLDataFactory.js +52 -52
  56. package/lib/helper/JSONResponse.js +126 -88
  57. package/lib/helper/Mochawesome.js +54 -29
  58. package/lib/helper/Playwright.js +2547 -1316
  59. package/lib/helper/Puppeteer.js +1578 -1181
  60. package/lib/helper/REST.js +209 -68
  61. package/lib/helper/WebDriver.js +1482 -1342
  62. package/lib/helper/errors/ConnectionRefused.js +6 -6
  63. package/lib/helper/errors/ElementAssertion.js +11 -16
  64. package/lib/helper/errors/ElementNotFound.js +5 -9
  65. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
  66. package/lib/helper/extras/Console.js +11 -11
  67. package/lib/helper/extras/PlaywrightLocator.js +110 -0
  68. package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
  69. package/lib/helper/extras/PlaywrightReactVueLocator.js +17 -8
  70. package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
  71. package/lib/helper/extras/Popup.js +22 -22
  72. package/lib/helper/extras/React.js +27 -28
  73. package/lib/helper/network/actions.js +36 -42
  74. package/lib/helper/network/utils.js +78 -84
  75. package/lib/helper/scripts/blurElement.js +5 -5
  76. package/lib/helper/scripts/focusElement.js +5 -5
  77. package/lib/helper/scripts/highlightElement.js +8 -8
  78. package/lib/helper/scripts/isElementClickable.js +34 -34
  79. package/lib/helper.js +2 -3
  80. package/lib/history.js +23 -19
  81. package/lib/hooks.js +8 -8
  82. package/lib/html.js +94 -104
  83. package/lib/index.js +38 -27
  84. package/lib/listener/config.js +30 -23
  85. package/lib/listener/emptyRun.js +54 -0
  86. package/lib/listener/enhancedGlobalRetry.js +110 -0
  87. package/lib/listener/exit.js +16 -18
  88. package/lib/listener/globalRetry.js +70 -0
  89. package/lib/listener/globalTimeout.js +181 -0
  90. package/lib/listener/helpers.js +76 -51
  91. package/lib/listener/mocha.js +10 -11
  92. package/lib/listener/result.js +11 -0
  93. package/lib/listener/retryEnhancer.js +85 -0
  94. package/lib/listener/steps.js +71 -59
  95. package/lib/listener/store.js +20 -0
  96. package/lib/locator.js +214 -197
  97. package/lib/mocha/asyncWrapper.js +274 -0
  98. package/lib/mocha/bdd.js +167 -0
  99. package/lib/mocha/cli.js +341 -0
  100. package/lib/mocha/factory.js +163 -0
  101. package/lib/mocha/featureConfig.js +89 -0
  102. package/lib/mocha/gherkin.js +231 -0
  103. package/lib/mocha/hooks.js +121 -0
  104. package/lib/mocha/index.js +21 -0
  105. package/lib/mocha/inject.js +46 -0
  106. package/lib/{interfaces → mocha}/scenarioConfig.js +58 -34
  107. package/lib/mocha/suite.js +89 -0
  108. package/lib/mocha/test.js +184 -0
  109. package/lib/mocha/types.d.ts +42 -0
  110. package/lib/mocha/ui.js +242 -0
  111. package/lib/output.js +141 -71
  112. package/lib/parser.js +54 -44
  113. package/lib/pause.js +173 -145
  114. package/lib/plugin/analyze.js +403 -0
  115. package/lib/plugin/{autoLogin.js → auth.js} +178 -79
  116. package/lib/plugin/autoDelay.js +36 -40
  117. package/lib/plugin/coverage.js +131 -78
  118. package/lib/plugin/customLocator.js +22 -21
  119. package/lib/plugin/customReporter.js +53 -0
  120. package/lib/plugin/enhancedRetryFailedStep.js +99 -0
  121. package/lib/plugin/heal.js +101 -110
  122. package/lib/plugin/htmlReporter.js +3648 -0
  123. package/lib/plugin/pageInfo.js +140 -0
  124. package/lib/plugin/pauseOnFail.js +12 -11
  125. package/lib/plugin/retryFailedStep.js +82 -47
  126. package/lib/plugin/screenshotOnFail.js +111 -92
  127. package/lib/plugin/stepByStepReport.js +159 -101
  128. package/lib/plugin/stepTimeout.js +20 -25
  129. package/lib/plugin/subtitles.js +38 -38
  130. package/lib/recorder.js +193 -130
  131. package/lib/rerun.js +94 -49
  132. package/lib/result.js +238 -0
  133. package/lib/retryCoordinator.js +207 -0
  134. package/lib/secret.js +20 -18
  135. package/lib/session.js +95 -89
  136. package/lib/step/base.js +239 -0
  137. package/lib/step/comment.js +10 -0
  138. package/lib/step/config.js +50 -0
  139. package/lib/step/func.js +46 -0
  140. package/lib/step/helper.js +50 -0
  141. package/lib/step/meta.js +99 -0
  142. package/lib/step/record.js +74 -0
  143. package/lib/step/retry.js +11 -0
  144. package/lib/step/section.js +55 -0
  145. package/lib/step.js +18 -329
  146. package/lib/steps.js +54 -0
  147. package/lib/store.js +38 -7
  148. package/lib/template/heal.js +3 -12
  149. package/lib/template/prompts/generatePageObject.js +31 -0
  150. package/lib/template/prompts/healStep.js +13 -0
  151. package/lib/template/prompts/writeStep.js +9 -0
  152. package/lib/test-server.js +334 -0
  153. package/lib/timeout.js +60 -0
  154. package/lib/transform.js +8 -8
  155. package/lib/translation.js +34 -21
  156. package/lib/utils/loaderCheck.js +124 -0
  157. package/lib/utils/mask_data.js +47 -0
  158. package/lib/utils/typescript.js +237 -0
  159. package/lib/utils.js +411 -228
  160. package/lib/workerStorage.js +37 -34
  161. package/lib/workers.js +532 -296
  162. package/package.json +124 -95
  163. package/translations/de-DE.js +5 -3
  164. package/translations/fr-FR.js +5 -4
  165. package/translations/index.js +22 -12
  166. package/translations/it-IT.js +4 -3
  167. package/translations/ja-JP.js +4 -3
  168. package/translations/nl-NL.js +76 -0
  169. package/translations/pl-PL.js +4 -3
  170. package/translations/pt-BR.js +4 -3
  171. package/translations/ru-RU.js +4 -3
  172. package/translations/utils.js +10 -0
  173. package/translations/zh-CN.js +4 -3
  174. package/translations/zh-TW.js +4 -3
  175. package/typings/index.d.ts +546 -185
  176. package/typings/promiseBasedTypes.d.ts +150 -875
  177. package/typings/types.d.ts +547 -992
  178. package/lib/cli.js +0 -249
  179. package/lib/dirname.js +0 -5
  180. package/lib/helper/Expect.js +0 -425
  181. package/lib/helper/ExpectHelper.js +0 -399
  182. package/lib/helper/MockServer.js +0 -223
  183. package/lib/helper/Nightmare.js +0 -1411
  184. package/lib/helper/Protractor.js +0 -1835
  185. package/lib/helper/SoftExpectHelper.js +0 -381
  186. package/lib/helper/TestCafe.js +0 -1410
  187. package/lib/helper/clientscripts/nightmare.js +0 -213
  188. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  189. package/lib/helper/testcafe/testcafe-utils.js +0 -63
  190. package/lib/interfaces/bdd.js +0 -98
  191. package/lib/interfaces/featureConfig.js +0 -69
  192. package/lib/interfaces/gherkin.js +0 -195
  193. package/lib/listener/artifacts.js +0 -19
  194. package/lib/listener/retry.js +0 -68
  195. package/lib/listener/timeout.js +0 -109
  196. package/lib/mochaFactory.js +0 -110
  197. package/lib/plugin/allure.js +0 -15
  198. package/lib/plugin/commentStep.js +0 -136
  199. package/lib/plugin/debugErrors.js +0 -67
  200. package/lib/plugin/eachElement.js +0 -127
  201. package/lib/plugin/fakerTransform.js +0 -49
  202. package/lib/plugin/retryTo.js +0 -121
  203. package/lib/plugin/selenoid.js +0 -371
  204. package/lib/plugin/standardActingHelpers.js +0 -9
  205. package/lib/plugin/tryTo.js +0 -105
  206. package/lib/plugin/wdio.js +0 -246
  207. package/lib/scenario.js +0 -222
  208. package/lib/ui.js +0 -238
  209. package/lib/within.js +0 -70
@@ -1,1835 +0,0 @@
1
- import path from 'path';
2
- import Helper from '@codeceptjs/helper';
3
- import { includes as stringIncludes } from '../assert/include.js';
4
- import { urlEquals, equals } from '../assert/equal.js';
5
- import { empty } from '../assert/empty.js';
6
- import { truth } from '../assert/truth';
7
-
8
- import {
9
- xpathLocator,
10
- fileExists,
11
- convertCssPropertiesToCamelCase,
12
- screenshotOutputFolder,
13
- } from '../utils.js';
14
-
15
- import { isColorProperty, convertColorToRGBA } from '../colorUtils';
16
- import ElementNotFound from './errors/ElementNotFound.js';
17
- import ConnectionRefused from './errors/ConnectionRefused.js';
18
- import Locator from '../locator.js';
19
-
20
- let EC;
21
- let Key;
22
- let Button;
23
- let ProtractorBy;
24
- let ProtractorExpectedConditions;
25
-
26
- let withinStore = {};
27
- let Runner;
28
-
29
- /**
30
- * Protractor helper is based on [Protractor library](http://www.protractortest.org) and used for testing web applications.
31
- *
32
- * Protractor requires [Selenium Server and ChromeDriver/GeckoDriver to be installed](http://codecept.io/quickstart/#prepare-selenium-server).
33
- * To test non-Angular applications please make sure you have `angular: false` in configuration file.
34
- *
35
- * ### Configuration
36
- *
37
- * This helper should be configured in codecept.conf.ts or codecept.conf.js
38
- *
39
- * * `url` - base url of website to be tested
40
- * * `browser` - browser in which perform testing
41
- * * `angular` (optional, default: true): disable this option to run tests for non-Angular applications.
42
- * * `driver` - which protractor driver to use (local, direct, session, hosted, sauce, browserstack). By default set to 'hosted' which requires selenium server to be started.
43
- * * `restart` (optional, default: true) - restart browser between tests.
44
- * * `smartWait`: (optional) **enables [SmartWait](http://codecept.io/acceptance/#smartwait)**; wait for additional milliseconds for element to appear. Enable for 5 secs: "smartWait": 5000
45
- * * `disableScreenshots` (optional, default: false) - don't save screenshot on failure
46
- * * `fullPageScreenshots` (optional, default: false) - make full page screenshots on failure.
47
- * * `uniqueScreenshotNames` (optional, default: false) - option to prevent screenshot override if you have scenarios with the same name in different suites
48
- * * `keepBrowserState` (optional, default: false) - keep browser state between tests when `restart` set to false.
49
- * * `seleniumAddress` - Selenium address to connect (default: http://localhost:4444/wd/hub)
50
- * * `rootElement` - Root element of AngularJS application (default: body)
51
- * * `getPageTimeout` (optional) sets default timeout for a page to be loaded. 10000 by default.
52
- * * `waitForTimeout`: (optional) sets default wait time in _ms_ for all `wait*` functions. 1000 by default.
53
- * * `scriptsTimeout`: (optional) timeout in milliseconds for each script run on the browser, 10000 by default.
54
- * * `windowSize`: (optional) default window size. Set to `maximize` or a dimension in the format `640x480`.
55
- * * `manualStart` (optional, default: false) - do not start browser before a test, start it manually inside a helper with `this.helpers.WebDriver._startBrowser()`
56
- * * `capabilities`: {} - list of [Desired Capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities)
57
- * * `proxy`: set proxy settings
58
- *
59
- * other options are the same as in [Protractor config](https://github.com/angular/protractor/blob/master/docs/referenceConf.js).
60
- *
61
- * #### Sample Config
62
- *
63
- * ```json
64
- * {
65
- * "helpers": {
66
- * "Protractor" : {
67
- * "url": "http://localhost",
68
- * "browser": "chrome",
69
- * "smartWait": 5000,
70
- * "restart": false
71
- * }
72
- * }
73
- * }
74
- * ```
75
- *
76
- * #### Config for Non-Angular application:
77
- *
78
- * ```json
79
- * {
80
- * "helpers": {
81
- * "Protractor" : {
82
- * "url": "http://localhost",
83
- * "browser": "chrome",
84
- * "angular": false
85
- * }
86
- * }
87
- * }
88
- * ```
89
- *
90
- * #### Config for Headless Chrome
91
- *
92
- * ```json
93
- * {
94
- * "helpers": {
95
- * "Protractor" : {
96
- * "url": "http://localhost",
97
- * "browser": "chrome",
98
- * "capabilities": {
99
- * "chromeOptions": {
100
- * "args": [ "--headless", "--disable-gpu", "--no-sandbox" ]
101
- * }
102
- * }
103
- * }
104
- * }
105
- * }
106
- * ```
107
- *
108
- * ## Access From Helpers
109
- *
110
- * Receive a WebDriverIO client from a custom helper by accessing `browser` property:
111
- *
112
- * ```js
113
- * this.helpers['Protractor'].browser
114
- * ```
115
- *
116
- * ## Methods
117
- */
118
- class Protractor extends Helper {
119
- constructor(config) {
120
- // process.env.SELENIUM_PROMISE_MANAGER = false; // eslint-disable-line
121
- super(config);
122
-
123
- this.isRunning = false;
124
- this._setConfig(config);
125
-
126
- console.log('Protractor helper is deprecated as well as Protractor itself.\nThis helper will be removed in next major release');
127
- }
128
-
129
- _validateConfig(config) {
130
- const defaults = {
131
- browser: 'chrome',
132
- url: 'http://localhost',
133
- seleniumAddress: 'http://localhost:4444/wd/hub',
134
- fullPageScreenshots: false,
135
- rootElement: 'body',
136
- allScriptsTimeout: 10000,
137
- scriptTimeout: 10000,
138
- waitForTimeout: 1000, // ms
139
- windowSize: null,
140
- getPageTimeout: 10000,
141
- driver: 'hosted',
142
- capabilities: {},
143
- angular: true,
144
- restart: true,
145
- };
146
-
147
- config = Object.assign(defaults, config);
148
-
149
- if (!config.allScriptsTimeout) config.allScriptsTimeout = config.scriptsTimeout;
150
- if (!config.scriptTimeout) config.scriptTimeout = config.scriptsTimeout;
151
- if (config.proxy) config.capabilities.proxy = config.proxy;
152
- if (config.browser) config.capabilities.browserName = config.browser;
153
-
154
- config.waitForTimeoutInSeconds = config.waitForTimeout / 1000; // convert to seconds
155
- return config;
156
- }
157
-
158
- async _init() {
159
- process.on('unhandledRejection', (reason) => {
160
- if (reason.message.indexOf('ECONNREFUSED') > 0) {
161
- this.browser = null;
162
- }
163
- });
164
-
165
- Runner = require('protractor/built/runner').Runner;
166
- ProtractorBy = require('protractor').ProtractorBy;
167
- Key = require('protractor').Key;
168
- Button = require('protractor').Button;
169
- ProtractorExpectedConditions = require('protractor').ProtractorExpectedConditions;
170
-
171
- return Promise.resolve();
172
- }
173
-
174
- static _checkRequirements() {
175
- try {
176
- require('protractor');
177
- require('assert').ok(require('protractor/built/runner').Runner);
178
- } catch (e) {
179
- return ['protractor@^5.3.0'];
180
- }
181
- }
182
-
183
- static _config() {
184
- return [
185
- { name: 'url', message: 'Base url of site to be tested', default: 'http://localhost' },
186
- { name: 'driver', message: 'Protractor driver (local, direct, session, hosted, sauce, browserstack)', default: 'hosted' },
187
- { name: 'browser', message: 'Browser in which testing will be performed', default: 'chrome' },
188
- { name: 'rootElement', message: 'Root element of AngularJS application', default: 'body' },
189
- {
190
- name: 'angular', message: 'Enable AngularJS synchronization', default: false, type: 'confirm',
191
- },
192
-
193
- ];
194
- }
195
-
196
- async _beforeStep() {
197
- if (!this.insideAngular) {
198
- return this.amOutsideAngularApp();
199
- }
200
- }
201
-
202
- async _beforeSuite() {
203
- if (!this.options.restart && !this.options.manualStart && !this.isRunning) {
204
- this.debugSection('Session', 'Starting singleton browser session');
205
- return this._startBrowser();
206
- }
207
- }
208
-
209
- async _startBrowser() {
210
- try {
211
- const runner = new Runner(this.options);
212
- this.browser = runner.createBrowser();
213
- await this.browser.ready;
214
- } catch (err) {
215
- if (err.toString().indexOf('ECONNREFUSED')) {
216
- throw new ConnectionRefused(err);
217
- }
218
- throw err;
219
- }
220
- if (this.options.angular) {
221
- await this.amInsideAngularApp();
222
- } else {
223
- await this.amOutsideAngularApp();
224
- }
225
-
226
- loadGlobals(this.browser);
227
-
228
- if (this.options.windowSize === 'maximize') {
229
- await this.resizeWindow(this.options.windowSize);
230
- } else if (this.options.windowSize) {
231
- const size = this.options.windowSize.split('x');
232
- await this.resizeWindow(parseInt(size[0], 10), parseInt(size[1], 10));
233
- }
234
- this.context = this.options.rootElement;
235
- this.isRunning = true;
236
- return this.browser.ready;
237
- }
238
-
239
- async _before() {
240
- if (this.options.restart && !this.options.manualStart) return this._startBrowser();
241
- if (!this.isRunning && !this.options.manualStart) return this._startBrowser();
242
- }
243
-
244
- async _after() {
245
- if (!this.browser) return;
246
- if (!this.isRunning) return;
247
- if (this.options.restart) {
248
- this.isRunning = false;
249
- return this.browser.quit();
250
- }
251
- if (this.options.keepBrowserState) return;
252
-
253
- const dialog = await this.grabPopupText();
254
- if (dialog) {
255
- await this.cancelPopup();
256
- }
257
- if (!this.options.keepCookies) {
258
- await this.browser.manage().deleteAllCookies();
259
- }
260
- let url;
261
- try {
262
- url = await this.browser.getCurrentUrl();
263
- } catch (err) {
264
- // Ignore, as above will throw if no webpage has been loaded
265
- }
266
- if (url && !/data:,/i.test(url)) {
267
- await this.browser.executeScript('localStorage.clear();');
268
- }
269
- return this.closeOtherTabs();
270
- }
271
-
272
- async _failed() {
273
- await this._withinEnd();
274
- }
275
-
276
- async _finishTest() {
277
- if (!this.options.restart && this.isRunning) return this.browser.quit();
278
- }
279
-
280
- async _withinBegin(locator) {
281
- withinStore.elFn = this.browser.findElement;
282
- withinStore.elsFn = this.browser.findElements;
283
-
284
- const frame = isFrameLocator(locator);
285
-
286
- if (frame) {
287
- if (Array.isArray(frame)) {
288
- withinStore.frame = frame.join('>');
289
- return this.switchTo(null)
290
- .then(() => frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve()));
291
- }
292
- withinStore.frame = frame;
293
- return this.switchTo(locator);
294
- }
295
-
296
- this.context = locator;
297
- const context = await global.element(guessLocator(locator) || global.by.css(locator));
298
- if (!context) throw new ElementNotFound(locator);
299
-
300
- this.browser.findElement = l => (l ? context.element(l).getWebElement() : context.getWebElement());
301
- this.browser.findElements = l => context.all(l).getWebElements();
302
- return context;
303
- }
304
-
305
- async _withinEnd() {
306
- if (!isWithin()) return;
307
- if (withinStore.frame) {
308
- withinStore = {};
309
- return this.switchTo(null);
310
- }
311
- this.browser.findElement = withinStore.elFn;
312
- this.browser.findElements = withinStore.elsFn;
313
- withinStore = {};
314
- this.context = this.options.rootElement;
315
- }
316
-
317
- _session() {
318
- const defaultSession = this.browser;
319
- return {
320
- start: async (opts) => {
321
- opts = this._validateConfig(Object.assign(this.options, opts));
322
- this.debugSection('New Browser', JSON.stringify(opts));
323
- const runner = new Runner(opts);
324
- const res = await this.browser.executeScript('return [window.outerWidth, window.outerHeight]');
325
- const browser = runner.createBrowser(null, this.browser);
326
- await browser.ready;
327
- await browser.waitForAngularEnabled(this.insideAngular);
328
- await browser.manage().window().setSize(parseInt(res[0], 10), parseInt(res[1], 10));
329
- return browser.ready;
330
- },
331
- stop: async (browser) => {
332
- return browser.close();
333
- },
334
- loadVars: async (browser) => {
335
- if (isWithin()) throw new Error('Can\'t start session inside within block');
336
- this.browser = browser;
337
- loadGlobals(this.browser);
338
- },
339
- restoreVars: async () => {
340
- if (isWithin()) await this._withinEnd();
341
- this.browser = defaultSession;
342
- loadGlobals(this.browser);
343
- },
344
- };
345
- }
346
-
347
- /**
348
- * Use [Protractor](https://www.protractortest.org/#/api) API inside a test.
349
- *
350
- * First argument is a description of an action.
351
- * Second argument is async function that gets this helper as parameter.
352
- *
353
- * { [`browser`](https://www.protractortest.org/#/api?view=ProtractorBrowser)) } object from Protractor API is available.
354
- *
355
- * ```js
356
- * I.useProtractorTo('change url via in-page navigation', async ({ browser }) {
357
- * await browser.setLocation('api');
358
- * });
359
- * ```
360
- *
361
- * @param {string} description used to show in logs.
362
- * @param {function} fn async functuion that executed with Protractor helper as argument
363
- */
364
- useProtractorTo(description, fn) {
365
- return this._useTo(...arguments);
366
- }
367
-
368
- /**
369
- * Switch to non-Angular mode,
370
- * start using WebDriver instead of Protractor in this session
371
- */
372
- async amOutsideAngularApp() {
373
- if (!this.browser) return;
374
- await this.browser.waitForAngularEnabled(false);
375
- return Promise.resolve(this.insideAngular = false);
376
- }
377
-
378
- /**
379
- * Enters Angular mode (switched on by default)
380
- * Should be used after "amOutsideAngularApp"
381
- */
382
- async amInsideAngularApp() {
383
- await this.browser.waitForAngularEnabled(true);
384
- return Promise.resolve(this.insideAngular = true);
385
- }
386
-
387
- /**
388
- * Get elements by different locator types, including strict locator
389
- * Should be used in custom helpers:
390
- *
391
- * ```js
392
- * this.helpers['Protractor']._locate({name: 'password'}).then //...
393
- * ```
394
- * To use SmartWait and wait for element to appear on a page, add `true` as second arg:
395
- *
396
- * ```js
397
- * this.helpers['Protractor']._locate({name: 'password'}, true).then //...
398
- * ```
399
- *
400
- */
401
- async _locate(locator, smartWait = false) {
402
- return this._smartWait(() => this.browser.findElements(guessLocator(locator) || global.by.css(locator)), smartWait);
403
- }
404
-
405
- async _smartWait(fn, enabled = true) {
406
- if (!this.options.smartWait || !enabled) return fn();
407
- await this.browser.manage().timeouts().implicitlyWait(this.options.smartWait);
408
- const res = await fn();
409
- await this.browser.manage().timeouts().implicitlyWait(0);
410
- return res;
411
- }
412
-
413
- /**
414
- * Find a checkbox by providing human readable text:
415
- *
416
- * ```js
417
- * this.helpers['Protractor']._locateCheckable('I agree with terms and conditions').then // ...
418
- * ```
419
- */
420
- async _locateCheckable(locator) {
421
- return findCheckable.call(this, this.browser, locator);
422
- }
423
-
424
- /**
425
- * Find a clickable element by providing human readable text:
426
- *
427
- * ```js
428
- * this.helpers['Protractor']._locateClickable('Next page').then // ...
429
- * ```
430
- */
431
- async _locateClickable(locator) {
432
- return findClickable.call(this, this.browser, locator);
433
- }
434
-
435
- /**
436
- * Find field elements by providing human readable text:
437
- *
438
- * ```js
439
- * this.helpers['Protractor']._locateFields('Your email').then // ...
440
- * ```
441
- */
442
- async _locateFields(locator) {
443
- return findFields.call(this, this.browser, locator);
444
- }
445
-
446
- /**
447
- * {{> amOnPage }}
448
- */
449
- async amOnPage(url) {
450
- if (!(/^\w+\:\/\//.test(url))) {
451
- url = this.options.url + url;
452
- }
453
- const res = await this.browser.get(url);
454
- this.debug(`Visited ${url}`);
455
- return res;
456
- }
457
-
458
- /**
459
- * {{> click }}
460
- */
461
- async click(locator, context = null) {
462
- let matcher = this.browser;
463
- if (context) {
464
- const els = await this._locate(context, true);
465
- assertElementExists(els, context);
466
- matcher = els[0];
467
- }
468
- const el = await findClickable.call(this, matcher, locator);
469
- return el.click();
470
- }
471
-
472
- /**
473
- * {{> doubleClick }}
474
- */
475
- async doubleClick(locator, context = null) {
476
- let matcher = this.browser;
477
- if (context) {
478
- const els = await this._locate(context, true);
479
- assertElementExists(els, context);
480
- matcher = els[0];
481
- }
482
- const el = await findClickable.call(this, matcher, locator);
483
- return this.browser.actions().doubleClick(el).perform();
484
- }
485
-
486
- /**
487
- * {{> rightClick }}
488
- */
489
- async rightClick(locator, context = null) {
490
- /**
491
- * just press button if no selector is given
492
- */
493
- if (locator === undefined) {
494
- return this.browser.actions().click(Button.RIGHT).perform();
495
- }
496
- let matcher = this.browser;
497
- if (context) {
498
- const els = await this._locate(context, true);
499
- assertElementExists(els, context);
500
- matcher = els[0];
501
- }
502
- const el = await findClickable.call(this, matcher, locator);
503
-
504
- await this.browser.actions().mouseMove(el).perform();
505
- return this.browser.actions().click(Button.RIGHT).perform();
506
- }
507
-
508
- /**
509
- * {{> moveCursorTo }}
510
- */
511
- async moveCursorTo(locator, offsetX = null, offsetY = null) {
512
- let offset = null;
513
- if (offsetX !== null || offsetY !== null) {
514
- offset = { x: offsetX, y: offsetY };
515
- }
516
- const els = await this._locate(locator, true);
517
- assertElementExists(els, locator);
518
- return this.browser.actions().mouseMove(els[0], offset).perform();
519
- }
520
-
521
- /**
522
- * {{> see }}
523
- */
524
- async see(text, context = null) {
525
- return proceedSee.call(this, 'assert', text, context);
526
- }
527
-
528
- /**
529
- * {{> seeTextEquals }}
530
- */
531
- async seeTextEquals(text, context = null) {
532
- return proceedSee.call(this, 'assert', text, context, true);
533
- }
534
-
535
- /**
536
- * {{> dontSee }}
537
- */
538
- dontSee(text, context = null) {
539
- return proceedSee.call(this, 'negate', text, context);
540
- }
541
-
542
- /**
543
- * {{> grabBrowserLogs }}
544
- */
545
- async grabBrowserLogs() {
546
- return this.browser.manage().logs().get('browser');
547
- }
548
-
549
- /**
550
- * {{> grabCurrentUrl }}
551
- */
552
- async grabCurrentUrl() {
553
- return this.browser.getCurrentUrl();
554
- }
555
-
556
- /**
557
- * {{> selectOption }}
558
- */
559
- async selectOption(select, option) {
560
- const fields = await findFields(this.browser, select);
561
- assertElementExists(fields, select, 'Selectable field');
562
- if (!Array.isArray(option)) {
563
- option = [option];
564
- }
565
- const field = fields[0];
566
- const promises = [];
567
- for (const key in option) {
568
- const opt = xpathLocator.literal(option[key]);
569
- let els = await field.findElements(global.by.xpath(Locator.select.byVisibleText(opt)));
570
- if (!els.length) {
571
- els = await field.findElements(global.by.xpath(Locator.select.byValue(opt)));
572
- }
573
- els.forEach(el => promises.push(el.click()));
574
- }
575
-
576
- return Promise.all(promises);
577
- }
578
-
579
- /**
580
- * {{> fillField }}
581
- */
582
- async fillField(field, value) {
583
- const els = await findFields(this.browser, field);
584
- await els[0].clear();
585
- return els[0].sendKeys(value.toString());
586
- }
587
-
588
- /**
589
- * {{> pressKey }}
590
- * {{ keys }}
591
- */
592
- async pressKey(key) {
593
- let modifier;
594
- if (Array.isArray(key) && ~['Control', 'Command', 'Shift', 'Alt'].indexOf(key[0])) { // eslint-disable-line no-bitwise
595
- modifier = Key[key[0].toUpperCase()];
596
- key = key[1];
597
- }
598
-
599
- // guess special key in Selenium Webdriver list
600
- if (Key[key.toUpperCase()]) {
601
- key = Key[key.toUpperCase()];
602
- }
603
-
604
- const action = this.browser.actions();
605
- if (modifier) action.keyDown(modifier);
606
- action.sendKeys(key);
607
- if (modifier) action.keyUp(modifier);
608
- return action.perform();
609
- }
610
-
611
- /**
612
- * {{> attachFile }}
613
- */
614
- async attachFile(locator, pathToFile) {
615
- const file = path.join(global.codecept_dir, pathToFile);
616
- if (!fileExists(file)) {
617
- throw new Error(`File at ${file} can not be found on local system`);
618
- }
619
- const els = await findFields(this.browser, locator);
620
- assertElementExists(els, locator, 'Field');
621
- if (this.options.browser !== 'phantomjs') {
622
- const remote = require('selenium-webdriver/remote');
623
- this.browser.setFileDetector(new remote.FileDetector());
624
- }
625
- return els[0].sendKeys(file);
626
- }
627
-
628
- /**
629
- * {{> seeInField }}
630
- */
631
- async seeInField(field, value) {
632
- const _value = (typeof value === 'boolean') ? value : value.toString();
633
- return proceedSeeInField.call(this, 'assert', field, _value);
634
- }
635
-
636
- /**
637
- * {{> dontSeeInField }}
638
- */
639
- async dontSeeInField(field, value) {
640
- const _value = (typeof value === 'boolean') ? value : value.toString();
641
- return proceedSeeInField.call(this, 'negate', field, _value);
642
- }
643
-
644
- /**
645
- * {{> appendField }}
646
- */
647
- async appendField(field, value) {
648
- const els = await findFields(this.browser, field);
649
- assertElementExists(els, field, 'Field');
650
- return els[0].sendKeys(value.toString());
651
- }
652
-
653
- /**
654
- * {{> clearField }}
655
- */
656
- async clearField(field) {
657
- const els = await findFields(this.browser, field);
658
- assertElementExists(els, field, 'Field');
659
- return els[0].clear();
660
- }
661
-
662
- /**
663
- * {{> checkOption }}
664
- */
665
- async checkOption(field, context = null) {
666
- let matcher = this.browser;
667
- if (context) {
668
- const els = await this._locate(context, true);
669
- assertElementExists(els, context);
670
- matcher = els[0];
671
- }
672
- const els = await findCheckable(matcher, field);
673
- assertElementExists(els, field, 'Checkbox or radio');
674
- const isSelected = await els[0].isSelected();
675
- if (!isSelected) return els[0].click();
676
- }
677
-
678
- /**
679
- * {{> uncheckOption }}
680
- */
681
- async uncheckOption(field, context = null) {
682
- let matcher = this.browser;
683
- if (context) {
684
- const els = await this._locate(context, true);
685
- assertElementExists(els, context);
686
- matcher = els[0];
687
- }
688
- const els = await findCheckable(matcher, field);
689
- assertElementExists(els, field, 'Checkbox or radio');
690
- const isSelected = await els[0].isSelected();
691
- if (isSelected) return els[0].click();
692
- }
693
-
694
- /**
695
- * {{> seeCheckboxIsChecked }}
696
- */
697
- async seeCheckboxIsChecked(field) {
698
- return proceedIsChecked.call(this, 'assert', field);
699
- }
700
-
701
- /**
702
- * {{> dontSeeCheckboxIsChecked }}
703
- */
704
- async dontSeeCheckboxIsChecked(field) {
705
- return proceedIsChecked.call(this, 'negate', field);
706
- }
707
-
708
- /**
709
- * {{> grabTextFromAll }}
710
- */
711
- async grabTextFromAll(locator) {
712
- const els = await this._locate(locator);
713
- const texts = [];
714
- for (const el of els) {
715
- texts.push(await el.getText());
716
- }
717
- return texts;
718
- }
719
-
720
- /**
721
- * {{> grabTextFrom }}
722
- */
723
- async grabTextFrom(locator) {
724
- const texts = await this.grabTextFromAll(locator);
725
- assertElementExists(texts, locator);
726
- if (texts.length > 1) {
727
- this.debugSection('GrabText', `Using first element out of ${texts.length}`);
728
- }
729
-
730
- return texts[0];
731
- }
732
-
733
- /**
734
- * {{> grabHTMLFromAll }}
735
- */
736
- async grabHTMLFromAll(locator) {
737
- const els = await this._locate(locator);
738
-
739
- const html = await Promise.all(els.map((el) => {
740
- return this.browser.executeScript('return arguments[0].innerHTML;', el);
741
- }));
742
-
743
- return html;
744
- }
745
-
746
- /**
747
- * {{> grabHTMLFrom }}
748
- */
749
- async grabHTMLFrom(locator) {
750
- const html = await this.grabHTMLFromAll(locator);
751
- assertElementExists(html, locator);
752
- if (html.length > 1) {
753
- this.debugSection('GrabHTMl', `Using first element out of ${html.length}`);
754
- }
755
-
756
- return html[0];
757
- }
758
-
759
- /**
760
- * {{> grabValueFromAll }}
761
- */
762
- async grabValueFromAll(locator) {
763
- const els = await findFields(this.browser, locator);
764
- const values = await Promise.all(els.map(el => el.getAttribute('value')));
765
-
766
- return values;
767
- }
768
-
769
- /**
770
- * {{> grabValueFrom }}
771
- */
772
- async grabValueFrom(locator) {
773
- const values = await this.grabValueFromAll(locator);
774
- assertElementExists(values, locator, 'Field');
775
- if (values.length > 1) {
776
- this.debugSection('GrabValue', `Using first element out of ${values.length}`);
777
- }
778
-
779
- return values[0];
780
- }
781
-
782
- /**
783
- * {{> grabCssPropertyFromAll }}
784
- */
785
- async grabCssPropertyFromAll(locator, cssProperty) {
786
- const els = await this._locate(locator, true);
787
- const values = await Promise.all(els.map(el => el.getCssValue(cssProperty)));
788
-
789
- return values;
790
- }
791
-
792
- /**
793
- * {{> grabCssPropertyFrom }}
794
- */
795
- async grabCssPropertyFrom(locator, cssProperty) {
796
- const cssValues = await this.grabCssPropertyFromAll(locator, cssProperty);
797
- assertElementExists(cssValues, locator);
798
-
799
- if (cssValues.length > 1) {
800
- this.debugSection('GrabCSS', `Using first element out of ${cssValues.length}`);
801
- }
802
-
803
- return cssValues[0];
804
- }
805
-
806
- /**
807
- * {{> grabAttributeFromAll }}
808
- */
809
- async grabAttributeFromAll(locator, attr) {
810
- const els = await this._locate(locator);
811
- const array = [];
812
-
813
- for (let index = 0; index < els.length; index++) {
814
- const el = els[index];
815
- array.push(await el.getAttribute(attr));
816
- }
817
- return array;
818
- }
819
-
820
- /**
821
- * {{> grabAttributeFrom }}
822
- */
823
- async grabAttributeFrom(locator, attr) {
824
- const attrs = await this.grabAttributeFromAll(locator, attr);
825
- assertElementExists(attrs, locator);
826
- if (attrs.length > 1) {
827
- this.debugSection('GrabAttribute', `Using first element out of ${attrs.length}`);
828
- }
829
-
830
- return attrs[0];
831
- }
832
-
833
- /**
834
- * {{> seeInTitle }}
835
- */
836
- async seeInTitle(text) {
837
- return this.browser.getTitle().then(title => stringIncludes('web page title').assert(text, title));
838
- }
839
-
840
- /**
841
- * {{> seeTitleEquals }}
842
- */
843
- async seeTitleEquals(text) {
844
- const title = await this.browser.getTitle();
845
- return equals('web page title').assert(title, text);
846
- }
847
-
848
- /**
849
- * {{> dontSeeInTitle }}
850
- */
851
- async dontSeeInTitle(text) {
852
- return this.browser.getTitle().then(title => stringIncludes('web page title').negate(text, title));
853
- }
854
-
855
- /**
856
- * {{> grabTitle }}
857
- */
858
- async grabTitle() {
859
- return this.browser.getTitle().then((title) => {
860
- this.debugSection('Title', title);
861
- return title;
862
- });
863
- }
864
-
865
- /**
866
- * {{> seeElement }}
867
- */
868
- async seeElement(locator) {
869
- let els = await this._locate(locator, true);
870
- els = await Promise.all(els.map(el => el.isDisplayed()));
871
- return empty('elements').negate(els.filter(v => v).fill('ELEMENT'));
872
- }
873
-
874
- /**
875
- * {{> dontSeeElement }}
876
- */
877
- async dontSeeElement(locator) {
878
- let els = await this._locate(locator, false);
879
- els = await Promise.all(els.map(el => el.isDisplayed()));
880
- return empty('elements').assert(els.filter(v => v).fill('ELEMENT'));
881
- }
882
-
883
- /**
884
- * {{> seeElementInDOM }}
885
- */
886
- async seeElementInDOM(locator) {
887
- return this.browser.findElements(guessLocator(locator) || global.by.css(locator)).then(els => empty('elements').negate(els.fill('ELEMENT')));
888
- }
889
-
890
- /**
891
- * {{> dontSeeElementInDOM }}
892
- */
893
- async dontSeeElementInDOM(locator) {
894
- return this.browser.findElements(guessLocator(locator) || global.by.css(locator)).then(els => empty('elements').assert(els.fill('ELEMENT')));
895
- }
896
-
897
- /**
898
- * {{> seeInSource }}
899
- */
900
- async seeInSource(text) {
901
- return this.browser.getPageSource().then(source => stringIncludes('HTML source of a page').assert(text, source));
902
- }
903
-
904
- /**
905
- * {{> grabSource }}
906
- */
907
- async grabSource() {
908
- return this.browser.getPageSource();
909
- }
910
-
911
- /**
912
- * {{> dontSeeInSource }}
913
- */
914
- async dontSeeInSource(text) {
915
- return this.browser.getPageSource().then(source => stringIncludes('HTML source of a page').negate(text, source));
916
- }
917
-
918
- /**
919
- * {{> seeNumberOfElements }}
920
- */
921
- async seeNumberOfElements(locator, num) {
922
- const elements = await this._locate(locator);
923
- return equals(`expected number of elements (${(new Locator(locator))}) is ${num}, but found ${elements.length}`).assert(elements.length, num);
924
- }
925
-
926
- /**
927
- * {{> seeNumberOfVisibleElements }}
928
- */
929
- async seeNumberOfVisibleElements(locator, num) {
930
- const res = await this.grabNumberOfVisibleElements(locator);
931
- return equals(`expected number of visible elements (${(new Locator(locator))}) is ${num}, but found ${res}`).assert(res, num);
932
- }
933
-
934
- /**
935
- * {{> grabNumberOfVisibleElements }}
936
- */
937
- async grabNumberOfVisibleElements(locator) {
938
- let els = await this._locate(locator);
939
- els = await Promise.all(els.map(el => el.isDisplayed()));
940
- return els.length;
941
- }
942
-
943
- /**
944
- * {{> seeCssPropertiesOnElements }}
945
- */
946
- async seeCssPropertiesOnElements(locator, cssProperties) {
947
- const els = await this._locate(locator);
948
- assertElementExists(els, locator);
949
-
950
- const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
951
-
952
- const attributeNames = Object.keys(cssPropertiesCamelCase);
953
- const expectedValues = attributeNames.map(name => cssPropertiesCamelCase[name]);
954
- const missingAttributes = [];
955
-
956
- for (const el of els) {
957
- const attributeValues = await Promise.all(attributeNames.map(attr => el.getCssValue(attr)));
958
-
959
- const missing = attributeValues.filter((actual, i) => {
960
- const prop = attributeNames[i];
961
- let propValue = actual;
962
- if (isColorProperty(prop) && propValue) {
963
- propValue = convertColorToRGBA(propValue);
964
- }
965
- return propValue !== expectedValues[i];
966
- });
967
- if (missing.length) {
968
- missingAttributes.push(...missing);
969
- }
970
- }
971
- return equals(`all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(missingAttributes.length, 0);
972
- }
973
-
974
- /**
975
- * {{> seeAttributesOnElements }}
976
- */
977
- async seeAttributesOnElements(locator, attributes) {
978
- const els = await this._locate(locator);
979
- assertElementExists(els, locator);
980
-
981
- const attributeNames = Object.keys(attributes);
982
- const expectedValues = attributeNames.map(name => attributes[name]);
983
- const missingAttributes = [];
984
-
985
- for (const el of els) {
986
- const attributeValues = await Promise.all(attributeNames.map(attr => el.getAttribute(attr)));
987
- const missing = attributeValues.filter((actual, i) => {
988
- if (expectedValues[i] instanceof RegExp) {
989
- return expectedValues[i].test(actual);
990
- }
991
- return actual !== expectedValues[i];
992
- });
993
- if (missing.length) {
994
- missingAttributes.push(...missing);
995
- }
996
- }
997
-
998
- return equals(`all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`).assert(missingAttributes.length, 0);
999
- }
1000
-
1001
- /**
1002
- * {{> executeScript }}
1003
- */
1004
- async executeScript() {
1005
- return this.browser.executeScript.apply(this.browser, arguments);
1006
- }
1007
-
1008
- /**
1009
- * {{> executeAsyncScript }}
1010
- */
1011
- async executeAsyncScript() {
1012
- this.browser.manage().timeouts().setScriptTimeout(this.options.scriptTimeout);
1013
- return this.browser.executeAsyncScript.apply(this.browser, arguments);
1014
- }
1015
-
1016
- /**
1017
- * {{> seeInCurrentUrl }}
1018
- */
1019
- async seeInCurrentUrl(url) {
1020
- return this.browser.getCurrentUrl().then(currentUrl => stringIncludes('url').assert(url, currentUrl));
1021
- }
1022
-
1023
- /**
1024
- * {{> dontSeeInCurrentUrl }}
1025
- */
1026
- async dontSeeInCurrentUrl(url) {
1027
- return this.browser.getCurrentUrl().then(currentUrl => stringIncludes('url').negate(url, currentUrl));
1028
- }
1029
-
1030
- /**
1031
- * {{> seeCurrentUrlEquals }}
1032
- */
1033
- async seeCurrentUrlEquals(url) {
1034
- return this.browser.getCurrentUrl().then(currentUrl => urlEquals(this.options.url).assert(url, currentUrl));
1035
- }
1036
-
1037
- /**
1038
- * {{> dontSeeCurrentUrlEquals }}
1039
- */
1040
- async dontSeeCurrentUrlEquals(url) {
1041
- return this.browser.getCurrentUrl().then(currentUrl => urlEquals(this.options.url).negate(url, currentUrl));
1042
- }
1043
-
1044
- /**
1045
- * {{> saveElementScreenshot }}
1046
- *
1047
- */
1048
- async saveElementScreenshot(locator, fileName) {
1049
- const outputFile = screenshotOutputFolder(fileName);
1050
-
1051
- const writeFile = (png, outputFile) => {
1052
- const fs = require('fs');
1053
- const stream = fs.createWriteStream(outputFile);
1054
- stream.write(Buffer.from(png, 'base64'));
1055
- stream.end();
1056
- return new Promise(resolve => stream.on('finish', resolve)); // eslint-disable-line no-promise-executor-return
1057
- };
1058
-
1059
- const res = await this._locate(locator);
1060
- assertElementExists(res, locator);
1061
- if (res.length > 1) this.debug(`[Elements] Using first element out of ${res.length}`);
1062
- const elem = res[0];
1063
- this.debug(`Screenshot of ${(new Locator(locator))} element has been saved to ${outputFile}`);
1064
- const png = await elem.takeScreenshot();
1065
- return writeFile(png, outputFile);
1066
- }
1067
-
1068
- /**
1069
- * {{> saveScreenshot }}
1070
- */
1071
- async saveScreenshot(fileName, fullPage = false) {
1072
- const outputFile = screenshotOutputFolder(fileName);
1073
-
1074
- const writeFile = (png, outputFile) => {
1075
- const fs = require('fs');
1076
- const stream = fs.createWriteStream(outputFile);
1077
- stream.write(Buffer.from(png, 'base64'));
1078
- stream.end();
1079
- return new Promise(resolve => stream.on('finish', resolve)); // eslint-disable-line no-promise-executor-return
1080
- };
1081
-
1082
- if (!fullPage) {
1083
- this.debug(`Screenshot has been saved to ${outputFile}`);
1084
- const png = await this.browser.takeScreenshot();
1085
- return writeFile(png, outputFile);
1086
- }
1087
-
1088
- let { width, height } = await this.browser.executeScript(() => ({ // eslint-disable-line
1089
- height: document.body.scrollHeight,
1090
- width: document.body.scrollWidth,
1091
- }));
1092
-
1093
- if (height < 100) height = 500;
1094
-
1095
- await this.browser.manage().window().setSize(width, height);
1096
- this.debug(`Screenshot has been saved to ${outputFile}, size: ${width}x${height}`);
1097
- const png = await this.browser.takeScreenshot();
1098
- return writeFile(png, outputFile);
1099
- }
1100
-
1101
- /**
1102
- * {{> clearCookie }}
1103
- */
1104
- async clearCookie(cookie = null) {
1105
- if (!cookie) {
1106
- return this.browser.manage().deleteAllCookies();
1107
- }
1108
- return this.browser.manage().deleteCookie(cookie);
1109
- }
1110
-
1111
- /**
1112
- * {{> seeCookie }}
1113
- */
1114
- async seeCookie(name) {
1115
- return this.browser.manage().getCookie(name).then(res => truth(`cookie ${name}`, 'to be set').assert(res));
1116
- }
1117
-
1118
- /**
1119
- * {{> dontSeeCookie }}
1120
- */
1121
- async dontSeeCookie(name) {
1122
- return this.browser.manage().getCookie(name).then(res => truth(`cookie ${name}`, 'to be set').negate(res));
1123
- }
1124
-
1125
- /**
1126
- * {{> grabCookie }}
1127
- *
1128
- * Returns cookie in JSON [format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
1129
- */
1130
- async grabCookie(name) {
1131
- if (!name) return this.browser.manage().getCookies();
1132
- return this.browser.manage().getCookie(name);
1133
- }
1134
-
1135
- /**
1136
- * Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt.
1137
- * Don't confuse popups with modal windows, as created by [various
1138
- * libraries](http://jster.net/category/windows-modals-popups). Appium: support only web testing
1139
- */
1140
- async acceptPopup() {
1141
- return this.browser.switchTo().alert().accept();
1142
- }
1143
-
1144
- /**
1145
- * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt.
1146
- */
1147
- async cancelPopup() {
1148
- return this.browser.switchTo().alert().dismiss();
1149
- }
1150
-
1151
- /**
1152
- * {{> seeInPopup }}
1153
- */
1154
- async seeInPopup(text) {
1155
- const popupAlert = await this.browser.switchTo().alert();
1156
- const res = await popupAlert.getText();
1157
- if (res === null) {
1158
- throw new Error('Popup is not opened');
1159
- }
1160
- stringIncludes('text in popup').assert(text, res);
1161
- }
1162
-
1163
- /**
1164
- * Grab the text within the popup. If no popup is visible then it will return null
1165
- *
1166
- * ```js
1167
- * await I.grabPopupText();
1168
- * ```
1169
- */
1170
- async grabPopupText() {
1171
- try {
1172
- const dialog = await this.browser.switchTo().alert();
1173
-
1174
- if (dialog) {
1175
- return dialog.getText();
1176
- }
1177
- } catch (e) {
1178
- if (e.message.match(/no.*?(alert|modal)/i)) {
1179
- // Don't throw an error
1180
- return null;
1181
- }
1182
- throw e;
1183
- }
1184
- }
1185
-
1186
- /**
1187
- * {{> resizeWindow }}
1188
- */
1189
- async resizeWindow(width, height) {
1190
- if (width === 'maximize') {
1191
- const res = await this.browser.executeScript('return [screen.width, screen.height]');
1192
- return this.browser.manage().window().setSize(parseInt(res[0], 10), parseInt(res[1], 10));
1193
- }
1194
- return this.browser.manage().window().setSize(parseInt(width, 10), parseInt(height, 10));
1195
- }
1196
-
1197
- /**
1198
- * {{> dragAndDrop }}
1199
- */
1200
- async dragAndDrop(srcElement, destElement) {
1201
- const srcEl = await this._locate(srcElement, true);
1202
- const destEl = await this._locate(destElement, true);
1203
- assertElementExists(srcEl, srcElement);
1204
- assertElementExists(destEl, destElement);
1205
- return this.browser
1206
- .actions()
1207
- .dragAndDrop(srcEl[0], destEl[0])
1208
- .perform();
1209
- }
1210
-
1211
- /**
1212
- * Close all tabs except for the current one.
1213
- *
1214
- * ```js
1215
- * I.closeOtherTabs();
1216
- * ```
1217
- */
1218
- async closeOtherTabs() {
1219
- const client = this.browser;
1220
-
1221
- const handles = await client.getAllWindowHandles();
1222
- const currentHandle = await client.getWindowHandle();
1223
- const otherHandles = handles.filter(handle => handle !== currentHandle);
1224
-
1225
- if (!otherHandles || !otherHandles.length) return;
1226
- let p = Promise.resolve();
1227
- otherHandles.forEach((handle) => {
1228
- p = p.then(() => client.switchTo().window(handle).then(() => client.close()));
1229
- });
1230
- p = p.then(() => client.switchTo().window(currentHandle));
1231
- return p;
1232
- }
1233
-
1234
- /**
1235
- * Close current tab
1236
- *
1237
- * ```js
1238
- * I.closeCurrentTab();
1239
- * ```
1240
- */
1241
- async closeCurrentTab() {
1242
- const client = this.browser;
1243
-
1244
- const currentHandle = await client.getWindowHandle();
1245
- const nextHandle = await this._getWindowHandle(-1);
1246
-
1247
- await client.switchTo().window(currentHandle);
1248
- await client.close();
1249
- return client.switchTo().window(nextHandle);
1250
- }
1251
-
1252
- /**
1253
- * Get the window handle relative to the current handle. i.e. the next handle or the previous.
1254
- * @param {Number} offset Offset from current handle index. i.e. offset < 0 will go to the previous handle and positive number will go to the next window handle in sequence.
1255
- */
1256
- async _getWindowHandle(offset = 0) {
1257
- const client = this.browser;
1258
- const handles = await client.getAllWindowHandles();
1259
- const index = handles.indexOf(await client.getWindowHandle());
1260
- const nextIndex = index + offset;
1261
-
1262
- return handles[nextIndex];
1263
- // return handles[(index + offset) % handles.length];
1264
- }
1265
-
1266
- /**
1267
- * Open new tab and switch to it
1268
- *
1269
- * ```js
1270
- * I.openNewTab();
1271
- * ```
1272
- */
1273
- async openNewTab() {
1274
- const client = this.browser;
1275
- await this.executeScript('window.open("about:blank")');
1276
- const handles = await client.getAllWindowHandles();
1277
- await client.switchTo().window(handles[handles.length - 1]);
1278
- }
1279
-
1280
- /**
1281
- * Switch focus to a particular tab by its number. It waits tabs loading and then switch tab
1282
- *
1283
- * ```js
1284
- * I.switchToNextTab();
1285
- * I.switchToNextTab(2);
1286
- * ```
1287
- */
1288
- async switchToNextTab(num = 1) {
1289
- const client = this.browser;
1290
- const newHandle = await this._getWindowHandle(num);
1291
-
1292
- if (!newHandle) {
1293
- throw new Error(`There is no ability to switch to next tab with offset ${num}`);
1294
- }
1295
- return client.switchTo().window(newHandle);
1296
- }
1297
-
1298
- /**
1299
- * Switch focus to a particular tab by its number. It waits tabs loading and then switch tab
1300
- *
1301
- * ```js
1302
- * I.switchToPreviousTab();
1303
- * I.switchToPreviousTab(2);
1304
- * ```
1305
- */
1306
- async switchToPreviousTab(num = 1) {
1307
- const client = this.browser;
1308
- const newHandle = await this._getWindowHandle(-1 * num);
1309
-
1310
- if (!newHandle) {
1311
- throw new Error(`There is no ability to switch to previous tab with offset ${num}`);
1312
- }
1313
- return client.switchTo().window(newHandle);
1314
- }
1315
-
1316
- /**
1317
- * {{> grabNumberOfOpenTabs }}
1318
- */
1319
- async grabNumberOfOpenTabs() {
1320
- const pages = await this.browser.getAllWindowHandles();
1321
- return pages.length;
1322
- }
1323
-
1324
- /**
1325
- * {{> switchTo }}
1326
- */
1327
- async switchTo(locator) {
1328
- if (Number.isInteger(locator)) {
1329
- return this.browser.switchTo().frame(locator);
1330
- }
1331
- if (!locator) {
1332
- return this.browser.switchTo().frame(null);
1333
- }
1334
-
1335
- const els = await this._locate(withStrictLocator.call(this, locator), true);
1336
- assertElementExists(els, locator);
1337
- return this.browser.switchTo().frame(els[0]);
1338
- }
1339
-
1340
- /**
1341
- * {{> wait }}
1342
- */
1343
- wait(sec) {
1344
- return this.browser.sleep(sec * 1000);
1345
- }
1346
-
1347
- /**
1348
- * {{> waitForElement }}
1349
- */
1350
- async waitForElement(locator, sec = null) {
1351
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1352
- const el = global.element(guessLocator(locator) || global.by.css(locator));
1353
- return this.browser.wait(EC.presenceOf(el), aSec * 1000);
1354
- }
1355
-
1356
- async waitUntilExists(locator, sec = null) {
1357
- console.log(`waitUntilExists deprecated:
1358
- * use 'waitForElement' to wait for element to be attached
1359
- * use 'waitForDetached to wait for element to be removed'`);
1360
- return this.waitForDetached(locator, sec);
1361
- }
1362
-
1363
- /**
1364
- * {{> waitForDetached }}
1365
- */
1366
- async waitForDetached(locator, sec = null) {
1367
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1368
- const el = global.element(guessLocator(locator) || global.by.css(locator));
1369
- return this.browser.wait(EC.not(EC.presenceOf(el)), aSec * 1000).catch((err) => {
1370
- if (err.message && err.message.indexOf('Wait timed out after') > -1) {
1371
- throw new Error(`element (${JSON.stringify(locator)}) still on page after ${sec} sec`);
1372
- } else throw err;
1373
- });
1374
- }
1375
-
1376
- /**
1377
- * Waits for element to become clickable for number of seconds.
1378
- *
1379
- * ```js
1380
- * I.waitForClickable('#link');
1381
- * ```
1382
- */
1383
- async waitForClickable(locator, sec = null) {
1384
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1385
- const el = global.element(guessLocator(locator) || global.by.css(locator));
1386
- return this.browser.wait(EC.elementToBeClickable(el), aSec * 1000);
1387
- }
1388
-
1389
- /**
1390
- * {{> waitForVisible }}
1391
- */
1392
- async waitForVisible(locator, sec = null) {
1393
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1394
- const el = global.element(guessLocator(locator) || global.by.css(locator));
1395
- return this.browser.wait(EC.visibilityOf(el), aSec * 1000);
1396
- }
1397
-
1398
- /**
1399
- * {{> waitToHide }}
1400
- */
1401
- async waitToHide(locator, sec = null) {
1402
- return this.waitForInvisible(locator, sec);
1403
- }
1404
-
1405
- /**
1406
- * {{> waitForInvisible }}
1407
- */
1408
- async waitForInvisible(locator, sec = null) {
1409
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1410
- const el = global.element(guessLocator(locator) || global.by.css(locator));
1411
- return this.browser.wait(EC.invisibilityOf(el), aSec * 1000);
1412
- }
1413
-
1414
- async waitForStalenessOf(locator, sec = null) {
1415
- console.log(`waitForStalenessOf deprecated.
1416
- * Use waitForDetached to wait for element to be removed from page
1417
- * Use waitForInvisible to wait for element to be hidden on page`);
1418
- return this.waitForInvisible(locator, sec);
1419
- }
1420
-
1421
- /**
1422
- * {{> waitNumberOfVisibleElements }}
1423
- */
1424
- async waitNumberOfVisibleElements(locator, num, sec = null) {
1425
- function visibilityCountOf(loc, expectedCount) {
1426
- return function () {
1427
- return global.element.all(loc)
1428
- .filter(el => el.isDisplayed())
1429
- .count()
1430
- .then(count => count === expectedCount);
1431
- };
1432
- }
1433
-
1434
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1435
- const guessLoc = guessLocator(locator) || global.by.css(locator);
1436
-
1437
- return this.browser.wait(visibilityCountOf(guessLoc, num), aSec * 1000)
1438
- .catch(() => {
1439
- throw Error(`The number of elements (${(new Locator(locator))}) is not ${num} after ${aSec} sec`);
1440
- });
1441
- }
1442
-
1443
- /**
1444
- * {{> waitForEnabled }}
1445
- */
1446
- async waitForEnabled(locator, sec = null) {
1447
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1448
- const el = global.element(guessLocator(locator) || global.by.css(locator));
1449
-
1450
- return this.browser.wait(EC.elementToBeClickable(el), aSec * 1000)
1451
- .catch(() => {
1452
- throw Error(`element (${(new Locator(locator))}) still not enabled after ${aSec} sec`);
1453
- });
1454
- }
1455
-
1456
- /**
1457
- * {{> waitForValue }}
1458
- */
1459
- async waitForValue(field, value, sec = null) {
1460
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1461
-
1462
- const valueToBeInElementValue = (loc) => {
1463
- return async () => {
1464
- const els = await findFields(this.browser, loc);
1465
-
1466
- if (!els) {
1467
- return false;
1468
- }
1469
- const values = await Promise.all(els.map(el => el.getAttribute('value')));
1470
- return values.filter(part => part.indexOf(value) >= 0).length > 0;
1471
- };
1472
- };
1473
-
1474
- return this.browser.wait(valueToBeInElementValue(field, value), aSec * 1000)
1475
- .catch(() => {
1476
- throw Error(`element (${field}) is not in DOM or there is no element(${field}) with value "${value}" after ${aSec} sec`);
1477
- });
1478
- }
1479
-
1480
- /**
1481
- * {{> waitForFunction }}
1482
- */
1483
- async waitForFunction(fn, argsOrSec = null, sec = null) {
1484
- let args = [];
1485
- if (argsOrSec) {
1486
- if (Array.isArray(argsOrSec)) {
1487
- args = argsOrSec;
1488
- } else if (typeof argsOrSec === 'number') {
1489
- sec = argsOrSec;
1490
- }
1491
- }
1492
-
1493
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1494
- return this.browser.wait(() => this.browser.executeScript.call(this.browser, fn, ...args), aSec * 1000);
1495
- }
1496
-
1497
- /**
1498
- * {{> waitInUrl }}
1499
- */
1500
- async waitInUrl(urlPart, sec = null) {
1501
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1502
- const waitTimeout = aSec * 1000;
1503
-
1504
- return this.browser.wait(EC.urlContains(urlPart), waitTimeout)
1505
- .catch(async (e) => {
1506
- const currUrl = await this.browser.getCurrentUrl();
1507
- if (/wait timed out after/i.test(e.message)) {
1508
- throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`);
1509
- } else {
1510
- throw e;
1511
- }
1512
- });
1513
- }
1514
-
1515
- /**
1516
- * {{> waitUrlEquals }}
1517
- */
1518
- async waitUrlEquals(urlPart, sec = null) {
1519
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1520
- const waitTimeout = aSec * 1000;
1521
- const baseUrl = this.options.url;
1522
- if (urlPart.indexOf('http') < 0) {
1523
- urlPart = baseUrl + urlPart;
1524
- }
1525
-
1526
- return this.browser.wait(EC.urlIs(urlPart), waitTimeout)
1527
- .catch(async (e) => {
1528
- const currUrl = await this.browser.getCurrentUrl();
1529
- if (/wait timed out after/i.test(e.message)) {
1530
- throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`);
1531
- } else {
1532
- throw e;
1533
- }
1534
- });
1535
- }
1536
-
1537
- /**
1538
- * {{> waitForText }}
1539
- */
1540
- async waitForText(text, sec = null, context = null) {
1541
- if (!context) {
1542
- context = this.context;
1543
- }
1544
- const el = global.element(guessLocator(context) || global.by.css(context));
1545
- const aSec = sec || this.options.waitForTimeoutInSeconds;
1546
- return this.browser.wait(EC.textToBePresentInElement(el, text), aSec * 1000);
1547
- }
1548
-
1549
- // ANGULAR SPECIFIC
1550
-
1551
- /**
1552
- * Moves to url
1553
- */
1554
- moveTo(path) {
1555
- return this.browser.setLocation(path);
1556
- }
1557
-
1558
- /**
1559
- * {{> refreshPage }}
1560
- */
1561
- refreshPage() {
1562
- return this.browser.refresh();
1563
- }
1564
-
1565
- /**
1566
- * Reloads page
1567
- */
1568
- refresh() {
1569
- console.log('Deprecated in favor of refreshPage');
1570
- return this.browser.refresh();
1571
- }
1572
-
1573
- /**
1574
- * {{> scrollTo }}
1575
- */
1576
- async scrollTo(locator, offsetX = 0, offsetY = 0) {
1577
- if (typeof locator === 'number' && typeof offsetX === 'number') {
1578
- offsetY = offsetX;
1579
- offsetX = locator;
1580
- locator = null;
1581
- }
1582
-
1583
- if (locator) {
1584
- const res = await this._locate(locator, true);
1585
- if (!res || res.length === 0) {
1586
- return truth(`elements of ${(new Locator(locator))}`, 'to be seen').assert(false);
1587
- }
1588
- const elem = res[0];
1589
- const location = await elem.getLocation();
1590
- /* eslint-disable prefer-arrow-callback */
1591
- return this.executeScript(function (x, y) { return window.scrollTo(x, y); }, location.x + offsetX, location.y + offsetY);
1592
- /* eslint-enable */
1593
- }
1594
-
1595
- /* eslint-disable prefer-arrow-callback, comma-dangle */
1596
- return this.executeScript(function (x, y) { return window.scrollTo(x, y); }, offsetX, offsetY);
1597
- /* eslint-enable */
1598
- }
1599
-
1600
- /**
1601
- * {{> scrollPageToTop }}
1602
- */
1603
- async scrollPageToTop() {
1604
- return this.executeScript('window.scrollTo(0, 0);');
1605
- }
1606
-
1607
- /**
1608
- * {{> scrollPageToBottom }}
1609
- */
1610
- async scrollPageToBottom() {
1611
- /* eslint-disable prefer-arrow-callback, comma-dangle */
1612
- return this.executeScript(function () {
1613
- const body = document.body;
1614
- const html = document.documentElement;
1615
- window.scrollTo(0, Math.max(
1616
- body.scrollHeight,
1617
- body.offsetHeight,
1618
- html.clientHeight,
1619
- html.scrollHeight,
1620
- html.offsetHeight
1621
- ));
1622
- });
1623
- /* eslint-enable */
1624
- }
1625
-
1626
- /**
1627
- * {{> grabPageScrollPosition }}
1628
- */
1629
- async grabPageScrollPosition() {
1630
- /* eslint-disable comma-dangle */
1631
- function getScrollPosition() {
1632
- return {
1633
- x: window.pageXOffset,
1634
- y: window.pageYOffset
1635
- };
1636
- }
1637
- /* eslint-enable comma-dangle */
1638
- return this.executeScript(getScrollPosition);
1639
- }
1640
-
1641
- /**
1642
- * Injects Angular module.
1643
- *
1644
- * ```js
1645
- * I.haveModule('modName', function() {
1646
- * angular.module('modName', []).value('foo', 'bar');
1647
- * });
1648
- * ```
1649
- */
1650
- haveModule(modName, fn) {
1651
- return this.browser.addMockModule(modName, fn);
1652
- }
1653
-
1654
- /**
1655
- * Removes mocked Angular module. If modName not specified - clears all mock modules.
1656
- *
1657
- * ```js
1658
- * I.resetModule(); // clears all
1659
- * I.resetModule('modName');
1660
- * ```
1661
- */
1662
- resetModule(modName) {
1663
- if (!modName) {
1664
- return this.browser.clearMockModules();
1665
- }
1666
- return this.browser.removeMockModule(modName);
1667
- }
1668
-
1669
- /**
1670
- * {{> setCookie }}
1671
- */
1672
- setCookie(cookie) {
1673
- return this.browser.manage().addCookie(cookie);
1674
- }
1675
- }
1676
-
1677
- export default Protractor;
1678
-
1679
- async function findCheckable(client, locator) {
1680
- const matchedLocator = guessLocator(locator);
1681
- if (matchedLocator) {
1682
- return client.findElements(matchedLocator);
1683
- }
1684
- const literal = xpathLocator.literal(locator);
1685
- let els = await client.findElements(global.by.xpath(Locator.checkable.byText(literal)));
1686
- if (els.length) {
1687
- return els;
1688
- }
1689
- els = await client.findElements(global.by.xpath(Locator.checkable.byName(literal)));
1690
- if (els.length) {
1691
- return els;
1692
- }
1693
- return client.findElements(global.by.css(locator));
1694
- }
1695
-
1696
- function withStrictLocator(locator) {
1697
- locator = new Locator(locator);
1698
- if (locator.isAccessibilityId()) return withAccessiblitiyLocator.call(this, locator.value);
1699
- return locator.simplify();
1700
- }
1701
-
1702
- function withAccessiblitiyLocator(locator) {
1703
- if (this.isWeb === false) {
1704
- return `accessibility id:${locator.slice(1)}`;
1705
- }
1706
- return `[aria-label="${locator.slice(1)}"]`;
1707
- // hook before webdriverio supports native ~ locators in web
1708
- }
1709
-
1710
- async function findFields(client, locator) {
1711
- const matchedLocator = guessLocator(locator);
1712
- if (matchedLocator) {
1713
- return client.findElements(matchedLocator);
1714
- }
1715
- const literal = xpathLocator.literal(locator);
1716
-
1717
- let els = await client.findElements(global.by.xpath(Locator.field.labelEquals(literal)));
1718
- if (els.length) {
1719
- return els;
1720
- }
1721
-
1722
- els = await client.findElements(global.by.xpath(Locator.field.labelContains(literal)));
1723
- if (els.length) {
1724
- return els;
1725
- }
1726
- els = await client.findElements(global.by.xpath(Locator.field.byName(literal)));
1727
- if (els.length) {
1728
- return els;
1729
- }
1730
- return client.findElements(global.by.css(locator));
1731
- }
1732
-
1733
- async function proceedSee(assertType, text, context) {
1734
- let description;
1735
- let locator;
1736
- if (!context) {
1737
- if (this.context === this.options.rootElement) {
1738
- locator = guessLocator(this.context) || global.by.css(this.context);
1739
- description = 'web application';
1740
- } else {
1741
- // inside within block
1742
- locator = global.by.xpath('.//*');
1743
- description = `current context ${(new Locator(context)).toString()}`;
1744
- }
1745
- } else {
1746
- locator = guessLocator(context) || global.by.css(context);
1747
- description = `element ${(new Locator(context)).toString()}`;
1748
- }
1749
- const enableSmartWait = !!this.context && assertType === 'assert';
1750
- const els = await this._smartWait(() => this.browser.findElements(locator), enableSmartWait);
1751
- const promises = [];
1752
- let source = '';
1753
- els.forEach(el => promises.push(el.getText().then(elText => source += `| ${elText}`)));
1754
- await Promise.all(promises);
1755
- return stringIncludes(description)[assertType](text, source);
1756
- }
1757
-
1758
- async function proceedSeeInField(assertType, field, value) {
1759
- const els = await findFields(this.browser, field);
1760
- assertElementExists(els, field, 'Field');
1761
- const el = els[0];
1762
- const tag = await el.getTagName();
1763
- const fieldVal = await el.getAttribute('value');
1764
- if (tag === 'select') {
1765
- // locate option by values and check them
1766
- const literal = xpathLocator.literal(fieldVal);
1767
- const textEl = await el.findElement(global.by.xpath(Locator.select.byValue(literal)));
1768
- const text = await textEl.getText();
1769
- return equals(`select option by ${field}`)[assertType](value, text);
1770
- }
1771
- return stringIncludes(`field by ${field}`)[assertType](value, fieldVal);
1772
- }
1773
-
1774
- async function proceedIsChecked(assertType, option) {
1775
- const els = await findCheckable(this.browser, option);
1776
- assertElementExists(els, option, 'Option');
1777
- const elsSelected = [];
1778
- els.forEach(el => elsSelected.push(el.isSelected()));
1779
- const values = await Promise.all(elsSelected);
1780
- const selected = values.reduce((prev, cur) => prev || cur);
1781
- return truth(`checkable ${option}`, 'to be checked')[assertType](selected);
1782
- }
1783
-
1784
- async function findClickable(matcher, locator) {
1785
- locator = new Locator(locator);
1786
- if (!locator.isFuzzy()) {
1787
- const els = await this._locate(locator, true);
1788
- assertElementExists(els, locator.value);
1789
- return els[0];
1790
- }
1791
- const literal = xpathLocator.literal(locator.value);
1792
- const narrowLocator = Locator.clickable.narrow(literal);
1793
- let els = await matcher.findElements(global.by.xpath(narrowLocator));
1794
- if (els.length) {
1795
- return els[0];
1796
- }
1797
-
1798
- els = await matcher.findElements(global.by.xpath(Locator.clickable.wide(literal)));
1799
- if (els.length) {
1800
- return els[0];
1801
- }
1802
- return matcher.findElement(global.by.css(locator.value));
1803
- }
1804
-
1805
- function guessLocator(locator) {
1806
- const l = new Locator(locator);
1807
- if (l.isFuzzy()) return false;
1808
- if (l.type) return global.by[l.type](l.value);
1809
- return false;
1810
- }
1811
-
1812
- function assertElementExists(res, locator, prefix, suffix) {
1813
- if (!res.length) {
1814
- throw new ElementNotFound(locator, prefix, suffix);
1815
- }
1816
- }
1817
-
1818
- function isFrameLocator(locator) {
1819
- locator = new Locator(locator);
1820
- if (locator.isFrame()) return locator.value;
1821
- return false;
1822
- }
1823
-
1824
- function isWithin() {
1825
- return Object.keys(withinStore).length !== 0;
1826
- }
1827
-
1828
- function loadGlobals(browser) {
1829
- global.browser = browser;
1830
- global.$ = browser.$;
1831
- global.$$ = browser.$$;
1832
- global.element = browser.element;
1833
- global.by = global.By = new ProtractorBy();
1834
- global.ExpectedConditions = EC = new ProtractorExpectedConditions(browser);
1835
- }