@letsrunit/playwright 0.4.1 → 0.5.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 +3 -1
- package/dist/index.js +78 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/field/aria-select.ts +33 -0
- package/src/field/calendar.ts +2 -1
- package/src/field/index.ts +4 -0
- package/src/field/radio-group.ts +21 -2
- package/src/field/slider.ts +33 -0
- package/src/field/toggle.ts +15 -0
package/README.md
CHANGED
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
|
-
|
|
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(
|
|
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,
|