@enekesabel/playwright-lite 0.2.1 → 0.3.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/README.md +18 -20
- package/dist/index.mjs +743 -187
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -480,20 +480,198 @@ var UtilityScript$1 = class {
|
|
|
480
480
|
};
|
|
481
481
|
const LiteUtilityScript = module$1.exports.UtilityScript();
|
|
482
482
|
//#endregion
|
|
483
|
+
//#region src/jsHandle.ts
|
|
484
|
+
const invalidArguments = "Too many arguments. If you need to pass more than 1 argument to the function wrap them in an object.";
|
|
485
|
+
function assertEvaluationOptions(options) {
|
|
486
|
+
if (options !== void 0 && (typeof options !== "object" || options === null || Array.isArray(options))) throw new Error(invalidArguments);
|
|
487
|
+
if (options?.exposeFunctions === true) throw new Error("Unsupported Playwright option: evaluate.exposeFunctions");
|
|
488
|
+
if (options?.exposeFunctions !== void 0 && typeof options.exposeFunctions !== "boolean") throw new Error("exposeFunctions must be a boolean");
|
|
489
|
+
}
|
|
490
|
+
function assertMaxArguments(count, maximum) {
|
|
491
|
+
if (count > maximum) throw new Error(invalidArguments);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* A reference to one value in the controlled document.
|
|
495
|
+
*
|
|
496
|
+
* Mirrors pinned 26a9e47 client/jsHandle.ts and server/javascript.ts: the value
|
|
497
|
+
* never leaves the document, and every member either evaluates against it or
|
|
498
|
+
* copies it out through the pinned serializers.
|
|
499
|
+
*/
|
|
500
|
+
var AdapterJSHandle = class {
|
|
501
|
+
value;
|
|
502
|
+
evaluation;
|
|
503
|
+
disposedError = "JSHandle is disposed!";
|
|
504
|
+
disposed = false;
|
|
505
|
+
preview;
|
|
506
|
+
constructor(value, evaluation) {
|
|
507
|
+
this.value = value;
|
|
508
|
+
this.evaluation = evaluation;
|
|
509
|
+
}
|
|
510
|
+
/** Internal boundary used by evaluation argument and target unwrapping. */
|
|
511
|
+
valueForEvaluation(evaluation) {
|
|
512
|
+
if (this.evaluation !== evaluation) throw new Error("JSHandles can be evaluated only in the context they were created!");
|
|
513
|
+
if (this.disposed) throw new Error(this.disposedError);
|
|
514
|
+
return this.value;
|
|
515
|
+
}
|
|
516
|
+
asElement() {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
async evaluate(pageFunction, arg, options) {
|
|
520
|
+
assertMaxArguments(arguments.length, 3);
|
|
521
|
+
assertEvaluationOptions(options);
|
|
522
|
+
return this.evaluation.byValue(pageFunction, typeof pageFunction === "function", arg, this);
|
|
523
|
+
}
|
|
524
|
+
async evaluateHandle(pageFunction, arg, options) {
|
|
525
|
+
assertMaxArguments(arguments.length, 3);
|
|
526
|
+
assertEvaluationOptions(options);
|
|
527
|
+
return this.evaluation.byHandle(pageFunction, typeof pageFunction === "function", arg, this);
|
|
528
|
+
}
|
|
529
|
+
/** Pinned javascript.ts:188 reads the property off the value itself. */
|
|
530
|
+
async getProperty(propertyName) {
|
|
531
|
+
const value = this.valueForEvaluation(this.evaluation);
|
|
532
|
+
return this.evaluation.handleFor(value[propertyName]);
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Pinned crExecutionContext.ts:75 lists own enumerable data properties, and
|
|
536
|
+
* javascript.ts:197 answers with an empty map for a value without an object.
|
|
537
|
+
*/
|
|
538
|
+
async getProperties() {
|
|
539
|
+
const value = this.valueForEvaluation(this.evaluation);
|
|
540
|
+
const properties = /* @__PURE__ */ new Map();
|
|
541
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return properties;
|
|
542
|
+
for (const name of Object.getOwnPropertyNames(value)) {
|
|
543
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, name);
|
|
544
|
+
if (!descriptor?.enumerable || !("value" in descriptor)) continue;
|
|
545
|
+
properties.set(name, this.evaluation.handleFor(descriptor.value));
|
|
546
|
+
}
|
|
547
|
+
return properties;
|
|
548
|
+
}
|
|
549
|
+
async jsonValue() {
|
|
550
|
+
return this.evaluation.jsonValue(this.valueForEvaluation(this.evaluation));
|
|
551
|
+
}
|
|
552
|
+
async dispose() {
|
|
553
|
+
this.toString();
|
|
554
|
+
this.disposed = true;
|
|
555
|
+
this.value = void 0;
|
|
556
|
+
}
|
|
557
|
+
async [Symbol.asyncDispose]() {
|
|
558
|
+
await this.dispose();
|
|
559
|
+
}
|
|
560
|
+
toString() {
|
|
561
|
+
return this.preview ??= this.computePreview();
|
|
562
|
+
}
|
|
563
|
+
computePreview() {
|
|
564
|
+
return previewValue(this.value);
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
/**
|
|
568
|
+
* Mirrors the handle preview pinned 26a9e47 crExecutionContext.ts:123
|
|
569
|
+
* (`renderPreview`) derives from a Chromium remote object: a value that crosses
|
|
570
|
+
* the protocol renders as `String(value)`, and an object renders as V8's
|
|
571
|
+
* `RemoteObject.description`. Handles created inside the document carry no
|
|
572
|
+
* remote object, so the description is reconstructed from the value.
|
|
573
|
+
*/
|
|
574
|
+
function previewValue(value) {
|
|
575
|
+
if (value === null) return "null";
|
|
576
|
+
if (typeof value === "bigint") return `${value}n`;
|
|
577
|
+
if (typeof value === "function") return String(value);
|
|
578
|
+
if (typeof value !== "object") return Object.is(value, -0) ? "-0" : String(value);
|
|
579
|
+
const tag = Object.prototype.toString.call(value).slice(8, -1);
|
|
580
|
+
if (tag === "Date" || tag === "RegExp") return String(value);
|
|
581
|
+
if (tag === "Error") return value.stack || String(value);
|
|
582
|
+
const name = tag === "Object" ? constructorName(value) : tag;
|
|
583
|
+
if (tag === "Map" || tag === "Set") return `${name}(${value.size})`;
|
|
584
|
+
if (tag === "Array" || ArrayBuffer.isView(value)) return `${name}(${value.length})`;
|
|
585
|
+
return name;
|
|
586
|
+
}
|
|
587
|
+
function constructorName(value) {
|
|
588
|
+
return value.constructor?.name || "Object";
|
|
589
|
+
}
|
|
590
|
+
//#endregion
|
|
591
|
+
//#region src/protocolValidation.ts
|
|
592
|
+
/**
|
|
593
|
+
* Client-boundary checks from the pinned Playwright protocol semantics.
|
|
594
|
+
* See packages/protocol/src/validatorPrimitives.ts and spec/frame.yml at
|
|
595
|
+
* 26a9e470a7b3c7822084b09fb7f13902c5f37b51.
|
|
596
|
+
*/
|
|
597
|
+
function validateString(value, name) {
|
|
598
|
+
if (value instanceof String) return value.valueOf();
|
|
599
|
+
if (typeof value === "string") return value;
|
|
600
|
+
throw new Error(`${name}: expected string, got ${typeof value}`);
|
|
601
|
+
}
|
|
602
|
+
function validateInteger(value, name) {
|
|
603
|
+
const integer = value instanceof Number ? value.valueOf() : value;
|
|
604
|
+
if (typeof integer !== "number") throw new Error(`${name}: expected integer, got ${typeof value}`);
|
|
605
|
+
if (!Number.isInteger(integer)) throw new Error(`${name}: expected integer, got float ${integer}`);
|
|
606
|
+
return integer;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* `delay` is `float?` in the pinned protocol for press, type and the pointer
|
|
610
|
+
* actions. Like the pointer options, a non-finite value is rejected instead of
|
|
611
|
+
* reaching the input path as NaN, and a boxed `Number` is unwrapped for the
|
|
612
|
+
* caller to forward instead of leaking an object into the input path.
|
|
613
|
+
*/
|
|
614
|
+
function validateDelay(value) {
|
|
615
|
+
if (value === void 0) return void 0;
|
|
616
|
+
const delay = value instanceof Number ? value.valueOf() : value;
|
|
617
|
+
if (typeof delay !== "number" || !Number.isFinite(delay)) throw new TypeError("delay: expected number");
|
|
618
|
+
return delay;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* `force` is `boolean?` in the pinned protocol for every action that takes it.
|
|
622
|
+
* Like the pointer options, a boxed `Boolean` is unwrapped so the caller
|
|
623
|
+
* forwards a primitive instead of an always-truthy object.
|
|
624
|
+
*/
|
|
625
|
+
function validateForce(method, value) {
|
|
626
|
+
if (value === void 0) return void 0;
|
|
627
|
+
const force = value instanceof Boolean ? value.valueOf() : value;
|
|
628
|
+
if (typeof force !== "boolean") throw new TypeError(`${method} force must be a boolean`);
|
|
629
|
+
return force;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* `signal` is a client-side option in the pinned API: it never reaches the
|
|
633
|
+
* protocol, so the adapter checks the value itself. `name` is the API member
|
|
634
|
+
* or option group the message speaks for, such as `click` or `Query`.
|
|
635
|
+
*/
|
|
636
|
+
function validateSignal(name, value) {
|
|
637
|
+
if (value === void 0 || value instanceof AbortSignal) return;
|
|
638
|
+
throw new TypeError(`${name} signal must be an AbortSignal`);
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Rejects options the adapter does not implement, shared by the `Locator` and
|
|
642
|
+
* `ElementHandle` forms of a member so both reject the same option with the
|
|
643
|
+
* same message. Returns the normalized `delay`, unwrapped like the pointer
|
|
644
|
+
* options.
|
|
645
|
+
*/
|
|
646
|
+
function rejectUnsupportedOptions(method, options, supported = []) {
|
|
647
|
+
if (!options) return void 0;
|
|
648
|
+
const unsupported = Object.keys(options).filter((key) => options[key] !== void 0 && !supported.includes(key));
|
|
649
|
+
if (unsupported.length > 0) throw new Error(`${method}(): unsupported Playwright option(s): ${unsupported.join(", ")}.`);
|
|
650
|
+
validateSignal(method, options.signal);
|
|
651
|
+
if (supported.includes("noWaitAfter")) validateNoWaitAfter(method, options.noWaitAfter);
|
|
652
|
+
return supported.includes("delay") ? validateDelay(options.delay) : void 0;
|
|
653
|
+
}
|
|
654
|
+
function validateNoWaitAfter(method, value) {
|
|
655
|
+
if (method !== "click" && method !== "press") return;
|
|
656
|
+
if (value === void 0 || typeof value === "boolean" || value instanceof Boolean) return;
|
|
657
|
+
throw new Error(`noWaitAfter: expected boolean, got ${typeof value}`);
|
|
658
|
+
}
|
|
659
|
+
//#endregion
|
|
483
660
|
//#region src/elementHandle.ts
|
|
484
661
|
/**
|
|
485
|
-
* A browser-native, fixed reference to one
|
|
662
|
+
* A browser-native, fixed reference to one node in the controlled document.
|
|
486
663
|
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
664
|
+
* Pinned dom.ts:120 derives ElementHandle from JSHandle; the node members below
|
|
665
|
+
* extend the handle members with it. This deliberately is not a Locator:
|
|
666
|
+
* selector operations remain scoped to `element`, even after the document
|
|
667
|
+
* replaces a matching node.
|
|
489
668
|
*/
|
|
490
|
-
var AdapterElementHandle = class {
|
|
669
|
+
var AdapterElementHandle = class extends AdapterJSHandle {
|
|
491
670
|
ownerPage;
|
|
492
|
-
|
|
493
|
-
disposed = false;
|
|
671
|
+
disposedError = "ElementHandle has been disposed";
|
|
494
672
|
constructor(ownerPage, element) {
|
|
673
|
+
super(element, ownerPage.evaluation);
|
|
495
674
|
this.ownerPage = ownerPage;
|
|
496
|
-
this.element = element;
|
|
497
675
|
}
|
|
498
676
|
async click(options) {
|
|
499
677
|
await this.ownerPage.clickSelector(this.requireElement(), "elementHandle.click", options?.timeout, void 0, options);
|
|
@@ -505,14 +683,79 @@ var AdapterElementHandle = class {
|
|
|
505
683
|
await this.ownerPage.hoverSelector(this.requireElement(), "elementHandle.hover", options?.timeout, void 0, options);
|
|
506
684
|
}
|
|
507
685
|
async check(options) {
|
|
508
|
-
await this.ownerPage.setCheckedSelector(this.requireElement(), true, "elementHandle.check", options);
|
|
686
|
+
await this.ownerPage.setCheckedSelector(this.requireElement(), true, "elementHandle.check", options, void 0, "check");
|
|
509
687
|
}
|
|
510
688
|
async uncheck(options) {
|
|
511
|
-
await this.ownerPage.setCheckedSelector(this.requireElement(), false, "elementHandle.uncheck", options);
|
|
689
|
+
await this.ownerPage.setCheckedSelector(this.requireElement(), false, "elementHandle.uncheck", options, void 0, "uncheck");
|
|
512
690
|
}
|
|
513
691
|
async setChecked(checked, options) {
|
|
514
692
|
await this.ownerPage.setCheckedSelector(this.requireElement(), checked, "elementHandle.setChecked", options);
|
|
515
693
|
}
|
|
694
|
+
async fill(value, options) {
|
|
695
|
+
rejectUnsupportedOptions("fill", options, [
|
|
696
|
+
"force",
|
|
697
|
+
"noWaitAfter",
|
|
698
|
+
"signal",
|
|
699
|
+
"timeout"
|
|
700
|
+
]);
|
|
701
|
+
const force = validateForce("fill", options?.force);
|
|
702
|
+
await withAbortPrefix("elementHandle.fill", () => this.ownerPage.fillSelector(this.requireElement(), value, "elementHandle.fill", options?.timeout, void 0, true, options?.signal, "fill", force));
|
|
703
|
+
}
|
|
704
|
+
async focus() {
|
|
705
|
+
await this.ownerPage.focusSelector(this.requireElement(), "elementHandle.focus");
|
|
706
|
+
}
|
|
707
|
+
async type(text, options) {
|
|
708
|
+
rejectUnsupportedOptions("type", options, [
|
|
709
|
+
"delay",
|
|
710
|
+
"noWaitAfter",
|
|
711
|
+
"signal",
|
|
712
|
+
"timeout"
|
|
713
|
+
]);
|
|
714
|
+
await withAbortPrefix("elementHandle.type", () => this.ownerPage.typeSelector(this.requireElement(), text, options, "elementHandle.type", true));
|
|
715
|
+
}
|
|
716
|
+
async selectOption(values, options) {
|
|
717
|
+
rejectUnsupportedOptions("selectOption", options, [
|
|
718
|
+
"force",
|
|
719
|
+
"noWaitAfter",
|
|
720
|
+
"signal",
|
|
721
|
+
"timeout"
|
|
722
|
+
]);
|
|
723
|
+
const force = validateForce("selectOption", options?.force);
|
|
724
|
+
return withAbortPrefix("elementHandle.selectOption", () => this.ownerPage.selectOptionSelector(this.requireElement(), values, "elementHandle.selectOption", options?.timeout, void 0, true, options?.signal, force));
|
|
725
|
+
}
|
|
726
|
+
async setInputFiles(files, options) {
|
|
727
|
+
rejectUnsupportedOptions("setInputFiles", options, [
|
|
728
|
+
"noWaitAfter",
|
|
729
|
+
"signal",
|
|
730
|
+
"timeout"
|
|
731
|
+
]);
|
|
732
|
+
await withAbortPrefix("elementHandle.setInputFiles", () => this.ownerPage.setInputFilesSelector(this.requireElement(), files, options, true, "elementHandle.setInputFiles"));
|
|
733
|
+
}
|
|
734
|
+
async dispatchEvent(type, eventInit = {}) {
|
|
735
|
+
await this.ownerPage.dispatchEventSelector(this.requireElement(), type, eventInit, "elementHandle.dispatchEvent");
|
|
736
|
+
}
|
|
737
|
+
async press(key, options) {
|
|
738
|
+
const delay = rejectUnsupportedOptions("press", options, [
|
|
739
|
+
"delay",
|
|
740
|
+
"noWaitAfter",
|
|
741
|
+
"signal",
|
|
742
|
+
"timeout"
|
|
743
|
+
]);
|
|
744
|
+
await withAbortPrefix("elementHandle.press", () => this.ownerPage.pressSelector(this.requireElement(), key, "elementHandle.press", options?.timeout, void 0, true, options?.signal, delay));
|
|
745
|
+
}
|
|
746
|
+
async selectText(options) {
|
|
747
|
+
rejectUnsupportedOptions("selectText", options, [
|
|
748
|
+
"force",
|
|
749
|
+
"signal",
|
|
750
|
+
"timeout"
|
|
751
|
+
]);
|
|
752
|
+
const force = validateForce("selectText", options?.force);
|
|
753
|
+
await withAbortPrefix("elementHandle.selectText", () => this.ownerPage.selectText(this.requireElement(), "elementHandle.selectText", options?.timeout, void 0, options?.signal, force));
|
|
754
|
+
}
|
|
755
|
+
async scrollIntoViewIfNeeded(options) {
|
|
756
|
+
rejectUnsupportedOptions("scrollIntoViewIfNeeded", options, ["signal", "timeout"]);
|
|
757
|
+
await withAbortPrefix("elementHandle.scrollIntoViewIfNeeded", () => this.ownerPage.scrollLocatorIntoView(this.requireElement(), "elementHandle.scrollIntoViewIfNeeded", options?.timeout, void 0, options?.signal));
|
|
758
|
+
}
|
|
516
759
|
async $(selector) {
|
|
517
760
|
return this.ownerPage.elementHandleFor(this.ownerPage.resolveWithinElement(this.requireElement(), selector, false));
|
|
518
761
|
}
|
|
@@ -534,6 +777,10 @@ var AdapterElementHandle = class {
|
|
|
534
777
|
assertEvaluationOptions(options);
|
|
535
778
|
return this.ownerPage.evaluation.byValue(pageFunction, typeof pageFunction === "function", arg, this.requireElement());
|
|
536
779
|
}
|
|
780
|
+
/** Kept here so releasing a node is attributed to ElementHandle. */
|
|
781
|
+
async dispose() {
|
|
782
|
+
await super.dispose();
|
|
783
|
+
}
|
|
537
784
|
async textContent() {
|
|
538
785
|
return this.requireElement().textContent;
|
|
539
786
|
}
|
|
@@ -554,6 +801,14 @@ var AdapterElementHandle = class {
|
|
|
554
801
|
asElement() {
|
|
555
802
|
return this;
|
|
556
803
|
}
|
|
804
|
+
/** Pinned dom.ts:135 previews a node through the injected script. */
|
|
805
|
+
computePreview() {
|
|
806
|
+
try {
|
|
807
|
+
return `JSHandle@${this.ownerPage.previewNode(this.requireElement())}`;
|
|
808
|
+
} catch {
|
|
809
|
+
return "JSHandle@node";
|
|
810
|
+
}
|
|
811
|
+
}
|
|
557
812
|
async isEnabled() {
|
|
558
813
|
return this.ownerPage.elementStateForHandle(this.requireElement(), "enabled");
|
|
559
814
|
}
|
|
@@ -578,43 +833,30 @@ var AdapterElementHandle = class {
|
|
|
578
833
|
return this.ownerPage.boundingBoxForElement(this.requireElement());
|
|
579
834
|
}
|
|
580
835
|
async waitForElementState(state, options = {}) {
|
|
581
|
-
await this.ownerPage.waitForElementState(this.requireElement(), state, options);
|
|
836
|
+
await withAbortPrefix("elementHandle.waitForElementState", () => this.ownerPage.waitForElementState(this.requireElement(), state, options));
|
|
582
837
|
}
|
|
583
838
|
async waitForSelector(selector, options = {}) {
|
|
584
839
|
return await this.ownerPage.waitForSelectorWithinElement(this.requireElement(), selector, options);
|
|
585
840
|
}
|
|
586
|
-
async dispose() {
|
|
587
|
-
this.disposed = true;
|
|
588
|
-
this.element = void 0;
|
|
589
|
-
}
|
|
590
|
-
/** Internal boundary used only by Page evaluation argument unwrapping. */
|
|
591
|
-
elementForEvaluation(ownerPage) {
|
|
592
|
-
if (ownerPage !== this.ownerPage) throw new Error("ElementHandle belongs to a different Page");
|
|
593
|
-
return this.requireElement();
|
|
594
|
-
}
|
|
595
841
|
requireElement() {
|
|
596
|
-
|
|
597
|
-
if (!this.element) throw new Error("ElementHandle has been disposed");
|
|
598
|
-
return this.element;
|
|
842
|
+
return this.valueForEvaluation(this.ownerPage.evaluation);
|
|
599
843
|
}
|
|
600
844
|
};
|
|
601
845
|
//#endregion
|
|
602
846
|
//#region src/evaluation.ts
|
|
603
|
-
const invalidArguments = "Too many arguments. If you need to pass more than 1 argument to the function wrap them in an object.";
|
|
604
|
-
function assertEvaluationOptions(options) {
|
|
605
|
-
if (options !== void 0 && (typeof options !== "object" || options === null || Array.isArray(options))) throw new Error(invalidArguments);
|
|
606
|
-
if (options?.exposeFunctions === true) throw new Error("Unsupported Playwright option: evaluate.exposeFunctions");
|
|
607
|
-
if (options?.exposeFunctions !== void 0 && typeof options.exposeFunctions !== "boolean") throw new Error("exposeFunctions must be a boolean");
|
|
608
|
-
}
|
|
609
|
-
function assertMaxArguments(count, maximum) {
|
|
610
|
-
if (count > maximum) throw new Error(invalidArguments);
|
|
611
|
-
}
|
|
612
847
|
/** Uses the pinned UtilityScript for by-value calls without a browser protocol. */
|
|
613
848
|
var Evaluation = class {
|
|
614
849
|
page;
|
|
615
850
|
utility;
|
|
851
|
+
/**
|
|
852
|
+
* The pinned protocol reports a node as a remote-object subtype. In the
|
|
853
|
+
* document that test is `instanceof Node`, so the constructor is captured
|
|
854
|
+
* before a page script can delete the global.
|
|
855
|
+
*/
|
|
856
|
+
node;
|
|
616
857
|
constructor(page) {
|
|
617
858
|
this.page = page;
|
|
859
|
+
this.node = page.window.Node;
|
|
618
860
|
}
|
|
619
861
|
get script() {
|
|
620
862
|
return this.utility ??= new LiteUtilityScript(this.page.window, false);
|
|
@@ -622,7 +864,7 @@ var Evaluation = class {
|
|
|
622
864
|
argument(value) {
|
|
623
865
|
const references = [];
|
|
624
866
|
const protocolValue = serializeValue(value, (candidate) => {
|
|
625
|
-
if (candidate instanceof
|
|
867
|
+
if (candidate instanceof AdapterJSHandle) {
|
|
626
868
|
references.push(candidate);
|
|
627
869
|
return { h: references.length - 1 };
|
|
628
870
|
}
|
|
@@ -632,10 +874,6 @@ var Evaluation = class {
|
|
|
632
874
|
const handles = [];
|
|
633
875
|
return {
|
|
634
876
|
serialized: serializeAsCallArgument$1(copy, (candidate) => {
|
|
635
|
-
if (candidate instanceof AdapterElementHandle) {
|
|
636
|
-
handles.push(candidate.elementForEvaluation(this.page));
|
|
637
|
-
return { h: handles.length - 1 };
|
|
638
|
-
}
|
|
639
877
|
if (candidate instanceof AdapterJSHandle) {
|
|
640
878
|
handles.push(candidate.valueForEvaluation(this));
|
|
641
879
|
return { h: handles.length - 1 };
|
|
@@ -645,31 +883,57 @@ var Evaluation = class {
|
|
|
645
883
|
handles
|
|
646
884
|
};
|
|
647
885
|
}
|
|
886
|
+
/** Replaces the handles inside a protocol argument with their values. */
|
|
887
|
+
unwrapHandles(value) {
|
|
888
|
+
const { serialized, handles } = this.argument(value);
|
|
889
|
+
return parseEvaluationResultValue$1(serialized, handles);
|
|
890
|
+
}
|
|
891
|
+
/** Pinned crExecutionContext.ts:142 answers a node with an ElementHandle. */
|
|
892
|
+
handleFor(value) {
|
|
893
|
+
return value instanceof this.node ? new AdapterElementHandle(this.page, value) : new AdapterJSHandle(value, this);
|
|
894
|
+
}
|
|
648
895
|
async byValue(expression, isFunction, arg, target) {
|
|
896
|
+
return protocolResult(parseEvaluationResultValue$1(await this.run(expression, isFunction, true, arg, target)));
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Pinned javascript.ts:249 evaluates with `returnByValue: false`, and the
|
|
900
|
+
* protocol's `awaitPromise` settles a returned promise before it hands back
|
|
901
|
+
* the handle.
|
|
902
|
+
*/
|
|
903
|
+
async byHandle(expression, isFunction, arg, target) {
|
|
904
|
+
return this.handleFor(await this.run(expression, isFunction, false, arg, target));
|
|
905
|
+
}
|
|
906
|
+
async run(expression, isFunction, returnByValue, arg, target) {
|
|
649
907
|
const normalized = normalizeExpression(String(expression), isFunction);
|
|
650
908
|
const { serialized, handles } = this.argument(arg);
|
|
651
909
|
const parameters = [serialized];
|
|
652
910
|
if (target !== void 0) {
|
|
653
|
-
handles.push(target);
|
|
911
|
+
handles.push(target instanceof AdapterJSHandle ? target.valueForEvaluation(this) : target);
|
|
654
912
|
parameters.unshift({ h: handles.length - 1 });
|
|
655
913
|
}
|
|
656
914
|
try {
|
|
657
|
-
return
|
|
915
|
+
return await this.script.evaluate(isFunction, returnByValue, normalized, parameters.length, ...parameters, ...handles);
|
|
658
916
|
} catch (error) {
|
|
659
917
|
throw evaluationError(error);
|
|
660
918
|
}
|
|
661
919
|
}
|
|
662
|
-
/**
|
|
920
|
+
/**
|
|
921
|
+
* Deserialize once, retaining predicate argument state across polls.
|
|
922
|
+
*
|
|
923
|
+
* The returned poll accepts the element a `Locator` predicate is called on,
|
|
924
|
+
* which the pinned element form passes ahead of the argument
|
|
925
|
+
* (`server/frames.ts` `waitForFunctionExpressionOnElement`).
|
|
926
|
+
*/
|
|
663
927
|
predicate(expression, isFunction, arg) {
|
|
664
928
|
const normalized = normalizeExpression(String(expression), isFunction);
|
|
665
929
|
const { serialized, handles } = this.argument(arg);
|
|
666
930
|
const argument = parseEvaluationResultValue$1(serialized, handles);
|
|
667
931
|
let callback;
|
|
668
|
-
return () => {
|
|
932
|
+
return (target) => {
|
|
669
933
|
try {
|
|
670
934
|
const result = callback ?? this.page.window.eval(normalized);
|
|
671
935
|
if (isFunction) callback = result;
|
|
672
|
-
const value = isFunction ? callback(argument) : result;
|
|
936
|
+
const value = isFunction ? callback(...target === void 0 ? [argument] : [target, argument]) : result;
|
|
673
937
|
if (value && typeof value.then === "function") return Promise.resolve(value).catch((error) => {
|
|
674
938
|
throw evaluationError(error);
|
|
675
939
|
});
|
|
@@ -704,28 +968,6 @@ function normalizeExpression(expression, isFunction) {
|
|
|
704
968
|
function evaluationError(error) {
|
|
705
969
|
return new Error(error instanceof Error ? error.stack || String(error) : String(error));
|
|
706
970
|
}
|
|
707
|
-
/** The existing waitForFunction handle. Additional JSHandle methods are unsupported. */
|
|
708
|
-
var AdapterJSHandle = class {
|
|
709
|
-
value;
|
|
710
|
-
evaluation;
|
|
711
|
-
disposed = false;
|
|
712
|
-
constructor(value, evaluation) {
|
|
713
|
-
this.value = value;
|
|
714
|
-
this.evaluation = evaluation;
|
|
715
|
-
}
|
|
716
|
-
valueForEvaluation(evaluation) {
|
|
717
|
-
if (this.evaluation !== evaluation) throw new Error("JSHandles can be evaluated only in the context they were created!");
|
|
718
|
-
if (this.disposed) throw new Error("JSHandle is disposed!");
|
|
719
|
-
return this.value;
|
|
720
|
-
}
|
|
721
|
-
async jsonValue() {
|
|
722
|
-
return this.evaluation.jsonValue(this.valueForEvaluation(this.evaluation));
|
|
723
|
-
}
|
|
724
|
-
async dispose() {
|
|
725
|
-
this.disposed = true;
|
|
726
|
-
this.value = void 0;
|
|
727
|
-
}
|
|
728
|
-
};
|
|
729
971
|
//#endregion
|
|
730
972
|
//#region node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/browser/dist/nodes/identity.js
|
|
731
973
|
const ALIAS = Symbol.for("yaml.alias");
|
|
@@ -14743,58 +14985,23 @@ var AdapterTimeoutError = class extends Error {
|
|
|
14743
14985
|
}
|
|
14744
14986
|
};
|
|
14745
14987
|
//#endregion
|
|
14746
|
-
//#region src/protocolValidation.ts
|
|
14747
|
-
/**
|
|
14748
|
-
* Client-boundary checks from the pinned Playwright protocol semantics.
|
|
14749
|
-
* See packages/protocol/src/validatorPrimitives.ts and spec/frame.yml at
|
|
14750
|
-
* 26a9e470a7b3c7822084b09fb7f13902c5f37b51.
|
|
14751
|
-
*/
|
|
14752
|
-
function validateString(value, name) {
|
|
14753
|
-
if (value instanceof String) return value.valueOf();
|
|
14754
|
-
if (typeof value === "string") return value;
|
|
14755
|
-
throw new Error(`${name}: expected string, got ${typeof value}`);
|
|
14756
|
-
}
|
|
14757
|
-
/**
|
|
14758
|
-
* `delay` is `float?` in the pinned protocol for press, type and the pointer
|
|
14759
|
-
* actions. Like the pointer options, a non-finite value is rejected instead of
|
|
14760
|
-
* reaching the input path as NaN, and a boxed `Number` is unwrapped for the
|
|
14761
|
-
* caller to forward instead of leaking an object into the input path.
|
|
14762
|
-
*/
|
|
14763
|
-
function validateDelay(value) {
|
|
14764
|
-
if (value === void 0) return void 0;
|
|
14765
|
-
const delay = value instanceof Number ? value.valueOf() : value;
|
|
14766
|
-
if (typeof delay !== "number" || !Number.isFinite(delay)) throw new TypeError("delay: expected number");
|
|
14767
|
-
return delay;
|
|
14768
|
-
}
|
|
14769
|
-
/**
|
|
14770
|
-
* `signal` is a client-side option in the pinned API: it never reaches the
|
|
14771
|
-
* protocol, so the adapter checks the value itself. `name` is the API member
|
|
14772
|
-
* or option group the message speaks for, such as `click` or `Query`.
|
|
14773
|
-
*/
|
|
14774
|
-
function validateSignal(name, value) {
|
|
14775
|
-
if (value === void 0 || value instanceof AbortSignal) return;
|
|
14776
|
-
throw new TypeError(`${name} signal must be an AbortSignal`);
|
|
14777
|
-
}
|
|
14778
|
-
function validateNoWaitAfter(method, value) {
|
|
14779
|
-
if (method !== "click" && method !== "press") return;
|
|
14780
|
-
if (value === void 0 || typeof value === "boolean" || value instanceof Boolean) return;
|
|
14781
|
-
throw new Error(`noWaitAfter: expected boolean, got ${typeof value}`);
|
|
14782
|
-
}
|
|
14783
|
-
//#endregion
|
|
14784
14988
|
//#region src/inputFiles.ts
|
|
14785
14989
|
/**
|
|
14786
14990
|
* Pinned 26a9e47 client/elementHandle.ts converts payloads before resolving the
|
|
14787
14991
|
* input; server/fileUploadUtils.ts encodes the bytes for InjectedScript. Keep
|
|
14788
14992
|
* that format without importing Node's Buffer or filesystem into the browser.
|
|
14993
|
+
*
|
|
14994
|
+
* `Locator.drop` carries the same converted payloads in the pinned client, so
|
|
14995
|
+
* `method` names the member whose message a rejected payload belongs to.
|
|
14789
14996
|
*/
|
|
14790
|
-
function inputFilePayloads(files) {
|
|
14997
|
+
function inputFilePayloads(files, method = "setInputFiles") {
|
|
14791
14998
|
const items = Array.isArray(files) ? files : [files];
|
|
14792
|
-
if (items.some((item) => typeof item === "string")) throw new Error(
|
|
14999
|
+
if (items.some((item) => typeof item === "string")) throw new Error(`${method}: file paths are not supported; pass in-memory Playwright payloads.`);
|
|
14793
15000
|
const payloads = items.map((item) => {
|
|
14794
|
-
if (!item || typeof item === "string" || item instanceof Blob || typeof item.name !== "string" || typeof item.mimeType !== "string" || !item.mimeType || !(item.buffer instanceof Uint8Array)) throw new TypeError(
|
|
15001
|
+
if (!item || typeof item === "string" || item instanceof Blob || typeof item.name !== "string" || typeof item.mimeType !== "string" || !item.mimeType || !(item.buffer instanceof Uint8Array)) throw new TypeError(`${method}: expected { name, mimeType, buffer } with a non-empty MIME type and byte buffer; File and Blob are not supported.`);
|
|
14795
15002
|
return item;
|
|
14796
15003
|
});
|
|
14797
|
-
if (payloads.reduce((size, item) => size + item.buffer.byteLength, 0) >= 52428800) throw new Error(
|
|
15004
|
+
if (payloads.reduce((size, item) => size + item.buffer.byteLength, 0) >= 52428800) throw new Error(`${method}: in-memory payloads must total less than 50Mb.`);
|
|
14798
15005
|
return payloads.map((item) => {
|
|
14799
15006
|
let binary = "";
|
|
14800
15007
|
for (let offset = 0; offset < item.buffer.byteLength; offset += 8192) binary += String.fromCharCode(...item.buffer.subarray(offset, offset + 8192));
|
|
@@ -15664,6 +15871,10 @@ var LocatorImpl = class LocatorImpl {
|
|
|
15664
15871
|
assertMaxArguments(arguments.length, 3);
|
|
15665
15872
|
return withAbortPrefix("locator.evaluate", () => this.ownerPage.locatorEvaluate(this.selector, this.label, pageFunction, arg, options));
|
|
15666
15873
|
}
|
|
15874
|
+
async evaluateHandle(pageFunction, arg, options) {
|
|
15875
|
+
assertMaxArguments(arguments.length, 3);
|
|
15876
|
+
return withAbortPrefix("locator.evaluateHandle", () => this.ownerPage.locatorEvaluateHandle(this.selector, this.label, pageFunction, arg, options));
|
|
15877
|
+
}
|
|
15667
15878
|
async evaluateAll(pageFunction, arg) {
|
|
15668
15879
|
assertMaxArguments(arguments.length, 2);
|
|
15669
15880
|
return this.ownerPage.$$eval(this.selector, pageFunction, arg);
|
|
@@ -15676,11 +15887,13 @@ var LocatorImpl = class LocatorImpl {
|
|
|
15676
15887
|
}
|
|
15677
15888
|
async fill(value, options) {
|
|
15678
15889
|
rejectUnsupportedOptions("fill", options, [
|
|
15890
|
+
"force",
|
|
15679
15891
|
"noWaitAfter",
|
|
15680
15892
|
"signal",
|
|
15681
15893
|
"timeout"
|
|
15682
15894
|
]);
|
|
15683
|
-
|
|
15895
|
+
const force = validateForce("fill", options?.force);
|
|
15896
|
+
await withAbortPrefix("locator.fill", () => this.ownerPage.fillSelector(this.selector, value, this.label, options?.timeout, void 0, true, options?.signal, "fill", force));
|
|
15684
15897
|
}
|
|
15685
15898
|
async setInputFiles(files, options) {
|
|
15686
15899
|
rejectUnsupportedOptions("setInputFiles", options, [
|
|
@@ -15690,6 +15903,9 @@ var LocatorImpl = class LocatorImpl {
|
|
|
15690
15903
|
]);
|
|
15691
15904
|
await withAbortPrefix("locator.setInputFiles", () => this.ownerPage.setInputFilesSelector(this.selector, files, options, true));
|
|
15692
15905
|
}
|
|
15906
|
+
async drop(payload, options) {
|
|
15907
|
+
await this.ownerPage.dropSelector(this.selector, this.label, payload, options);
|
|
15908
|
+
}
|
|
15693
15909
|
async press(key, options) {
|
|
15694
15910
|
const delay = rejectUnsupportedOptions("press", options, [
|
|
15695
15911
|
"delay",
|
|
@@ -15709,11 +15925,13 @@ var LocatorImpl = class LocatorImpl {
|
|
|
15709
15925
|
}
|
|
15710
15926
|
async clear(options) {
|
|
15711
15927
|
rejectUnsupportedOptions("clear", options, [
|
|
15928
|
+
"force",
|
|
15712
15929
|
"noWaitAfter",
|
|
15713
15930
|
"signal",
|
|
15714
15931
|
"timeout"
|
|
15715
15932
|
]);
|
|
15716
|
-
|
|
15933
|
+
const force = validateForce("clear", options?.force);
|
|
15934
|
+
await withAbortPrefix("locator.clear", () => this.ownerPage.fillSelector(this.selector, "", this.label, options?.timeout, void 0, true, options?.signal, "clear", force));
|
|
15717
15935
|
}
|
|
15718
15936
|
async hover(options) {
|
|
15719
15937
|
await this.ownerPage.hoverSelector(this.selector, this.label, options?.timeout, void 0, {
|
|
@@ -15751,15 +15969,22 @@ var LocatorImpl = class LocatorImpl {
|
|
|
15751
15969
|
}
|
|
15752
15970
|
async selectOption(values, options) {
|
|
15753
15971
|
rejectUnsupportedOptions("selectOption", options, [
|
|
15972
|
+
"force",
|
|
15754
15973
|
"noWaitAfter",
|
|
15755
15974
|
"signal",
|
|
15756
15975
|
"timeout"
|
|
15757
15976
|
]);
|
|
15758
|
-
|
|
15977
|
+
const force = validateForce("selectOption", options?.force);
|
|
15978
|
+
return withAbortPrefix("locator.selectOption", () => this.ownerPage.selectOptionSelector(this.selector, values, this.label, options?.timeout, void 0, true, options?.signal, force));
|
|
15759
15979
|
}
|
|
15760
15980
|
async selectText(options) {
|
|
15761
|
-
rejectUnsupportedOptions("selectText", options, [
|
|
15762
|
-
|
|
15981
|
+
rejectUnsupportedOptions("selectText", options, [
|
|
15982
|
+
"force",
|
|
15983
|
+
"signal",
|
|
15984
|
+
"timeout"
|
|
15985
|
+
]);
|
|
15986
|
+
const force = validateForce("selectText", options?.force);
|
|
15987
|
+
await withAbortPrefix("locator.selectText", () => this.ownerPage.selectText(this.selector, this.label, options?.timeout, void 0, options?.signal, force));
|
|
15763
15988
|
}
|
|
15764
15989
|
async scrollIntoViewIfNeeded(options) {
|
|
15765
15990
|
rejectUnsupportedOptions("scrollIntoViewIfNeeded", options, ["signal", "timeout"]);
|
|
@@ -15793,6 +16018,10 @@ var LocatorImpl = class LocatorImpl {
|
|
|
15793
16018
|
timeout: options.timeout
|
|
15794
16019
|
}, this.label));
|
|
15795
16020
|
}
|
|
16021
|
+
async waitForFunction(pageFunction, arg, options = {}) {
|
|
16022
|
+
rejectUnsupportedOptions("waitForFunction", options, ["signal", "timeout"]);
|
|
16023
|
+
await this.ownerPage.waitForFunctionOnSelector(this.selector, this.label, pageFunction, arg, options);
|
|
16024
|
+
}
|
|
15796
16025
|
};
|
|
15797
16026
|
/**
|
|
15798
16027
|
* Single validation helper for the structured brand payload.
|
|
@@ -15805,15 +16034,6 @@ function requireBrand(value, context) {
|
|
|
15805
16034
|
if (typeof payload !== "object" || payload === null || typeof payload.getSelector !== "function" || typeof payload.resolveElements !== "function") throw new TypeError(`${context}: expected an PlaywrightLite Locator, got incompatible branded object`);
|
|
15806
16035
|
return payload;
|
|
15807
16036
|
}
|
|
15808
|
-
/** Returns the normalized `delay`, unwrapped like the pointer options. */
|
|
15809
|
-
function rejectUnsupportedOptions(method, options, supported = []) {
|
|
15810
|
-
if (!options) return void 0;
|
|
15811
|
-
const unsupported = Object.keys(options).filter((key) => options[key] !== void 0 && !supported.includes(key));
|
|
15812
|
-
if (unsupported.length > 0) throw new Error(`${method}(): unsupported Playwright option(s): ${unsupported.join(", ")}.`);
|
|
15813
|
-
validateSignal(method, options.signal);
|
|
15814
|
-
if (supported.includes("noWaitAfter")) validateNoWaitAfter(method, options.noWaitAfter);
|
|
15815
|
-
return supported.includes("delay") ? validateDelay(options.delay) : void 0;
|
|
15816
|
-
}
|
|
15817
16037
|
function cssObjectToString(style) {
|
|
15818
16038
|
return Object.entries(style).map(([key, value]) => {
|
|
15819
16039
|
return `${key.startsWith("--") ? key : key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`)}: ${value}`;
|
|
@@ -15860,6 +16080,11 @@ var PageImpl = class PageImpl {
|
|
|
15860
16080
|
_injected;
|
|
15861
16081
|
_injectedTestIdAttributeName;
|
|
15862
16082
|
pointerTarget;
|
|
16083
|
+
/** Pinned input.ts Mouse starts at the document origin and tracks its moves. */
|
|
16084
|
+
pointerPosition = {
|
|
16085
|
+
x: 0,
|
|
16086
|
+
y: 0
|
|
16087
|
+
};
|
|
15863
16088
|
defaultTimeout;
|
|
15864
16089
|
defaultNavigationTimeout;
|
|
15865
16090
|
constructor(browserWindow, testIdAttribute = DEFAULT_TEST_ID_ATTRIBUTE) {
|
|
@@ -15909,6 +16134,9 @@ var PageImpl = class PageImpl {
|
|
|
15909
16134
|
elementHandleFor(element) {
|
|
15910
16135
|
return element ? new AdapterElementHandle(this, element) : null;
|
|
15911
16136
|
}
|
|
16137
|
+
previewNode(node) {
|
|
16138
|
+
return this.injected.previewNode(node);
|
|
16139
|
+
}
|
|
15912
16140
|
requireSingle(selector, label) {
|
|
15913
16141
|
const element = this.resolveLocatorElement(selector, true);
|
|
15914
16142
|
if (!element) throw new Error(`No elements found for locator ${label}`);
|
|
@@ -15964,7 +16192,7 @@ var PageImpl = class PageImpl {
|
|
|
15964
16192
|
return await withAbortPrefix("page.waitForSelector", () => this.waitForSelectorInRoot(this.document, selector, options));
|
|
15965
16193
|
}
|
|
15966
16194
|
async waitForSelectorWithinElement(root, selector, options = {}) {
|
|
15967
|
-
return await this.waitForSelectorInRoot(root, selector, options, false);
|
|
16195
|
+
return await withAbortPrefix("elementHandle.waitForSelector", () => this.waitForSelectorInRoot(root, selector, options, "elementHandle.waitForSelector", false));
|
|
15968
16196
|
}
|
|
15969
16197
|
/**
|
|
15970
16198
|
* Adapts the client Locator._expect protocol to InjectedScript.expect.
|
|
@@ -16057,7 +16285,7 @@ var PageImpl = class PageImpl {
|
|
|
16057
16285
|
try {
|
|
16058
16286
|
if (options.modifiers) await this.keyboard.ensureModifiers(options.modifiers, deadline);
|
|
16059
16287
|
this.assertActionDeadline(deadline, action);
|
|
16060
|
-
await this.movePointer(target.point, deadline, action);
|
|
16288
|
+
await this.movePointer(target.point, deadline, action, options.steps);
|
|
16061
16289
|
if (!options.trial && action !== "hover") await this.dispatchClick(target.element, target.point, action === "dblclick" ? 2 : options.clickCount ?? 1, options, deadline, action);
|
|
16062
16290
|
} finally {
|
|
16063
16291
|
interception = interceptor?.stop() ?? "done";
|
|
@@ -16092,15 +16320,21 @@ var PageImpl = class PageImpl {
|
|
|
16092
16320
|
if (!checked && "isRadio" in state && state.isRadio) throw new Error("Cannot uncheck radio button. Radio buttons can only be unchecked by selecting another radio button in the same group.");
|
|
16093
16321
|
return state.matches === checked;
|
|
16094
16322
|
}
|
|
16095
|
-
async fillSelector(selector, value, label, timeout, deadline = this.createActionDeadline(timeout), strict = true, signal) {
|
|
16323
|
+
async fillSelector(selector, value, label, timeout, deadline = this.createActionDeadline(timeout), strict = true, signal, apiMethod = "fill", force = false) {
|
|
16324
|
+
value = validateString(value, "value");
|
|
16096
16325
|
this.attachActionSignal(deadline, signal);
|
|
16097
16326
|
const { element } = await this.retryActionability(selector, label, "fill", [
|
|
16098
16327
|
"visible",
|
|
16099
16328
|
"enabled",
|
|
16100
16329
|
"editable"
|
|
16101
|
-
], false, deadline, void 0, void 0, strict);
|
|
16330
|
+
], false, deadline, void 0, void 0, strict, force);
|
|
16102
16331
|
this.assertActionDeadline(deadline, "fill");
|
|
16103
|
-
|
|
16332
|
+
let result;
|
|
16333
|
+
try {
|
|
16334
|
+
result = this.actionableInjected.fill(element, value);
|
|
16335
|
+
} catch (error) {
|
|
16336
|
+
throw injectedFillError(asError(error), selector, label, apiMethod, force);
|
|
16337
|
+
}
|
|
16104
16338
|
if (result === "error:notconnected") throw new Error(`Element is not connected for locator ${label}`);
|
|
16105
16339
|
if (result === "done") return;
|
|
16106
16340
|
if (result !== "needsinput") throw new Error(`Unexpected fill result for locator ${label}: ${result}`);
|
|
@@ -16110,10 +16344,10 @@ var PageImpl = class PageImpl {
|
|
|
16110
16344
|
}
|
|
16111
16345
|
async pressSelector(selector, key, label, timeout, deadline = this.createActionDeadline(timeout), strict = true, signal, delay) {
|
|
16112
16346
|
this.attachActionSignal(deadline, signal);
|
|
16113
|
-
const element = await this.query(selector, label, {
|
|
16347
|
+
const element = typeof selector === "string" ? await this.query(selector, label, {
|
|
16114
16348
|
signal,
|
|
16115
16349
|
timeout
|
|
16116
|
-
}, strict, (candidate) => candidate, deadline);
|
|
16350
|
+
}, strict, (candidate) => candidate, deadline) : this.resolvePointerElement(selector, label, strict);
|
|
16117
16351
|
this.assertActionDeadline(deadline, "press");
|
|
16118
16352
|
this.focusElement(element);
|
|
16119
16353
|
await this.keyboard.press(key, { delay }, deadline);
|
|
@@ -16133,21 +16367,21 @@ var PageImpl = class PageImpl {
|
|
|
16133
16367
|
await this.performPointerAction(selector, label, "hover", options, deadline);
|
|
16134
16368
|
}
|
|
16135
16369
|
async setCheckedSelector(selector, checked, label, options = {}, deadline = this.createActionDeadline(options.timeout), apiMethod = "setChecked") {
|
|
16136
|
-
options = assertPointerActionOptions(
|
|
16370
|
+
options = assertPointerActionOptions(apiMethod, options);
|
|
16137
16371
|
if (typeof checked !== "boolean") throw new TypeError("checked must be a boolean");
|
|
16138
16372
|
await this.performPointerAction(selector, label, "click", options, deadline, checked, apiMethod);
|
|
16139
16373
|
}
|
|
16140
|
-
async selectOptionSelector(selector, values, label, timeout, deadline = this.createActionDeadline(timeout), strict = true, signal) {
|
|
16374
|
+
async selectOptionSelector(selector, values, label, timeout, deadline = this.createActionDeadline(timeout), strict = true, signal, force = false) {
|
|
16375
|
+
const options = selectOptionValues(values, this.evaluation);
|
|
16141
16376
|
this.attachActionSignal(deadline, signal);
|
|
16142
|
-
const options = (values === null ? [] : Array.isArray(values) ? values : [values]).map((value) => typeof value === "string" ? { valueOrLabel: value } : value);
|
|
16143
16377
|
let lastError;
|
|
16144
16378
|
while (true) {
|
|
16145
16379
|
if (Date.now() >= deadline.expiresAt) throw new AdapterTimeoutError(`select option: Timeout ${deadline.timeout}ms exceeded.${lastError ? ` ${lastError.message}` : ""}`, { cause: lastError });
|
|
16146
|
-
const { element } = await this.retryActionability(selector, label, "select option", ["visible", "enabled"], false, deadline, void 0, void 0, strict);
|
|
16380
|
+
const { element } = await this.retryActionability(selector, label, "select option", ["visible", "enabled"], false, deadline, void 0, void 0, strict, force);
|
|
16147
16381
|
this.assertActionDeadline(deadline, "select option");
|
|
16148
16382
|
const result = this.actionableInjected.selectOptions(element, options);
|
|
16149
16383
|
if (Array.isArray(result)) return result;
|
|
16150
|
-
lastError = result === "error:optionnotenabled" ? /* @__PURE__ */ new Error("
|
|
16384
|
+
lastError = result === "error:optionnotenabled" ? /* @__PURE__ */ new Error("option being selected is not enabled") : result === "error:notconnected" ? /* @__PURE__ */ new Error(`Element is not connected for locator ${label}`) : /* @__PURE__ */ new Error("Options not found");
|
|
16151
16385
|
const remaining = deadline.expiresAt - Date.now();
|
|
16152
16386
|
if (remaining <= 0) throw new AdapterTimeoutError(`select option: Timeout ${deadline.timeout}ms exceeded. ${lastError.message}`, { cause: lastError });
|
|
16153
16387
|
try {
|
|
@@ -16158,15 +16392,15 @@ var PageImpl = class PageImpl {
|
|
|
16158
16392
|
}
|
|
16159
16393
|
}
|
|
16160
16394
|
}
|
|
16161
|
-
async selectText(selector, label, timeout, deadline = this.createActionDeadline(timeout), signal) {
|
|
16395
|
+
async selectText(selector, label, timeout, deadline = this.createActionDeadline(timeout), signal, force = false) {
|
|
16162
16396
|
this.attachActionSignal(deadline, signal);
|
|
16163
|
-
const { element } = await this.retryActionability(selector, label, "select text", ["visible"], false, deadline);
|
|
16397
|
+
const { element } = await this.retryActionability(selector, label, "select text", ["visible"], false, deadline, void 0, void 0, void 0, force);
|
|
16164
16398
|
this.assertActionDeadline(deadline, "select text");
|
|
16165
16399
|
if (this.actionableInjected.selectText(element) === "error:notconnected") throw new Error(`Element is not connected for locator ${label}`);
|
|
16166
16400
|
}
|
|
16167
16401
|
async scrollLocatorIntoView(selector, label, timeout, deadline = this.createActionDeadline(timeout), signal) {
|
|
16168
16402
|
this.attachActionSignal(deadline, signal);
|
|
16169
|
-
const { element } = await this.retryActionability(selector, label, "scroll into view", ["stable"], false, deadline);
|
|
16403
|
+
const { element } = await this.retryActionability(selector, label, "scroll into view", ["stable"], false, deadline, void 0, {});
|
|
16170
16404
|
this.assertActionDeadline(deadline, "scroll into view");
|
|
16171
16405
|
this.scrollIntoViewIfNeeded(element);
|
|
16172
16406
|
}
|
|
@@ -16184,13 +16418,13 @@ var PageImpl = class PageImpl {
|
|
|
16184
16418
|
throw new AdapterTimeoutError(`locator.waitFor: Timeout ${timeout}ms exceeded.\nCall log:\n - waiting for ${formatLocator(selector)} to be ${options.state}\n - Timed out waiting for ${label} to become ${options.state}.`, { cause: error });
|
|
16185
16419
|
}
|
|
16186
16420
|
}
|
|
16187
|
-
async setInputFilesSelector(selector, files, options = {}, strict = false) {
|
|
16421
|
+
async setInputFilesSelector(selector, files, options = {}, strict = false, label = typeof selector === "string" ? selector : "elementHandle") {
|
|
16188
16422
|
assertPageActionOptions("setInputFiles", options, ["noWaitAfter", "strict"]);
|
|
16189
16423
|
if (options.strict !== void 0 && typeof options.strict !== "boolean") throw new TypeError("setInputFiles strict must be a boolean");
|
|
16190
16424
|
const payloads = inputFilePayloads(files);
|
|
16191
16425
|
const deadline = this.createActionDeadline(options.timeout);
|
|
16192
16426
|
this.attachActionSignal(deadline, options.signal);
|
|
16193
|
-
await this.query(selector,
|
|
16427
|
+
await this.query(selector, label, {
|
|
16194
16428
|
signal: options.signal,
|
|
16195
16429
|
timeout: options.timeout
|
|
16196
16430
|
}, strict || options.strict === true, (element) => {
|
|
@@ -16205,6 +16439,148 @@ var PageImpl = class PageImpl {
|
|
|
16205
16439
|
}, deadline);
|
|
16206
16440
|
}
|
|
16207
16441
|
/**
|
|
16442
|
+
* Simulates an external drag-and-drop onto the resolved element.
|
|
16443
|
+
*
|
|
16444
|
+
* Mirrors pinned 26a9e47 `server/dom.ts` ElementHandle._drop: the element is
|
|
16445
|
+
* awaited visible and stable (never enabled), then one synthetic
|
|
16446
|
+
* `DataTransfer` carries the payload through `dragenter`, `dragover` and
|
|
16447
|
+
* `drop` at the action point. A `dragover` handler that does not call
|
|
16448
|
+
* `preventDefault()` rejects the drop, which ends with `dragleave`.
|
|
16449
|
+
*/
|
|
16450
|
+
async dropSelector(selector, label, payload, options = {}, strict = true) {
|
|
16451
|
+
assertPageActionOptions("drop", options, ["position"]);
|
|
16452
|
+
const files = payload?.files === void 0 ? [] : inputFilePayloads(payload.files, "drop");
|
|
16453
|
+
const data = Object.entries(payload?.data ?? {});
|
|
16454
|
+
if (files.length === 0 && data.length === 0) throw new Error("At least one of \"files\" or \"data\" must be provided.");
|
|
16455
|
+
const deadline = this.createActionDeadline(options.timeout);
|
|
16456
|
+
this.attachActionSignal(deadline, options.signal);
|
|
16457
|
+
const { element, point } = await this.retryActionability(selector, label, "drop", ["visible", "stable"], true, deadline, options.position, {
|
|
16458
|
+
...options,
|
|
16459
|
+
strict
|
|
16460
|
+
});
|
|
16461
|
+
this.assertActionDeadline(deadline, "drop");
|
|
16462
|
+
this.dispatchDrop(element, point, files, data);
|
|
16463
|
+
}
|
|
16464
|
+
/** The page function of pinned `server/dom.ts` ElementHandle._drop. */
|
|
16465
|
+
dispatchDrop(element, point, files, data) {
|
|
16466
|
+
const transfer = new this.window.DataTransfer();
|
|
16467
|
+
for (const file of files) {
|
|
16468
|
+
const bytes = Uint8Array.from(this.window.atob(file.buffer), (character) => character.charCodeAt(0));
|
|
16469
|
+
transfer.items.add(new this.window.File([bytes], file.name, { type: file.mimeType }));
|
|
16470
|
+
}
|
|
16471
|
+
for (const [mimeType, value] of data) transfer.setData(mimeType, value);
|
|
16472
|
+
const dragEvent = (type) => new this.window.DragEvent(type, {
|
|
16473
|
+
bubbles: true,
|
|
16474
|
+
cancelable: true,
|
|
16475
|
+
composed: true,
|
|
16476
|
+
clientX: point.x,
|
|
16477
|
+
clientY: point.y,
|
|
16478
|
+
dataTransfer: transfer
|
|
16479
|
+
});
|
|
16480
|
+
element.dispatchEvent(dragEvent("dragenter"));
|
|
16481
|
+
const over = dragEvent("dragover");
|
|
16482
|
+
element.dispatchEvent(over);
|
|
16483
|
+
if (!over.defaultPrevented) {
|
|
16484
|
+
element.dispatchEvent(dragEvent("dragleave"));
|
|
16485
|
+
throw new Error("Drop target did not accept the drop — its dragover handler did not call preventDefault()");
|
|
16486
|
+
}
|
|
16487
|
+
element.dispatchEvent(dragEvent("drop"));
|
|
16488
|
+
}
|
|
16489
|
+
/**
|
|
16490
|
+
* Adds a `<script>` tag to the controlled document.
|
|
16491
|
+
*
|
|
16492
|
+
* Mirrors pinned 26a9e47 `server/frames.ts` Frame._addScriptTag and its
|
|
16493
|
+
* `addScriptUrl`/`addScriptContent` page functions: a `url` script resolves
|
|
16494
|
+
* on its load event and fails naming the source, while `content` is injected
|
|
16495
|
+
* as `text/javascript` unless `type` says otherwise.
|
|
16496
|
+
*/
|
|
16497
|
+
async addScriptTag(options = {}) {
|
|
16498
|
+
const { url = null, content = null, type = "" } = assertAddTagOptions("addScriptTag", options);
|
|
16499
|
+
return await this.raceWithCSPError(async () => {
|
|
16500
|
+
const script = this.document.createElement("script");
|
|
16501
|
+
if (url !== null) {
|
|
16502
|
+
script.src = url;
|
|
16503
|
+
if (type) script.type = type;
|
|
16504
|
+
await this.appendLoadedTag(script, () => `Failed to load script at ${script.src}`);
|
|
16505
|
+
return this.elementHandleFor(script);
|
|
16506
|
+
}
|
|
16507
|
+
script.type = type || "text/javascript";
|
|
16508
|
+
script.text = content;
|
|
16509
|
+
let error = null;
|
|
16510
|
+
script.onerror = (event) => error = event;
|
|
16511
|
+
this.document.head.appendChild(script);
|
|
16512
|
+
if (error) throw error;
|
|
16513
|
+
await new Promise((resolve) => this.window.setTimeout(resolve));
|
|
16514
|
+
return this.elementHandleFor(script);
|
|
16515
|
+
});
|
|
16516
|
+
}
|
|
16517
|
+
/**
|
|
16518
|
+
* Adds a `<link rel="stylesheet">` or `<style type="text/css">` tag to the
|
|
16519
|
+
* controlled document.
|
|
16520
|
+
*
|
|
16521
|
+
* Mirrors pinned 26a9e47 `server/frames.ts` Frame._addStyleTag and its
|
|
16522
|
+
* `addStyleUrl`/`addStyleContent` page functions, which resolve on the load
|
|
16523
|
+
* event of the inserted tag. The pinned page functions reject with the raw
|
|
16524
|
+
* error event; this names the stylesheet instead, as the pinned script tag
|
|
16525
|
+
* does.
|
|
16526
|
+
*/
|
|
16527
|
+
async addStyleTag(options = {}) {
|
|
16528
|
+
const { url = null, content = null } = assertAddTagOptions("addStyleTag", options);
|
|
16529
|
+
return await this.raceWithCSPError(async () => {
|
|
16530
|
+
if (url !== null) {
|
|
16531
|
+
const link = this.document.createElement("link");
|
|
16532
|
+
link.rel = "stylesheet";
|
|
16533
|
+
link.href = url;
|
|
16534
|
+
await this.appendLoadedTag(link, () => `Failed to load style at ${link.href}`);
|
|
16535
|
+
return this.elementHandleFor(link);
|
|
16536
|
+
}
|
|
16537
|
+
const style = this.document.createElement("style");
|
|
16538
|
+
style.type = "text/css";
|
|
16539
|
+
style.appendChild(this.document.createTextNode(content));
|
|
16540
|
+
await this.appendLoadedTag(style, () => "Failed to apply the injected style content");
|
|
16541
|
+
return this.elementHandleFor(style);
|
|
16542
|
+
});
|
|
16543
|
+
}
|
|
16544
|
+
/** Appends a tag to the document head and settles on its load event. */
|
|
16545
|
+
appendLoadedTag(tag, failure) {
|
|
16546
|
+
const loaded = new Promise((resolve, reject) => {
|
|
16547
|
+
tag.onload = () => resolve();
|
|
16548
|
+
tag.onerror = () => reject(new Error(failure()));
|
|
16549
|
+
});
|
|
16550
|
+
this.document.head.appendChild(tag);
|
|
16551
|
+
return loaded;
|
|
16552
|
+
}
|
|
16553
|
+
/**
|
|
16554
|
+
* Fails a tag insertion the document's Content Security Policy blocked.
|
|
16555
|
+
*
|
|
16556
|
+
* Mirrors pinned 26a9e47 `server/frames.ts` Frame._raceWithCSPError, which
|
|
16557
|
+
* races the insertion against the browser's CSP report. That report reaches
|
|
16558
|
+
* the pinned code as a console message; inside the document the browser
|
|
16559
|
+
* reports the same violation as a `securitypolicyviolation` event.
|
|
16560
|
+
*/
|
|
16561
|
+
async raceWithCSPError(action) {
|
|
16562
|
+
let violation;
|
|
16563
|
+
let onViolation = () => {};
|
|
16564
|
+
const violated = new Promise((resolve) => {
|
|
16565
|
+
onViolation = (event) => {
|
|
16566
|
+
violation = event;
|
|
16567
|
+
resolve();
|
|
16568
|
+
};
|
|
16569
|
+
});
|
|
16570
|
+
this.document.addEventListener("securitypolicyviolation", onViolation);
|
|
16571
|
+
let result;
|
|
16572
|
+
let error;
|
|
16573
|
+
const completed = action().then((value) => void (result = value), (reason) => void (error = reason));
|
|
16574
|
+
try {
|
|
16575
|
+
await Promise.race([completed, violated]);
|
|
16576
|
+
} finally {
|
|
16577
|
+
this.document.removeEventListener("securitypolicyviolation", onViolation);
|
|
16578
|
+
}
|
|
16579
|
+
if (violation) throw new Error(`Content Security Policy directive "${violation.violatedDirective}" blocked ${violation.blockedURI || "an inline resource"}`);
|
|
16580
|
+
if (error) throw error;
|
|
16581
|
+
return result;
|
|
16582
|
+
}
|
|
16583
|
+
/**
|
|
16208
16584
|
* Serializes the controlled document.
|
|
16209
16585
|
*
|
|
16210
16586
|
* Mirrors pinned 26a9e47 `server/frames.ts` Frame._content: serialize the
|
|
@@ -16268,8 +16644,13 @@ var PageImpl = class PageImpl {
|
|
|
16268
16644
|
});
|
|
16269
16645
|
}
|
|
16270
16646
|
async fill(selector, value, options) {
|
|
16271
|
-
assertPageActionOptions("fill", options, [
|
|
16272
|
-
|
|
16647
|
+
assertPageActionOptions("fill", options, [
|
|
16648
|
+
"force",
|
|
16649
|
+
"noWaitAfter",
|
|
16650
|
+
"strict"
|
|
16651
|
+
]);
|
|
16652
|
+
const force = validateForce("fill", options?.force);
|
|
16653
|
+
await withAbortPrefix("page.fill", () => this.fillSelector(selector, value, `page.fill(${JSON.stringify(selector)})`, options?.timeout, void 0, options?.strict === true, options?.signal, "fill", force));
|
|
16273
16654
|
}
|
|
16274
16655
|
async setInputFiles(selector, files, options) {
|
|
16275
16656
|
await withAbortPrefix("page.setInputFiles", () => this.setInputFilesSelector(selector, files, options));
|
|
@@ -16315,20 +16696,25 @@ var PageImpl = class PageImpl {
|
|
|
16315
16696
|
});
|
|
16316
16697
|
}
|
|
16317
16698
|
async selectOption(selector, values, options) {
|
|
16318
|
-
assertPageActionOptions("selectOption", options, [
|
|
16319
|
-
|
|
16699
|
+
assertPageActionOptions("selectOption", options, [
|
|
16700
|
+
"force",
|
|
16701
|
+
"noWaitAfter",
|
|
16702
|
+
"strict"
|
|
16703
|
+
]);
|
|
16704
|
+
const force = validateForce("selectOption", options?.force);
|
|
16705
|
+
return withAbortPrefix("page.selectOption", () => this.selectOptionSelector(selector, values, `page.selectOption(${JSON.stringify(selector)})`, options?.timeout, void 0, options?.strict === true, options?.signal, force));
|
|
16320
16706
|
}
|
|
16321
16707
|
async check(selector, options) {
|
|
16322
16708
|
await this.setCheckedSelector(selector, true, `page.check(${JSON.stringify(selector)})`, {
|
|
16323
16709
|
...options,
|
|
16324
16710
|
strict: options?.strict ?? false
|
|
16325
|
-
});
|
|
16711
|
+
}, void 0, "check");
|
|
16326
16712
|
}
|
|
16327
16713
|
async uncheck(selector, options) {
|
|
16328
16714
|
await this.setCheckedSelector(selector, false, `page.uncheck(${JSON.stringify(selector)})`, {
|
|
16329
16715
|
...options,
|
|
16330
16716
|
strict: options?.strict ?? false
|
|
16331
|
-
});
|
|
16717
|
+
}, void 0, "uncheck");
|
|
16332
16718
|
}
|
|
16333
16719
|
async setChecked(selector, checked, options) {
|
|
16334
16720
|
await this.setCheckedSelector(selector, checked, `page.setChecked(${JSON.stringify(selector)})`, {
|
|
@@ -16348,10 +16734,11 @@ var PageImpl = class PageImpl {
|
|
|
16348
16734
|
}
|
|
16349
16735
|
async dispatchEventSelector(selector, type, eventInit, label, timeout, deadline = this.createActionDeadline(timeout), strict = true, signal) {
|
|
16350
16736
|
this.attachActionSignal(deadline, signal);
|
|
16737
|
+
const init = this.evaluation.unwrapHandles(eventInit);
|
|
16351
16738
|
await this.query(selector, label, {
|
|
16352
16739
|
signal,
|
|
16353
16740
|
timeout
|
|
16354
|
-
}, strict, (element) => this.actionableInjected.dispatchEvent(element, type,
|
|
16741
|
+
}, strict, (element) => this.actionableInjected.dispatchEvent(element, type, init), deadline);
|
|
16355
16742
|
}
|
|
16356
16743
|
/**
|
|
16357
16744
|
* Pinned 26a9e47 client/frame.ts and server/frames.ts default to load,
|
|
@@ -16460,6 +16847,12 @@ var PageImpl = class PageImpl {
|
|
|
16460
16847
|
assertEvaluationOptions(options);
|
|
16461
16848
|
return this._evaluateExpression(pageFunction, typeof pageFunction === "function", arg);
|
|
16462
16849
|
}
|
|
16850
|
+
/** Keeps the result in the document, referenced by a handle. */
|
|
16851
|
+
async evaluateHandle(pageFunction, arg, options) {
|
|
16852
|
+
assertMaxArguments(arguments.length, 3);
|
|
16853
|
+
assertEvaluationOptions(options);
|
|
16854
|
+
return this.evaluation.byHandle(pageFunction, typeof pageFunction === "function", arg);
|
|
16855
|
+
}
|
|
16463
16856
|
/** Evaluates through the pinned Playwright UtilityScript. */
|
|
16464
16857
|
async $eval(selector, callback, arg) {
|
|
16465
16858
|
assertMaxArguments(arguments.length, 3);
|
|
@@ -16483,28 +16876,39 @@ var PageImpl = class PageImpl {
|
|
|
16483
16876
|
}
|
|
16484
16877
|
/**
|
|
16485
16878
|
* Polls a predicate in the controlled document until it returns a
|
|
16486
|
-
* truthy value.
|
|
16487
|
-
*
|
|
16488
|
-
* Mirrors pinned 26a9e47 server/frames.ts:1626-1694:
|
|
16489
|
-
* - pollingInterval must be >0 (frames.ts:1628)
|
|
16490
|
-
* - expression is normalized (frames.ts:1629)
|
|
16491
|
-
* - isFunction=true → eval once, call each poll (frames.ts:1640-1642)
|
|
16492
|
-
* - isFunction=false → re-eval each poll (frames.ts:1643-1644,
|
|
16493
|
-
* since evaledExpression is never cached)
|
|
16494
|
-
* - abort mechanism cleans up pending timers (frames.ts:1679-1681)
|
|
16495
|
-
* - timeout races independently (handles never-settling predicates)
|
|
16879
|
+
* truthy value. Public API: derives isFunction from typeof pageFunction.
|
|
16496
16880
|
*
|
|
16497
16881
|
* Returns a minimal handle with `jsonValue()` and `dispose()`,
|
|
16498
16882
|
* mirroring pinned client JSHandle interface.
|
|
16499
16883
|
*/
|
|
16500
|
-
/**
|
|
16501
|
-
* Public API: derives isFunction from typeof pageFunction.
|
|
16502
|
-
*/
|
|
16503
16884
|
async waitForFunction(pageFunction, arg, options) {
|
|
16504
16885
|
return this._waitForFunctionExpression(typeof pageFunction === "function" ? pageFunction : String(pageFunction), typeof pageFunction === "function", arg, options);
|
|
16505
16886
|
}
|
|
16506
16887
|
/**
|
|
16507
16888
|
* Internal: accepts explicit isFunction for bridge transport.
|
|
16889
|
+
*/
|
|
16890
|
+
async _waitForFunctionExpression(pageFunction, isFunction, arg, options) {
|
|
16891
|
+
const value = await this.pollPredicate("page.waitForFunction", pageFunction, isFunction, arg, options);
|
|
16892
|
+
return this.evaluation.handleFor(value);
|
|
16893
|
+
}
|
|
16894
|
+
/**
|
|
16895
|
+
* The `Locator` form of the member. Mirrors pinned 26a9e47
|
|
16896
|
+
* server/frames.ts:1696-1712 `waitForFunctionExpressionOnElement`: the
|
|
16897
|
+
* selector is re-resolved strictly on every poll and its element is passed
|
|
16898
|
+
* to the predicate ahead of `arg`, so a re-rendered element is tolerated.
|
|
16899
|
+
* The pinned client discards the predicate's value (`client/locator.ts`
|
|
16900
|
+
* `waitForFunction` awaits the channel call and returns nothing), and it
|
|
16901
|
+
* sends neither `polling` nor a handle back.
|
|
16902
|
+
*/
|
|
16903
|
+
async waitForFunctionOnSelector(selector, label, pageFunction, arg, options) {
|
|
16904
|
+
await this.pollPredicate("locator.waitForFunction", pageFunction, typeof pageFunction === "function", arg, options, {
|
|
16905
|
+
selector,
|
|
16906
|
+
label
|
|
16907
|
+
});
|
|
16908
|
+
}
|
|
16909
|
+
/**
|
|
16910
|
+
* Shared polling engine of both forms. Resolves with the first truthy
|
|
16911
|
+
* predicate value.
|
|
16508
16912
|
*
|
|
16509
16913
|
* Mirrors pinned 26a9e47 server/frames.ts:1626-1694:
|
|
16510
16914
|
* - pollingInterval must be >0 (frames.ts:1628)
|
|
@@ -16514,38 +16918,66 @@ var PageImpl = class PageImpl {
|
|
|
16514
16918
|
* since evaledExpression is never cached)
|
|
16515
16919
|
* - abort mechanism cleans up pending timers (frames.ts:1679-1681)
|
|
16516
16920
|
* - timeout races independently (handles never-settling predicates)
|
|
16921
|
+
*
|
|
16922
|
+
* `target` names the locator of the element form: it is re-resolved
|
|
16923
|
+
* strictly on every poll, so a locator that matches nothing yet keeps
|
|
16924
|
+
* polling while a strict mode violation ends the wait.
|
|
16517
16925
|
*/
|
|
16518
|
-
async
|
|
16926
|
+
async pollPredicate(apiName, pageFunction, isFunction, arg, options, target) {
|
|
16519
16927
|
const timeout = this.resolveTimeout(options?.timeout, 3e4);
|
|
16928
|
+
validateSignal("waitForFunction", options?.signal);
|
|
16929
|
+
const signal = options?.signal;
|
|
16520
16930
|
const predicate = this.evaluation.predicate(pageFunction, isFunction, arg);
|
|
16521
16931
|
const polling = options?.polling ?? "raf";
|
|
16522
16932
|
if (typeof polling === "string" && polling !== "raf") throw new Error("Unknown polling option: " + polling);
|
|
16523
16933
|
if (typeof polling === "number" && polling <= 0) throw new Error("Cannot poll with non-positive interval: " + polling);
|
|
16524
|
-
return new Promise((resolve, reject) => {
|
|
16934
|
+
return withAbortPrefix(apiName, () => new Promise((resolve, reject) => {
|
|
16525
16935
|
let aborted = false;
|
|
16526
16936
|
let timeoutId;
|
|
16527
16937
|
let pollTimerId;
|
|
16528
16938
|
let rafId;
|
|
16939
|
+
if (signal?.aborted) {
|
|
16940
|
+
reject(actionAborted(signal, false));
|
|
16941
|
+
return;
|
|
16942
|
+
}
|
|
16529
16943
|
if (timeout > 0) timeoutId = this.window.setTimeout(() => {
|
|
16530
16944
|
cleanup();
|
|
16531
|
-
reject(new AdapterTimeoutError(
|
|
16945
|
+
reject(new AdapterTimeoutError(`${apiName}: Timeout ${timeout}ms exceeded.` + (target ? queryCallLog(target.selector) : "")));
|
|
16532
16946
|
}, timeout);
|
|
16533
16947
|
const cleanup = () => {
|
|
16534
16948
|
aborted = true;
|
|
16949
|
+
signal?.removeEventListener("abort", onAbort);
|
|
16535
16950
|
if (timeoutId !== void 0) this.window.clearTimeout(timeoutId);
|
|
16536
16951
|
if (pollTimerId !== void 0) this.window.clearTimeout(pollTimerId);
|
|
16537
16952
|
if (rafId !== void 0) this.window.cancelAnimationFrame(rafId);
|
|
16538
16953
|
};
|
|
16954
|
+
const onAbort = () => {
|
|
16955
|
+
cleanup();
|
|
16956
|
+
reject(actionAborted(signal, true));
|
|
16957
|
+
};
|
|
16958
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
16539
16959
|
const check = () => {
|
|
16540
16960
|
if (aborted) return;
|
|
16961
|
+
let element;
|
|
16962
|
+
if (target) try {
|
|
16963
|
+
element = this.queryElement(target.selector, target.label, true);
|
|
16964
|
+
} catch (e) {
|
|
16965
|
+
if (!isRetryableQueryError(e)) {
|
|
16966
|
+
cleanup();
|
|
16967
|
+
reject(e);
|
|
16968
|
+
return;
|
|
16969
|
+
}
|
|
16970
|
+
scheduleNext();
|
|
16971
|
+
return;
|
|
16972
|
+
}
|
|
16541
16973
|
try {
|
|
16542
|
-
const result = predicate();
|
|
16974
|
+
const result = predicate(element);
|
|
16543
16975
|
if (result && typeof result?.then === "function") {
|
|
16544
16976
|
result.then((v) => {
|
|
16545
16977
|
if (aborted) return;
|
|
16546
16978
|
if (v) {
|
|
16547
16979
|
cleanup();
|
|
16548
|
-
resolve(
|
|
16980
|
+
resolve(v);
|
|
16549
16981
|
} else scheduleNext();
|
|
16550
16982
|
}, (e) => {
|
|
16551
16983
|
if (aborted) return;
|
|
@@ -16556,7 +16988,7 @@ var PageImpl = class PageImpl {
|
|
|
16556
16988
|
}
|
|
16557
16989
|
if (result) {
|
|
16558
16990
|
cleanup();
|
|
16559
|
-
resolve(
|
|
16991
|
+
resolve(result);
|
|
16560
16992
|
return;
|
|
16561
16993
|
}
|
|
16562
16994
|
} catch (e) {
|
|
@@ -16572,7 +17004,7 @@ var PageImpl = class PageImpl {
|
|
|
16572
17004
|
else pollTimerId = this.window.setTimeout(check, polling);
|
|
16573
17005
|
};
|
|
16574
17006
|
check();
|
|
16575
|
-
});
|
|
17007
|
+
}));
|
|
16576
17008
|
}
|
|
16577
17009
|
getByRole(role, options = {}) {
|
|
16578
17010
|
const roleSelector = getByRoleSelector(role, options);
|
|
@@ -16693,10 +17125,16 @@ var PageImpl = class PageImpl {
|
|
|
16693
17125
|
}, true, (element) => this.injectedAriaSnapshot(element, options));
|
|
16694
17126
|
}
|
|
16695
17127
|
async locatorEvaluate(selector, label, pageFunction, arg, options) {
|
|
17128
|
+
return this.evaluation.byValue(pageFunction, typeof pageFunction === "function", arg, await this.locatorEvaluationTarget(selector, label, options));
|
|
17129
|
+
}
|
|
17130
|
+
async locatorEvaluateHandle(selector, label, pageFunction, arg, options) {
|
|
17131
|
+
return this.evaluation.byHandle(pageFunction, typeof pageFunction === "function", arg, await this.locatorEvaluationTarget(selector, label, options));
|
|
17132
|
+
}
|
|
17133
|
+
/** Pinned locator.ts resolves the element before it evaluates against it. */
|
|
17134
|
+
async locatorEvaluationTarget(selector, label, options) {
|
|
16696
17135
|
assertEvaluationOptions(options);
|
|
16697
17136
|
const { exposeFunctions: _exposeFunctions, ...queryOptions } = options ?? {};
|
|
16698
|
-
|
|
16699
|
-
return this.evaluation.byValue(pageFunction, typeof pageFunction === "function", arg, element);
|
|
17137
|
+
return this.query(selector, label, queryOptions, true, (element) => element);
|
|
16700
17138
|
}
|
|
16701
17139
|
async queryState(selector, state, options, strict, label = `page.is${state[0].toUpperCase()}${state.slice(1)}(${JSON.stringify(selector)})`) {
|
|
16702
17140
|
return this.query(selector, label, options, strict, (element) => {
|
|
@@ -16729,6 +17167,7 @@ var PageImpl = class PageImpl {
|
|
|
16729
17167
|
async waitForElementState(element, state, options = {}) {
|
|
16730
17168
|
assertElementHandleStateOptions(state, options);
|
|
16731
17169
|
const deadline = this.createActionDeadline(options.timeout);
|
|
17170
|
+
this.attachActionSignal(deadline, options.signal);
|
|
16732
17171
|
const timeoutError = () => new AdapterTimeoutError(`elementHandle.waitForElementState: Timeout ${deadline.timeout}ms exceeded.`);
|
|
16733
17172
|
while (true) {
|
|
16734
17173
|
if (deadline.expiresAt !== Infinity && Date.now() >= deadline.expiresAt) throw timeoutError();
|
|
@@ -16737,7 +17176,7 @@ var PageImpl = class PageImpl {
|
|
|
16737
17176
|
await this.wait(deadline.expiresAt === Infinity ? 10 : Math.min(10, deadline.expiresAt - Date.now()));
|
|
16738
17177
|
}
|
|
16739
17178
|
}
|
|
16740
|
-
async waitForSelectorInRoot(root, selector, options, allowsStrict = true) {
|
|
17179
|
+
async waitForSelectorInRoot(root, selector, options, apiName = "page.waitForSelector", allowsStrict = true) {
|
|
16741
17180
|
assertWaitForSelectorOptions(options, allowsStrict);
|
|
16742
17181
|
const state = options.state ?? "visible";
|
|
16743
17182
|
const timeout = this.resolveTimeout(options.timeout, DEFAULT_ACTION_TIMEOUT);
|
|
@@ -16749,7 +17188,7 @@ var PageImpl = class PageImpl {
|
|
|
16749
17188
|
const visible = !!element && this.elementState(element, "visible").matches === true;
|
|
16750
17189
|
if (state === "attached" && element || state === "visible" && visible) return this.elementHandleFor(element);
|
|
16751
17190
|
if (state === "detached" && !element || state === "hidden" && !visible) return null;
|
|
16752
|
-
if (Date.now() >= deadline) throw new AdapterTimeoutError(
|
|
17191
|
+
if (Date.now() >= deadline) throw new AdapterTimeoutError(`${apiName}: Timeout ${timeout}ms exceeded.\nCall log:\n - waiting for ${formatLocator(selector)} to be ${state}`);
|
|
16753
17192
|
const delay = Math.min(QUERY_RETRY_DELAY, deadline - Date.now());
|
|
16754
17193
|
if (!await waitForExpectationRetry(this.window, delay, signal)) throw actionAborted(signal, true);
|
|
16755
17194
|
}
|
|
@@ -16778,11 +17217,11 @@ var PageImpl = class PageImpl {
|
|
|
16778
17217
|
if (signal?.aborted) throw actionAborted(signal, false);
|
|
16779
17218
|
const deadline = actionDeadline?.expiresAt ?? (timeout === 0 ? Infinity : Date.now() + timeout);
|
|
16780
17219
|
while (true) try {
|
|
16781
|
-
return await evaluate(this.
|
|
17220
|
+
return await evaluate(this.resolvePointerElement(selector, label, strict || options?.strict === true));
|
|
16782
17221
|
} catch (error) {
|
|
16783
17222
|
if (!isRetryableQueryError(error)) throw error;
|
|
16784
17223
|
const remaining = deadline - Date.now();
|
|
16785
|
-
if (remaining <= 0) throw new AdapterTimeoutError(`Timeout ${timeout}ms exceeded
|
|
17224
|
+
if (remaining <= 0) throw new AdapterTimeoutError(`Timeout ${timeout}ms exceeded.${queryCallLog(selector)}`, { cause: error });
|
|
16786
17225
|
const delay = Math.min(QUERY_RETRY_DELAY, remaining);
|
|
16787
17226
|
if (!await waitForExpectationRetry(this.window, delay, signal)) throw actionAborted(signal, true);
|
|
16788
17227
|
}
|
|
@@ -16838,7 +17277,7 @@ var PageImpl = class PageImpl {
|
|
|
16838
17277
|
}
|
|
16839
17278
|
return /* @__PURE__ */ new Error("Element is not stable");
|
|
16840
17279
|
}
|
|
16841
|
-
async retryActionability(selector, label, actionName, states, checkHitTarget, deadline, position, pointerOptions, strict = pointerOptions?.strict ?? true) {
|
|
17280
|
+
async retryActionability(selector, label, actionName, states, checkHitTarget, deadline, position, pointerOptions, strict = pointerOptions?.strict ?? true, force = pointerOptions?.force ?? false) {
|
|
16842
17281
|
let lastError;
|
|
16843
17282
|
let retry = 0;
|
|
16844
17283
|
const log = [];
|
|
@@ -16851,10 +17290,12 @@ var PageImpl = class PageImpl {
|
|
|
16851
17290
|
if (Date.now() >= deadline.expiresAt) throwTimeout();
|
|
16852
17291
|
try {
|
|
16853
17292
|
const element = this.resolvePointerElement(selector, label, strict);
|
|
16854
|
-
if (pointerOptions && !
|
|
16855
|
-
if (!
|
|
17293
|
+
if (pointerOptions && !force) log.push(` - waiting for element to be ${states.includes("enabled") ? "visible, enabled and stable" : "visible and stable"}`);
|
|
17294
|
+
if (!force) await this.ensureActionable(element, states, deadline);
|
|
16856
17295
|
if (Date.now() >= deadline.expiresAt) throwTimeout();
|
|
16857
|
-
if (actionName
|
|
17296
|
+
if (actionName === "scroll into view") {
|
|
17297
|
+
if (!this.hasLayoutBox(element)) throw new Error("Element is not visible");
|
|
17298
|
+
} else if (pointerOptions?.scroll !== "none") {
|
|
16858
17299
|
if (!pointerOptions || position) this.scrollIntoView(element, position);
|
|
16859
17300
|
else if (retry % 4 === 0) this.scrollIntoViewIfNeeded(element);
|
|
16860
17301
|
else element.scrollIntoView({
|
|
@@ -16871,8 +17312,8 @@ var PageImpl = class PageImpl {
|
|
|
16871
17312
|
behavior: "instant"
|
|
16872
17313
|
});
|
|
16873
17314
|
}
|
|
16874
|
-
if (!
|
|
16875
|
-
const point = checkHitTarget ? this.ensureReceivesEvents(element, position,
|
|
17315
|
+
if (!force) await this.ensureActionable(element, states, deadline);
|
|
17316
|
+
const point = checkHitTarget ? this.ensureReceivesEvents(element, position, force) : actionPoint(element, position, this.window);
|
|
16876
17317
|
if (Date.now() >= deadline.expiresAt) throwTimeout();
|
|
16877
17318
|
return {
|
|
16878
17319
|
element,
|
|
@@ -16880,7 +17321,7 @@ var PageImpl = class PageImpl {
|
|
|
16880
17321
|
};
|
|
16881
17322
|
} catch (error) {
|
|
16882
17323
|
if (typeof selector !== "string" && !selector.isConnected) throw new Error("Element is not attached to the DOM", { cause: error });
|
|
16883
|
-
if (!isRetryableActionError(error) ||
|
|
17324
|
+
if (!isRetryableActionError(error) || force && !asError(error).message.startsWith("No elements found for locator")) throw error;
|
|
16884
17325
|
lastError = asError(error);
|
|
16885
17326
|
const remaining = deadline.expiresAt - Date.now();
|
|
16886
17327
|
const delay = pointerOptions ? [
|
|
@@ -16906,6 +17347,17 @@ var PageImpl = class PageImpl {
|
|
|
16906
17347
|
}
|
|
16907
17348
|
}
|
|
16908
17349
|
}
|
|
17350
|
+
/**
|
|
17351
|
+
* Whether the element is rendered at all. `display: contents` has no box of
|
|
17352
|
+
* its own, so its contents answer for it, exactly as the scroll below does.
|
|
17353
|
+
*/
|
|
17354
|
+
hasLayoutBox(element) {
|
|
17355
|
+
if (element.getClientRects().length > 0) return true;
|
|
17356
|
+
if (this.window.getComputedStyle(element).display !== "contents") return false;
|
|
17357
|
+
const range = this.document.createRange();
|
|
17358
|
+
range.selectNodeContents(element);
|
|
17359
|
+
return range.getClientRects().length > 0;
|
|
17360
|
+
}
|
|
16909
17361
|
scrollIntoView(element, position) {
|
|
16910
17362
|
if (typeof element.scrollIntoView !== "function") return;
|
|
16911
17363
|
element.scrollIntoView({
|
|
@@ -17006,7 +17458,20 @@ var PageImpl = class PageImpl {
|
|
|
17006
17458
|
}
|
|
17007
17459
|
}));
|
|
17008
17460
|
}
|
|
17009
|
-
|
|
17461
|
+
/**
|
|
17462
|
+
* Mirrors pinned input.ts Mouse.move: `steps` interpolated positions between
|
|
17463
|
+
* the pointer's previous location and `point`, the last landing exactly on
|
|
17464
|
+
* it. `steps` defaults to 1, a single move to the destination.
|
|
17465
|
+
*/
|
|
17466
|
+
async movePointer(point, deadline, action, steps = 1) {
|
|
17467
|
+
const from = this.pointerPosition;
|
|
17468
|
+
this.pointerPosition = point;
|
|
17469
|
+
for (let step = 1; step <= steps; step++) await this.movePointerTo({
|
|
17470
|
+
x: from.x + (point.x - from.x) * (step / steps),
|
|
17471
|
+
y: from.y + (point.y - from.y) * (step / steps)
|
|
17472
|
+
}, deadline, action);
|
|
17473
|
+
}
|
|
17474
|
+
async movePointerTo(point, deadline, action) {
|
|
17010
17475
|
const target = this.eventTargetAtPoint(point);
|
|
17011
17476
|
const previous = this.pointerTarget;
|
|
17012
17477
|
if (previous !== target && previous?.isConnected) {
|
|
@@ -17081,6 +17546,7 @@ var PageImpl = class PageImpl {
|
|
|
17081
17546
|
insertPressedText(element, text, inputType = "insertText", eventData = text, deadline) {
|
|
17082
17547
|
this.assertActionDeadline(deadline, "press");
|
|
17083
17548
|
if (!isEditableElement(element, this.window)) return;
|
|
17549
|
+
if (this.insertTextAtCaret(element, text, inputType)) return;
|
|
17084
17550
|
if (isFillableInputWithoutSelection(element, this.window)) {
|
|
17085
17551
|
element.value += text;
|
|
17086
17552
|
this.dispatchInputEvent(element, eventData, inputType);
|
|
@@ -17088,10 +17554,30 @@ var PageImpl = class PageImpl {
|
|
|
17088
17554
|
}
|
|
17089
17555
|
this.replaceSelectedText(element, text, inputType, eventData);
|
|
17090
17556
|
}
|
|
17091
|
-
|
|
17557
|
+
/**
|
|
17558
|
+
* Inserts text at the caret the browser itself keeps, dispatching its own
|
|
17559
|
+
* input event. Playwright types through the browser's editing engine; the
|
|
17560
|
+
* pinned in-document input emulation reaches for this editing command for
|
|
17561
|
+
* the same reason (coreBundle `_insertText`). It is the only script
|
|
17562
|
+
* primitive that finds the caret in an input type that exposes no selection
|
|
17563
|
+
* API, and in an editable host inside a shadow root, whose selection the
|
|
17564
|
+
* document reports retargeted to the host.
|
|
17565
|
+
*/
|
|
17566
|
+
insertTextAtCaret(element, text, inputType) {
|
|
17567
|
+
if (inputType !== "insertText") return false;
|
|
17568
|
+
if (this.deepActiveElement() !== element) return false;
|
|
17569
|
+
return this.document.execCommand("insertText", false, text);
|
|
17570
|
+
}
|
|
17571
|
+
deepActiveElement() {
|
|
17572
|
+
let target = this.document.activeElement ?? this.document.body;
|
|
17573
|
+
while (target.shadowRoot?.mode === "open" && target.shadowRoot.activeElement) target = target.shadowRoot.activeElement;
|
|
17574
|
+
return target;
|
|
17575
|
+
}
|
|
17576
|
+
insertKeyboardText(element, text, inputType = "insertText", eventData = text, deadline, typedByKey = false) {
|
|
17092
17577
|
this.assertActionDeadline(deadline, "press");
|
|
17093
17578
|
if (!isEditableElement(element, this.window)) return;
|
|
17094
17579
|
if (!this.dispatchBeforeInput(element, eventData, inputType)) return;
|
|
17580
|
+
if (typedByKey && !this.dispatchTextInput(element, text)) return;
|
|
17095
17581
|
this.assertActionDeadline(deadline, "press");
|
|
17096
17582
|
this.insertPressedText(element, text, inputType, eventData, deadline);
|
|
17097
17583
|
}
|
|
@@ -17172,6 +17658,11 @@ var PageImpl = class PageImpl {
|
|
|
17172
17658
|
inputType
|
|
17173
17659
|
}));
|
|
17174
17660
|
}
|
|
17661
|
+
dispatchTextInput(element, text) {
|
|
17662
|
+
const event = this.document.createEvent("TextEvent");
|
|
17663
|
+
event.initTextEvent("textInput", true, true, this.window, text);
|
|
17664
|
+
return element.dispatchEvent(event);
|
|
17665
|
+
}
|
|
17175
17666
|
dispatchInputEvent(element, data = null, inputType = "insertText") {
|
|
17176
17667
|
const InputEvent = this.window.InputEvent;
|
|
17177
17668
|
element.dispatchEvent(InputEvent ? new InputEvent("input", {
|
|
@@ -17403,7 +17894,7 @@ var BrowserKeyboard = class {
|
|
|
17403
17894
|
if (dispatchKeyPress) await this.waitForKeyboardPhase(deadline);
|
|
17404
17895
|
if (keyPressAllowed && description.text && description.key !== "Enter") {
|
|
17405
17896
|
this.page.checkKeyboardActionDeadline(deadline);
|
|
17406
|
-
this.page.insertKeyboardText(this.activeTarget(), description.text, "insertText", description.text, deadline);
|
|
17897
|
+
this.page.insertKeyboardText(this.activeTarget(), description.text, "insertText", description.text, deadline, true);
|
|
17407
17898
|
}
|
|
17408
17899
|
if (keyPressAllowed && description.key === "Enter") {
|
|
17409
17900
|
this.page.checkKeyboardActionDeadline(deadline);
|
|
@@ -17423,9 +17914,7 @@ var BrowserKeyboard = class {
|
|
|
17423
17914
|
if (keyDownState?.allowed && keyUpAllowed && keyUpTarget === keyDownState.target && this.activeTarget() === keyDownState.target) this.page.applyKeyupDefault(keyDownState.target, description, this.pressedModifiers);
|
|
17424
17915
|
}
|
|
17425
17916
|
activeTarget() {
|
|
17426
|
-
|
|
17427
|
-
while (target.shadowRoot?.mode === "open" && target.shadowRoot.activeElement) target = target.shadowRoot.activeElement;
|
|
17428
|
-
return target;
|
|
17917
|
+
return this.page.deepActiveElement();
|
|
17429
17918
|
}
|
|
17430
17919
|
descriptionFor(key) {
|
|
17431
17920
|
const resolved = resolveKeyboardKey(key, this.page.window);
|
|
@@ -17554,6 +18043,17 @@ function validateTimeout(timeout, name) {
|
|
|
17554
18043
|
if (typeof timeout !== "number" || timeout < 0 || !Number.isFinite(timeout)) throw new TypeError(`${name} must be a non-negative finite number`);
|
|
17555
18044
|
return timeout;
|
|
17556
18045
|
}
|
|
18046
|
+
/**
|
|
18047
|
+
* Options of `addScriptTag` and `addStyleTag`. The pinned client reads `path`
|
|
18048
|
+
* from disk into `content` before the call leaves the process, which this
|
|
18049
|
+
* runtime cannot do; every other shape reaches the pinned server check for a
|
|
18050
|
+
* `url` or `content` property unexamined, including a non-object argument.
|
|
18051
|
+
*/
|
|
18052
|
+
function assertAddTagOptions(method, options) {
|
|
18053
|
+
if (options?.path !== void 0) throw new Error(`${method}: the \`path\` option is not supported; pass \`url\` or \`content\`.`);
|
|
18054
|
+
if (!options?.url && !options?.content) throw new Error("Provide an object with a `url`, `path` or `content` property");
|
|
18055
|
+
return options;
|
|
18056
|
+
}
|
|
17557
18057
|
/** Returns the normalized `delay`, unwrapped like the pointer options. */
|
|
17558
18058
|
function assertPageActionOptions(method, options, supported = []) {
|
|
17559
18059
|
if (!options) return void 0;
|
|
@@ -17580,7 +18080,7 @@ function assertPointerActionOptions(method, options) {
|
|
|
17580
18080
|
"scroll",
|
|
17581
18081
|
"strict"
|
|
17582
18082
|
];
|
|
17583
|
-
if (method === "click" || method === "dblclick") supported.push("button", "delay", "modifiers");
|
|
18083
|
+
if (method === "click" || method === "dblclick") supported.push("button", "delay", "modifiers", "steps");
|
|
17584
18084
|
if (method === "click") supported.push("clickCount");
|
|
17585
18085
|
if (method === "hover") supported.push("modifiers");
|
|
17586
18086
|
if (!options) return {};
|
|
@@ -17593,7 +18093,11 @@ function assertPointerActionOptions(method, options) {
|
|
|
17593
18093
|
const value = options[key];
|
|
17594
18094
|
if (value instanceof Boolean) options[key] = value.valueOf();
|
|
17595
18095
|
}
|
|
17596
|
-
for (const key of [
|
|
18096
|
+
for (const key of [
|
|
18097
|
+
"delay",
|
|
18098
|
+
"clickCount",
|
|
18099
|
+
"steps"
|
|
18100
|
+
]) {
|
|
17597
18101
|
const value = options[key];
|
|
17598
18102
|
if (value instanceof Number) options[key] = value.valueOf();
|
|
17599
18103
|
}
|
|
@@ -17611,8 +18115,12 @@ function assertPointerActionOptions(method, options) {
|
|
|
17611
18115
|
"right"
|
|
17612
18116
|
].includes(options.button)) throw new TypeError("button: expected one of (left|right|middle)");
|
|
17613
18117
|
if (options.scroll !== void 0 && !["auto", "none"].includes(options.scroll)) throw new TypeError("scroll: expected one of (auto|none)");
|
|
17614
|
-
for (const key of [
|
|
17615
|
-
|
|
18118
|
+
for (const key of [
|
|
18119
|
+
"delay",
|
|
18120
|
+
"clickCount",
|
|
18121
|
+
"steps"
|
|
18122
|
+
]) if (options[key] !== void 0 && (typeof options[key] !== "number" || !Number.isFinite(options[key]))) throw new TypeError(`${key}: expected number`);
|
|
18123
|
+
for (const key of ["clickCount", "steps"]) if (options[key] !== void 0 && !Number.isInteger(options[key])) throw new TypeError(`${key}: expected integer, got float ${options[key]}`);
|
|
17616
18124
|
if (options.modifiers !== void 0 && (!Array.isArray(options.modifiers) || options.modifiers.some((value) => ![
|
|
17617
18125
|
"Alt",
|
|
17618
18126
|
"Control",
|
|
@@ -17659,7 +18167,8 @@ function assertElementHandleStateOptions(state, options) {
|
|
|
17659
18167
|
"disabled",
|
|
17660
18168
|
"editable"
|
|
17661
18169
|
].includes(state)) throw new Error(`Unsupported element state: ${state}`);
|
|
17662
|
-
for (const key of Object.keys(options)) if (key !== "timeout") throw new Error(`Unsupported waitForElementState option: ${key}`);
|
|
18170
|
+
for (const key of Object.keys(options)) if (key !== "signal" && key !== "timeout") throw new Error(`Unsupported waitForElementState option: ${key}`);
|
|
18171
|
+
validateSignal("waitForElementState", options.signal);
|
|
17663
18172
|
if (options.timeout !== void 0) validateTimeout(options.timeout, "waitForElementState timeout");
|
|
17664
18173
|
}
|
|
17665
18174
|
/**
|
|
@@ -17681,9 +18190,56 @@ function isRetryableQueryError(error) {
|
|
|
17681
18190
|
const message = asError(error).message;
|
|
17682
18191
|
return message.startsWith("No elements found for locator") || message === "Element is not connected";
|
|
17683
18192
|
}
|
|
18193
|
+
/**
|
|
18194
|
+
* Mirrors the pinned client's convertSelectOptionValues, which lets the first
|
|
18195
|
+
* entry decide whether the list is read as plain values/labels, followed by the
|
|
18196
|
+
* FrameSelectOptionParams protocol validation of every entry.
|
|
18197
|
+
*/
|
|
18198
|
+
function selectOptionValues(values, evaluation) {
|
|
18199
|
+
if (values === null) return [];
|
|
18200
|
+
const list = Array.isArray(values) ? values : [values];
|
|
18201
|
+
list.forEach((value, index) => {
|
|
18202
|
+
if (value === null) throw new Error(`options[${index}]: expected object, got null`);
|
|
18203
|
+
});
|
|
18204
|
+
if (list[0] instanceof AdapterElementHandle) return list.map((value) => value.valueForEvaluation(evaluation));
|
|
18205
|
+
if (typeof list[0] === "string" || list[0] instanceof String) return list.map((value, index) => ({ valueOrLabel: validateString(value, `options[${index}].valueOrLabel`) }));
|
|
18206
|
+
return list.map((value, index) => validateSelectOptionValue(value, `options[${index}]`));
|
|
18207
|
+
}
|
|
18208
|
+
function validateSelectOptionValue(value, name) {
|
|
18209
|
+
if (typeof value !== "object") throw new Error(`${name}: expected object, got ${typeof value}`);
|
|
18210
|
+
const source = value;
|
|
18211
|
+
const option = {};
|
|
18212
|
+
for (const key of [
|
|
18213
|
+
"valueOrLabel",
|
|
18214
|
+
"value",
|
|
18215
|
+
"label"
|
|
18216
|
+
]) if (source[key] !== void 0) option[key] = validateString(source[key], `${name}.${key}`);
|
|
18217
|
+
if (source.index !== void 0) option.index = validateInteger(source.index, `${name}.index`);
|
|
18218
|
+
return option;
|
|
18219
|
+
}
|
|
17684
18220
|
function formatLocator(selector) {
|
|
17685
18221
|
return `locator('${selector.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}')`;
|
|
17686
18222
|
}
|
|
18223
|
+
/**
|
|
18224
|
+
* The call log line naming what a query waited for. A handle already holds its
|
|
18225
|
+
* element, so pinned ElementHandle diagnostics name no locator to wait for.
|
|
18226
|
+
*/
|
|
18227
|
+
function queryCallLog(selector) {
|
|
18228
|
+
if (typeof selector !== "string") return "";
|
|
18229
|
+
return `\nCall log:\n - waiting for ${formatLocator(selector)}`;
|
|
18230
|
+
}
|
|
18231
|
+
/**
|
|
18232
|
+
* Pinned Playwright reports an InjectedScript fill rejection through the
|
|
18233
|
+
* calling member and the action's call log, not as the bare injected message:
|
|
18234
|
+
* `page.fill: Error: Element is not an <input>, <textarea> or [contenteditable]
|
|
18235
|
+
* element` followed by `Call log:`. `label` carries the Page form the way
|
|
18236
|
+
* performPointerAction derives its prefix.
|
|
18237
|
+
*/
|
|
18238
|
+
function injectedFillError(error, selector, label, apiMethod, force) {
|
|
18239
|
+
const prefix = label.startsWith("page.fill(") ? "page.fill" : label.startsWith("elementHandle.") ? label : `locator.${apiMethod}`;
|
|
18240
|
+
const waitingFor = typeof selector === "string" ? `\n - waiting for ${formatLocator(selector)}` : "";
|
|
18241
|
+
return new Error(`${prefix}: Error: ${error.message}\nCall log:${waitingFor}\n - attempting fill action` + (force ? "" : "\n - waiting for element to be visible, enabled and editable"), { cause: error });
|
|
18242
|
+
}
|
|
17687
18243
|
function presentOriginalXPath(error, selector) {
|
|
17688
18244
|
const source = asError(error);
|
|
17689
18245
|
if (!selector.startsWith("//")) return source;
|