@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.
@@ -1 +1 @@
1
- {"version":3,"file":"dom-actions.d.ts","sourceRoot":"","sources":["../src/dom-actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dom-actions.d.ts","sourceRoot":"","sources":["../src/dom-actions.ts"],"names":[],"mappings":"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"}
@@ -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 handle.setInputFiles(paths);
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
- await commitFilesToExactInput(input, paths, 'chooser input');
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 assertPointMutable(page, x, y, 'select', 'selectOption');
6013
- await page.mouse.click(x, y);
6014
- await delay(40);
6015
- for (const frame of page.frames()) {
6016
- const applied = await frame.evaluate(async (payload) => {
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 (payload.value !== null && normalize(o.value) !== normalize(payload.value))
6106
+ if (requested.value !== null && normalize(o.value) !== normalize(requested.value))
6023
6107
  return false;
6024
- if (payload.label !== null && normalize(o.textContent) !== normalize(payload.label))
6108
+ if (requested.label !== null && normalize(o.textContent) !== normalize(requested.label))
6025
6109
  return false;
6026
- if (payload.index !== undefined && o.index !== payload.index)
6110
+ if (requested.index !== undefined && o.index !== requested.index)
6027
6111
  return false;
6028
6112
  return true;
6029
6113
  };
6030
- const a = document.activeElement;
6031
- if (!a || a.tagName !== 'SELECT')
6032
- return false;
6033
- const sel = a;
6034
- if (sel.matches(':disabled') || sel.getAttribute('aria-disabled') === 'true' || sel.getAttribute('aria-readonly') === 'true')
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
- if (sel.closest('[inert], [aria-disabled="true"], [aria-readonly="true"]'))
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 candidate = payload.index !== undefined
6039
- ? sel.options.item(payload.index)
6040
- : Array.from(sel.options).find(optionMatchesPayload) ?? null;
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
- sel.selectedIndex = candidate.index;
6044
- sel.dispatchEvent(new Event('input', { bubbles: true }));
6045
- sel.dispatchEvent(new Event('change', { bubbles: true }));
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 = sel.selectedOptions[0];
6048
- return sel.selectedIndex === candidate.index && selected === candidate && optionMatchesPayload(selected);
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
- throw new Error('selectOption: no focused <select> after click — use geometra_pick_listbox_option for custom dropdowns');
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')`.