codeceptjs 4.0.0-beta.5 → 4.0.0-beta.7.esm-aria

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