browsecraft 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/chunk-77HRTGXZ.js +2004 -0
- package/dist/chunk-77HRTGXZ.js.map +1 -0
- package/dist/chunk-KIPQFK3Y.cjs +2021 -0
- package/dist/chunk-KIPQFK3Y.cjs.map +1 -0
- package/dist/cli.cjs +297 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +295 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +831 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +984 -0
- package/dist/index.d.ts +984 -0
- package/dist/index.js +632 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkKIPQFK3Y_cjs = require('./chunk-KIPQFK3Y.cjs');
|
|
4
|
+
var browsecraftBdd = require('browsecraft-bdd');
|
|
5
|
+
|
|
6
|
+
// src/expect.ts
|
|
7
|
+
var DEFAULT_ASSERTION_TIMEOUT = 5e3;
|
|
8
|
+
function expect(subject) {
|
|
9
|
+
if (subject instanceof // We can't import Page directly for instanceof due to circular deps,
|
|
10
|
+
// so we check for the contextId property which only Page has
|
|
11
|
+
Object && "contextId" in subject && "session" in subject && !("target" in subject)) {
|
|
12
|
+
return new PageAssertions(subject);
|
|
13
|
+
}
|
|
14
|
+
return new ElementAssertions(subject);
|
|
15
|
+
}
|
|
16
|
+
var PageAssertions = class _PageAssertions {
|
|
17
|
+
page;
|
|
18
|
+
_not = false;
|
|
19
|
+
constructor(page) {
|
|
20
|
+
this.page = page;
|
|
21
|
+
}
|
|
22
|
+
/** Negate the assertion */
|
|
23
|
+
get not() {
|
|
24
|
+
const negated = new _PageAssertions(this.page);
|
|
25
|
+
negated._not = !this._not;
|
|
26
|
+
return negated;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Assert the page URL contains or matches the expected value.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* await expect(page).toHaveURL('/dashboard');
|
|
33
|
+
* await expect(page).toHaveURL(/dashboard/);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
async toHaveURL(expected, options) {
|
|
37
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
38
|
+
const isNot = this._not;
|
|
39
|
+
await retry(
|
|
40
|
+
`URL to ${isNot ? "not " : ""}match ${expected}`,
|
|
41
|
+
async () => {
|
|
42
|
+
const actual = await this.page.url();
|
|
43
|
+
const matches = typeof expected === "string" ? actual.includes(expected) : expected.test(actual);
|
|
44
|
+
return isNot ? !matches : matches;
|
|
45
|
+
},
|
|
46
|
+
(pass) => {
|
|
47
|
+
if (!pass) {
|
|
48
|
+
return `Expected URL ${isNot ? "not " : ""}to match ${expected}`;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
},
|
|
52
|
+
timeout
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Assert the page title matches.
|
|
57
|
+
*
|
|
58
|
+
* ```ts
|
|
59
|
+
* await expect(page).toHaveTitle('Dashboard');
|
|
60
|
+
* await expect(page).toHaveTitle(/Dashboard/);
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
async toHaveTitle(expected, options) {
|
|
64
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
65
|
+
const isNot = this._not;
|
|
66
|
+
await retry(
|
|
67
|
+
`title to ${isNot ? "not " : ""}match "${expected}"`,
|
|
68
|
+
async () => {
|
|
69
|
+
const actual = await this.page.title();
|
|
70
|
+
const matches = typeof expected === "string" ? actual.includes(expected) : expected.test(actual);
|
|
71
|
+
return isNot ? !matches : matches;
|
|
72
|
+
},
|
|
73
|
+
(pass) => {
|
|
74
|
+
if (!pass) {
|
|
75
|
+
return `Expected title ${isNot ? "not " : ""}to match "${expected}"`;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
},
|
|
79
|
+
timeout
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Assert the page content (HTML body text) contains the expected text.
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* await expect(page).toHaveContent('Welcome back');
|
|
87
|
+
* await expect(page).toHaveContent(/logged in as \w+/);
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
async toHaveContent(expected, options) {
|
|
91
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
92
|
+
const isNot = this._not;
|
|
93
|
+
let lastContent = "";
|
|
94
|
+
await retry(
|
|
95
|
+
`page to ${isNot ? "not " : ""}contain "${expected}"`,
|
|
96
|
+
async () => {
|
|
97
|
+
const content = await this.page.evaluate('document.body?.innerText || ""');
|
|
98
|
+
lastContent = content.slice(0, 200);
|
|
99
|
+
const matches = typeof expected === "string" ? content.includes(expected) : expected.test(content);
|
|
100
|
+
return isNot ? !matches : matches;
|
|
101
|
+
},
|
|
102
|
+
(pass) => {
|
|
103
|
+
if (!pass) {
|
|
104
|
+
return `Expected page ${isNot ? "not " : ""}to contain "${expected}"`;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
},
|
|
108
|
+
timeout
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var ElementAssertions = class _ElementAssertions {
|
|
113
|
+
element;
|
|
114
|
+
_not = false;
|
|
115
|
+
constructor(element) {
|
|
116
|
+
this.element = element;
|
|
117
|
+
}
|
|
118
|
+
/** Negate the assertion */
|
|
119
|
+
get not() {
|
|
120
|
+
const negated = new _ElementAssertions(this.element);
|
|
121
|
+
negated._not = !this._not;
|
|
122
|
+
return negated;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Assert the element is visible on the page.
|
|
126
|
+
*
|
|
127
|
+
* ```ts
|
|
128
|
+
* await expect(page.get('Welcome')).toBeVisible();
|
|
129
|
+
* await expect(page.get('Error')).not.toBeVisible();
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
async toBeVisible(options) {
|
|
133
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
134
|
+
const isNot = this._not;
|
|
135
|
+
await retry(
|
|
136
|
+
`element to ${isNot ? "not " : ""}be visible`,
|
|
137
|
+
async () => {
|
|
138
|
+
const visible = await this.element.isVisible();
|
|
139
|
+
return isNot ? !visible : visible;
|
|
140
|
+
},
|
|
141
|
+
(pass) => {
|
|
142
|
+
if (!pass) {
|
|
143
|
+
return `Expected element ${isNot ? "not " : ""}to be visible`;
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
},
|
|
147
|
+
timeout
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Assert the element is hidden (not visible).
|
|
152
|
+
*
|
|
153
|
+
* ```ts
|
|
154
|
+
* await expect(page.get('Loading')).toBeHidden();
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
async toBeHidden(options) {
|
|
158
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
159
|
+
const isNot = this._not;
|
|
160
|
+
await retry(
|
|
161
|
+
`element to ${isNot ? "not " : ""}be hidden`,
|
|
162
|
+
async () => {
|
|
163
|
+
const visible = await this.element.isVisible();
|
|
164
|
+
const hidden = !visible;
|
|
165
|
+
return isNot ? !hidden : hidden;
|
|
166
|
+
},
|
|
167
|
+
(pass) => {
|
|
168
|
+
if (!pass) {
|
|
169
|
+
return `Expected element ${isNot ? "not " : ""}to be hidden`;
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
},
|
|
173
|
+
timeout
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Assert the element's text content matches exactly.
|
|
178
|
+
*
|
|
179
|
+
* ```ts
|
|
180
|
+
* await expect(page.get('h1')).toHaveText('Welcome');
|
|
181
|
+
* await expect(page.get('.count')).toHaveText(/\d+ items/);
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
async toHaveText(expected, options) {
|
|
185
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
186
|
+
const isNot = this._not;
|
|
187
|
+
let lastActual = "";
|
|
188
|
+
await retry(
|
|
189
|
+
`element to ${isNot ? "not " : ""}have text "${expected}"`,
|
|
190
|
+
async () => {
|
|
191
|
+
const actual = (await this.element.textContent()).trim();
|
|
192
|
+
lastActual = actual;
|
|
193
|
+
const matches = typeof expected === "string" ? actual === expected : expected.test(actual);
|
|
194
|
+
return isNot ? !matches : matches;
|
|
195
|
+
},
|
|
196
|
+
(pass) => {
|
|
197
|
+
if (!pass) {
|
|
198
|
+
return `Expected element ${isNot ? "not " : ""}to have text "${expected}", but got "${lastActual}"`;
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
},
|
|
202
|
+
timeout
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Assert the element's text content contains the expected substring.
|
|
207
|
+
*
|
|
208
|
+
* ```ts
|
|
209
|
+
* await expect(page.get('.message')).toContainText('success');
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
async toContainText(expected, options) {
|
|
213
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
214
|
+
const isNot = this._not;
|
|
215
|
+
let lastActual = "";
|
|
216
|
+
await retry(
|
|
217
|
+
`element to ${isNot ? "not " : ""}contain text "${expected}"`,
|
|
218
|
+
async () => {
|
|
219
|
+
const actual = (await this.element.textContent()).trim();
|
|
220
|
+
lastActual = actual;
|
|
221
|
+
const matches = typeof expected === "string" ? actual.includes(expected) : expected.test(actual);
|
|
222
|
+
return isNot ? !matches : matches;
|
|
223
|
+
},
|
|
224
|
+
(pass) => {
|
|
225
|
+
if (!pass) {
|
|
226
|
+
return `Expected element ${isNot ? "not " : ""}to contain text "${expected}", but got "${lastActual}"`;
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
},
|
|
230
|
+
timeout
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Assert the number of matching elements.
|
|
235
|
+
*
|
|
236
|
+
* ```ts
|
|
237
|
+
* await expect(page.get('li')).toHaveCount(5);
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
async toHaveCount(expected, options) {
|
|
241
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
242
|
+
const isNot = this._not;
|
|
243
|
+
let lastCount = 0;
|
|
244
|
+
await retry(
|
|
245
|
+
`element count to ${isNot ? "not " : ""}be ${expected}`,
|
|
246
|
+
async () => {
|
|
247
|
+
const count = await this.element.count();
|
|
248
|
+
lastCount = count;
|
|
249
|
+
const matches = count === expected;
|
|
250
|
+
return isNot ? !matches : matches;
|
|
251
|
+
},
|
|
252
|
+
(pass) => {
|
|
253
|
+
if (!pass) {
|
|
254
|
+
return `Expected ${isNot ? "not " : ""}${expected} elements, but found ${lastCount}`;
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
},
|
|
258
|
+
timeout
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Assert the element has a specific attribute value.
|
|
263
|
+
*
|
|
264
|
+
* ```ts
|
|
265
|
+
* await expect(page.get('input')).toHaveAttribute('type', 'email');
|
|
266
|
+
* await expect(page.get('a')).toHaveAttribute('href', /login/);
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
async toHaveAttribute(name, expected, options) {
|
|
270
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
271
|
+
const isNot = this._not;
|
|
272
|
+
let lastValue = null;
|
|
273
|
+
await retry(
|
|
274
|
+
`element to ${isNot ? "not " : ""}have attribute "${name}"${expected !== void 0 ? `="${expected}"` : ""}`,
|
|
275
|
+
async () => {
|
|
276
|
+
const value = await this.element.getAttribute(name);
|
|
277
|
+
lastValue = value;
|
|
278
|
+
if (expected === void 0) {
|
|
279
|
+
const has = value !== null;
|
|
280
|
+
return isNot ? !has : has;
|
|
281
|
+
}
|
|
282
|
+
if (value === null) {
|
|
283
|
+
return isNot;
|
|
284
|
+
}
|
|
285
|
+
const matches = typeof expected === "string" ? value === expected : expected.test(value);
|
|
286
|
+
return isNot ? !matches : matches;
|
|
287
|
+
},
|
|
288
|
+
(pass) => {
|
|
289
|
+
if (!pass) {
|
|
290
|
+
if (expected === void 0) {
|
|
291
|
+
return `Expected element ${isNot ? "not " : ""}to have attribute "${name}"`;
|
|
292
|
+
}
|
|
293
|
+
return `Expected attribute "${name}" ${isNot ? "not " : ""}to be "${expected}", but got "${lastValue}"`;
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
296
|
+
},
|
|
297
|
+
timeout
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Assert an input/textarea/select has a specific value.
|
|
302
|
+
*
|
|
303
|
+
* ```ts
|
|
304
|
+
* await expect(page.get('Email')).toHaveValue('user@test.com');
|
|
305
|
+
* ```
|
|
306
|
+
*/
|
|
307
|
+
async toHaveValue(expected, options) {
|
|
308
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
309
|
+
const isNot = this._not;
|
|
310
|
+
let lastValue = "";
|
|
311
|
+
await retry(
|
|
312
|
+
`element to ${isNot ? "not " : ""}have value "${expected}"`,
|
|
313
|
+
async () => {
|
|
314
|
+
const located = await this.element.locate(timeout);
|
|
315
|
+
const ref = located.node.sharedId ? { sharedId: located.node.sharedId, handle: located.node.handle } : null;
|
|
316
|
+
if (!ref) return false;
|
|
317
|
+
const result = await this.element.page.session.script.callFunction({
|
|
318
|
+
functionDeclaration: 'function(el) { return el.value ?? ""; }',
|
|
319
|
+
target: { context: this.element.page.contextId },
|
|
320
|
+
arguments: [ref],
|
|
321
|
+
awaitPromise: false
|
|
322
|
+
});
|
|
323
|
+
const value = result.type === "success" && result.result?.type === "string" ? result.result.value : "";
|
|
324
|
+
lastValue = value;
|
|
325
|
+
const matches = typeof expected === "string" ? value === expected : expected.test(value);
|
|
326
|
+
return isNot ? !matches : matches;
|
|
327
|
+
},
|
|
328
|
+
(pass) => {
|
|
329
|
+
if (!pass) {
|
|
330
|
+
return `Expected value ${isNot ? "not " : ""}to be "${expected}", but got "${lastValue}"`;
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
},
|
|
334
|
+
timeout
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Assert a checkbox/radio is checked.
|
|
339
|
+
*
|
|
340
|
+
* ```ts
|
|
341
|
+
* await expect(page.get('Terms')).toBeChecked();
|
|
342
|
+
* await expect(page.get('Newsletter')).not.toBeChecked();
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
async toBeChecked(options) {
|
|
346
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
347
|
+
const isNot = this._not;
|
|
348
|
+
await retry(
|
|
349
|
+
`element to ${isNot ? "not " : ""}be checked`,
|
|
350
|
+
async () => {
|
|
351
|
+
const located = await this.element.locate(timeout);
|
|
352
|
+
const ref = located.node.sharedId ? { sharedId: located.node.sharedId, handle: located.node.handle } : null;
|
|
353
|
+
if (!ref) return false;
|
|
354
|
+
const result = await this.element.page.session.script.callFunction({
|
|
355
|
+
functionDeclaration: "function(el) { return !!el.checked; }",
|
|
356
|
+
target: { context: this.element.page.contextId },
|
|
357
|
+
arguments: [ref],
|
|
358
|
+
awaitPromise: false
|
|
359
|
+
});
|
|
360
|
+
const checked = result.type === "success" && result.result?.type === "boolean" && result.result.value === true;
|
|
361
|
+
return isNot ? !checked : checked;
|
|
362
|
+
},
|
|
363
|
+
(pass) => {
|
|
364
|
+
if (!pass) {
|
|
365
|
+
return `Expected element ${isNot ? "not " : ""}to be checked`;
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
},
|
|
369
|
+
timeout
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Assert an element is enabled (not disabled).
|
|
374
|
+
*
|
|
375
|
+
* ```ts
|
|
376
|
+
* await expect(page.get('Submit')).toBeEnabled();
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
async toBeEnabled(options) {
|
|
380
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
381
|
+
const isNot = this._not;
|
|
382
|
+
await retry(
|
|
383
|
+
`element to ${isNot ? "not " : ""}be enabled`,
|
|
384
|
+
async () => {
|
|
385
|
+
const located = await this.element.locate(timeout);
|
|
386
|
+
const ref = located.node.sharedId ? { sharedId: located.node.sharedId, handle: located.node.handle } : null;
|
|
387
|
+
if (!ref) return false;
|
|
388
|
+
const result = await this.element.page.session.script.callFunction({
|
|
389
|
+
functionDeclaration: "function(el) { return !el.disabled; }",
|
|
390
|
+
target: { context: this.element.page.contextId },
|
|
391
|
+
arguments: [ref],
|
|
392
|
+
awaitPromise: false
|
|
393
|
+
});
|
|
394
|
+
const enabled = result.type === "success" && result.result?.type === "boolean" && result.result.value === true;
|
|
395
|
+
return isNot ? !enabled : enabled;
|
|
396
|
+
},
|
|
397
|
+
(pass) => {
|
|
398
|
+
if (!pass) {
|
|
399
|
+
return `Expected element ${isNot ? "not " : ""}to be enabled`;
|
|
400
|
+
}
|
|
401
|
+
return null;
|
|
402
|
+
},
|
|
403
|
+
timeout
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Assert an element is disabled.
|
|
408
|
+
*
|
|
409
|
+
* ```ts
|
|
410
|
+
* await expect(page.get('Submit')).toBeDisabled();
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
async toBeDisabled(options) {
|
|
414
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
415
|
+
const isNot = this._not;
|
|
416
|
+
await retry(
|
|
417
|
+
`element to ${isNot ? "not " : ""}be disabled`,
|
|
418
|
+
async () => {
|
|
419
|
+
const located = await this.element.locate(timeout);
|
|
420
|
+
const ref = located.node.sharedId ? { sharedId: located.node.sharedId, handle: located.node.handle } : null;
|
|
421
|
+
if (!ref) return false;
|
|
422
|
+
const result = await this.element.page.session.script.callFunction({
|
|
423
|
+
functionDeclaration: "function(el) { return !!el.disabled; }",
|
|
424
|
+
target: { context: this.element.page.contextId },
|
|
425
|
+
arguments: [ref],
|
|
426
|
+
awaitPromise: false
|
|
427
|
+
});
|
|
428
|
+
const disabled = result.type === "success" && result.result?.type === "boolean" && result.result.value === true;
|
|
429
|
+
return isNot ? !disabled : disabled;
|
|
430
|
+
},
|
|
431
|
+
(pass) => {
|
|
432
|
+
if (!pass) {
|
|
433
|
+
return `Expected element ${isNot ? "not " : ""}to be disabled`;
|
|
434
|
+
}
|
|
435
|
+
return null;
|
|
436
|
+
},
|
|
437
|
+
timeout
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Assert an element has a specific CSS class.
|
|
442
|
+
*
|
|
443
|
+
* ```ts
|
|
444
|
+
* await expect(page.get('.alert')).toHaveClass('alert-success');
|
|
445
|
+
* ```
|
|
446
|
+
*/
|
|
447
|
+
async toHaveClass(expected, options) {
|
|
448
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
449
|
+
const isNot = this._not;
|
|
450
|
+
let lastClasses = "";
|
|
451
|
+
await retry(
|
|
452
|
+
`element to ${isNot ? "not " : ""}have class "${expected}"`,
|
|
453
|
+
async () => {
|
|
454
|
+
const classes = await this.element.getAttribute("class") ?? "";
|
|
455
|
+
lastClasses = classes;
|
|
456
|
+
const matches = typeof expected === "string" ? classes.split(/\s+/).includes(expected) : expected.test(classes);
|
|
457
|
+
return isNot ? !matches : matches;
|
|
458
|
+
},
|
|
459
|
+
(pass) => {
|
|
460
|
+
if (!pass) {
|
|
461
|
+
return `Expected element ${isNot ? "not " : ""}to have class "${expected}", but got "${lastClasses}"`;
|
|
462
|
+
}
|
|
463
|
+
return null;
|
|
464
|
+
},
|
|
465
|
+
timeout
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Assert an element has a specific CSS property value.
|
|
470
|
+
*
|
|
471
|
+
* ```ts
|
|
472
|
+
* await expect(page.get('.banner')).toHaveCSS('color', 'rgb(255, 0, 0)');
|
|
473
|
+
* await expect(page.get('.box')).toHaveCSS('display', 'flex');
|
|
474
|
+
* ```
|
|
475
|
+
*/
|
|
476
|
+
async toHaveCSS(property, expected, options) {
|
|
477
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
478
|
+
const isNot = this._not;
|
|
479
|
+
let lastValue = "";
|
|
480
|
+
await retry(
|
|
481
|
+
`element to ${isNot ? "not " : ""}have CSS "${property}: ${expected}"`,
|
|
482
|
+
async () => {
|
|
483
|
+
const located = await this.element.locate(timeout);
|
|
484
|
+
const ref = located.node.sharedId ? { sharedId: located.node.sharedId, handle: located.node.handle } : null;
|
|
485
|
+
if (!ref) return false;
|
|
486
|
+
const result = await this.element.page.session.script.callFunction({
|
|
487
|
+
functionDeclaration: `function(el, prop) { return window.getComputedStyle(el).getPropertyValue(prop); }`,
|
|
488
|
+
target: { context: this.element.page.contextId },
|
|
489
|
+
arguments: [ref, { type: "string", value: property }],
|
|
490
|
+
awaitPromise: false
|
|
491
|
+
});
|
|
492
|
+
const value = result.type === "success" && result.result?.type === "string" ? result.result.value : "";
|
|
493
|
+
lastValue = value;
|
|
494
|
+
const matches = typeof expected === "string" ? value === expected : expected.test(value);
|
|
495
|
+
return isNot ? !matches : matches;
|
|
496
|
+
},
|
|
497
|
+
(pass) => {
|
|
498
|
+
if (!pass) {
|
|
499
|
+
return `Expected CSS "${property}" ${isNot ? "not " : ""}to be "${expected}", but got "${lastValue}"`;
|
|
500
|
+
}
|
|
501
|
+
return null;
|
|
502
|
+
},
|
|
503
|
+
timeout
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Assert an element has a specific id attribute.
|
|
508
|
+
*
|
|
509
|
+
* ```ts
|
|
510
|
+
* await expect(page.get('Submit')).toHaveId('submit-btn');
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
async toHaveId(expected, options) {
|
|
514
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
515
|
+
const isNot = this._not;
|
|
516
|
+
let lastId = null;
|
|
517
|
+
await retry(
|
|
518
|
+
`element to ${isNot ? "not " : ""}have id "${expected}"`,
|
|
519
|
+
async () => {
|
|
520
|
+
const id = await this.element.getAttribute("id");
|
|
521
|
+
lastId = id;
|
|
522
|
+
const matches = id === expected;
|
|
523
|
+
return isNot ? !matches : matches;
|
|
524
|
+
},
|
|
525
|
+
(pass) => {
|
|
526
|
+
if (!pass) {
|
|
527
|
+
return `Expected element ${isNot ? "not " : ""}to have id "${expected}", but got "${lastId}"`;
|
|
528
|
+
}
|
|
529
|
+
return null;
|
|
530
|
+
},
|
|
531
|
+
timeout
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Assert an element has a specific placeholder text.
|
|
536
|
+
*
|
|
537
|
+
* ```ts
|
|
538
|
+
* await expect(page.get({ selector: '#email' })).toHavePlaceholder('Enter your email');
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
541
|
+
async toHavePlaceholder(expected, options) {
|
|
542
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
543
|
+
const isNot = this._not;
|
|
544
|
+
let lastPlaceholder = null;
|
|
545
|
+
await retry(
|
|
546
|
+
`element to ${isNot ? "not " : ""}have placeholder "${expected}"`,
|
|
547
|
+
async () => {
|
|
548
|
+
const placeholder = await this.element.getAttribute("placeholder");
|
|
549
|
+
lastPlaceholder = placeholder;
|
|
550
|
+
if (placeholder === null) return isNot;
|
|
551
|
+
const matches = typeof expected === "string" ? placeholder === expected : expected.test(placeholder);
|
|
552
|
+
return isNot ? !matches : matches;
|
|
553
|
+
},
|
|
554
|
+
(pass) => {
|
|
555
|
+
if (!pass) {
|
|
556
|
+
return `Expected placeholder ${isNot ? "not " : ""}to be "${expected}", but got "${lastPlaceholder}"`;
|
|
557
|
+
}
|
|
558
|
+
return null;
|
|
559
|
+
},
|
|
560
|
+
timeout
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Assert an element has a specific ARIA role.
|
|
565
|
+
*
|
|
566
|
+
* ```ts
|
|
567
|
+
* await expect(page.get('Submit')).toHaveRole('button');
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
async toHaveRole(expected, options) {
|
|
571
|
+
const timeout = options?.timeout ?? DEFAULT_ASSERTION_TIMEOUT;
|
|
572
|
+
const isNot = this._not;
|
|
573
|
+
let lastRole = "";
|
|
574
|
+
await retry(
|
|
575
|
+
`element to ${isNot ? "not " : ""}have role "${expected}"`,
|
|
576
|
+
async () => {
|
|
577
|
+
const located = await this.element.locate(timeout);
|
|
578
|
+
const ref = located.node.sharedId ? { sharedId: located.node.sharedId, handle: located.node.handle } : null;
|
|
579
|
+
if (!ref) return false;
|
|
580
|
+
const result = await this.element.page.session.script.callFunction({
|
|
581
|
+
functionDeclaration: `function(el) {
|
|
582
|
+
return el.getAttribute('role') || el.computedRole || '';
|
|
583
|
+
}`,
|
|
584
|
+
target: { context: this.element.page.contextId },
|
|
585
|
+
arguments: [ref],
|
|
586
|
+
awaitPromise: false
|
|
587
|
+
});
|
|
588
|
+
const role = result.type === "success" && result.result?.type === "string" ? result.result.value : "";
|
|
589
|
+
lastRole = role;
|
|
590
|
+
const matches = role === expected;
|
|
591
|
+
return isNot ? !matches : matches;
|
|
592
|
+
},
|
|
593
|
+
(pass) => {
|
|
594
|
+
if (!pass) {
|
|
595
|
+
return `Expected element ${isNot ? "not " : ""}to have role "${expected}", but got "${lastRole}"`;
|
|
596
|
+
}
|
|
597
|
+
return null;
|
|
598
|
+
},
|
|
599
|
+
timeout
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
async function retry(description, check, formatError, timeout) {
|
|
604
|
+
const interval = 100;
|
|
605
|
+
const startTime = Date.now();
|
|
606
|
+
let lastResult = false;
|
|
607
|
+
while (Date.now() - startTime < timeout) {
|
|
608
|
+
try {
|
|
609
|
+
lastResult = await check();
|
|
610
|
+
if (lastResult) return;
|
|
611
|
+
} catch {
|
|
612
|
+
lastResult = false;
|
|
613
|
+
}
|
|
614
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
615
|
+
}
|
|
616
|
+
try {
|
|
617
|
+
lastResult = await check();
|
|
618
|
+
if (lastResult) return;
|
|
619
|
+
} catch {
|
|
620
|
+
lastResult = false;
|
|
621
|
+
}
|
|
622
|
+
const errorMsg = formatError(lastResult) ?? `Assertion failed: ${description}`;
|
|
623
|
+
throw new AssertionError(errorMsg);
|
|
624
|
+
}
|
|
625
|
+
var AssertionError = class extends Error {
|
|
626
|
+
name = "AssertionError";
|
|
627
|
+
constructor(message) {
|
|
628
|
+
super(message);
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
Object.defineProperty(exports, "Browser", {
|
|
633
|
+
enumerable: true,
|
|
634
|
+
get: function () { return chunkKIPQFK3Y_cjs.Browser; }
|
|
635
|
+
});
|
|
636
|
+
Object.defineProperty(exports, "BrowserContext", {
|
|
637
|
+
enumerable: true,
|
|
638
|
+
get: function () { return chunkKIPQFK3Y_cjs.BrowserContext; }
|
|
639
|
+
});
|
|
640
|
+
Object.defineProperty(exports, "ElementHandle", {
|
|
641
|
+
enumerable: true,
|
|
642
|
+
get: function () { return chunkKIPQFK3Y_cjs.ElementHandle; }
|
|
643
|
+
});
|
|
644
|
+
Object.defineProperty(exports, "Page", {
|
|
645
|
+
enumerable: true,
|
|
646
|
+
get: function () { return chunkKIPQFK3Y_cjs.Page; }
|
|
647
|
+
});
|
|
648
|
+
Object.defineProperty(exports, "afterAll", {
|
|
649
|
+
enumerable: true,
|
|
650
|
+
get: function () { return chunkKIPQFK3Y_cjs.afterAll; }
|
|
651
|
+
});
|
|
652
|
+
Object.defineProperty(exports, "afterEach", {
|
|
653
|
+
enumerable: true,
|
|
654
|
+
get: function () { return chunkKIPQFK3Y_cjs.afterEach; }
|
|
655
|
+
});
|
|
656
|
+
Object.defineProperty(exports, "beforeAll", {
|
|
657
|
+
enumerable: true,
|
|
658
|
+
get: function () { return chunkKIPQFK3Y_cjs.beforeAll; }
|
|
659
|
+
});
|
|
660
|
+
Object.defineProperty(exports, "beforeEach", {
|
|
661
|
+
enumerable: true,
|
|
662
|
+
get: function () { return chunkKIPQFK3Y_cjs.beforeEach; }
|
|
663
|
+
});
|
|
664
|
+
Object.defineProperty(exports, "defineConfig", {
|
|
665
|
+
enumerable: true,
|
|
666
|
+
get: function () { return chunkKIPQFK3Y_cjs.defineConfig; }
|
|
667
|
+
});
|
|
668
|
+
Object.defineProperty(exports, "describe", {
|
|
669
|
+
enumerable: true,
|
|
670
|
+
get: function () { return chunkKIPQFK3Y_cjs.describe; }
|
|
671
|
+
});
|
|
672
|
+
Object.defineProperty(exports, "resetTestState", {
|
|
673
|
+
enumerable: true,
|
|
674
|
+
get: function () { return chunkKIPQFK3Y_cjs.resetTestState; }
|
|
675
|
+
});
|
|
676
|
+
Object.defineProperty(exports, "resolveConfig", {
|
|
677
|
+
enumerable: true,
|
|
678
|
+
get: function () { return chunkKIPQFK3Y_cjs.resolveConfig; }
|
|
679
|
+
});
|
|
680
|
+
Object.defineProperty(exports, "runAfterAllHooks", {
|
|
681
|
+
enumerable: true,
|
|
682
|
+
get: function () { return chunkKIPQFK3Y_cjs.runAfterAllHooks; }
|
|
683
|
+
});
|
|
684
|
+
Object.defineProperty(exports, "runTest", {
|
|
685
|
+
enumerable: true,
|
|
686
|
+
get: function () { return chunkKIPQFK3Y_cjs.runTest; }
|
|
687
|
+
});
|
|
688
|
+
Object.defineProperty(exports, "test", {
|
|
689
|
+
enumerable: true,
|
|
690
|
+
get: function () { return chunkKIPQFK3Y_cjs.test; }
|
|
691
|
+
});
|
|
692
|
+
Object.defineProperty(exports, "testRegistry", {
|
|
693
|
+
enumerable: true,
|
|
694
|
+
get: function () { return chunkKIPQFK3Y_cjs.testRegistry; }
|
|
695
|
+
});
|
|
696
|
+
Object.defineProperty(exports, "After", {
|
|
697
|
+
enumerable: true,
|
|
698
|
+
get: function () { return browsecraftBdd.After; }
|
|
699
|
+
});
|
|
700
|
+
Object.defineProperty(exports, "AfterFeature", {
|
|
701
|
+
enumerable: true,
|
|
702
|
+
get: function () { return browsecraftBdd.AfterFeature; }
|
|
703
|
+
});
|
|
704
|
+
Object.defineProperty(exports, "AfterStep", {
|
|
705
|
+
enumerable: true,
|
|
706
|
+
get: function () { return browsecraftBdd.AfterStep; }
|
|
707
|
+
});
|
|
708
|
+
Object.defineProperty(exports, "BddAfterAll", {
|
|
709
|
+
enumerable: true,
|
|
710
|
+
get: function () { return browsecraftBdd.AfterAll; }
|
|
711
|
+
});
|
|
712
|
+
Object.defineProperty(exports, "BddBeforeAll", {
|
|
713
|
+
enumerable: true,
|
|
714
|
+
get: function () { return browsecraftBdd.BeforeAll; }
|
|
715
|
+
});
|
|
716
|
+
Object.defineProperty(exports, "BddExecutor", {
|
|
717
|
+
enumerable: true,
|
|
718
|
+
get: function () { return browsecraftBdd.BddExecutor; }
|
|
719
|
+
});
|
|
720
|
+
Object.defineProperty(exports, "Before", {
|
|
721
|
+
enumerable: true,
|
|
722
|
+
get: function () { return browsecraftBdd.Before; }
|
|
723
|
+
});
|
|
724
|
+
Object.defineProperty(exports, "BeforeFeature", {
|
|
725
|
+
enumerable: true,
|
|
726
|
+
get: function () { return browsecraftBdd.BeforeFeature; }
|
|
727
|
+
});
|
|
728
|
+
Object.defineProperty(exports, "BeforeStep", {
|
|
729
|
+
enumerable: true,
|
|
730
|
+
get: function () { return browsecraftBdd.BeforeStep; }
|
|
731
|
+
});
|
|
732
|
+
Object.defineProperty(exports, "BrowsecraftDataTable", {
|
|
733
|
+
enumerable: true,
|
|
734
|
+
get: function () { return browsecraftBdd.BrowsecraftDataTable; }
|
|
735
|
+
});
|
|
736
|
+
Object.defineProperty(exports, "Given", {
|
|
737
|
+
enumerable: true,
|
|
738
|
+
get: function () { return browsecraftBdd.Given; }
|
|
739
|
+
});
|
|
740
|
+
Object.defineProperty(exports, "Step", {
|
|
741
|
+
enumerable: true,
|
|
742
|
+
get: function () { return browsecraftBdd.Step; }
|
|
743
|
+
});
|
|
744
|
+
Object.defineProperty(exports, "Then", {
|
|
745
|
+
enumerable: true,
|
|
746
|
+
get: function () { return browsecraftBdd.Then; }
|
|
747
|
+
});
|
|
748
|
+
Object.defineProperty(exports, "When", {
|
|
749
|
+
enumerable: true,
|
|
750
|
+
get: function () { return browsecraftBdd.When; }
|
|
751
|
+
});
|
|
752
|
+
Object.defineProperty(exports, "and", {
|
|
753
|
+
enumerable: true,
|
|
754
|
+
get: function () { return browsecraftBdd.and; }
|
|
755
|
+
});
|
|
756
|
+
Object.defineProperty(exports, "autoGenerateSteps", {
|
|
757
|
+
enumerable: true,
|
|
758
|
+
get: function () { return browsecraftBdd.autoGenerateSteps; }
|
|
759
|
+
});
|
|
760
|
+
Object.defineProperty(exports, "autoGenerateStepsFromDocument", {
|
|
761
|
+
enumerable: true,
|
|
762
|
+
get: function () { return browsecraftBdd.autoGenerateStepsFromDocument; }
|
|
763
|
+
});
|
|
764
|
+
Object.defineProperty(exports, "but", {
|
|
765
|
+
enumerable: true,
|
|
766
|
+
get: function () { return browsecraftBdd.but; }
|
|
767
|
+
});
|
|
768
|
+
Object.defineProperty(exports, "clearFeatures", {
|
|
769
|
+
enumerable: true,
|
|
770
|
+
get: function () { return browsecraftBdd.clearFeatures; }
|
|
771
|
+
});
|
|
772
|
+
Object.defineProperty(exports, "createExecutor", {
|
|
773
|
+
enumerable: true,
|
|
774
|
+
get: function () { return browsecraftBdd.createExecutor; }
|
|
775
|
+
});
|
|
776
|
+
Object.defineProperty(exports, "defineParameterType", {
|
|
777
|
+
enumerable: true,
|
|
778
|
+
get: function () { return browsecraftBdd.defineParameterType; }
|
|
779
|
+
});
|
|
780
|
+
Object.defineProperty(exports, "feature", {
|
|
781
|
+
enumerable: true,
|
|
782
|
+
get: function () { return browsecraftBdd.feature; }
|
|
783
|
+
});
|
|
784
|
+
Object.defineProperty(exports, "getSupportedLanguages", {
|
|
785
|
+
enumerable: true,
|
|
786
|
+
get: function () { return browsecraftBdd.getSupportedLanguages; }
|
|
787
|
+
});
|
|
788
|
+
Object.defineProperty(exports, "given", {
|
|
789
|
+
enumerable: true,
|
|
790
|
+
get: function () { return browsecraftBdd.given; }
|
|
791
|
+
});
|
|
792
|
+
Object.defineProperty(exports, "globalRegistry", {
|
|
793
|
+
enumerable: true,
|
|
794
|
+
get: function () { return browsecraftBdd.globalRegistry; }
|
|
795
|
+
});
|
|
796
|
+
Object.defineProperty(exports, "matchesTags", {
|
|
797
|
+
enumerable: true,
|
|
798
|
+
get: function () { return browsecraftBdd.matchesTags; }
|
|
799
|
+
});
|
|
800
|
+
Object.defineProperty(exports, "parseGherkin", {
|
|
801
|
+
enumerable: true,
|
|
802
|
+
get: function () { return browsecraftBdd.parseGherkin; }
|
|
803
|
+
});
|
|
804
|
+
Object.defineProperty(exports, "parseTagExpression", {
|
|
805
|
+
enumerable: true,
|
|
806
|
+
get: function () { return browsecraftBdd.parseTagExpression; }
|
|
807
|
+
});
|
|
808
|
+
Object.defineProperty(exports, "pending", {
|
|
809
|
+
enumerable: true,
|
|
810
|
+
get: function () { return browsecraftBdd.pending; }
|
|
811
|
+
});
|
|
812
|
+
Object.defineProperty(exports, "runFeatures", {
|
|
813
|
+
enumerable: true,
|
|
814
|
+
get: function () { return browsecraftBdd.runFeatures; }
|
|
815
|
+
});
|
|
816
|
+
Object.defineProperty(exports, "scenario", {
|
|
817
|
+
enumerable: true,
|
|
818
|
+
get: function () { return browsecraftBdd.scenario; }
|
|
819
|
+
});
|
|
820
|
+
Object.defineProperty(exports, "then", {
|
|
821
|
+
enumerable: true,
|
|
822
|
+
get: function () { return browsecraftBdd.then; }
|
|
823
|
+
});
|
|
824
|
+
Object.defineProperty(exports, "when", {
|
|
825
|
+
enumerable: true,
|
|
826
|
+
get: function () { return browsecraftBdd.when; }
|
|
827
|
+
});
|
|
828
|
+
exports.AssertionError = AssertionError;
|
|
829
|
+
exports.expect = expect;
|
|
830
|
+
//# sourceMappingURL=index.cjs.map
|
|
831
|
+
//# sourceMappingURL=index.cjs.map
|