@ricsam/isolate-playwright 0.1.9 → 0.1.11

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.
@@ -53,6 +53,10 @@ function getLocator(page, selectorType, selectorValue, optionsJson) {
53
53
  const roleOptions = options ? { ...options } : undefined;
54
54
  if (roleOptions) {
55
55
  delete roleOptions.nth;
56
+ delete roleOptions.filter;
57
+ if (roleOptions.name && typeof roleOptions.name === "object" && roleOptions.name.$regex) {
58
+ roleOptions.name = new RegExp(roleOptions.name.$regex, roleOptions.name.$flags);
59
+ }
56
60
  }
57
61
  let locator;
58
62
  switch (selectorType) {
@@ -80,6 +84,16 @@ function getLocator(page, selectorType, selectorValue, optionsJson) {
80
84
  if (nthIndex !== undefined) {
81
85
  locator = locator.nth(nthIndex);
82
86
  }
87
+ if (options?.filter) {
88
+ const filterOpts = { ...options.filter };
89
+ if (filterOpts.hasText && typeof filterOpts.hasText === "object" && filterOpts.hasText.$regex) {
90
+ filterOpts.hasText = new RegExp(filterOpts.hasText.$regex, filterOpts.hasText.$flags);
91
+ }
92
+ if (filterOpts.hasNotText && typeof filterOpts.hasNotText === "object" && filterOpts.hasNotText.$regex) {
93
+ filterOpts.hasNotText = new RegExp(filterOpts.hasNotText.$regex, filterOpts.hasNotText.$flags);
94
+ }
95
+ locator = locator.filter(filterOpts);
96
+ }
83
97
  return locator;
84
98
  }
85
99
  async function executeLocatorAction(locator, action, actionArg, timeout) {
@@ -129,6 +143,27 @@ async function executeLocatorAction(locator, action, actionArg, timeout) {
129
143
  return await locator.isChecked();
130
144
  case "count":
131
145
  return await locator.count();
146
+ case "getAttribute":
147
+ return await locator.getAttribute(String(actionArg ?? ""), { timeout });
148
+ case "isDisabled":
149
+ return await locator.isDisabled();
150
+ case "isHidden":
151
+ return await locator.isHidden();
152
+ case "innerHTML":
153
+ return await locator.innerHTML({ timeout });
154
+ case "innerText":
155
+ return await locator.innerText({ timeout });
156
+ case "allTextContents":
157
+ return await locator.allTextContents();
158
+ case "allInnerTexts":
159
+ return await locator.allInnerTexts();
160
+ case "waitFor": {
161
+ const opts = actionArg && typeof actionArg === "object" ? actionArg : {};
162
+ await locator.waitFor({ state: opts.state, timeout: opts.timeout ?? timeout });
163
+ return null;
164
+ }
165
+ case "boundingBox":
166
+ return await locator.boundingBox({ timeout });
132
167
  default:
133
168
  throw new Error(`Unknown action: ${action}`);
134
169
  }
@@ -148,13 +183,22 @@ async function executeExpectAssertion(locator, matcher, expected, negated, timeo
148
183
  }
149
184
  case "toContainText": {
150
185
  const text = await locator.textContent({ timeout });
151
- const matches = text?.includes(String(expected)) ?? false;
186
+ let matches;
187
+ let expectedDisplay;
188
+ if (expected && typeof expected === "object" && expected.$regex) {
189
+ const regex = new RegExp(expected.$regex, expected.$flags);
190
+ matches = regex.test(text ?? "");
191
+ expectedDisplay = String(regex);
192
+ } else {
193
+ matches = text?.includes(String(expected)) ?? false;
194
+ expectedDisplay = String(expected);
195
+ }
152
196
  if (negated) {
153
197
  if (matches)
154
- throw new Error(`Expected text to not contain "${expected}", but got "${text}"`);
198
+ throw new Error(`Expected text to not contain ${expectedDisplay}, but got "${text}"`);
155
199
  } else {
156
200
  if (!matches)
157
- throw new Error(`Expected text to contain "${expected}", but got "${text}"`);
201
+ throw new Error(`Expected text to contain ${expectedDisplay}, but got "${text}"`);
158
202
  }
159
203
  break;
160
204
  }
@@ -192,6 +236,110 @@ async function executeExpectAssertion(locator, matcher, expected, negated, timeo
192
236
  }
193
237
  break;
194
238
  }
239
+ case "toHaveAttribute": {
240
+ const { name, value } = expected;
241
+ const actual = await locator.getAttribute(name, { timeout });
242
+ if (value instanceof RegExp || value && typeof value === "object" && value.$regex) {
243
+ const regex = value.$regex ? new RegExp(value.$regex, value.$flags) : value;
244
+ const matches = regex.test(actual ?? "");
245
+ if (negated) {
246
+ if (matches)
247
+ throw new Error(`Expected attribute "${name}" to not match ${regex}, but got "${actual}"`);
248
+ } else {
249
+ if (!matches)
250
+ throw new Error(`Expected attribute "${name}" to match ${regex}, but got "${actual}"`);
251
+ }
252
+ } else {
253
+ const matches = actual === String(value);
254
+ if (negated) {
255
+ if (matches)
256
+ throw new Error(`Expected attribute "${name}" to not be "${value}", but it was`);
257
+ } else {
258
+ if (!matches)
259
+ throw new Error(`Expected attribute "${name}" to be "${value}", but got "${actual}"`);
260
+ }
261
+ }
262
+ break;
263
+ }
264
+ case "toHaveText": {
265
+ const text = await locator.textContent({ timeout }) ?? "";
266
+ let matches;
267
+ let expectedDisplay;
268
+ if (expected && typeof expected === "object" && expected.$regex) {
269
+ const regex = new RegExp(expected.$regex, expected.$flags);
270
+ matches = regex.test(text);
271
+ expectedDisplay = String(regex);
272
+ } else {
273
+ matches = text === String(expected);
274
+ expectedDisplay = JSON.stringify(expected);
275
+ }
276
+ if (negated) {
277
+ if (matches)
278
+ throw new Error(`Expected text to not be ${expectedDisplay}, but got "${text}"`);
279
+ } else {
280
+ if (!matches)
281
+ throw new Error(`Expected text to be ${expectedDisplay}, but got "${text}"`);
282
+ }
283
+ break;
284
+ }
285
+ case "toHaveCount": {
286
+ const count = await locator.count();
287
+ const expectedCount = Number(expected);
288
+ if (negated) {
289
+ if (count === expectedCount)
290
+ throw new Error(`Expected count to not be ${expectedCount}, but it was`);
291
+ } else {
292
+ if (count !== expectedCount)
293
+ throw new Error(`Expected count to be ${expectedCount}, but got ${count}`);
294
+ }
295
+ break;
296
+ }
297
+ case "toBeHidden": {
298
+ const isHidden = await locator.isHidden();
299
+ if (negated) {
300
+ if (isHidden)
301
+ throw new Error("Expected element to not be hidden, but it was hidden");
302
+ } else {
303
+ if (!isHidden)
304
+ throw new Error("Expected element to be hidden, but it was not");
305
+ }
306
+ break;
307
+ }
308
+ case "toBeDisabled": {
309
+ const isDisabled = await locator.isDisabled();
310
+ if (negated) {
311
+ if (isDisabled)
312
+ throw new Error("Expected element to not be disabled, but it was disabled");
313
+ } else {
314
+ if (!isDisabled)
315
+ throw new Error("Expected element to be disabled, but it was not");
316
+ }
317
+ break;
318
+ }
319
+ case "toBeFocused": {
320
+ const isFocused = await locator.evaluate((el) => document.activeElement === el).catch(() => false);
321
+ if (negated) {
322
+ if (isFocused)
323
+ throw new Error("Expected element to not be focused, but it was focused");
324
+ } else {
325
+ if (!isFocused)
326
+ throw new Error("Expected element to be focused, but it was not");
327
+ }
328
+ break;
329
+ }
330
+ case "toBeEmpty": {
331
+ const text = await locator.textContent({ timeout });
332
+ const value = await locator.inputValue({ timeout }).catch(() => null);
333
+ const isEmpty = value !== null ? value === "" : (text ?? "") === "";
334
+ if (negated) {
335
+ if (isEmpty)
336
+ throw new Error("Expected element to not be empty, but it was");
337
+ } else {
338
+ if (!isEmpty)
339
+ throw new Error("Expected element to be empty, but it was not");
340
+ }
341
+ break;
342
+ }
195
343
  default:
196
344
  throw new Error(`Unknown matcher: ${matcher}`);
197
345
  }
@@ -237,7 +385,12 @@ function createPlaywrightHandler(page, options) {
237
385
  return { ok: true };
238
386
  }
239
387
  case "evaluate": {
240
- const [script] = op.args;
388
+ const [script, arg] = op.args;
389
+ if (op.args.length > 1) {
390
+ const fn = new Function("return (" + script + ")")();
391
+ const result2 = await page.evaluate(fn, arg);
392
+ return { ok: true, value: result2 };
393
+ }
241
394
  const result = await page.evaluate(script);
242
395
  return { ok: true, value: result };
243
396
  }
@@ -287,6 +440,34 @@ function createPlaywrightHandler(page, options) {
287
440
  }
288
441
  };
