codeceptjs 3.5.6 → 3.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +5 -1
  2. package/docs/advanced.md +1 -1
  3. package/docs/bdd.md +11 -7
  4. package/docs/build/ApiDataFactory.js +2 -1
  5. package/docs/build/Appium.js +26 -23
  6. package/docs/build/Expect.js +422 -0
  7. package/docs/build/Nightmare.js +53 -56
  8. package/docs/build/Playwright.js +202 -103
  9. package/docs/build/Protractor.js +66 -69
  10. package/docs/build/Puppeteer.js +124 -81
  11. package/docs/build/TestCafe.js +56 -55
  12. package/docs/build/WebDriver.js +81 -82
  13. package/docs/changelog.md +562 -1
  14. package/docs/commands.md +12 -0
  15. package/docs/community-helpers.md +8 -4
  16. package/docs/examples.md +8 -2
  17. package/docs/helpers/Appium.md +50 -32
  18. package/docs/helpers/Expect.md +275 -0
  19. package/docs/helpers/Nightmare.md +141 -94
  20. package/docs/helpers/Playwright.md +360 -261
  21. package/docs/helpers/Protractor.md +229 -169
  22. package/docs/helpers/Puppeteer.md +270 -187
  23. package/docs/helpers/TestCafe.md +201 -149
  24. package/docs/helpers/WebDriver.md +252 -178
  25. package/docs/mobile.md +17 -21
  26. package/docs/plugins.md +35 -1
  27. package/docs/webapi/amOnPage.mustache +1 -1
  28. package/docs/webapi/appendField.mustache +1 -1
  29. package/docs/webapi/attachFile.mustache +1 -1
  30. package/docs/webapi/blur.mustache +1 -0
  31. package/docs/webapi/checkOption.mustache +1 -1
  32. package/docs/webapi/clearCookie.mustache +1 -1
  33. package/docs/webapi/clearField.mustache +1 -1
  34. package/docs/webapi/click.mustache +1 -1
  35. package/docs/webapi/clickLink.mustache +1 -1
  36. package/docs/webapi/closeCurrentTab.mustache +1 -1
  37. package/docs/webapi/closeOtherTabs.mustache +1 -1
  38. package/docs/webapi/dontSee.mustache +1 -1
  39. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +1 -1
  40. package/docs/webapi/dontSeeCookie.mustache +1 -1
  41. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +1 -1
  42. package/docs/webapi/dontSeeElement.mustache +1 -1
  43. package/docs/webapi/dontSeeElementInDOM.mustache +1 -1
  44. package/docs/webapi/dontSeeInCurrentUrl.mustache +1 -1
  45. package/docs/webapi/dontSeeInField.mustache +1 -1
  46. package/docs/webapi/dontSeeInSource.mustache +1 -1
  47. package/docs/webapi/dontSeeInTitle.mustache +1 -1
  48. package/docs/webapi/doubleClick.mustache +1 -1
  49. package/docs/webapi/downloadFile.mustache +1 -1
  50. package/docs/webapi/dragAndDrop.mustache +1 -1
  51. package/docs/webapi/dragSlider.mustache +1 -1
  52. package/docs/webapi/executeAsyncScript.mustache +0 -2
  53. package/docs/webapi/executeScript.mustache +0 -2
  54. package/docs/webapi/fillField.mustache +1 -1
  55. package/docs/webapi/focus.mustache +1 -0
  56. package/docs/webapi/forceClick.mustache +1 -1
  57. package/docs/webapi/forceRightClick.mustache +1 -1
  58. package/docs/webapi/grabCookie.mustache +1 -1
  59. package/docs/webapi/grabDataFromPerformanceTiming.mustache +1 -1
  60. package/docs/webapi/moveCursorTo.mustache +1 -1
  61. package/docs/webapi/openNewTab.mustache +1 -1
  62. package/docs/webapi/pressKey.mustache +1 -1
  63. package/docs/webapi/pressKeyDown.mustache +1 -1
  64. package/docs/webapi/pressKeyUp.mustache +1 -1
  65. package/docs/webapi/pressKeyWithKeyNormalization.mustache +1 -1
  66. package/docs/webapi/refreshPage.mustache +1 -1
  67. package/docs/webapi/resizeWindow.mustache +1 -1
  68. package/docs/webapi/rightClick.mustache +1 -1
  69. package/docs/webapi/saveElementScreenshot.mustache +1 -1
  70. package/docs/webapi/saveScreenshot.mustache +1 -1
  71. package/docs/webapi/say.mustache +1 -1
  72. package/docs/webapi/scrollIntoView.mustache +1 -1
  73. package/docs/webapi/scrollPageToBottom.mustache +1 -1
  74. package/docs/webapi/scrollPageToTop.mustache +1 -1
  75. package/docs/webapi/scrollTo.mustache +1 -1
  76. package/docs/webapi/see.mustache +1 -1
  77. package/docs/webapi/seeAttributesOnElements.mustache +1 -1
  78. package/docs/webapi/seeCheckboxIsChecked.mustache +1 -1
  79. package/docs/webapi/seeCookie.mustache +1 -1
  80. package/docs/webapi/seeCssPropertiesOnElements.mustache +1 -1
  81. package/docs/webapi/seeCurrentUrlEquals.mustache +1 -1
  82. package/docs/webapi/seeElement.mustache +1 -1
  83. package/docs/webapi/seeElementInDOM.mustache +1 -1
  84. package/docs/webapi/seeInCurrentUrl.mustache +1 -1
  85. package/docs/webapi/seeInField.mustache +1 -1
  86. package/docs/webapi/seeInPopup.mustache +1 -1
  87. package/docs/webapi/seeInSource.mustache +1 -1
  88. package/docs/webapi/seeInTitle.mustache +1 -1
  89. package/docs/webapi/seeNumberOfElements.mustache +1 -1
  90. package/docs/webapi/seeNumberOfVisibleElements.mustache +1 -1
  91. package/docs/webapi/seeTextEquals.mustache +1 -1
  92. package/docs/webapi/seeTitleEquals.mustache +1 -1
  93. package/docs/webapi/selectOption.mustache +1 -1
  94. package/docs/webapi/setCookie.mustache +1 -1
  95. package/docs/webapi/setGeoLocation.mustache +1 -1
  96. package/docs/webapi/switchTo.mustache +1 -1
  97. package/docs/webapi/switchToNextTab.mustache +1 -1
  98. package/docs/webapi/switchToPreviousTab.mustache +1 -1
  99. package/docs/webapi/type.mustache +1 -1
  100. package/docs/webapi/uncheckOption.mustache +1 -1
  101. package/docs/webapi/wait.mustache +1 -1
  102. package/docs/webapi/waitForClickable.mustache +1 -1
  103. package/docs/webapi/waitForDetached.mustache +1 -1
  104. package/docs/webapi/waitForElement.mustache +1 -1
  105. package/docs/webapi/waitForEnabled.mustache +1 -1
  106. package/docs/webapi/waitForFunction.mustache +1 -1
  107. package/docs/webapi/waitForInvisible.mustache +1 -1
  108. package/docs/webapi/waitForText.mustache +1 -1
  109. package/docs/webapi/waitForValue.mustache +1 -1
  110. package/docs/webapi/waitForVisible.mustache +1 -1
  111. package/docs/webapi/waitInUrl.mustache +1 -1
  112. package/docs/webapi/waitNumberOfVisibleElements.mustache +1 -1
  113. package/docs/webapi/waitToHide.mustache +1 -1
  114. package/docs/webapi/waitUrlEquals.mustache +1 -1
  115. package/docs/webdriver.md +1 -1
  116. package/lib/cli.js +3 -1
  117. package/lib/command/dryRun.js +2 -1
  118. package/lib/helper/ApiDataFactory.js +2 -1
  119. package/lib/helper/Appium.js +8 -5
  120. package/lib/helper/Expect.js +422 -0
  121. package/lib/helper/Playwright.js +127 -32
  122. package/lib/helper/Puppeteer.js +47 -4
  123. package/lib/interfaces/gherkin.js +21 -2
  124. package/lib/output.js +1 -1
  125. package/lib/plugin/autoLogin.js +41 -3
  126. package/lib/utils.js +1 -1
  127. package/lib/workers.js +23 -2
  128. package/package.json +25 -16
  129. package/translations/de-DE.js +5 -0
  130. package/translations/fr-FR.js +1 -0
  131. package/translations/it-IT.js +1 -0
  132. package/translations/ja-JP.js +5 -0
  133. package/translations/pl-PL.js +5 -0
  134. package/translations/pt-BR.js +1 -0
  135. package/translations/ru-RU.js +1 -0
  136. package/translations/zh-CN.js +5 -0
  137. package/translations/zh-TW.js +5 -0
  138. package/typings/promiseBasedTypes.d.ts +928 -862
  139. package/typings/types.d.ts +932 -845
