@geometra/proxy 1.65.0 → 1.65.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dom-actions.d.ts.map +1 -1
- package/dist/dom-actions.js +174 -34
- package/dist/dom-actions.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dom-actions.d.ts","sourceRoot":"","sources":["../src/dom-actions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAkC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAE/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEnE,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;AAgDvE,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC,CAAA;IACpC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC,CAAA;IACrC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC,CAAA;CACvC;AAED,wBAAgB,qBAAqB,IAAI,eAAe,CAMvD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAIjE;
|
|
1
|
+
{"version":3,"file":"dom-actions.d.ts","sourceRoot":"","sources":["../src/dom-actions.ts"],"names":[],"mappings":"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"}
|
package/dist/dom-actions.js
CHANGED
|
@@ -527,6 +527,65 @@ async function assertPointMutable(page, x, y, intent, action) {
|
|
|
527
527
|
await element.dispose();
|
|
528
528
|
}
|
|
529
529
|
}
|
|
530
|
+
async function elementOwnsPagePoint(element, x, y) {
|
|
531
|
+
try {
|
|
532
|
+
const bounds = await element.boundingBox();
|
|
533
|
+
if (!bounds || bounds.width <= 0 || bounds.height <= 0 ||
|
|
534
|
+
x < bounds.x || y < bounds.y || x >= bounds.x + bounds.width || y >= bounds.y + bounds.height) {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
return await element.evaluate((target, payload) => {
|
|
538
|
+
const rect = target.getBoundingClientRect();
|
|
539
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
540
|
+
return false;
|
|
541
|
+
const clientX = rect.left + ((payload.x - payload.bounds.x) / payload.bounds.width) * rect.width;
|
|
542
|
+
const clientY = rect.top + ((payload.y - payload.bounds.y) / payload.bounds.height) * rect.height;
|
|
543
|
+
const hit = document.elementFromPoint(clientX, clientY);
|
|
544
|
+
return hit === target || (hit !== null && target.contains(hit));
|
|
545
|
+
}, { x, y, bounds });
|
|
546
|
+
}
|
|
547
|
+
catch {
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async function frameChainOwnsPagePoint(frame, x, y) {
|
|
552
|
+
let current = frame;
|
|
553
|
+
while (true) {
|
|
554
|
+
const parent = current.parentFrame();
|
|
555
|
+
if (!parent)
|
|
556
|
+
return true;
|
|
557
|
+
let frameElement = null;
|
|
558
|
+
try {
|
|
559
|
+
frameElement = await current.frameElement();
|
|
560
|
+
if (!(await elementOwnsPagePoint(frameElement, x, y)))
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
catch {
|
|
564
|
+
return false;
|
|
565
|
+
}
|
|
566
|
+
finally {
|
|
567
|
+
await frameElement?.dispose();
|
|
568
|
+
}
|
|
569
|
+
current = parent;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
async function nativeSelectAtPoint(page, x, y) {
|
|
573
|
+
for (const frame of page.frames()) {
|
|
574
|
+
const selects = await frame.locator('select').elementHandles();
|
|
575
|
+
let match = null;
|
|
576
|
+
for (const select of selects) {
|
|
577
|
+
if (!match && await elementOwnsPagePoint(select, x, y) && await frameChainOwnsPagePoint(frame, x, y)) {
|
|
578
|
+
match = select;
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
await select.dispose();
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (match)
|
|
585
|
+
return match;
|
|
586
|
+
}
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
530
589
|
async function locatorAnchorY(locator) {
|
|
531
590
|
const bounds = await locator.boundingBox();
|
|
532
591
|
return bounds ? bounds.y + bounds.height / 2 : undefined;
|
|
@@ -3332,7 +3391,7 @@ async function clearAmbiguousRetainedFiles(handle, actualNames, initialNames) {
|
|
|
3332
3391
|
}
|
|
3333
3392
|
return 'the changed selection was cleared best-effort without trying another candidate';
|
|
3334
3393
|
}
|
|
3335
|
-
async function commitFilesToExactInput(handle, paths, targetName) {
|
|
3394
|
+
async function commitFilesToExactInput(handle, paths, targetName, setFiles = async (selectedPaths) => handle.setInputFiles(selectedPaths)) {
|
|
3336
3395
|
const { guard, initial } = await beginFileInputCommitGuard(handle);
|
|
3337
3396
|
try {
|
|
3338
3397
|
if (!initial.validFileInput) {
|
|
@@ -3352,7 +3411,7 @@ async function commitFilesToExactInput(handle, paths, targetName) {
|
|
|
3352
3411
|
}
|
|
3353
3412
|
let operationError;
|
|
3354
3413
|
try {
|
|
3355
|
-
await
|
|
3414
|
+
await setFiles(paths);
|
|
3356
3415
|
}
|
|
3357
3416
|
catch (error) {
|
|
3358
3417
|
operationError = error;
|
|
@@ -3426,7 +3485,16 @@ async function attachViaChooser(page, paths, clickX, clickY) {
|
|
|
3426
3485
|
page.mouse.click(clickX, clickY),
|
|
3427
3486
|
]);
|
|
3428
3487
|
const input = chooser.element();
|
|
3429
|
-
|
|
3488
|
+
try {
|
|
3489
|
+
await commitFilesToExactInput(input, paths, 'chooser input', async (selectedPaths) => {
|
|
3490
|
+
await chooser.setFiles(selectedPaths);
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
finally {
|
|
3494
|
+
// Playwright transfers ownership of this ElementHandle to the file-chooser
|
|
3495
|
+
// listener. Release it on success and every preflight/error path.
|
|
3496
|
+
await input.dispose().catch(() => { });
|
|
3497
|
+
}
|
|
3430
3498
|
}
|
|
3431
3499
|
/**
|
|
3432
3500
|
* Map common file extensions to MIME types so react-dropzone's `accept` filter
|
|
@@ -4085,13 +4153,18 @@ async function locatorCurrentValue(locator) {
|
|
|
4085
4153
|
function normalizedFieldValue(value) {
|
|
4086
4154
|
return value.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
4087
4155
|
}
|
|
4088
|
-
function fieldValueMatches(actual, expected) {
|
|
4156
|
+
function fieldValueMatches(actual, expected, inputType) {
|
|
4089
4157
|
if (!actual)
|
|
4090
4158
|
return false;
|
|
4091
4159
|
const normalizedActual = normalizedFieldValue(actual);
|
|
4092
4160
|
const normalizedExpected = normalizedFieldValue(expected);
|
|
4093
4161
|
if (!normalizedActual || !normalizedExpected)
|
|
4094
4162
|
return false;
|
|
4163
|
+
if (inputType?.toLowerCase() === 'tel') {
|
|
4164
|
+
const actualDigits = normalizedActual.replace(/\D/g, '');
|
|
4165
|
+
const expectedDigits = normalizedExpected.replace(/\D/g, '');
|
|
4166
|
+
return actualDigits.length > 0 && actualDigits === expectedDigits;
|
|
4167
|
+
}
|
|
4095
4168
|
return normalizedActual === normalizedExpected;
|
|
4096
4169
|
}
|
|
4097
4170
|
async function setLocatorTextValue(locator, value, opts) {
|
|
@@ -5347,10 +5420,10 @@ export async function setFieldText(page, fieldLabel, value, opts) {
|
|
|
5347
5420
|
}
|
|
5348
5421
|
await delay(40);
|
|
5349
5422
|
const current = await locatorCurrentValue(locator);
|
|
5350
|
-
if (fieldValueMatches(current, normalized))
|
|
5423
|
+
if (fieldValueMatches(current, normalized, inputType))
|
|
5351
5424
|
return;
|
|
5352
5425
|
const displayed = await locatorDisplayedValues(locator);
|
|
5353
|
-
if (displayed.some(candidate => fieldValueMatches(candidate, normalized)))
|
|
5426
|
+
if (displayed.some(candidate => fieldValueMatches(candidate, normalized, inputType)))
|
|
5354
5427
|
return;
|
|
5355
5428
|
throw new Error(`setFieldText: set "${fieldLabel}" but could not confirm value ${JSON.stringify(value)}`);
|
|
5356
5429
|
}
|
|
@@ -6009,52 +6082,119 @@ export async function selectNativeOption(page, x, y, opt) {
|
|
|
6009
6082
|
if (opt.index !== undefined && (!Number.isFinite(opt.index) || !Number.isInteger(opt.index) || opt.index < 0)) {
|
|
6010
6083
|
throw new Error('selectOption: index must be a non-negative integer');
|
|
6011
6084
|
}
|
|
6012
|
-
await
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6085
|
+
const select = await nativeSelectAtPoint(page, x, y);
|
|
6086
|
+
if (!select) {
|
|
6087
|
+
throw new Error('selectOption: supplied coordinates do not target a native <select>');
|
|
6088
|
+
}
|
|
6089
|
+
const payload = {
|
|
6090
|
+
value: opt.value ?? null,
|
|
6091
|
+
label: opt.label ?? null,
|
|
6092
|
+
index: typeof opt.index === 'number' && Number.isFinite(opt.index) ? opt.index : undefined,
|
|
6093
|
+
};
|
|
6094
|
+
try {
|
|
6095
|
+
const reason = await select.evaluate(mutationBlockReasonInPage, 'select');
|
|
6096
|
+
if (reason)
|
|
6097
|
+
throw new Error(`selectOption: matched control is not mutable (${reason})`);
|
|
6098
|
+
const optionReady = await select.evaluate((target, requested) => {
|
|
6099
|
+
if (!(target instanceof HTMLSelectElement))
|
|
6100
|
+
return false;
|
|
6017
6101
|
const normalize = (input) => input?.replace(/\s+/g, ' ').trim().toLowerCase() ?? '';
|
|
6018
6102
|
const enabled = (o) => !o.disabled && !(o.parentElement instanceof HTMLOptGroupElement && o.parentElement.disabled);
|
|
6019
6103
|
const optionMatchesPayload = (o) => {
|
|
6020
6104
|
if (o.disabled || (o.parentElement instanceof HTMLOptGroupElement && o.parentElement.disabled))
|
|
6021
6105
|
return false;
|
|
6022
|
-
if (
|
|
6106
|
+
if (requested.value !== null && normalize(o.value) !== normalize(requested.value))
|
|
6023
6107
|
return false;
|
|
6024
|
-
if (
|
|
6108
|
+
if (requested.label !== null && normalize(o.textContent) !== normalize(requested.label))
|
|
6025
6109
|
return false;
|
|
6026
|
-
if (
|
|
6110
|
+
if (requested.index !== undefined && o.index !== requested.index)
|
|
6027
6111
|
return false;
|
|
6028
6112
|
return true;
|
|
6029
6113
|
};
|
|
6030
|
-
const
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6114
|
+
const candidate = requested.index !== undefined
|
|
6115
|
+
? target.options.item(requested.index)
|
|
6116
|
+
: Array.from(target.options).find(optionMatchesPayload) ?? null;
|
|
6117
|
+
return !!candidate && enabled(candidate) && optionMatchesPayload(candidate);
|
|
6118
|
+
}, payload);
|
|
6119
|
+
if (!optionReady) {
|
|
6120
|
+
throw new Error('selectOption: no enabled <option> matches the requested identity');
|
|
6121
|
+
}
|
|
6122
|
+
const applied = await select.evaluate(async (target, requested) => {
|
|
6123
|
+
if (!(target instanceof HTMLSelectElement))
|
|
6035
6124
|
return false;
|
|
6036
|
-
|
|
6125
|
+
function commitMutationBlockReason(control) {
|
|
6126
|
+
function composedParent(node) {
|
|
6127
|
+
if (node.parentElement)
|
|
6128
|
+
return node.parentElement;
|
|
6129
|
+
const root = node.getRootNode();
|
|
6130
|
+
return root instanceof ShadowRoot ? root.host : null;
|
|
6131
|
+
}
|
|
6132
|
+
function composedDescendantOf(node, ancestor) {
|
|
6133
|
+
let current = node;
|
|
6134
|
+
while (current) {
|
|
6135
|
+
if (current === ancestor)
|
|
6136
|
+
return true;
|
|
6137
|
+
current = composedParent(current);
|
|
6138
|
+
}
|
|
6139
|
+
return false;
|
|
6140
|
+
}
|
|
6141
|
+
try {
|
|
6142
|
+
if (control.matches(':disabled'))
|
|
6143
|
+
return 'disabled';
|
|
6144
|
+
}
|
|
6145
|
+
catch { /* non-HTML namespaces may reject pseudo classes */ }
|
|
6146
|
+
let current = control;
|
|
6147
|
+
while (current) {
|
|
6148
|
+
if (current.hasAttribute('inert'))
|
|
6149
|
+
return 'inert';
|
|
6150
|
+
if (current.getAttribute('aria-disabled')?.trim().toLowerCase() === 'true')
|
|
6151
|
+
return 'aria-disabled';
|
|
6152
|
+
if (current.getAttribute('aria-readonly')?.trim().toLowerCase() === 'true')
|
|
6153
|
+
return 'aria-readonly';
|
|
6154
|
+
if (current instanceof HTMLFieldSetElement && current.disabled) {
|
|
6155
|
+
const firstLegend = Array.from(current.children).find(child => child instanceof HTMLLegendElement);
|
|
6156
|
+
if (!(firstLegend instanceof HTMLLegendElement) || !composedDescendantOf(control, firstLegend)) {
|
|
6157
|
+
return 'disabled fieldset';
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
current = composedParent(current);
|
|
6161
|
+
}
|
|
6162
|
+
return null;
|
|
6163
|
+
}
|
|
6164
|
+
if (commitMutationBlockReason(target))
|
|
6037
6165
|
return false;
|
|
6038
|
-
const
|
|
6039
|
-
|
|
6040
|
-
|
|
6166
|
+
const normalize = (input) => input?.replace(/\s+/g, ' ').trim().toLowerCase() ?? '';
|
|
6167
|
+
const enabled = (o) => !o.disabled && !(o.parentElement instanceof HTMLOptGroupElement && o.parentElement.disabled);
|
|
6168
|
+
const optionMatchesPayload = (o) => {
|
|
6169
|
+
if (!enabled(o))
|
|
6170
|
+
return false;
|
|
6171
|
+
if (requested.value !== null && normalize(o.value) !== normalize(requested.value))
|
|
6172
|
+
return false;
|
|
6173
|
+
if (requested.label !== null && normalize(o.textContent) !== normalize(requested.label))
|
|
6174
|
+
return false;
|
|
6175
|
+
if (requested.index !== undefined && o.index !== requested.index)
|
|
6176
|
+
return false;
|
|
6177
|
+
return true;
|
|
6178
|
+
};
|
|
6179
|
+
const candidate = requested.index !== undefined
|
|
6180
|
+
? target.options.item(requested.index)
|
|
6181
|
+
: Array.from(target.options).find(optionMatchesPayload) ?? null;
|
|
6041
6182
|
if (!candidate || !enabled(candidate) || !optionMatchesPayload(candidate))
|
|
6042
6183
|
return false;
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6184
|
+
target.selectedIndex = candidate.index;
|
|
6185
|
+
target.dispatchEvent(new Event('input', { bubbles: true }));
|
|
6186
|
+
target.dispatchEvent(new Event('change', { bubbles: true }));
|
|
6046
6187
|
await new Promise(resolveTimer => setTimeout(resolveTimer, 40));
|
|
6047
|
-
const selected =
|
|
6048
|
-
return
|
|
6049
|
-
},
|
|
6050
|
-
value: opt.value ?? null,
|
|
6051
|
-
label: opt.label ?? null,
|
|
6052
|
-
index: typeof opt.index === 'number' && Number.isFinite(opt.index) ? opt.index : undefined,
|
|
6053
|
-
});
|
|
6188
|
+
const selected = target.selectedOptions[0];
|
|
6189
|
+
return target.selectedIndex === candidate.index && selected === candidate && optionMatchesPayload(selected);
|
|
6190
|
+
}, payload);
|
|
6054
6191
|
if (applied)
|
|
6055
6192
|
return;
|
|
6056
6193
|
}
|
|
6057
|
-
|
|
6194
|
+
finally {
|
|
6195
|
+
await select.dispose();
|
|
6196
|
+
}
|
|
6197
|
+
throw new Error('selectOption: native <select> or requested option changed before commit');
|
|
6058
6198
|
}
|
|
6059
6199
|
/**
|
|
6060
6200
|
* Custom listbox / combobox (ARIA): optional click to open, then `getByRole('option')`.
|