ex-pw 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1663 @@
1
+ // src/index.ts
2
+ import { expect as playwrightExpect } from "@playwright/test";
3
+
4
+ // src/matchers/locator/toBeClickable.ts
5
+ async function toBeClickable(locator, options = {}) {
6
+ const assertionName = "toBeClickable";
7
+ const timeout = options.timeout ?? this.timeout;
8
+ let pass = false;
9
+ let errorMessage = "";
10
+ try {
11
+ await locator.click({ trial: true, timeout });
12
+ pass = true;
13
+ } catch (error) {
14
+ pass = false;
15
+ errorMessage = error instanceof Error ? error.message : String(error);
16
+ }
17
+ const message = () => {
18
+ if (this.isNot) {
19
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
20
+ isNot: this.isNot
21
+ }) + `
22
+
23
+ Expected: element to NOT be clickable
24
+ Received: element is clickable`;
25
+ }
26
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
27
+ isNot: this.isNot
28
+ }) + `
29
+
30
+ Expected: element '${locator}' to be clickable
31
+ Received: element is not clickable
32
+ ` + (errorMessage ? `
33
+ Error: ${errorMessage}` : "");
34
+ };
35
+ return {
36
+ pass,
37
+ message,
38
+ name: assertionName
39
+ };
40
+ }
41
+
42
+ // src/matchers/locator/toBeCheckable.ts
43
+ async function toBeCheckable(locator, options = {}) {
44
+ const assertionName = "toBeCheckable";
45
+ const timeout = options.timeout ?? this.timeout;
46
+ let pass = false;
47
+ let errorMessage = "";
48
+ try {
49
+ await locator.check({ trial: true, timeout });
50
+ pass = true;
51
+ } catch (error) {
52
+ pass = false;
53
+ errorMessage = error instanceof Error ? error.message : String(error);
54
+ }
55
+ const message = () => {
56
+ if (this.isNot) {
57
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
58
+ isNot: this.isNot
59
+ }) + `
60
+
61
+ Expected: element '${locator}' to NOT be checkable
62
+ Received: element is checkable`;
63
+ }
64
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
65
+ isNot: this.isNot
66
+ }) + `
67
+
68
+ Expected: element '${locator}' to be checkable
69
+ Received: element is not checkable
70
+ ` + (errorMessage ? `
71
+ Error: ${errorMessage}` : "");
72
+ };
73
+ return {
74
+ pass,
75
+ message,
76
+ name: assertionName
77
+ };
78
+ }
79
+
80
+ // src/matchers/locator/toBeRequired.ts
81
+ import { expect } from "@playwright/test";
82
+ async function toBeRequired(locator, options = {}) {
83
+ const assertionName = "toBeRequired";
84
+ const timeout = options.timeout ?? this.timeout;
85
+ const intervals = options.intervals;
86
+ let pass = false;
87
+ let errorMessage = "";
88
+ try {
89
+ await expect.poll(
90
+ async () => {
91
+ const hasRequiredAttr = await locator.getAttribute("required");
92
+ if (hasRequiredAttr !== null) return true;
93
+ const ariaRequired = await locator.getAttribute("aria-required");
94
+ if (ariaRequired === "true") return true;
95
+ const isRequired = await locator.evaluate((el) => {
96
+ if ("required" in el) {
97
+ return el.required;
98
+ }
99
+ return false;
100
+ });
101
+ return isRequired;
102
+ },
103
+ { timeout, intervals }
104
+ ).toBe(true);
105
+ pass = true;
106
+ } catch (error) {
107
+ pass = false;
108
+ errorMessage = error instanceof Error ? error.message : String(error);
109
+ }
110
+ const message = () => {
111
+ if (this.isNot) {
112
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
113
+ isNot: this.isNot
114
+ }) + `
115
+
116
+ Expected: element '${locator}' to NOT be required
117
+ Received: element is required`;
118
+ }
119
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
120
+ isNot: this.isNot
121
+ }) + `
122
+
123
+ Expected: element '${locator}' to be required
124
+ Received: element is not required
125
+ ` + (errorMessage ? `
126
+ Details: ${errorMessage}` : "");
127
+ };
128
+ return {
129
+ pass,
130
+ message,
131
+ name: assertionName
132
+ };
133
+ }
134
+
135
+ // src/matchers/locator/toBeInvalid.ts
136
+ import { expect as expect2 } from "@playwright/test";
137
+ var INVALID_CLASSES = [
138
+ "ng-invalid",
139
+ // Angular
140
+ "is-invalid",
141
+ // Bootstrap
142
+ "error",
143
+ // Common pattern
144
+ "has-error",
145
+ // Another common pattern
146
+ "invalid"
147
+ // Generic
148
+ ];
149
+ async function toBeInvalid(locator, options = {}) {
150
+ const assertionName = "toBeInvalid";
151
+ const timeout = options.timeout ?? this.timeout;
152
+ const intervals = options.intervals;
153
+ let pass = false;
154
+ let errorMessage = "";
155
+ try {
156
+ await expect2.poll(
157
+ async () => {
158
+ const ariaInvalid = await locator.getAttribute("aria-invalid");
159
+ if (ariaInvalid === "true") return true;
160
+ const isNativeInvalid = await locator.evaluate((el) => {
161
+ if ("validity" in el) {
162
+ return !el.validity.valid;
163
+ }
164
+ return false;
165
+ });
166
+ if (isNativeInvalid) return true;
167
+ const classAttr = await locator.getAttribute("class");
168
+ if (classAttr) {
169
+ const classes = classAttr.split(/\s+/);
170
+ for (const invalidClass of INVALID_CLASSES) {
171
+ if (classes.includes(invalidClass)) return true;
172
+ }
173
+ }
174
+ return false;
175
+ },
176
+ { timeout, intervals }
177
+ ).toBe(true);
178
+ pass = true;
179
+ } catch (error) {
180
+ pass = false;
181
+ errorMessage = error instanceof Error ? error.message : String(error);
182
+ }
183
+ const message = () => {
184
+ if (this.isNot) {
185
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
186
+ isNot: this.isNot
187
+ }) + `
188
+
189
+ Expected: element '${locator}' to NOT be invalid
190
+ Received: element is invalid`;
191
+ }
192
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
193
+ isNot: this.isNot
194
+ }) + `
195
+
196
+ Expected: element '${locator}' to be invalid
197
+ Received: element is valid
198
+ ` + (errorMessage ? `
199
+ Details: ${errorMessage}` : "");
200
+ };
201
+ return {
202
+ pass,
203
+ message,
204
+ name: assertionName
205
+ };
206
+ }
207
+
208
+ // src/matchers/locator/toBeValid.ts
209
+ import { expect as expect3 } from "@playwright/test";
210
+ var INVALID_CLASSES2 = [
211
+ "ng-invalid",
212
+ // Angular
213
+ "is-invalid",
214
+ // Bootstrap
215
+ "error",
216
+ // Common pattern
217
+ "has-error",
218
+ // Another common pattern
219
+ "invalid"
220
+ // Generic
221
+ ];
222
+ async function toBeValid(locator, options = {}) {
223
+ const assertionName = "toBeValid";
224
+ const timeout = options.timeout ?? this.timeout;
225
+ const intervals = options.intervals;
226
+ let pass = false;
227
+ let errorMessage = "";
228
+ try {
229
+ await expect3.poll(
230
+ async () => {
231
+ const ariaInvalid = await locator.getAttribute("aria-invalid");
232
+ if (ariaInvalid === "true") return false;
233
+ const hasValidity = await locator.evaluate((el) => "validity" in el);
234
+ if (hasValidity) {
235
+ const isNativeValid = await locator.evaluate((el) => {
236
+ return el.validity.valid;
237
+ });
238
+ if (!isNativeValid) return false;
239
+ }
240
+ const classAttr = await locator.getAttribute("class");
241
+ if (classAttr) {
242
+ const classes = classAttr.split(/\s+/);
243
+ for (const invalidClass of INVALID_CLASSES2) {
244
+ if (classes.includes(invalidClass)) return false;
245
+ }
246
+ }
247
+ return true;
248
+ },
249
+ { timeout, intervals }
250
+ ).toBe(true);
251
+ pass = true;
252
+ } catch (error) {
253
+ pass = false;
254
+ errorMessage = error instanceof Error ? error.message : String(error);
255
+ }
256
+ const message = () => {
257
+ if (this.isNot) {
258
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
259
+ isNot: this.isNot
260
+ }) + `
261
+
262
+ Expected: element '${locator}' to NOT be valid
263
+ Received: element is valid`;
264
+ }
265
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
266
+ isNot: this.isNot
267
+ }) + `
268
+
269
+ Expected: element '${locator}' to be valid
270
+ Received: element is invalid
271
+ ` + (errorMessage ? `
272
+ Details: ${errorMessage}` : "");
273
+ };
274
+ return {
275
+ pass,
276
+ message,
277
+ name: assertionName
278
+ };
279
+ }
280
+
281
+ // src/matchers/locator/toHaveCount.ts
282
+ import { expect as expect4 } from "@playwright/test";
283
+ async function toHaveCountGreaterThan(locator, count, options = {}) {
284
+ const assertionName = "toHaveCountGreaterThan";
285
+ const timeout = options.timeout ?? this.timeout;
286
+ const intervals = options.intervals;
287
+ let pass = false;
288
+ let actualCount = 0;
289
+ try {
290
+ await expect4.poll(
291
+ async () => {
292
+ actualCount = await locator.count();
293
+ return actualCount;
294
+ },
295
+ { timeout, intervals }
296
+ ).toBeGreaterThan(count);
297
+ pass = true;
298
+ } catch {
299
+ pass = false;
300
+ }
301
+ const message = () => {
302
+ if (this.isNot) {
303
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
304
+ isNot: this.isNot
305
+ }) + `
306
+
307
+ Expected: element '${locator}' count to NOT be greater than ${count}
308
+ Received: ${actualCount}`;
309
+ }
310
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
311
+ isNot: this.isNot
312
+ }) + `
313
+
314
+ Expected: element '${locator}' count to be greater than ${count}
315
+ Received: ${actualCount}`;
316
+ };
317
+ return {
318
+ pass,
319
+ message,
320
+ name: assertionName,
321
+ expected: `> ${count}`,
322
+ actual: actualCount
323
+ };
324
+ }
325
+ async function toHaveCountGreaterThanOrEqual(locator, count, options = {}) {
326
+ const assertionName = "toHaveCountGreaterThanOrEqual";
327
+ const timeout = options.timeout ?? this.timeout;
328
+ const intervals = options.intervals;
329
+ let pass = false;
330
+ let actualCount = 0;
331
+ try {
332
+ await expect4.poll(
333
+ async () => {
334
+ actualCount = await locator.count();
335
+ return actualCount;
336
+ },
337
+ { timeout, intervals }
338
+ ).toBeGreaterThanOrEqual(count);
339
+ pass = true;
340
+ } catch {
341
+ pass = false;
342
+ }
343
+ const message = () => {
344
+ if (this.isNot) {
345
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
346
+ isNot: this.isNot
347
+ }) + `
348
+
349
+ Expected: element '${locator}' count to NOT be greater than or equal to ${count}
350
+ Received: ${actualCount}`;
351
+ }
352
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
353
+ isNot: this.isNot
354
+ }) + `
355
+
356
+ Expected: element '${locator}' count to be greater than or equal to ${count}
357
+ Received: ${actualCount}`;
358
+ };
359
+ return {
360
+ pass,
361
+ message,
362
+ name: assertionName,
363
+ expected: `>= ${count}`,
364
+ actual: actualCount
365
+ };
366
+ }
367
+ async function toHaveCountLessThan(locator, count, options = {}) {
368
+ const assertionName = "toHaveCountLessThan";
369
+ const timeout = options.timeout ?? this.timeout;
370
+ const intervals = options.intervals;
371
+ let pass = false;
372
+ let actualCount = 0;
373
+ try {
374
+ await expect4.poll(
375
+ async () => {
376
+ actualCount = await locator.count();
377
+ return actualCount;
378
+ },
379
+ { timeout, intervals }
380
+ ).toBeLessThan(count);
381
+ pass = true;
382
+ } catch {
383
+ pass = false;
384
+ }
385
+ const message = () => {
386
+ if (this.isNot) {
387
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
388
+ isNot: this.isNot
389
+ }) + `
390
+
391
+ Expected: element '${locator}' count to NOT be less than ${count}
392
+ Received: ${actualCount}`;
393
+ }
394
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
395
+ isNot: this.isNot
396
+ }) + `
397
+
398
+ Expected: element '${locator}' count to be less than ${count}
399
+ Received: ${actualCount}`;
400
+ };
401
+ return {
402
+ pass,
403
+ message,
404
+ name: assertionName,
405
+ expected: `< ${count}`,
406
+ actual: actualCount
407
+ };
408
+ }
409
+ async function toHaveCountLessThanOrEqual(locator, count, options = {}) {
410
+ const assertionName = "toHaveCountLessThanOrEqual";
411
+ const timeout = options.timeout ?? this.timeout;
412
+ const intervals = options.intervals;
413
+ let pass = false;
414
+ let actualCount = 0;
415
+ try {
416
+ await expect4.poll(
417
+ async () => {
418
+ actualCount = await locator.count();
419
+ return actualCount;
420
+ },
421
+ { timeout, intervals }
422
+ ).toBeLessThanOrEqual(count);
423
+ pass = true;
424
+ } catch {
425
+ pass = false;
426
+ }
427
+ const message = () => {
428
+ if (this.isNot) {
429
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
430
+ isNot: this.isNot
431
+ }) + `
432
+
433
+ Expected: element '${locator}' count to NOT be less than or equal to ${count}
434
+ Received: ${actualCount}`;
435
+ }
436
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
437
+ isNot: this.isNot
438
+ }) + `
439
+
440
+ Expected: element '${locator}' count to be less than or equal to ${count}
441
+ Received: ${actualCount}`;
442
+ };
443
+ return {
444
+ pass,
445
+ message,
446
+ name: assertionName,
447
+ expected: `<= ${count}`,
448
+ actual: actualCount
449
+ };
450
+ }
451
+
452
+ // src/matchers/locator/toHaveWidth.ts
453
+ import { expect as expect5 } from "@playwright/test";
454
+ async function toHaveWidth(locator, expected, options) {
455
+ const timeout = options?.timeout ?? this.timeout;
456
+ const intervals = options?.intervals;
457
+ try {
458
+ await expect5.poll(async () => {
459
+ const box = await locator.boundingBox();
460
+ return box ? box.width : null;
461
+ }, { timeout, intervals }).toBe(expected);
462
+ return {
463
+ message: () => `expected element '${locator}' to have width ${expected}`,
464
+ pass: true
465
+ };
466
+ } catch (e) {
467
+ return {
468
+ message: () => `expected element '${locator}' to have width ${expected}`,
469
+ pass: false
470
+ };
471
+ }
472
+ }
473
+
474
+ // src/matchers/locator/toHaveHeight.ts
475
+ import { expect as expect6 } from "@playwright/test";
476
+ async function toHaveHeight(locator, expected, options) {
477
+ const timeout = options?.timeout ?? this.timeout;
478
+ const intervals = options?.intervals;
479
+ try {
480
+ await expect6.poll(async () => {
481
+ const box = await locator.boundingBox();
482
+ return box ? box.height : null;
483
+ }, { timeout, intervals }).toBe(expected);
484
+ return {
485
+ message: () => `expected element '${locator}' to have height ${expected}`,
486
+ pass: true
487
+ };
488
+ } catch (e) {
489
+ return {
490
+ message: () => `expected element '${locator}' to have height ${expected}`,
491
+ pass: false
492
+ };
493
+ }
494
+ }
495
+
496
+ // src/matchers/locator/toHaveSize.ts
497
+ import { expect as expect7 } from "@playwright/test";
498
+ async function toHaveSize(locator, width, height, options) {
499
+ const timeout = options?.timeout ?? this.timeout;
500
+ const intervals = options?.intervals;
501
+ try {
502
+ await expect7.poll(async () => {
503
+ const box = await locator.boundingBox();
504
+ return box ? { width: box.width, height: box.height } : null;
505
+ }, { timeout, intervals }).toEqual({ width, height });
506
+ return {
507
+ message: () => `expected element '${locator}' to have size { width: ${width}, height: ${height} }`,
508
+ pass: true
509
+ };
510
+ } catch (e) {
511
+ return {
512
+ message: () => `expected element '${locator}' to have size { width: ${width}, height: ${height} }`,
513
+ pass: false
514
+ };
515
+ }
516
+ }
517
+
518
+ // src/matchers/locator/toHaveLoadedImage.ts
519
+ import { expect as expect8 } from "@playwright/test";
520
+ async function toHaveLoadedImage(locator, options) {
521
+ const timeout = options?.timeout ?? this.timeout;
522
+ const intervals = options?.intervals;
523
+ try {
524
+ await expect8.poll(async () => {
525
+ return await locator.evaluate((el) => {
526
+ if (!(el instanceof HTMLImageElement)) {
527
+ throw new Error("Element is not an HTMLImageElement");
528
+ }
529
+ return el.complete && el.naturalWidth > 0;
530
+ });
531
+ }, { timeout, intervals }).toBe(true);
532
+ return {
533
+ message: () => `expected element '${locator}' to have loaded image`,
534
+ pass: true
535
+ };
536
+ } catch (e) {
537
+ if (e.message && e.message.includes("Element is not an HTMLImageElement")) {
538
+ return {
539
+ message: () => `expected element '${locator}' to have loaded image, but it is not an HTMLImageElement`,
540
+ pass: false
541
+ };
542
+ }
543
+ return {
544
+ message: () => `expected element '${locator}' to have loaded image`,
545
+ pass: false
546
+ };
547
+ }
548
+ }
549
+
550
+ // src/matchers/locator/index.ts
551
+ var locatorMatchers = {
552
+ toBeClickable,
553
+ toBeCheckable,
554
+ toBeRequired,
555
+ toBeInvalid,
556
+ toBeValid,
557
+ toHaveCountGreaterThan,
558
+ toHaveCountGreaterThanOrEqual,
559
+ toHaveCountLessThan,
560
+ toHaveCountLessThanOrEqual,
561
+ toHaveWidth,
562
+ toHaveHeight,
563
+ toHaveSize,
564
+ toHaveLoadedImage
565
+ };
566
+
567
+ // src/matchers/page/toHaveNoErrors.ts
568
+ function toHaveNoErrors(testObject, options = {}) {
569
+ const assertionName = "toHaveNoErrors";
570
+ const { ignore } = options;
571
+ const testInfo = testObject.info();
572
+ let errors = testInfo.errors || [];
573
+ if (ignore) {
574
+ errors = errors.filter((error) => {
575
+ const message2 = error.message || "";
576
+ return !ignore.test(message2);
577
+ });
578
+ }
579
+ const pass = errors.length === 0;
580
+ const errorMessages = errors.map((e, i) => ` ${i + 1}. ${e.message || "Unknown error"}`).join("\n");
581
+ const message = () => {
582
+ if (this.isNot) {
583
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
584
+ isNot: this.isNot
585
+ }) + `
586
+
587
+ Expected: to have errors
588
+ Received: no errors`;
589
+ }
590
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
591
+ isNot: this.isNot
592
+ }) + `
593
+
594
+ Expected: no errors
595
+ Received: ${errors.length} error(s)
596
+
597
+ ` + errorMessages;
598
+ };
599
+ return {
600
+ pass,
601
+ message,
602
+ name: assertionName,
603
+ expected: 0,
604
+ actual: errors.length
605
+ };
606
+ }
607
+
608
+ // src/matchers/page/toHaveCookie.ts
609
+ import { expect as expect9 } from "@playwright/test";
610
+ async function toHaveCookie(page, name, options = {}) {
611
+ const assertionName = "toHaveCookie";
612
+ const { value, domain, timeout = this.timeout, intervals } = options;
613
+ let pass = false;
614
+ let actualValue;
615
+ let foundCookie = false;
616
+ try {
617
+ await expect9.poll(
618
+ async () => {
619
+ const cookies = await page.context().cookies();
620
+ const cookie = cookies.find((c) => {
621
+ if (c.name !== name) return false;
622
+ if (domain && c.domain !== domain) return false;
623
+ return true;
624
+ });
625
+ if (!cookie) {
626
+ foundCookie = false;
627
+ actualValue = void 0;
628
+ return false;
629
+ }
630
+ foundCookie = true;
631
+ actualValue = cookie.value;
632
+ if (value !== void 0) {
633
+ if (typeof value === "string") {
634
+ return cookie.value === value;
635
+ } else {
636
+ return value.test(cookie.value);
637
+ }
638
+ }
639
+ return true;
640
+ },
641
+ { timeout, intervals }
642
+ ).toBe(true);
643
+ pass = true;
644
+ } catch {
645
+ pass = false;
646
+ }
647
+ const expectedDesc = value ? `cookie "${name}" with value ${value instanceof RegExp ? value : `"${value}"`}` : `cookie "${name}"`;
648
+ const message = () => {
649
+ if (this.isNot) {
650
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
651
+ isNot: this.isNot
652
+ }) + `
653
+
654
+ Expected: NOT to have ${expectedDesc}
655
+ Received: cookie found with value "${actualValue}"`;
656
+ }
657
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
658
+ isNot: this.isNot
659
+ }) + `
660
+
661
+ Expected: ${expectedDesc}
662
+ ` + (foundCookie ? `Received: cookie found with value "${actualValue}"` : `Received: cookie not found`);
663
+ };
664
+ return {
665
+ pass,
666
+ message,
667
+ name: assertionName,
668
+ expected: value ?? name,
669
+ actual: actualValue
670
+ };
671
+ }
672
+
673
+ // src/matchers/page/toHaveLocalStorage.ts
674
+ import { expect as expect10 } from "@playwright/test";
675
+
676
+ // src/utils/matcherUtils.ts
677
+ function formatValue(value) {
678
+ if (value === null) return "null";
679
+ if (value === void 0) return "undefined";
680
+ if (typeof value === "string") return `"${value}"`;
681
+ if (typeof value === "object") {
682
+ try {
683
+ return JSON.stringify(value, null, 2);
684
+ } catch {
685
+ return String(value);
686
+ }
687
+ }
688
+ return String(value);
689
+ }
690
+
691
+ // src/matchers/page/toHaveLocalStorage.ts
692
+ async function toHaveLocalStorage(page, key, options = {}) {
693
+ const assertionName = "toHaveLocalStorage";
694
+ const { value, timeout = this.timeout, intervals } = options;
695
+ let pass = false;
696
+ let actualValue;
697
+ let foundKey = false;
698
+ try {
699
+ await expect10.poll(
700
+ async () => {
701
+ const storageState = await page.context().storageState();
702
+ const pageUrl = page.url();
703
+ let origin;
704
+ try {
705
+ origin = new URL(pageUrl).origin;
706
+ } catch {
707
+ origin = pageUrl;
708
+ }
709
+ const originStorage = storageState.origins.find(
710
+ (o) => o.origin === origin
711
+ );
712
+ if (!originStorage) {
713
+ foundKey = false;
714
+ actualValue = void 0;
715
+ return false;
716
+ }
717
+ const item = originStorage.localStorage.find((ls) => ls.name === key);
718
+ if (!item) {
719
+ foundKey = false;
720
+ actualValue = void 0;
721
+ return false;
722
+ }
723
+ foundKey = true;
724
+ try {
725
+ actualValue = JSON.parse(item.value);
726
+ } catch {
727
+ actualValue = item.value;
728
+ }
729
+ if (value !== void 0) {
730
+ try {
731
+ expect10(actualValue).toEqual(value);
732
+ return true;
733
+ } catch {
734
+ return false;
735
+ }
736
+ }
737
+ return true;
738
+ },
739
+ { timeout, intervals }
740
+ ).toBe(true);
741
+ pass = true;
742
+ } catch {
743
+ pass = false;
744
+ }
745
+ const expectedDesc = value !== void 0 ? `localStorage key "${key}" with value ${formatValue(value)}` : `localStorage key "${key}"`;
746
+ const message = () => {
747
+ if (this.isNot) {
748
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
749
+ isNot: this.isNot
750
+ }) + `
751
+
752
+ Expected: NOT to have ${expectedDesc}
753
+ Received: key found with value ${formatValue(actualValue)}`;
754
+ }
755
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
756
+ isNot: this.isNot
757
+ }) + `
758
+
759
+ Expected: ${expectedDesc}
760
+ ` + (foundKey ? `Received: key found with value ${formatValue(actualValue)}` : `Received: key not found`);
761
+ };
762
+ return {
763
+ pass,
764
+ message,
765
+ name: assertionName,
766
+ expected: value ?? key,
767
+ actual: actualValue
768
+ };
769
+ }
770
+
771
+ // src/matchers/page/toHaveSessionStorage.ts
772
+ import { expect as expect11 } from "@playwright/test";
773
+ async function toHaveSessionStorage(page, key, options = {}) {
774
+ const assertionName = "toHaveSessionStorage";
775
+ const { value, timeout = this.timeout, intervals } = options;
776
+ let pass = false;
777
+ let actualValue;
778
+ let foundKey = false;
779
+ try {
780
+ await expect11.poll(
781
+ async () => {
782
+ const rawValue = await page.evaluate((k) => {
783
+ return window.sessionStorage.getItem(k);
784
+ }, key);
785
+ if (rawValue === null) {
786
+ foundKey = false;
787
+ actualValue = void 0;
788
+ return false;
789
+ }
790
+ foundKey = true;
791
+ try {
792
+ actualValue = JSON.parse(rawValue);
793
+ } catch {
794
+ actualValue = rawValue;
795
+ }
796
+ if (value !== void 0) {
797
+ try {
798
+ expect11(actualValue).toEqual(value);
799
+ return true;
800
+ } catch {
801
+ return false;
802
+ }
803
+ }
804
+ return true;
805
+ },
806
+ { timeout, intervals }
807
+ ).toBe(true);
808
+ pass = true;
809
+ } catch {
810
+ pass = false;
811
+ }
812
+ const expectedDesc = value !== void 0 ? `sessionStorage key "${key}" with value ${formatValue(value)}` : `sessionStorage key "${key}"`;
813
+ const message = () => {
814
+ if (this.isNot) {
815
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
816
+ isNot: this.isNot
817
+ }) + `
818
+
819
+ Expected: NOT to have ${expectedDesc}
820
+ Received: key found with value ${formatValue(actualValue)}`;
821
+ }
822
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
823
+ isNot: this.isNot
824
+ }) + `
825
+
826
+ Expected: ${expectedDesc}
827
+ ` + (foundKey ? `Received: key found with value ${formatValue(actualValue)}` : `Received: key not found`);
828
+ };
829
+ return {
830
+ pass,
831
+ message,
832
+ name: assertionName,
833
+ expected: value ?? key,
834
+ actual: actualValue
835
+ };
836
+ }
837
+
838
+ // src/matchers/page/toHaveClipboardText.ts
839
+ import { expect as expect12 } from "@playwright/test";
840
+ async function toHaveClipboardText(page, expected, options) {
841
+ const timeout = options?.timeout ?? this.timeout;
842
+ const intervals = options?.intervals;
843
+ try {
844
+ const assertion = expect12.poll(async () => {
845
+ return await page.evaluate(() => navigator.clipboard.readText());
846
+ }, { timeout, intervals });
847
+ if (expected instanceof RegExp) {
848
+ await assertion.toMatch(expected);
849
+ } else {
850
+ await assertion.toBe(expected);
851
+ }
852
+ return {
853
+ message: () => `expected clipboard to have text ${expected}`,
854
+ pass: true
855
+ };
856
+ } catch (e) {
857
+ return {
858
+ message: () => `expected clipboard to have text ${expected} but got error or different text: ${e.message}`,
859
+ pass: false
860
+ };
861
+ }
862
+ }
863
+
864
+ // src/matchers/page/toHaveRequest.ts
865
+ import { expect as expect13 } from "@playwright/test";
866
+ async function toHaveRequest(page, options = {}) {
867
+ const assertionName = "toHaveRequest";
868
+ const { method, status, url, timeout = this.timeout, intervals } = options;
869
+ let matchedRequest;
870
+ let requests = [];
871
+ let pass = false;
872
+ try {
873
+ await expect13.poll(
874
+ async () => {
875
+ requests = await page.requests();
876
+ for (const request of requests) {
877
+ let matches = true;
878
+ if (url !== void 0) {
879
+ const requestUrl = request.url();
880
+ if (typeof url === "string") {
881
+ if (!requestUrl.includes(url)) matches = false;
882
+ } else {
883
+ if (!url.test(requestUrl)) matches = false;
884
+ }
885
+ }
886
+ if (matches && method !== void 0) {
887
+ if (request.method().toUpperCase() !== method.toUpperCase()) matches = false;
888
+ }
889
+ if (matches && status !== void 0) {
890
+ const response = await request.response();
891
+ if (!response || response.status() !== status) matches = false;
892
+ }
893
+ if (matches) {
894
+ matchedRequest = request;
895
+ return true;
896
+ }
897
+ }
898
+ return false;
899
+ },
900
+ { timeout, intervals }
901
+ ).toBe(true);
902
+ pass = true;
903
+ } catch {
904
+ pass = false;
905
+ }
906
+ const criteriaDesc = [
907
+ url ? `URL matching ${url instanceof RegExp ? url : `"${url}"`}` : null,
908
+ method ? `method "${method}"` : null,
909
+ status ? `status ${status}` : null
910
+ ].filter(Boolean).join(", ");
911
+ const message = () => {
912
+ if (this.isNot) {
913
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
914
+ isNot: this.isNot
915
+ }) + `
916
+
917
+ Expected: NOT to have request with ${criteriaDesc || "any criteria"}
918
+ Received: found matching request: ${matchedRequest?.method()} ${matchedRequest?.url()}`;
919
+ }
920
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
921
+ isNot: this.isNot
922
+ }) + `
923
+
924
+ Expected: request with ${criteriaDesc || "any criteria"}
925
+ Received: no matching request found among ${requests.length} captured requests
926
+
927
+ Note: page.requests() only returns up to 100 most recent requests`;
928
+ };
929
+ return {
930
+ pass,
931
+ message,
932
+ name: assertionName
933
+ };
934
+ }
935
+
936
+ // src/matchers/page/toHaveConsoleMessage.ts
937
+ import { expect as expect14 } from "@playwright/test";
938
+ async function toHaveConsoleMessage(page, options = {}) {
939
+ const assertionName = "toHaveConsoleMessage";
940
+ const { type, text, timeout = this.timeout, intervals } = options;
941
+ let matchedMessage;
942
+ let messages = [];
943
+ let pass = false;
944
+ try {
945
+ await expect14.poll(
946
+ async () => {
947
+ messages = await page.consoleMessages();
948
+ matchedMessage = messages.find((msg) => {
949
+ if (type !== void 0) {
950
+ if (msg.type() !== type) return false;
951
+ }
952
+ if (text !== void 0) {
953
+ const msgText = msg.text();
954
+ if (typeof text === "string") {
955
+ if (!msgText.includes(text)) return false;
956
+ } else {
957
+ if (!text.test(msgText)) return false;
958
+ }
959
+ }
960
+ return true;
961
+ });
962
+ return matchedMessage !== void 0;
963
+ },
964
+ { timeout, intervals }
965
+ ).toBe(true);
966
+ pass = true;
967
+ } catch {
968
+ pass = false;
969
+ }
970
+ const criteriaDesc = [
971
+ type ? `type "${type}"` : null,
972
+ text ? `text matching ${text instanceof RegExp ? text : `"${text}"`}` : null
973
+ ].filter(Boolean).join(", ");
974
+ const message = () => {
975
+ if (this.isNot) {
976
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
977
+ isNot: this.isNot
978
+ }) + `
979
+
980
+ Expected: NOT to have console message with ${criteriaDesc || "any criteria"}
981
+ Received: found matching message: "${matchedMessage?.text()}"`;
982
+ }
983
+ const capturedMessages = messages.slice(0, 5).map((m) => ` [${m.type()}] ${m.text().substring(0, 50)}${m.text().length > 50 ? "..." : ""}`).join("\n");
984
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
985
+ isNot: this.isNot
986
+ }) + `
987
+
988
+ Expected: console message with ${criteriaDesc || "any criteria"}
989
+ Received: no matching message found among ${messages.length} captured messages
990
+ ` + (capturedMessages ? `
991
+ Recent messages:
992
+ ${capturedMessages}` : "") + `
993
+
994
+ Note: page.consoleMessages() only returns up to 200 most recent messages`;
995
+ };
996
+ return {
997
+ pass,
998
+ message,
999
+ name: assertionName
1000
+ };
1001
+ }
1002
+
1003
+ // src/matchers/page/toHavePageError.ts
1004
+ import { expect as expect15 } from "@playwright/test";
1005
+ async function toHavePageError(page, options = {}) {
1006
+ const assertionName = "toHavePageError";
1007
+ const { message: errorMessage, name: errorName, timeout = this.timeout, intervals } = options;
1008
+ let matchedError;
1009
+ let errors = [];
1010
+ let pass = false;
1011
+ try {
1012
+ await expect15.poll(
1013
+ async () => {
1014
+ errors = await page.pageErrors();
1015
+ matchedError = errors.find((error) => {
1016
+ if (errorMessage !== void 0) {
1017
+ const msg = error.message;
1018
+ if (typeof errorMessage === "string") {
1019
+ if (!msg.includes(errorMessage)) return false;
1020
+ } else {
1021
+ if (!errorMessage.test(msg)) return false;
1022
+ }
1023
+ }
1024
+ if (errorName !== void 0) {
1025
+ if (error.name !== errorName) return false;
1026
+ }
1027
+ return true;
1028
+ });
1029
+ return matchedError !== void 0;
1030
+ },
1031
+ { timeout, intervals }
1032
+ ).toBe(true);
1033
+ pass = true;
1034
+ } catch {
1035
+ pass = false;
1036
+ }
1037
+ const criteriaDesc = [
1038
+ errorMessage ? `message matching ${errorMessage instanceof RegExp ? errorMessage : `"${errorMessage}"`}` : null,
1039
+ errorName ? `name "${errorName}"` : null
1040
+ ].filter(Boolean).join(", ");
1041
+ const message = () => {
1042
+ if (this.isNot) {
1043
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1044
+ isNot: this.isNot
1045
+ }) + `
1046
+
1047
+ Expected: NOT to have page error with ${criteriaDesc || "any criteria"}
1048
+ Received: found matching error: ${matchedError?.name}: ${matchedError?.message}`;
1049
+ }
1050
+ const capturedErrors = errors.slice(0, 5).map((e) => ` ${e.name}: ${e.message.substring(0, 60)}${e.message.length > 60 ? "..." : ""}`).join("\n");
1051
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1052
+ isNot: this.isNot
1053
+ }) + `
1054
+
1055
+ Expected: page error with ${criteriaDesc || "any criteria"}
1056
+ Received: no matching error found among ${errors.length} captured errors
1057
+ ` + (capturedErrors ? `
1058
+ Recent errors:
1059
+ ${capturedErrors}` : "") + `
1060
+
1061
+ Note: page.pageErrors() only returns up to 200 most recent errors`;
1062
+ };
1063
+ return {
1064
+ pass,
1065
+ message,
1066
+ name: assertionName
1067
+ };
1068
+ }
1069
+
1070
+ // src/matchers/page/index.ts
1071
+ var pageMatchers = {
1072
+ toHaveNoErrors,
1073
+ toHaveCookie,
1074
+ toHaveLocalStorage,
1075
+ toHaveSessionStorage,
1076
+ toHaveClipboardText,
1077
+ toHaveRequest,
1078
+ toHaveConsoleMessage,
1079
+ toHavePageError
1080
+ };
1081
+
1082
+ // src/matchers/api/toMatchJSON.ts
1083
+ import { expect as expect16 } from "@playwright/test";
1084
+ async function toMatchJSON(response, expected) {
1085
+ const assertionName = "toMatchJSON";
1086
+ let pass = false;
1087
+ let actualBody;
1088
+ let parseError = null;
1089
+ try {
1090
+ actualBody = await response.json();
1091
+ } catch (error) {
1092
+ parseError = error instanceof Error ? error.message : String(error);
1093
+ }
1094
+ if (parseError) {
1095
+ pass = false;
1096
+ } else {
1097
+ try {
1098
+ expect16(actualBody).toEqual(expected);
1099
+ pass = true;
1100
+ } catch {
1101
+ pass = false;
1102
+ }
1103
+ }
1104
+ const message = () => {
1105
+ if (parseError) {
1106
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1107
+ isNot: this.isNot
1108
+ }) + `
1109
+
1110
+ Failed to parse response body as JSON:
1111
+ ${parseError}`;
1112
+ }
1113
+ const diff = this.utils.diff(expected, actualBody);
1114
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1115
+ isNot: this.isNot
1116
+ }) + "\n\n" + (this.isNot ? `Expected: not ${formatValue(expected)}
1117
+ ` : `Expected: ${formatValue(expected)}
1118
+ `) + `Received: ${formatValue(actualBody)}` + (diff ? `
1119
+
1120
+ Difference:
1121
+ ${diff}` : "");
1122
+ };
1123
+ return {
1124
+ pass,
1125
+ message,
1126
+ name: assertionName,
1127
+ expected,
1128
+ actual: actualBody
1129
+ };
1130
+ }
1131
+
1132
+ // src/matchers/api/toMatchSchema.ts
1133
+ async function toMatchSchema(response, schema) {
1134
+ const assertionName = "toMatchSchema";
1135
+ let pass = false;
1136
+ let actualBody;
1137
+ let parseError = null;
1138
+ let validationErrors = [];
1139
+ try {
1140
+ actualBody = await response.json();
1141
+ } catch (error) {
1142
+ parseError = error instanceof Error ? error.message : String(error);
1143
+ }
1144
+ if (parseError) {
1145
+ pass = false;
1146
+ } else {
1147
+ const result = schema.safeParse(actualBody);
1148
+ pass = result.success;
1149
+ if (!result.success) {
1150
+ validationErrors = formatZodErrors(result.error);
1151
+ }
1152
+ }
1153
+ const message = () => {
1154
+ if (parseError) {
1155
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1156
+ isNot: this.isNot
1157
+ }) + `
1158
+
1159
+ Failed to parse response body as JSON:
1160
+ ${parseError}`;
1161
+ }
1162
+ if (this.isNot) {
1163
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1164
+ isNot: this.isNot
1165
+ }) + `
1166
+
1167
+ Expected: response to NOT match schema
1168
+ Received: response matches schema
1169
+
1170
+ Body: ${formatValue(actualBody)}`;
1171
+ }
1172
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1173
+ isNot: this.isNot
1174
+ }) + `
1175
+
1176
+ Expected: response to match schema
1177
+ Received: validation failed
1178
+
1179
+ Validation errors:
1180
+ ${validationErrors.map((e) => ` \u2022 ${e}`).join("\n")}
1181
+
1182
+ Body: ${formatValue(actualBody)}`;
1183
+ };
1184
+ return {
1185
+ pass,
1186
+ message,
1187
+ name: assertionName,
1188
+ expected: "valid schema",
1189
+ actual: actualBody
1190
+ };
1191
+ }
1192
+ function formatZodErrors(error) {
1193
+ return error.errors.map((e) => {
1194
+ const path = e.path.length > 0 ? `${e.path.join(".")}: ` : "";
1195
+ return `${path}${e.message}`;
1196
+ });
1197
+ }
1198
+
1199
+ // src/matchers/api/toHaveStatus.ts
1200
+ async function toHaveStatus(response, expected) {
1201
+ const assertionName = "toHaveStatus";
1202
+ const actualStatus = response.status();
1203
+ let pass = false;
1204
+ if (typeof expected === "number") {
1205
+ pass = actualStatus === expected;
1206
+ } else {
1207
+ pass = actualStatus >= expected.min && actualStatus <= expected.max;
1208
+ }
1209
+ const expectedDesc = typeof expected === "number" ? `${expected}` : `${expected.min}-${expected.max}`;
1210
+ const message = () => {
1211
+ if (this.isNot) {
1212
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1213
+ isNot: this.isNot
1214
+ }) + `
1215
+
1216
+ Expected: status NOT to be ${expectedDesc}
1217
+ Received: ${actualStatus}`;
1218
+ }
1219
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1220
+ isNot: this.isNot
1221
+ }) + `
1222
+
1223
+ Expected: status ${expectedDesc}
1224
+ Received: ${actualStatus}`;
1225
+ };
1226
+ return {
1227
+ pass,
1228
+ message,
1229
+ name: assertionName,
1230
+ expected: expectedDesc,
1231
+ actual: actualStatus
1232
+ };
1233
+ }
1234
+
1235
+ // src/matchers/api/toHaveHeader.ts
1236
+ async function toHaveHeader(response, name, options = {}) {
1237
+ const assertionName = "toHaveHeader";
1238
+ const { value } = options;
1239
+ const headers = response.headers();
1240
+ const lowerName = name.toLowerCase();
1241
+ const actualValue = Object.entries(headers).find(
1242
+ ([key]) => key.toLowerCase() === lowerName
1243
+ )?.[1];
1244
+ let pass = actualValue !== void 0;
1245
+ if (pass && value !== void 0) {
1246
+ if (typeof value === "string") {
1247
+ pass = actualValue === value;
1248
+ } else {
1249
+ pass = value.test(actualValue);
1250
+ }
1251
+ }
1252
+ const expectedDesc = value ? `header "${name}" with value ${value instanceof RegExp ? value : `"${value}"`}` : `header "${name}"`;
1253
+ const message = () => {
1254
+ if (this.isNot) {
1255
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1256
+ isNot: this.isNot
1257
+ }) + `
1258
+
1259
+ Expected: NOT to have ${expectedDesc}
1260
+ Received: header found with value "${actualValue}"`;
1261
+ }
1262
+ return this.utils.matcherHint(assertionName, void 0, void 0, {
1263
+ isNot: this.isNot
1264
+ }) + `
1265
+
1266
+ Expected: ${expectedDesc}
1267
+ ` + (actualValue !== void 0 ? `Received: header found with value "${actualValue}"` : `Received: header not found`);
1268
+ };
1269
+ return {
1270
+ pass,
1271
+ message,
1272
+ name: assertionName,
1273
+ expected: value ?? name,
1274
+ actual: actualValue
1275
+ };
1276
+ }
1277
+
1278
+ // src/matchers/api/toRespondWithin.ts
1279
+ async function toRespondWithin(response, timeout) {
1280
+ if (!(response instanceof Promise) && typeof response !== "function") {
1281
+ return {
1282
+ message: () => `toRespondWithin: expected received value to be a Promise or async function, but got ${typeof response}. Did you await the request? Remove 'wait' to allow measurement.`,
1283
+ pass: false
1284
+ };
1285
+ }
1286
+ const start = Date.now();
1287
+ try {
1288
+ if (typeof response === "function") {
1289
+ await response();
1290
+ } else {
1291
+ await response;
1292
+ }
1293
+ } catch (e) {
1294
+ }
1295
+ const duration = Date.now() - start;
1296
+ const pass = duration <= timeout;
1297
+ if (pass) {
1298
+ return {
1299
+ message: () => `expected request not to answer within ${timeout}ms, took ${duration}ms`,
1300
+ pass: true
1301
+ };
1302
+ } else {
1303
+ return {
1304
+ message: () => `expected request to answer within ${timeout}ms, but took ${duration}ms`,
1305
+ pass: false
1306
+ };
1307
+ }
1308
+ }
1309
+
1310
+ // src/matchers/api/index.ts
1311
+ var apiMatchers = {
1312
+ toMatchJSON,
1313
+ toMatchSchema,
1314
+ toHaveStatus,
1315
+ toHaveHeader,
1316
+ toRespondWithin
1317
+ };
1318
+
1319
+ // src/matchers/asymmetric/toBeWithinRange.ts
1320
+ function toBeWithinRange(received, min, max) {
1321
+ const pass = typeof received === "number" && received >= min && received <= max;
1322
+ return {
1323
+ message: () => pass ? `expected ${received} not to be within range ${min} - ${max}` : `expected ${received} to be within range ${min} - ${max}`,
1324
+ pass
1325
+ };
1326
+ }
1327
+
1328
+ // src/matchers/asymmetric/toBeUUID.ts
1329
+ var UUID_PATTERNS = {
1330
+ v1: /^[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
1331
+ v4: /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
1332
+ v5: /^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
1333
+ any: /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
1334
+ };
1335
+ function toBeUUID(received, version) {
1336
+ const pattern = version ? UUID_PATTERNS[version] : UUID_PATTERNS.any;
1337
+ const versionLabel = version || "any";
1338
+ const pass = typeof received === "string" && pattern.test(received);
1339
+ return {
1340
+ message: () => pass ? `expected ${received} not to be a valid UUID (${versionLabel})` : `expected ${received} to be a valid UUID (${versionLabel})`,
1341
+ pass
1342
+ };
1343
+ }
1344
+
1345
+ // src/matchers/asymmetric/toBeISODate.ts
1346
+ var ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?(Z|[+-]\d{2}:?\d{2})?$/;
1347
+ function toBeISODate(received) {
1348
+ let pass = false;
1349
+ if (typeof received === "string" && ISO_DATE_PATTERN.test(received)) {
1350
+ const date = new Date(received);
1351
+ if (!isNaN(date.getTime())) {
1352
+ pass = true;
1353
+ }
1354
+ }
1355
+ return {
1356
+ message: () => pass ? `expected ${received} not to be a valid ISO date` : `expected ${received} to be a valid ISO date`,
1357
+ pass
1358
+ };
1359
+ }
1360
+
1361
+ // src/matchers/asymmetric/toBeDateString.ts
1362
+ var FORMAT_PATTERNS = {
1363
+ "YYYY-MM-DD": /^\d{4}-\d{2}-\d{2}$/,
1364
+ "MM/DD/YYYY": /^\d{2}\/\d{2}\/\d{4}$/,
1365
+ "DD/MM/YYYY": /^\d{2}\/\d{2}\/\d{4}$/,
1366
+ "YYYY/MM/DD": /^\d{4}\/\d{2}\/\d{2}$/,
1367
+ "MM-DD-YYYY": /^\d{2}-\d{2}-\d{4}$/,
1368
+ "DD-MM-YYYY": /^\d{2}-\d{2}-\d{4}$/,
1369
+ "YYYYMMDD": /^\d{8}$/
1370
+ };
1371
+ function toBeDateString(received, format) {
1372
+ const pattern = FORMAT_PATTERNS[format];
1373
+ if (!pattern) {
1374
+ throw new Error(
1375
+ `Unsupported date format: ${format}. Supported formats: ${Object.keys(FORMAT_PATTERNS).join(", ")}`
1376
+ );
1377
+ }
1378
+ const pass = typeof received === "string" && pattern.test(received);
1379
+ return {
1380
+ message: () => pass ? `expected ${received} not to be a date string (${format})` : `expected ${received} to be a date string (${format})`,
1381
+ pass
1382
+ };
1383
+ }
1384
+
1385
+ // src/matchers/asymmetric/toBeEmail.ts
1386
+ var EMAIL_PATTERN = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
1387
+ function toBeEmail(received) {
1388
+ const pass = typeof received === "string" && EMAIL_PATTERN.test(received);
1389
+ return {
1390
+ message: () => pass ? `expected ${received} not to be a valid email` : `expected ${received} to be a valid email`,
1391
+ pass
1392
+ };
1393
+ }
1394
+
1395
+ // src/matchers/asymmetric/toBeURL.ts
1396
+ function toBeURL(received, options = {}) {
1397
+ const { protocol } = options;
1398
+ let pass = false;
1399
+ if (typeof received === "string") {
1400
+ try {
1401
+ const url = new URL(received);
1402
+ pass = true;
1403
+ if (protocol) {
1404
+ const urlProtocol = url.protocol.replace(":", "");
1405
+ const allowedProtocols = Array.isArray(protocol) ? protocol : [protocol];
1406
+ if (!allowedProtocols.includes(urlProtocol)) {
1407
+ pass = false;
1408
+ }
1409
+ }
1410
+ } catch {
1411
+ pass = false;
1412
+ }
1413
+ }
1414
+ const protocolMsg = protocol ? ` (protocol: ${Array.isArray(protocol) ? protocol.join("|") : protocol})` : "";
1415
+ return {
1416
+ message: () => pass ? `expected ${received} not to be a valid URL${protocolMsg}` : `expected ${received} to be a valid URL${protocolMsg}`,
1417
+ pass
1418
+ };
1419
+ }
1420
+
1421
+ // src/matchers/asymmetric/toBeJSON.ts
1422
+ function toBeJSON(received) {
1423
+ let pass = false;
1424
+ if (typeof received === "string") {
1425
+ try {
1426
+ JSON.parse(received);
1427
+ pass = true;
1428
+ } catch {
1429
+ pass = false;
1430
+ }
1431
+ }
1432
+ return {
1433
+ message: () => pass ? `expected ${received} not to be valid JSON` : `expected ${received} to be valid JSON`,
1434
+ pass
1435
+ };
1436
+ }
1437
+
1438
+ // src/matchers/asymmetric/toStartWith.ts
1439
+ function toStartWith(received, expected) {
1440
+ const pass = typeof received === "string" && received.startsWith(expected);
1441
+ return {
1442
+ message: () => pass ? `expected ${received} not to start with ${expected}` : `expected ${received} to start with ${expected}`,
1443
+ pass
1444
+ };
1445
+ }
1446
+
1447
+ // src/matchers/asymmetric/toEndWith.ts
1448
+ function toEndWith(received, expected) {
1449
+ const pass = typeof received === "string" && received.endsWith(expected);
1450
+ return {
1451
+ message: () => pass ? `expected ${received} not to end with ${expected}` : `expected ${received} to end with ${expected}`,
1452
+ pass
1453
+ };
1454
+ }
1455
+
1456
+ // src/matchers/asymmetric/toBeUpperCase.ts
1457
+ function toBeUpperCase(received) {
1458
+ const pass = typeof received === "string" && received === received.toUpperCase() && received !== received.toLowerCase();
1459
+ return {
1460
+ message: () => pass ? `expected ${received} not to be upper case` : `expected ${received} to be upper case`,
1461
+ pass
1462
+ };
1463
+ }
1464
+
1465
+ // src/matchers/asymmetric/toBeLowerCase.ts
1466
+ function toBeLowerCase(received) {
1467
+ const pass = typeof received === "string" && received === received.toLowerCase() && received !== received.toUpperCase();
1468
+ return {
1469
+ message: () => pass ? `expected ${received} not to be lower case` : `expected ${received} to be lower case`,
1470
+ pass
1471
+ };
1472
+ }
1473
+
1474
+ // src/matchers/asymmetric/toBeKebabCase.ts
1475
+ function toBeKebabCase(received) {
1476
+ const pass = typeof received === "string" && /^[a-z0-9]+(-[a-z0-9]+)*$/.test(received);
1477
+ return {
1478
+ message: () => pass ? `expected ${received} not to be kebab-case` : `expected ${received} to be kebab-case`,
1479
+ pass
1480
+ };
1481
+ }
1482
+
1483
+ // src/matchers/asymmetric/toBeCamelCase.ts
1484
+ function toBeCamelCase(received) {
1485
+ const pass = typeof received === "string" && /^[a-z][a-zA-Z0-9]*$/.test(received) && !/[_\-]/.test(received);
1486
+ return {
1487
+ message: () => pass ? `expected ${received} not to be camelCase` : `expected ${received} to be camelCase`,
1488
+ pass
1489
+ };
1490
+ }
1491
+
1492
+ // src/matchers/asymmetric/toBeSnakeCase.ts
1493
+ function toBeSnakeCase(received) {
1494
+ const pass = typeof received === "string" && /^[a-z0-9]+(_[a-z0-9]+)*$/.test(received);
1495
+ return {
1496
+ message: () => pass ? `expected ${received} not to be snake_case` : `expected ${received} to be snake_case`,
1497
+ pass
1498
+ };
1499
+ }
1500
+
1501
+ // src/matchers/asymmetric/toBePascalCase.ts
1502
+ function toBePascalCase(received) {
1503
+ const pass = typeof received === "string" && /^[A-Z][a-zA-Z0-9]*$/.test(received) && !/[_\-]/.test(received);
1504
+ return {
1505
+ message: () => pass ? `expected ${received} not to be PascalCase` : `expected ${received} to be PascalCase`,
1506
+ pass
1507
+ };
1508
+ }
1509
+
1510
+ // src/matchers/asymmetric/index.ts
1511
+ var asymmetricMatchers = {
1512
+ toBeWithinRange,
1513
+ toBeUUID,
1514
+ toBeISODate,
1515
+ toBeDateString,
1516
+ toBeEmail,
1517
+ toBeURL,
1518
+ toBeJSON,
1519
+ toStartWith,
1520
+ toEndWith,
1521
+ toBeUpperCase,
1522
+ toBeLowerCase,
1523
+ toBeKebabCase,
1524
+ toBeCamelCase,
1525
+ toBeSnakeCase,
1526
+ toBePascalCase
1527
+ };
1528
+
1529
+ // src/matchers/general/toBeSorted.ts
1530
+ import { expect as expect17 } from "@playwright/test";
1531
+ function isLocator(value) {
1532
+ return value !== null && typeof value === "object" && "allInnerTexts" in value;
1533
+ }
1534
+ function checkSorted(values, descending, key) {
1535
+ return values.every((item, index) => {
1536
+ if (index === 0) return true;
1537
+ const prev = values[index - 1];
1538
+ let a = key ? typeof key === "function" ? key(prev) : prev[key] : prev;
1539
+ let b = key ? typeof key === "function" ? key(item) : item[key] : item;
1540
+ if (a instanceof Date) a = a.getTime();
1541
+ if (b instanceof Date) b = b.getTime();
1542
+ if (descending) {
1543
+ return a >= b;
1544
+ }
1545
+ return a <= b;
1546
+ });
1547
+ }
1548
+ async function toBeSorted(received, options) {
1549
+ const {
1550
+ descending = false,
1551
+ key,
1552
+ useTextContent = false,
1553
+ compareAsNumbers = false,
1554
+ timeout = this.timeout,
1555
+ intervals
1556
+ } = options || {};
1557
+ if (Array.isArray(received)) {
1558
+ const pass = checkSorted(received, descending, key);
1559
+ return {
1560
+ message: () => pass ? `expected array not to be sorted ${descending ? "descending" : "ascending"}` : `expected array to be sorted ${descending ? "descending" : "ascending"}, but received: [${received.join(", ")}]`,
1561
+ pass
1562
+ };
1563
+ }
1564
+ if (isLocator(received)) {
1565
+ const locator = received;
1566
+ let values = [];
1567
+ let pass = false;
1568
+ try {
1569
+ await expect17.poll(
1570
+ async () => {
1571
+ values = useTextContent ? await locator.allTextContents() : await locator.allInnerTexts();
1572
+ const compareValues = compareAsNumbers ? values.map((v) => parseFloat(v.replace(/[^0-9.-]/g, ""))) : values;
1573
+ return checkSorted(compareValues, descending);
1574
+ },
1575
+ { timeout, intervals }
1576
+ ).toBe(true);
1577
+ pass = true;
1578
+ } catch {
1579
+ pass = false;
1580
+ }
1581
+ return {
1582
+ message: () => pass ? `expected locator elements not to be sorted ${descending ? "descending" : "ascending"}` : `expected locator elements to be sorted ${descending ? "descending" : "ascending"}, but received: [${values.join(", ")}]`,
1583
+ pass
1584
+ };
1585
+ }
1586
+ return {
1587
+ message: () => `expected an array or Locator, but received: ${typeof received}`,
1588
+ pass: false
1589
+ };
1590
+ }
1591
+
1592
+ // src/matchers/general/index.ts
1593
+ var generalMatchers = {
1594
+ toBeSorted
1595
+ };
1596
+
1597
+ // src/matchers/index.ts
1598
+ var allMatchers = {
1599
+ ...locatorMatchers,
1600
+ ...pageMatchers,
1601
+ ...apiMatchers,
1602
+ ...generalMatchers,
1603
+ ...asymmetricMatchers
1604
+ };
1605
+
1606
+ // src/index.ts
1607
+ var exPwExpect = playwrightExpect.extend(allMatchers);
1608
+ var expect18 = exPwExpect;
1609
+ var exPw = {
1610
+ ...allMatchers
1611
+ };
1612
+ var index_default = exPw;
1613
+ export {
1614
+ apiMatchers,
1615
+ asymmetricMatchers,
1616
+ index_default as default,
1617
+ expect18 as expect,
1618
+ generalMatchers,
1619
+ locatorMatchers,
1620
+ pageMatchers,
1621
+ toBeCamelCase,
1622
+ toBeCheckable,
1623
+ toBeClickable,
1624
+ toBeDateString,
1625
+ toBeEmail,
1626
+ toBeISODate,
1627
+ toBeInvalid,
1628
+ toBeJSON,
1629
+ toBeKebabCase,
1630
+ toBeLowerCase,
1631
+ toBePascalCase,
1632
+ toBeRequired,
1633
+ toBeSnakeCase,
1634
+ toBeSorted,
1635
+ toBeURL,
1636
+ toBeUUID,
1637
+ toBeUpperCase,
1638
+ toBeValid,
1639
+ toBeWithinRange,
1640
+ toEndWith,
1641
+ toHaveClipboardText,
1642
+ toHaveConsoleMessage,
1643
+ toHaveCookie,
1644
+ toHaveCountGreaterThan,
1645
+ toHaveCountGreaterThanOrEqual,
1646
+ toHaveCountLessThan,
1647
+ toHaveCountLessThanOrEqual,
1648
+ toHaveHeader,
1649
+ toHaveHeight,
1650
+ toHaveLoadedImage,
1651
+ toHaveLocalStorage,
1652
+ toHaveNoErrors,
1653
+ toHavePageError,
1654
+ toHaveRequest,
1655
+ toHaveSessionStorage,
1656
+ toHaveSize,
1657
+ toHaveStatus,
1658
+ toHaveWidth,
1659
+ toMatchJSON,
1660
+ toMatchSchema,
1661
+ toRespondWithin,
1662
+ toStartWith
1663
+ };