@@ -0,0 +1,422 @@
1
+ const chai = require('chai');
2
+ const output = require('../output');
3
+
4
+ const { expect } = chai;
5
+
6
+ chai.use(require('chai-string'));
7
+ // @ts-ignore
8
+ chai.use(require('chai-exclude'));
9
+ chai.use(require('chai-match-pattern'));
10
+
11
+ /**
12
+ * This helper allows performing assertions based on Chai.
13
+ *
14
+ * ### Examples
15
+ *
16
+ * Zero-configuration when paired with other helpers like REST, Playwright:
17
+ *
18
+ * ```js
19
+ * // inside codecept.conf.js
20
+ *{
21
+ * helpers: {
22
+ * Playwright: {...},
23
+ * ExpectHelper: {},
24
+ * }
25
+ *}
26
+ * ```
27
+ *
28
+ * ## Methods
29
+ */
30
+ class ExpectHelper {
31
+ /**
32
+ *
33
+ * @param {*} actualValue
34
+ * @param {*} expectedValue
35
+ * @param {*} customErrorMsg
36
+ */
37
+ expectEqual(actualValue, expectedValue, customErrorMsg = '') {
38
+ // @ts-ignore
39
+ output.step(`I expect "${JSON.stringify(actualValue)}" to equal "${JSON.stringify(expectedValue)}"`);
40
+ return expect(actualValue, customErrorMsg).to.equal(expectedValue);
41
+ }
42
+
43
+ /**
44
+ *
45
+ * @param {*} actualValue
46
+ * @param {*} expectedValue
47
+ * @param {*} customErrorMsg
48
+ */
49
+ expectNotEqual(actualValue, expectedValue, customErrorMsg = '') {
50
+ // @ts-ignore
51
+ output.step(`I expect "${JSON.stringify(actualValue)}" to not equal "${JSON.stringify(expectedValue)}"`);
52
+ return expect(actualValue, customErrorMsg).not.to.equal(expectedValue);
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @param {*} actualValue
58
+ * @param {*} expectedValue
59
+ * @param {*} customErrorMsg
60
+
61
+ */
62
+ expectDeepEqual(actualValue, expectedValue, customErrorMsg = '') {
63
+ // @ts-ignore
64
+ output.step(`I expect "${JSON.stringify(actualValue)}" to deep equal "${JSON.stringify(expectedValue)}"`);
65
+ return expect(actualValue, customErrorMsg).to.deep.equal(expectedValue);
66
+ }
67
+
68
+ /**
69
+ *
70
+ * @param {*} actualValue
71
+ * @param {*} expectedValue
72
+ * @param {*} customErrorMsg
73
+ */
74
+ expectNotDeepEqual(actualValue, expectedValue, customErrorMsg = '') {
75
+ // @ts-ignore
76
+ output.step(`I expect "${JSON.stringify(actualValue)}" to not deep equal "${JSON.stringify(expectedValue)}"`);
77
+ return expect(actualValue, customErrorMsg).to.not.deep.equal(expectedValue);
78
+ }
79
+
80
+ /**
81
+ *
82
+ * @param {*} actualValue
83
+ * @param {*} expectedValueToContain
84
+ * @param {*} customErrorMsg
85
+ */
86
+ expectContain(actualValue, expectedValueToContain, customErrorMsg = '') {
87
+ // @ts-ignore
88
+ output.step(`I expect "${JSON.stringify(actualValue)}" to contain "${JSON.stringify(expectedValueToContain)}"`);
89
+ return expect(actualValue, customErrorMsg).to.contain(
90
+ expectedValueToContain,
91
+ );
92
+ }
93
+
94
+ /**
95
+ *
96
+ * @param {*} actualValue
97
+ * @param {*} expectedValueToNotContain
98
+ * @param {*} customErrorMsg
99
+ */
100
+ expectNotContain(
101
+ actualValue,
102
+ expectedValueToNotContain,
103
+ customErrorMsg = '',
104
+ ) {
105
+ // @ts-ignore
106
+ output.step(`I expect "${JSON.stringify(actualValue)}" to not contain "${JSON.stringify(expectedValueToNotContain)}"`);
107
+ return expect(actualValue, customErrorMsg).not.to.contain(
108
+ expectedValueToNotContain,
109
+ );
110
+ }
111
+
112
+ /**
113
+ *
114
+ * @param {*} actualValue
115
+ * @param {*} expectedValueToStartWith
116
+ * @param {*} customErrorMsg
117
+ */
118
+ expectStartsWith(actualValue, expectedValueToStartWith, customErrorMsg = '') {
119
+ // @ts-ignore
120
+ output.step(`I expect "${JSON.stringify(actualValue)}" to start with "${JSON.stringify(expectedValueToStartWith)}"`);
121
+ return expect(actualValue, customErrorMsg).to.startsWith(
122
+ expectedValueToStartWith,
123
+ );
124
+ }
125
+
126
+ /**
127
+ *
128
+ * @param {*} actualValue
129
+ * @param {*} expectedValueToNotStartWith
130
+ * @param {*} customErrorMsg
131
+ */
132
+ expectNotStartsWith(
133
+ actualValue,
134
+ expectedValueToNotStartWith,
135
+ customErrorMsg = '',
136
+ ) {
137
+ // @ts-ignore
138
+ output.step(`I expect "${JSON.stringify(actualValue)}" to not start with "${JSON.stringify(expectedValueToNotStartWith)}"`);
139
+ return expect(actualValue, customErrorMsg).not.to.startsWith(
140
+ expectedValueToNotStartWith,
141
+ );
142
+ }
143
+
144
+ /**
145
+ * @param {*} actualValue
146
+ * @param {*} expectedValueToEndWith
147
+ * @param {*} customErrorMsg
148
+ */
149
+ expectEndsWith(actualValue, expectedValueToEndWith, customErrorMsg = '') {
150
+ // @ts-ignore
151
+ output.step(`I expect "${JSON.stringify(actualValue)}" to end with "${JSON.stringify(expectedValueToEndWith)}"`);
152
+ return expect(actualValue, customErrorMsg).to.endsWith(
153
+ expectedValueToEndWith,
154
+ );
155
+ }
156
+
157
+ /**
158
+ * @param {*} actualValue
159
+ * @param {*} expectedValueToNotEndWith
160
+ * @param {*} customErrorMsg
161
+ */
162
+ expectNotEndsWith(
163
+ actualValue,
164
+ expectedValueToNotEndWith,
165
+ customErrorMsg = '',
166
+ ) {
167
+ // @ts-ignore
168
+ output.step(`I expect "${JSON.stringify(actualValue)}" to not end with "${JSON.stringify(expectedValueToNotEndWith)}"`);
169
+ return expect(actualValue, customErrorMsg).not.to.endsWith(
170
+ expectedValueToNotEndWith,
171
+ );
172
+ }
173
+
174
+ /**
175
+ * @param {*} targetData
176
+ * @param {*} jsonSchema
177
+ * @param {*} customErrorMsg
178
+ */
179
+ expectJsonSchema(targetData, jsonSchema, customErrorMsg = '') {
180
+ // @ts-ignore
181
+ output.step(`I expect "${JSON.stringify(targetData)}" to match this JSON schema "${JSON.stringify(jsonSchema)}"`);
182
+ chai.use(require('chai-json-schema'));
183
+ return expect(targetData, customErrorMsg).to.be.jsonSchema(jsonSchema);
184
+ }
185
+
186
+ /**
187
+ * @param {*} targetData
188
+ * @param {*} jsonSchema
189
+ * @param {*} customErrorMsg
190
+ * @param {*} ajvOptions Pass AJV options
191
+ */
192
+ expectJsonSchemaUsingAJV(
193
+ targetData,
194
+ jsonSchema,
195
+ customErrorMsg = '',
196
+ ajvOptions = { allErrors: true },
197
+ ) {
198
+ // @ts-ignore
199
+ output.step(`I expect "${JSON.stringify(targetData)}" to match this JSON schema using AJV "${JSON.stringify(jsonSchema)}"`);
200
+ chai.use(require('chai-json-schema-ajv').create(ajvOptions));
201
+ return expect(targetData, customErrorMsg).to.be.jsonSchema(jsonSchema);
202
+ }
203
+
204
+ /**
205
+ * @param {*} targetData
206
+ * @param {*} propertyName
207
+ * @param {*} customErrorMsg
208
+ */
209
+ expectHasProperty(targetData, propertyName, customErrorMsg = '') {
210
+ // @ts-ignore
211
+ output.step(`I expect "${JSON.stringify(targetData)}" to have property: "${JSON.stringify(propertyName)}"`);
212
+ return expect(targetData, customErrorMsg).to.have.property(propertyName);
213
+ }
214
+
215
+ /**
216
+ * @param {*} targetData
217
+ * @param {*} propertyName
218
+ * @param {*} customErrorMsg
219
+ */
220
+ expectHasAProperty(targetData, propertyName, customErrorMsg = '') {
221
+ // @ts-ignore
222
+ output.step(`I expect "${JSON.stringify(targetData)}" to have a property: "${JSON.stringify(propertyName)}"`);
223
+ return expect(targetData, customErrorMsg).to.have.a.property(propertyName);
224
+ }
225
+
226
+ /**
227
+ * @param {*} targetData
228
+ * @param {*} type
229
+ * @param {*} customErrorMsg
230
+ */
231
+ expectToBeA(targetData, type, customErrorMsg = '') {
232
+ // @ts-ignore
233
+ output.step(`I expect "${JSON.stringify(targetData)}" to be a "${JSON.stringify(type)}"`);
234
+ return expect(targetData, customErrorMsg).to.be.a(type);
235
+ }
236
+
237
+ /**
238
+ * @param {*} targetData
239
+ * @param {*} type
240
+ * @param {*} customErrorMsg
241
+ */
242
+ expectToBeAn(targetData, type, customErrorMsg = '') {
243
+ // @ts-ignore
244
+ output.step(`I expect "${JSON.stringify(targetData)}" to be an "${JSON.stringify(type)}"`);
245
+ return expect(targetData, customErrorMsg).to.be.an(type);
246
+ }
247
+
248
+ /**
249
+ * @param {*} targetData
250
+ * @param {*} regex
251
+ * @param {*} customErrorMsg
252
+ */
253
+ expectMatchRegex(targetData, regex, customErrorMsg = '') {
254
+ // @ts-ignore
255
+ output.step(`I expect "${JSON.stringify(targetData)}" to match the regex "${JSON.stringify(regex)}"`);
256
+ return expect(targetData, customErrorMsg).to.match(regex);
257
+ }
258
+
259
+ /**
260
+ * @param {*} targetData
261
+ * @param {*} length
262
+ * @param {*} customErrorMsg
263
+ */
264
+ expectLengthOf(targetData, length, customErrorMsg = '') {
265
+ // @ts-ignore
266
+ output.step(`I expect "${JSON.stringify(targetData)}" to have length of "${JSON.stringify(length)}"`);
267
+ return expect(targetData, customErrorMsg).to.have.lengthOf(length);
268
+ }
269
+
270
+ /**
271
+ * @param {*} targetData
272
+ * @param {*} customErrorMsg
273
+ */
274
+ expectEmpty(targetData, customErrorMsg = '') {
275
+ // @ts-ignore
276
+ output.step(`I expect "${JSON.stringify(targetData)}" to be empty`);
277
+ return expect(targetData, customErrorMsg).to.be.empty;
278
+ }
279
+
280
+ /**
281
+ * @param {*} targetData
282
+ * @param {*} customErrorMsg
283
+ */
284
+ expectTrue(targetData, customErrorMsg = '') {
285
+ // @ts-ignore
286
+ output.step(`I expect "${JSON.stringify(targetData)}" to be true`);
287
+ return expect(targetData, customErrorMsg).to.be.true;
288
+ }
289
+
290
+ /**
291
+ * @param {*} targetData
292
+ * @param {*} customErrorMsg
293
+ */
294
+ expectFalse(targetData, customErrorMsg = '') {
295
+ // @ts-ignore
296
+ output.step(`I expect "${JSON.stringify(targetData)}" to be false`);
297
+ return expect(targetData, customErrorMsg).to.be.false;
298
+ }
299
+
300
+ /**
301
+ * @param {*} targetData
302
+ * @param {*} aboveThan number | Date
303
+ * @param {*} customErrorMsg
304
+ */
305
+ expectAbove(targetData, aboveThan, customErrorMsg = '') {
306
+ // @ts-ignore
307
+ output.step(`I expect "${JSON.stringify(targetData)}" to be above ${JSON.stringify(aboveThan)}`);
308
+ return expect(targetData, customErrorMsg).to.be.above(aboveThan);
309
+ }
310
+
311
+ /**
312
+ * @param {*} targetData
313
+ * @param {*} belowThan number | Date
314
+ * @param {*} customErrorMsg
315
+ */
316
+ expectBelow(targetData, belowThan, customErrorMsg = '') {
317
+ // @ts-ignore
318
+ output.step(`I expect "${JSON.stringify(targetData)}" to be below ${JSON.stringify(belowThan)}`);
319
+ return expect(targetData, customErrorMsg).to.be.below(belowThan);
320
+ }
321
+
322
+ /**
323
+ * @param {*} targetData
324
+ * @param {*} lengthAboveThan
325
+ * @param {*} customErrorMsg
326
+ */
327
+ expectLengthAboveThan(targetData, lengthAboveThan, customErrorMsg = '') {
328
+ // @ts-ignore
329
+ output.step(`I expect "${JSON.stringify(targetData)}" to have length of above ${JSON.stringify(lengthAboveThan)}`);
330
+ return expect(targetData, customErrorMsg).to.have.lengthOf.above(
331
+ lengthAboveThan,
332
+ );
333
+ }
334
+
335
+ /**
336
+ * @param {*} targetData
337
+ * @param {*} lengthBelowThan
338
+ * @param {*} customErrorMsg
339
+ */
340
+ expectLengthBelowThan(targetData, lengthBelowThan, customErrorMsg = '') {
341
+ // @ts-ignore
342
+ output.step(`I expect "${JSON.stringify(targetData)}" to have length of below ${JSON.stringify(lengthBelowThan)}`);
343
+ return expect(targetData, customErrorMsg).to.have.lengthOf.below(
344
+ lengthBelowThan,
345
+ );
346
+ }
347
+
348
+ /**
349
+ * @param {*} actualValue
350
+ * @param {*} expectedValue
351
+ * @param {*} customErrorMsg
352
+ */
353
+ expectEqualIgnoreCase(actualValue, expectedValue, customErrorMsg = '') {
354
+ // @ts-ignore
355
+ output.step(`I expect and ingore case "${JSON.stringify(actualValue)}" to equal "${JSON.stringify(expectedValue)}"`);
356
+ return expect(actualValue, customErrorMsg).to.equalIgnoreCase(
357
+ expectedValue,
358
+ );
359
+ }
360
+
361
+ /**
362
+ * expects members of two arrays are deeply equal
363
+ * @param {*} actualValue
364
+ * @param {*} expectedValue
365
+ * @param {*} customErrorMsg
366
+ */
367
+ expectDeepMembers(actualValue, expectedValue, customErrorMsg = '') {
368
+ // @ts-ignore
369
+ output.step(`I expect members of "${JSON.stringify(actualValue)}" and "${JSON.stringify(expectedValue)}" arrays are deeply equal`);
370
+ return expect(actualValue, customErrorMsg).to.have.deep.members(
371
+ expectedValue,
372
+ );
373
+ }
374
+
375
+ /**
376
+ * expects an array to be a superset of another array
377
+ * @param {*} superset
378
+ * @param {*} set
379
+ * @param {*} customErrorMsg
380
+ */
381
+ expectDeepIncludeMembers(superset, set, customErrorMsg = '') {
382
+ // @ts-ignore
383
+ output.step(`I expect "${JSON.stringify(superset)}" array to be a superset of "${JSON.stringify(set)}" array`);
384
+ return expect(superset, customErrorMsg).to.deep.include.members(
385
+ set,
386
+ );
387
+ }
388
+
389
+ /**
390
+ * expects members of two JSON objects are deeply equal excluding some properties
391
+ * @param {*} actualValue
392
+ * @param {*} expectedValue
393
+ * @param {*} fieldsToExclude
394
+ * @param {*} customErrorMsg
395
+ */
396
+ expectDeepEqualExcluding(
397
+ actualValue,
398
+ expectedValue,
399
+ fieldsToExclude,
400
+ customErrorMsg = '',
401
+ ) {
402
+ // @ts-ignore
403
+ output.step(`I expect members of "${JSON.stringify(actualValue)}" and "${JSON.stringify(expectedValue)}" JSON objects are deeply equal excluding properties: ${JSON.stringify(fieldsToExclude)}`);
404
+ return expect(actualValue, customErrorMsg)
405
+ .excludingEvery(fieldsToExclude)
406
+ .to.deep.equal(expectedValue);
407
+ }
408
+
409
+ /**
410
+ * expects a JSON object matches a provided pattern
411
+ * @param {*} actualValue
412
+ * @param {*} expectedPattern
413
+ * @param {*} customErrorMsg
414
+ */
415
+ expectMatchesPattern(actualValue, expectedPattern, customErrorMsg = '') {
416
+ // @ts-ignore
417
+ output.step(`I expect "${JSON.stringify(actualValue)}" to match the ${JSON.stringify(expectedPattern)} pattern`);
418
+ return expect(actualValue, customErrorMsg).to.matchPattern(expectedPattern);
419
+ }
420
+ }
421
+
422
+ module.exports = ExpectHelper;
@@ -116,6 +116,10 @@ const config = {};
116
116
  * npm i playwright-core@^1.18 --save
117
117
  * ```
118
118
  *
119
+ * Breaking Changes: if you use Playwright v1.38 and later, it will no longer download browsers automatically.
120
+ *
121
+ * Run `npx playwright install` to download browsers after `npm install`.
122
+ *
119
123
  * Using playwright-core package, will prevent the download of browser binaries and allow connecting to an existing browser installation or for connecting to a remote one.
120
124
  *
121
125
  *
@@ -942,14 +946,14 @@ class Playwright extends Helper {
942
946
  * Set headers for all next requests
943
947
  *
944
948
  * ```js
945
- * I.haveRequestHeaders({
949
+ * I.setPlaywrightRequestHeaders({
946
950
  * 'X-Sent-By': 'CodeceptJS',
947
951
  * });
948
952
  * ```
949
953
  *
950
954
  * @param {object} customHeaders headers to set
951
955
  */
952
- async haveRequestHeaders(customHeaders) {
956
+ async setPlaywrightRequestHeaders(customHeaders) {
953
957
  if (!customHeaders) {
954
958
  throw new Error('Cannot send empty headers.');
955
959
  }
@@ -993,6 +997,38 @@ class Playwright extends Helper {
993
997
  await el.blur(options);
994
998
  return this._waitForAction();
995
999
  }
1000
+ /**
1001
+ * Return the checked status of given element.
1002
+ *
1003
+ * @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
1004
+ * @param {object} [options] See https://playwright.dev/docs/api/class-locator#locator-is-checked
1005
+ * @return {Promise<boolean>}
1006
+ *
1007
+ */
1008
+
1009
+ async grabCheckedElementStatus(locator, options = {}) {
1010
+ const supportedTypes = ['checkbox', 'radio'];
1011
+ const el = await this._locateElement(locator);
1012
+ const type = await el.getAttribute('type');
1013
+
1014
+ if (supportedTypes.includes(type)) {
1015
+ return el.isChecked(options);
1016
+ }
1017
+ throw new Error(`Element is not a ${supportedTypes.join(' or ')} input`);
1018
+ }
1019
+ /**
1020
+ * Return the disabled status of given element.
1021
+ *
1022
+ * @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
1023
+ * @param {object} [options] See https://playwright.dev/docs/api/class-locator#locator-is-disabled
1024
+ * @return {Promise<boolean>}
1025
+ *
1026
+ */
1027
+
1028
+ async grabDisabledElementStatus(locator, options = {}) {
1029
+ const el = await this._locateElement(locator);
1030
+ return el.isDisabled(options);
1031
+ }
996
1032
 
997
1033
  /**
998
1034
  * {{> dragAndDrop }}
@@ -2365,10 +2401,11 @@ class Playwright extends Helper {
2365
2401
  locator = new Locator(locator, 'css');
2366
2402
 
2367
2403
  const context = await this._getContext();
2368
- const waiter = context.waitForSelector(buildLocatorString(locator), { timeout: waitTimeout, state: 'attached' });
2369
- return waiter.catch((err) => {
2370
- throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${err.message}`);
2371
- });
2404
+ try {
2405
+ await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'attached' });
2406
+ } catch (e) {
2407
+ throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${e.message}`);
2408
+ }
2372
2409
  }
2373
2410
 
2374
2411
  /**
@@ -2380,10 +2417,26 @@ class Playwright extends Helper {
2380
2417
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
2381
2418
  locator = new Locator(locator, 'css');
2382
2419
  const context = await this._getContext();
2383
- const waiter = context.waitForSelector(buildLocatorString(locator), { timeout: waitTimeout, state: 'visible' });
2384
- return waiter.catch((err) => {
2385
- throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${err.message}`);
2386
- });
2420
+ let count = 0;
2421
+
2422
+ // we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
2423
+ let waiter;
2424
+ if (this.frame) {
2425
+ do {
2426
+ waiter = await this.frame.locator(buildLocatorString(locator)).first().isVisible();
2427
+ await this.wait(1);
2428
+ count += 1000;
2429
+ if (waiter) break;
2430
+ } while (count <= waitTimeout);
2431
+
2432
+ if (!waiter) throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec.`);
2433
+ }
2434
+
2435
+ try {
2436
+ await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'visible' });
2437
+ } catch (e) {
2438
+ throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${e.message}`);
2439
+ }
2387
2440
  }
