@letsrunit/playwright 0.4.2 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -226,7 +226,8 @@ async function elementKind(target) {
226
226
  // src/field/calendar.ts
227
227
  async function getDialog(root, options) {
228
228
  const role = await root.getAttribute("role", options).catch(() => null);
229
- if (role !== "combobox") return null;
229
+ const hasPopup = await root.getAttribute("aria-haspopup", options).catch(() => null);
230
+ if (role !== "combobox" && !hasPopup) return null;
230
231
  const ariaControls = await root.getAttribute("aria-controls", options).catch(() => null);
231
232
  if (!ariaControls) return null;
232
233
  const calendar = root.page().locator(`#${ariaControls}`);
@@ -905,6 +906,30 @@ async function setNativeInput({ el, tag }, value, options) {
905
906
  return false;
906
907
  }
907
908
 
909
+ // src/field/aria-select.ts
910
+ async function selectAria({ el }, value, options) {
911
+ if (typeof value !== "string" && typeof value !== "number") return false;
912
+ const role = await el.getAttribute("role", options).catch(() => null);
913
+ if (role !== "combobox") return false;
914
+ const ariaControls = await el.getAttribute("aria-controls", options).catch(() => null);
915
+ if (!ariaControls) return false;
916
+ const ariaExpanded = await el.getAttribute("aria-expanded", options).catch(() => null);
917
+ if (ariaExpanded !== "true") await el.click(options);
918
+ const stringValue = String(value);
919
+ const listbox = el.page().locator(`#${ariaControls}`);
920
+ const byValue = listbox.locator(`[role="option"][value="${stringValue}"]`);
921
+ if (await byValue.count() >= 1) {
922
+ await byValue.first().click(options);
923
+ return true;
924
+ }
925
+ const byName = listbox.getByRole("option", { name: stringValue });
926
+ if (await byName.count() >= 1) {
927
+ await byName.first().click(options);
928
+ return true;
929
+ }
930
+ return false;
931
+ }
932
+
908
933
  // src/field/otp.ts
909
934
  async function setOtpValue({ el, tag }, value, options) {
910
935
  if (typeof value !== "string") return false;
@@ -929,11 +954,25 @@ async function setRadioGroup({ el }, value, options) {
929
954
  await radio.check(options);
930
955
  return true;
931
956
  }
932
- const radioByLabel = el.getByLabel(String(value), { exact: true }).locator("input[type=radio]");
957
+ const radioByLabel = el.getByLabel(stringValue, { exact: true }).locator("input[type=radio]");
933
958
  if (await radioByLabel.count() === 1) {
934
959
  await radioByLabel.check(options);
935
960
  return true;
936
961
  }
962
+ const ariaRadio = el.locator(`[role="radio"][value="${stringValue}"]`);
963
+ if (await ariaRadio.count() >= 1) {
964
+ const item = ariaRadio.first();
965
+ const ariaChecked = await item.getAttribute("aria-checked", options).catch(() => null);
966
+ if (ariaChecked !== "true") await item.click(options);
967
+ return true;
968
+ }
969
+ const ariaRadioByLabel = el.getByLabel(stringValue, { exact: true }).locator('[role="radio"]');
970
+ if (await ariaRadioByLabel.count() >= 1) {
971
+ const item = ariaRadioByLabel.first();
972
+ const ariaChecked = await item.getAttribute("aria-checked", options).catch(() => null);
973
+ if (ariaChecked !== "true") await item.click(options);
974
+ return true;
975
+ }
937
976
  return false;
938
977
  }
939
978
 
@@ -951,12 +990,22 @@ async function setSliderValue({ el, tag }, value, options) {
951
990
  if (initialValue === value) return true;
952
991
  const { centerX, centerY } = await prepareMouse(slider, options);
953
992
  const page = slider.page();
993
+ let unresponsive = false;
954
994
  try {
955
995
  const ratio = await calculateRatio(slider, initialValue, value, centerX, centerY, orientation, options);
956
996
  await seekValue(slider, initialValue, value, centerX, centerY, orientation, ratio, options);
997
+ } catch (e) {
998
+ if (e instanceof Error && e.message === "Slider appears to be disabled or unresponsive") {
999
+ unresponsive = true;
1000
+ } else {
1001
+ throw e;
1002
+ }
957
1003
  } finally {
958
1004
  await page.mouse.up();
959
1005
  }
1006
+ if (unresponsive) {
1007
+ return setSliderByKeyboard(slider, initialValue, value, options);
1008
+ }
960
1009
  return true;
961
1010
  }
962
1011
  async function prepareMouse(slider, options) {
@@ -1026,6 +1075,31 @@ async function moveMouse(page, centerX, centerY, orientation, distance) {
1026
1075
  await page.mouse.move(centerX + distance, centerY);
1027
1076
  }
1028
1077
  }
1078
+ async function setSliderByKeyboard(slider, initialValue, targetValue, options) {
1079
+ const stepStr = await slider.getAttribute("aria-valuestep", options).catch(() => null);
1080
+ const step = stepStr ? parseFloat(stepStr) : 1;
1081
+ const presses = Math.round(Math.abs(targetValue - initialValue) / step);
1082
+ const key = targetValue > initialValue ? "ArrowRight" : "ArrowLeft";
1083
+ await slider.focus(options);
1084
+ const page = slider.page();
1085
+ for (let i = 0; i < presses; i++) {
1086
+ await page.keyboard.press(key);
1087
+ }
1088
+ const nowStr = await slider.getAttribute("aria-valuenow", options).catch(() => null);
1089
+ const reached = nowStr !== null && Math.abs(parseFloat(nowStr) - targetValue) < 1e-3;
1090
+ return reached;
1091
+ }
1092
+
1093
+ // src/field/toggle.ts
1094
+ async function setToggle({ el }, value, options) {
1095
+ if (typeof value !== "boolean" && value !== null) return false;
1096
+ const role = await el.getAttribute("role", options).catch(() => null);
1097
+ if (role !== "checkbox" && role !== "switch") return false;
1098
+ const ariaChecked = await el.getAttribute("aria-checked", options).catch(() => null);
1099
+ const isChecked = ariaChecked === "true";
1100
+ if (Boolean(value) !== isChecked) await el.click(options);
1101
+ return true;
1102
+ }
1029
1103
 
1030
1104
  // src/field/index.ts
1031
1105
  function toString(value) {
@@ -1041,11 +1115,13 @@ async function setFieldValue(el, value, options) {
1041
1115
  const setValue = chain(
1042
1116
  // native
1043
1117
  selectNative,
1118
+ selectAria,
1044
1119
  setNativeCheckbox,
1045
1120
  setRadioGroup,
1046
1121
  setNativeDate,
1047
1122
  setNativeInput,
1048
1123
  // aria / components
1124
+ setToggle,
1049
1125
  setDateTextInput,
1050
1126
  setDateGroup,
1051
1127
  setCalendarDate,