289
442
  }
443
+ case "goBack": {
444
+ const [waitUntil] = op.args;
445
+ await page.goBack({
446
+ timeout,
447
+ waitUntil: waitUntil ?? "load"
448
+ });
449
+ return { ok: true };
450
+ }
451
+ case "goForward": {
452
+ const [waitUntil] = op.args;
453
+ await page.goForward({
454
+ timeout,
455
+ waitUntil: waitUntil ?? "load"
456
+ });
457
+ return { ok: true };
458
+ }
459
+ case "waitForURL": {
460
+ const [url, customTimeout, waitUntil] = op.args;
461
+ await page.waitForURL(url, {
462
+ timeout: customTimeout ?? timeout,
463
+ waitUntil: waitUntil ?? undefined
464
+ });
465
+ return { ok: true };
466
+ }
467
+ case "clearCookies": {
468
+ await page.context().clearCookies();
469
+ return { ok: true };
470
+ }
290
471
  default:
291
472
  return { ok: false, error: { name: "Error", message: `Unknown operation: ${op.type}` } };
292
473
  }
@@ -404,15 +585,22 @@ async function setupPlaywright(context, options) {
404
585
  `);
405
586
  context.evalSync(`
406
587
  (function() {
588
+ let __pw_currentUrl = '';
407
589
  globalThis.page = {
408
590
  async goto(url, options) {
409
- return __pw_invoke("goto", [url, options?.waitUntil || null]);
591
+ const result = await __pw_invoke("goto", [url, options?.waitUntil || null]);
592
+ const resolvedUrl = await __pw_invoke("url", []);
593
+ __pw_currentUrl = resolvedUrl || url;
594
+ return result;
410
595
  },
411
596
  async reload() {
412
- return __pw_invoke("reload", []);
597
+ const result = await __pw_invoke("reload", []);
598
+ const resolvedUrl = await __pw_invoke("url", []);
599
+ if (resolvedUrl) __pw_currentUrl = resolvedUrl;
600
+ return result;
413
601
  },
414
- async url() {
415
- return __pw_invoke("url", []);
602
+ url() {
603
+ return __pw_currentUrl;
416
604
  },
417
605
  async title() {
418
606
  return __pw_invoke("title", []);
@@ -429,15 +617,50 @@ async function setupPlaywright(context, options) {
429
617
  async waitForLoadState(state) {
430
618
  return __pw_invoke("waitForLoadState", [state || null]);
431
619
  },
432
- async evaluate(script) {
433
- return __pw_invoke("evaluate", [script]);
620
+ async evaluate(script, arg) {
621
+ const hasArg = arguments.length > 1;
622
+ if (hasArg) {
623
+ const serialized = typeof script === "function" ? script.toString() : script;
624
+ return __pw_invoke("evaluate", [serialized, arg]);
625
+ }
626
+ const serialized = typeof script === "function" ? "(" + script.toString() + ")()" : script;
627
+ return __pw_invoke("evaluate", [serialized]);
434
628
  },
435
629
  locator(selector) { return new Locator("css", selector, null); },
436
- getByRole(role, options) { return new Locator("role", role, options ? JSON.stringify(options) : null); },
630
+ getByRole(role, options) {
631
+ if (options) {
632
+ const serialized = { ...options };
633
+ if (options.name instanceof RegExp) {
634
+ serialized.name = { $regex: options.name.source, $flags: options.name.flags };
635
+ }
636
+ return new Locator("role", role, JSON.stringify(serialized));
637
+ }
638
+ return new Locator("role", role, null);
639
+ },
437
640
  getByText(text) { return new Locator("text", text, null); },
438
641
  getByLabel(label) { return new Locator("label", label, null); },
439
642
  getByPlaceholder(p) { return new Locator("placeholder", p, null); },
440
643
  getByTestId(id) { return new Locator("testId", id, null); },
644
+ async goBack(options) {
645
+ await __pw_invoke("goBack", [options?.waitUntil || null]);
646
+ const resolvedUrl = await __pw_invoke("url", []);
647
+ if (resolvedUrl) __pw_currentUrl = resolvedUrl;
648
+ },
649
+ async goForward(options) {
650
+ await __pw_invoke("goForward", [options?.waitUntil || null]);
651
+ const resolvedUrl = await __pw_invoke("url", []);
652
+ if (resolvedUrl) __pw_currentUrl = resolvedUrl;
653
+ },
654
+ async waitForURL(url, options) {
655
+ return __pw_invoke("waitForURL", [url, options?.timeout || null, options?.waitUntil || null]);
656
+ },
657
+ context() {
658
+ return {
659
+ async clearCookies() {
660
+ return __pw_invoke("clearCookies", []);
661
+ }
662
+ };
663
+ },
441
664
  async click(selector) { return this.locator(selector).click(); },
442
665
  async fill(selector, value) { return this.locator(selector).fill(value); },
443
666
  request: {
@@ -531,10 +754,70 @@ async function setupPlaywright(context, options) {
531
754
  async count() {
532
755
  return __pw_invoke("locatorAction", [...this._getInfo(), "count", null]);
533
756
  }
757
+ async getAttribute(name) {
758
+ return __pw_invoke("locatorAction", [...this._getInfo(), "getAttribute", name]);
759
+ }
760
+ async isDisabled() {
761
+ return __pw_invoke("locatorAction", [...this._getInfo(), "isDisabled", null]);
762
+ }
763
+ async isHidden() {
764
+ return __pw_invoke("locatorAction", [...this._getInfo(), "isHidden", null]);
765
+ }
766
+ async innerHTML() {
767
+ return __pw_invoke("locatorAction", [...this._getInfo(), "innerHTML", null]);
768
+ }
769
+ async innerText() {
770
+ return __pw_invoke("locatorAction", [...this._getInfo(), "innerText", null]);
771
+ }
772
+ async allTextContents() {
773
+ return __pw_invoke("locatorAction", [...this._getInfo(), "allTextContents", null]);
774
+ }
775
+ async allInnerTexts() {
776
+ return __pw_invoke("locatorAction", [...this._getInfo(), "allInnerTexts", null]);
777
+ }
778
+ async waitFor(options) {
779
+ return __pw_invoke("locatorAction", [...this._getInfo(), "waitFor", options || {}]);
780
+ }
781
+ async boundingBox() {
782
+ return __pw_invoke("locatorAction", [...this._getInfo(), "boundingBox", null]);
783
+ }
784
+ locator(selector) {
785
+ const parentSelector = this.#type === 'css' ? this.#value : null;
786
+ if (parentSelector) {
787
+ return new Locator("css", parentSelector + " " + selector, this.#options);
788
+ }
789
+ // For non-css locators, use css with the combined approach
790
+ return new Locator("css", selector, this.#options);
791
+ }
792
+ async all() {
793
+ const n = await this.count();
794
+ const result = [];
795
+ for (let i = 0; i < n; i++) {
796
+ result.push(this.nth(i));
797
+ }
798
+ return result;
799
+ }
534
800
  nth(index) {
535
801
  const existingOpts = this.#options ? JSON.parse(this.#options) : {};
536
802
  return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, nth: index }));
537
803
  }
804
+ first() {
805
+ return this.nth(0);
806
+ }
807
+ last() {
808
+ return this.nth(-1);
809
+ }
810
+ filter(options) {
811
+ const existingOpts = this.#options ? JSON.parse(this.#options) : {};
812
+ const serializedFilter = { ...options };
813
+ if (options.hasText instanceof RegExp) {
814
+ serializedFilter.hasText = { $regex: options.hasText.source, $flags: options.hasText.flags };
815
+ }
816
+ if (options.hasNotText instanceof RegExp) {
817
+ serializedFilter.hasNotText = { $regex: options.hasNotText.source, $flags: options.hasNotText.flags };
818
+ }
819
+ return new Locator(this.#type, this.#value, JSON.stringify({ ...existingOpts, filter: serializedFilter }));
820
+ }
538
821
  }
539
822
  globalThis.Locator = Locator;
540
823
  })();
@@ -550,7 +833,8 @@ async function setupPlaywright(context, options) {
550
833
  return __pw_invoke("expectLocator", [...info, "toBeVisible", null, false, options?.timeout]);
551
834
  },
552
835
  async toContainText(expected, options) {
553
- return __pw_invoke("expectLocator", [...info, "toContainText", expected, false, options?.timeout]);
836
+ const serialized = expected instanceof RegExp ? { $regex: expected.source, $flags: expected.flags } : expected;
837
+ return __pw_invoke("expectLocator", [...info, "toContainText", serialized, false, options?.timeout]);
554
838
  },
555
839
  async toHaveValue(expected, options) {
556
840
  return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, false, options?.timeout]);
@@ -561,12 +845,35 @@ async function setupPlaywright(context, options) {
561
845
  async toBeChecked(options) {
562
846
  return __pw_invoke("expectLocator", [...info, "toBeChecked", null, false, options?.timeout]);
563
847
  },
848
+ async toHaveAttribute(name, value, options) {
849
+ return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value }, false, options?.timeout]);
850
+ },
851
+ async toHaveText(expected, options) {
852
+ const serialized = expected instanceof RegExp ? { $regex: expected.source, $flags: expected.flags } : expected;
853
+ return __pw_invoke("expectLocator", [...info, "toHaveText", serialized, false, options?.timeout]);
854
+ },
855
+ async toHaveCount(count, options) {
856
+ return __pw_invoke("expectLocator", [...info, "toHaveCount", count, false, options?.timeout]);
857
+ },
858
+ async toBeHidden(options) {
859
+ return __pw_invoke("expectLocator", [...info, "toBeHidden", null, false, options?.timeout]);
860
+ },
861
+ async toBeDisabled(options) {
862
+ return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, false, options?.timeout]);
863
+ },
864
+ async toBeFocused(options) {
865
+ return __pw_invoke("expectLocator", [...info, "toBeFocused", null, false, options?.timeout]);
866
+ },
867
+ async toBeEmpty(options) {
868
+ return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, false, options?.timeout]);
869
+ },
564
870
  not: {
565
871
  async toBeVisible(options) {
566
872
  return __pw_invoke("expectLocator", [...info, "toBeVisible", null, true, options?.timeout]);
567
873
  },
568
874
  async toContainText(expected, options) {
569
- return __pw_invoke("expectLocator", [...info, "toContainText", expected, true, options?.timeout]);
875
+ const serialized = expected instanceof RegExp ? { $regex: expected.source, $flags: expected.flags } : expected;
876
+ return __pw_invoke("expectLocator", [...info, "toContainText", serialized, true, options?.timeout]);
570
877
  },
571
878
  async toHaveValue(expected, options) {
572
879
  return __pw_invoke("expectLocator", [...info, "toHaveValue", expected, true, options?.timeout]);
@@ -577,6 +884,28 @@ async function setupPlaywright(context, options) {
577
884
  async toBeChecked(options) {
578
885
  return __pw_invoke("expectLocator", [...info, "toBeChecked", null, true, options?.timeout]);
579
886
  },
887
+ async toHaveAttribute(name, value, options) {
888
+ return __pw_invoke("expectLocator", [...info, "toHaveAttribute", { name, value }, true, options?.timeout]);
889
+ },
890
+ async toHaveText(expected, options) {
891
+ const serialized = expected instanceof RegExp ? { $regex: expected.source, $flags: expected.flags } : expected;
892
+ return __pw_invoke("expectLocator", [...info, "toHaveText", serialized, true, options?.timeout]);
893
+ },
894
+ async toHaveCount(count, options) {
895
+ return __pw_invoke("expectLocator", [...info, "toHaveCount", count, true, options?.timeout]);
896
+ },
897
+ async toBeHidden(options) {
898
+ return __pw_invoke("expectLocator", [...info, "toBeHidden", null, true, options?.timeout]);
899
+ },
900
+ async toBeDisabled(options) {
901
+ return __pw_invoke("expectLocator", [...info, "toBeDisabled", null, true, options?.timeout]);
902
+ },
903
+ async toBeFocused(options) {
904
+ return __pw_invoke("expectLocator", [...info, "toBeFocused", null, true, options?.timeout]);
905
+ },
906
+ async toBeEmpty(options) {
907
+ return __pw_invoke("expectLocator", [...info, "toBeEmpty", null, true, options?.timeout]);
908
+ },
580
909
  }
581
910
  };
582
911
 
@@ -634,4 +963,4 @@ async function setupPlaywright(context, options) {
634
963
  };
635
964
  }
636
965
 
637
- //# debugId=959DB7FC441CA4D264756E2164756E21
966
+ //# debugId=2CC694A6398FE85E64756E2164756E21