2388
2441
 
2389
2442
  /**
@@ -2393,10 +2446,27 @@ class Playwright extends Helper {
2393
2446
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
2394
2447
  locator = new Locator(locator, 'css');
2395
2448
  const context = await this._getContext();
2396
- const waiter = context.waitForSelector(buildLocatorString(locator), { timeout: waitTimeout, state: 'hidden' });
2397
- return waiter.catch((err) => {
2398
- throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${err.message}`);
2399
- });
2449
+ let waiter;
2450
+ let count = 0;
2451
+
2452
+ // we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
2453
+ if (this.frame) {
2454
+ do {
2455
+ waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden();
2456
+ await this.wait(1);
2457
+ count += 1000;
2458
+ if (waiter) break;
2459
+ } while (count <= waitTimeout);
2460
+
2461
+ if (!waiter) throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec.`);
2462
+ return;
2463
+ }
2464
+
2465
+ try {
2466
+ await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'hidden' });
2467
+ } catch (e) {
2468
+ throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${e.message}`);
2469
+ }
2400
2470
  }
2401
2471
 
2402
2472
  /**
@@ -2406,7 +2476,23 @@ class Playwright extends Helper {
2406
2476
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
2407
2477
  locator = new Locator(locator, 'css');
2408
2478
  const context = await this._getContext();
2409
- return context.waitForSelector(buildLocatorString(locator), { timeout: waitTimeout, state: 'hidden' }).catch((err) => {
2479
+ let waiter;
2480
+ let count = 0;
2481
+
2482
+ // we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
2483
+ if (this.frame) {
2484
+ do {
2485
+ waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden();
2486
+ await this.wait(1);
2487
+ count += 1000;
2488
+ if (waiter) break;
2489
+ } while (count <= waitTimeout);
2490
+
2491
+ if (!waiter) throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec.`);
2492
+ return;
2493
+ }
2494
+
2495
+ return context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'hidden' }).catch((err) => {
2410
2496
  throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`);
2411
2497
  });
2412
2498
  }
@@ -2473,7 +2559,12 @@ class Playwright extends Helper {
2473
2559
  if (context) {
2474
2560
  const locator = new Locator(context, 'css');
2475
2561
  if (!locator.isXPath()) {
2476
- waiter = contextObject.waitForSelector(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> text=${text}`, { timeout: waitTimeout, state: 'visible' });
2562
+ try {
2563
+ await contextObject.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> text=${text}`).first().waitFor({ timeout: waitTimeout, state: 'visible' });
2564
+ } catch (e) {
2565
+ console.log(e);
2566
+ throw new Error(`Text "${text}" was not found on page after ${waitTimeout / 1000} sec\n${e.message}`);
2567
+ }
2477
2568
  }
2478
2569
 
2479
2570
  if (locator.isXPath()) {
@@ -2486,18 +2577,17 @@ class Playwright extends Helper {
2486
2577
  }
2487
2578
  } else {
2488
2579
  // we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
2489
- if (this.frame) {
2490
- const { setTimeout } = require('timers/promises');
2491
- await setTimeout(waitTimeout);
2492
- waiter = await this.frame.locator(`:has-text('${text}')`).first().isVisible();
2493
- if (!waiter) throw new Error(`Text "${text}" was not found on page after ${waitTimeout / 1000} sec`);
2494
- return;
2495
- }
2496
- waiter = contextObject.waitForFunction(text => document.body && document.body.innerText.indexOf(text) > -1, text, { timeout: waitTimeout });
2580
+ // eslint-disable-next-line no-lonely-if
2581
+ const _contextObject = this.frame ? this.frame : contextObject;
2582
+ let count = 0;
2583
+ do {
2584
+ waiter = await _contextObject.locator(`:has-text('${text}')`).first().isVisible();
2585
+ await this.wait(1);
2586
+ count += 1000;
2587
+ } while (count <= waitTimeout);
2588
+
2589
+ if (!waiter) throw new Error(`Text "${text}" was not found on page after ${waitTimeout / 1000} sec`);
2497
2590
  }
2498
- return waiter.catch((err) => {
2499
- throw new Error(`Text "${text}" was not found on page after ${waitTimeout / 1000} sec\n${err.message}`);
2500
- });
2501
2591
  }
2502
2592
 
2503
2593
  /**
@@ -2656,17 +2746,21 @@ class Playwright extends Helper {
2656
2746
  let waiter;
2657
2747
  const context = await this._getContext();
2658
2748
  if (!locator.isXPath()) {
2659
- waiter = context.waitForSelector(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()}`, { timeout: waitTimeout, state: 'detached' });
2749
+ try {
2750
+ await context.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()}`).first().waitFor({ timeout: waitTimeout, state: 'detached' });
2751
+ } catch (e) {
2752
+ throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${e.message}`);
2753
+ }
2660
2754
  } else {
2661
2755
  const visibleFn = function ([locator, $XPath]) {
2662
2756
  eval($XPath); // eslint-disable-line no-eval
2663
2757
  return $XPath(null, locator).length === 0;
2664
2758
  };
2665
2759
  waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString()], { timeout: waitTimeout });
2760
+ return waiter.catch((err) => {
2761
+ throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`);
2762
+ });
2666
2763
  }
2667
- return waiter.catch((err) => {
2668
- throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`);
2669
- });
2670
2764
  }
2671
2765
 
2672
2766
  async _waitForAction() {
@@ -3354,7 +3448,8 @@ async function proceedSee(assertType, text, context, strict = false) {
3354
3448
  if (!context) {
3355
3449
  const el = await this.context;
3356
3450
 
3357
- allText = [await el.locator('body').innerText()];
3451
+ allText = el.constructor.name ? [await el.locator('body').innerText()] : [await el.innerText()];
3452
+
3358
3453
  description = 'web application';
3359
3454
  } else {
3360
3455
  const locator = new Locator(context, 'css');