@redrockswebdevelopment/formstack-form-testing 0.1.27 → 0.1.29
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.d.ts +51 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +200 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -978,6 +978,19 @@ export function buildLiveFormProductValue(value, current = {}) {
|
|
|
978
978
|
export function buildLiveFormRatingValue(value) {
|
|
979
979
|
return { value: String(value) };
|
|
980
980
|
}
|
|
981
|
+
export function readLiveFormRatingValue(value) {
|
|
982
|
+
if (typeof value === "string" || typeof value === "number")
|
|
983
|
+
return String(value);
|
|
984
|
+
if (!value || typeof value !== "object")
|
|
985
|
+
return null;
|
|
986
|
+
const selected = value.value;
|
|
987
|
+
return typeof selected === "string" || typeof selected === "number" ? String(selected) : null;
|
|
988
|
+
}
|
|
989
|
+
export function normalizeLiveFormRatingValue(value) {
|
|
990
|
+
if (typeof value === "string" || typeof value === "number")
|
|
991
|
+
return buildLiveFormRatingValue(value);
|
|
992
|
+
return buildLiveFormRatingValue(value.value);
|
|
993
|
+
}
|
|
981
994
|
export function buildLiveFormMatrixValue(selections) {
|
|
982
995
|
return {
|
|
983
996
|
value: selections.map((selection) => {
|
|
@@ -2077,6 +2090,88 @@ export async function waitForLiveFormMatrixState(page, options) {
|
|
|
2077
2090
|
}
|
|
2078
2091
|
return latest;
|
|
2079
2092
|
}
|
|
2093
|
+
export async function readLiveFormRatingState(page, options) {
|
|
2094
|
+
if (!hasEvaluate(page)) {
|
|
2095
|
+
throw new Error("readLiveFormRatingState requires a Playwright page with evaluate().");
|
|
2096
|
+
}
|
|
2097
|
+
return page.evaluate((stateOptions) => {
|
|
2098
|
+
const normalizeText = (value) => (value ?? "").replace(/\s+/g, " ").trim();
|
|
2099
|
+
const root = stateOptions.selector != null
|
|
2100
|
+
? document.querySelector(stateOptions.selector)
|
|
2101
|
+
: stateOptions.internalLabel != null
|
|
2102
|
+
? document.querySelector(`[data-internal-label="${CSS.escape(stateOptions.internalLabel)}"]`)
|
|
2103
|
+
: stateOptions.fieldId == null
|
|
2104
|
+
? null
|
|
2105
|
+
: document.querySelector(`#fsCell${CSS.escape(String(stateOptions.fieldId))}, #fsFieldCell${CSS.escape(String(stateOptions.fieldId))}, [data-testid="field-${CSS.escape(String(stateOptions.fieldId))}"]`);
|
|
2106
|
+
const candidates = root
|
|
2107
|
+
? Array.from(root.querySelectorAll("input, button, [role=\"radio\"], [role=\"button\"], [aria-checked], [aria-pressed], [aria-selected], [data-value], [data-rating], [data-score]"))
|
|
2108
|
+
: [];
|
|
2109
|
+
const controlValue = (element) => {
|
|
2110
|
+
const htmlInput = element instanceof HTMLInputElement ? element.value : "";
|
|
2111
|
+
const text = normalizeText(element.textContent);
|
|
2112
|
+
return element.getAttribute("value") ??
|
|
2113
|
+
(htmlInput.length > 0 ? htmlInput : null) ??
|
|
2114
|
+
element.getAttribute("data-value") ??
|
|
2115
|
+
element.getAttribute("data-rating") ??
|
|
2116
|
+
element.getAttribute("data-score") ??
|
|
2117
|
+
element.getAttribute("aria-label")?.match(/\d+(?:\.\d+)?/)?.[0] ??
|
|
2118
|
+
(text.length > 0 ? text : null);
|
|
2119
|
+
};
|
|
2120
|
+
const isSelected = (element) => {
|
|
2121
|
+
const checked = element instanceof HTMLInputElement ? element.checked : null;
|
|
2122
|
+
const ariaChecked = element.getAttribute("aria-checked");
|
|
2123
|
+
const ariaPressed = element.getAttribute("aria-pressed");
|
|
2124
|
+
const ariaSelected = element.getAttribute("aria-selected");
|
|
2125
|
+
const classes = Array.from(element.classList).join(" ");
|
|
2126
|
+
return checked === true ||
|
|
2127
|
+
ariaChecked === "true" ||
|
|
2128
|
+
ariaPressed === "true" ||
|
|
2129
|
+
ariaSelected === "true" ||
|
|
2130
|
+
/\b(active|checked|selected|filled|on)\b/i.test(classes);
|
|
2131
|
+
};
|
|
2132
|
+
const controls = candidates
|
|
2133
|
+
.map((element) => ({
|
|
2134
|
+
tagName: element.tagName.toLowerCase(),
|
|
2135
|
+
id: element.getAttribute("id"),
|
|
2136
|
+
value: controlValue(element),
|
|
2137
|
+
text: normalizeText(element.textContent),
|
|
2138
|
+
ariaChecked: element.getAttribute("aria-checked"),
|
|
2139
|
+
ariaPressed: element.getAttribute("aria-pressed"),
|
|
2140
|
+
ariaSelected: element.getAttribute("aria-selected"),
|
|
2141
|
+
checked: element instanceof HTMLInputElement ? element.checked : null,
|
|
2142
|
+
selected: isSelected(element),
|
|
2143
|
+
}))
|
|
2144
|
+
.filter((control) => control.value !== null && control.value.length > 0);
|
|
2145
|
+
const selectedControls = controls.filter((control) => control.selected);
|
|
2146
|
+
const selectedValue = selectedControls.length > 0
|
|
2147
|
+
? selectedControls[selectedControls.length - 1]?.value ?? null
|
|
2148
|
+
: null;
|
|
2149
|
+
const allValues = controls
|
|
2150
|
+
.map((control) => control.value)
|
|
2151
|
+
.filter((value) => typeof value === "string" && value.length > 0);
|
|
2152
|
+
return {
|
|
2153
|
+
fieldMatched: Boolean(root),
|
|
2154
|
+
selectedValue,
|
|
2155
|
+
allValues,
|
|
2156
|
+
...(stateOptions.expectedValue === undefined ? {} : { expectedValue: stateOptions.expectedValue }),
|
|
2157
|
+
matched: Boolean(root) &&
|
|
2158
|
+
selectedValue !== null &&
|
|
2159
|
+
(stateOptions.expectedValue === undefined || selectedValue === stateOptions.expectedValue),
|
|
2160
|
+
controls,
|
|
2161
|
+
};
|
|
2162
|
+
}, options);
|
|
2163
|
+
}
|
|
2164
|
+
export async function waitForLiveFormRatingState(page, options) {
|
|
2165
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
2166
|
+
const intervalMs = options.intervalMs ?? 100;
|
|
2167
|
+
const startedAt = Date.now();
|
|
2168
|
+
let latest = await readLiveFormRatingState(page, options);
|
|
2169
|
+
while (!latest.matched && Date.now() - startedAt < timeoutMs) {
|
|
2170
|
+
await delay(intervalMs);
|
|
2171
|
+
latest = await readLiveFormRatingState(page, options);
|
|
2172
|
+
}
|
|
2173
|
+
return latest;
|
|
2174
|
+
}
|
|
2080
2175
|
export async function dispatchLiveFormFieldDomEvents(page, options) {
|
|
2081
2176
|
if (!hasEvaluate(page)) {
|
|
2082
2177
|
throw new Error("dispatchLiveFormFieldDomEvents requires a Playwright page with evaluate().");
|
|
@@ -2088,14 +2183,30 @@ export async function dispatchLiveFormFieldDomEvents(page, options) {
|
|
|
2088
2183
|
: dispatchOptions.target.fieldId == null
|
|
2089
2184
|
? null
|
|
2090
2185
|
: document.querySelector(`#fsCell${CSS.escape(String(dispatchOptions.target.fieldId))}, [data-testid="field-${CSS.escape(String(dispatchOptions.target.fieldId))}"]`);
|
|
2091
|
-
const
|
|
2186
|
+
const isSelectedControl = (control) => {
|
|
2187
|
+
if (control instanceof HTMLInputElement && ["checkbox", "radio"].includes(control.type))
|
|
2188
|
+
return control.checked;
|
|
2189
|
+
if (control instanceof HTMLInputElement && control.type === "hidden")
|
|
2190
|
+
return false;
|
|
2191
|
+
const ariaChecked = control.getAttribute("aria-checked");
|
|
2192
|
+
const ariaPressed = control.getAttribute("aria-pressed");
|
|
2193
|
+
const ariaSelected = control.getAttribute("aria-selected");
|
|
2194
|
+
const classes = Array.from(control.classList).join(" ");
|
|
2195
|
+
return ariaChecked === "true" ||
|
|
2196
|
+
ariaPressed === "true" ||
|
|
2197
|
+
ariaSelected === "true" ||
|
|
2198
|
+
/\b(active|checked|selected|filled|on)\b/i.test(classes);
|
|
2199
|
+
};
|
|
2200
|
+
const allControls = root
|
|
2201
|
+
? Array.from(root.querySelectorAll("input, textarea, select, button, [role=\"radio\"], [role=\"button\"], [aria-checked], [aria-pressed], [aria-selected]"))
|
|
2202
|
+
: [];
|
|
2092
2203
|
const controls = dispatchOptions.onlyCheckedChoices ?? true
|
|
2093
2204
|
? allControls.filter((control) => {
|
|
2094
|
-
if (control instanceof HTMLInputElement
|
|
2095
|
-
return control
|
|
2096
|
-
if (control
|
|
2097
|
-
return
|
|
2098
|
-
return
|
|
2205
|
+
if (control instanceof HTMLInputElement || control instanceof HTMLButtonElement)
|
|
2206
|
+
return isSelectedControl(control);
|
|
2207
|
+
if (control.matches("[role=\"radio\"], [role=\"button\"], [aria-checked], [aria-pressed], [aria-selected]"))
|
|
2208
|
+
return isSelectedControl(control);
|
|
2209
|
+
return !(control instanceof HTMLInputElement && control.type === "hidden");
|
|
2099
2210
|
})
|
|
2100
2211
|
: allControls;
|
|
2101
2212
|
for (const control of controls) {
|
|
@@ -2112,7 +2223,7 @@ export async function dispatchLiveFormFieldDomEvents(page, options) {
|
|
|
2112
2223
|
id: control.getAttribute("id"),
|
|
2113
2224
|
name: control.getAttribute("name"),
|
|
2114
2225
|
value: control.getAttribute("value"),
|
|
2115
|
-
currentValue: "value" in control ? control.value : null,
|
|
2226
|
+
currentValue: "value" in control ? String(control.value) : null,
|
|
2116
2227
|
checked: control instanceof HTMLInputElement && ["checkbox", "radio"].includes(control.type) ? control.checked : null,
|
|
2117
2228
|
})),
|
|
2118
2229
|
};
|
|
@@ -3797,6 +3908,49 @@ export async function setLiveFormMatrixValues(page, options) {
|
|
|
3797
3908
|
(visibility?.matched ?? true),
|
|
3798
3909
|
};
|
|
3799
3910
|
}
|
|
3911
|
+
export async function setLiveFormRatingValue(page, options) {
|
|
3912
|
+
const payload = normalizeLiveFormRatingValue(options.value);
|
|
3913
|
+
const value = payload.value;
|
|
3914
|
+
const transaction = await performLiveFormFieldTransaction(page, {
|
|
3915
|
+
write: {
|
|
3916
|
+
...(options.write ?? {}),
|
|
3917
|
+
target: options.target,
|
|
3918
|
+
value: payload,
|
|
3919
|
+
notify: options.write?.notify ?? true,
|
|
3920
|
+
},
|
|
3921
|
+
fieldKind: "rating",
|
|
3922
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3923
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3924
|
+
afterQuiet: options.afterQuiet ?? { quietMs: 200, timeoutMs: 5_000 },
|
|
3925
|
+
...(options.afterValidation === undefined ? {} : { afterValidation: options.afterValidation }),
|
|
3926
|
+
});
|
|
3927
|
+
const dom = await waitForLiveFormRatingState(page, {
|
|
3928
|
+
...options.target,
|
|
3929
|
+
expectedValue: value,
|
|
3930
|
+
...(options.write?.timeoutMs === undefined ? {} : { timeoutMs: options.write.timeoutMs }),
|
|
3931
|
+
});
|
|
3932
|
+
const visibilityTargets = [
|
|
3933
|
+
...(options.expectVisible ?? []).map((target) => ({ ...target, visible: true })),
|
|
3934
|
+
...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
|
|
3935
|
+
];
|
|
3936
|
+
const visibility = visibilityTargets.length > 0
|
|
3937
|
+
? await waitForLiveFormVisibility(page, { targets: visibilityTargets })
|
|
3938
|
+
: undefined;
|
|
3939
|
+
return {
|
|
3940
|
+
payload,
|
|
3941
|
+
value,
|
|
3942
|
+
transaction,
|
|
3943
|
+
dom,
|
|
3944
|
+
...(visibility ? { visibility } : {}),
|
|
3945
|
+
ok: transaction.write.valueMatched &&
|
|
3946
|
+
(transaction.event?.matched ?? true) &&
|
|
3947
|
+
(transaction.domDispatch ? transaction.domDispatch.matchedControlCount >= 1 : true) &&
|
|
3948
|
+
(transaction.quiet?.quiet ?? true) &&
|
|
3949
|
+
((transaction.validation?.inlineErrors.length ?? 0) === 0) &&
|
|
3950
|
+
dom.matched &&
|
|
3951
|
+
(visibility?.matched ?? true),
|
|
3952
|
+
};
|
|
3953
|
+
}
|
|
3800
3954
|
export async function answerProgressiveLiveFormField(page, options) {
|
|
3801
3955
|
const validationOptions = options.afterValidation === undefined ? undefined : typeof options.afterValidation === "object" ? options.afterValidation : {};
|
|
3802
3956
|
const checkboxTransaction = options.fieldKind === "checkbox"
|
|
@@ -3843,6 +3997,17 @@ export async function answerProgressiveLiveFormField(page, options) {
|
|
|
3843
3997
|
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3844
3998
|
})
|
|
3845
3999
|
: undefined;
|
|
4000
|
+
const ratingTransaction = !checkboxTransaction && !choiceTransaction && !selectTransaction && !matrixTransaction && options.fieldKind === "rating"
|
|
4001
|
+
? await setLiveFormRatingValue(page, {
|
|
4002
|
+
target: options.target,
|
|
4003
|
+
value: options.value,
|
|
4004
|
+
...(options.write === undefined ? {} : { write: options.write }),
|
|
4005
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
4006
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
4007
|
+
afterQuiet: options.afterQuiet ?? true,
|
|
4008
|
+
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
4009
|
+
})
|
|
4010
|
+
: undefined;
|
|
3846
4011
|
const transaction = checkboxTransaction
|
|
3847
4012
|
? checkboxTransaction.transaction
|
|
3848
4013
|
: choiceTransaction
|
|
@@ -3851,19 +4016,21 @@ export async function answerProgressiveLiveFormField(page, options) {
|
|
|
3851
4016
|
? selectTransaction.transaction
|
|
3852
4017
|
: matrixTransaction
|
|
3853
4018
|
? matrixTransaction.transaction
|
|
3854
|
-
:
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
4019
|
+
: ratingTransaction
|
|
4020
|
+
? ratingTransaction.transaction
|
|
4021
|
+
: await performLiveFormFieldTransaction(page, {
|
|
4022
|
+
write: {
|
|
4023
|
+
...(options.write ?? {}),
|
|
4024
|
+
target: options.target,
|
|
4025
|
+
value: options.value,
|
|
4026
|
+
notify: options.write?.notify ?? true,
|
|
4027
|
+
},
|
|
4028
|
+
...(options.fieldKind ? { fieldKind: options.fieldKind } : {}),
|
|
4029
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
4030
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
4031
|
+
afterQuiet: options.afterQuiet ?? true,
|
|
4032
|
+
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
4033
|
+
});
|
|
3867
4034
|
const visibilityTargets = [
|
|
3868
4035
|
...(options.expectVisible ?? []),
|
|
3869
4036
|
...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
|
|
@@ -5338,7 +5505,19 @@ export const liveFormFieldRecipes = {
|
|
|
5338
5505
|
});
|
|
5339
5506
|
},
|
|
5340
5507
|
}),
|
|
5341
|
-
setRatingApi: (name, internalLabel, value, options) =>
|
|
5508
|
+
setRatingApi: (name, internalLabel, value, options = {}) => ({
|
|
5509
|
+
name,
|
|
5510
|
+
run: async (harness) => {
|
|
5511
|
+
await setLiveFormRatingValue(harness.page, {
|
|
5512
|
+
target: { internalLabel },
|
|
5513
|
+
value,
|
|
5514
|
+
write: { notify: options.notify ?? true },
|
|
5515
|
+
waitForEvent: options.waitForEvent ?? (options.notify ?? true),
|
|
5516
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
5517
|
+
afterQuiet: options.waitForQuiet ?? true,
|
|
5518
|
+
});
|
|
5519
|
+
},
|
|
5520
|
+
}),
|
|
5342
5521
|
setProductApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormProductValue(value), options),
|
|
5343
5522
|
setFieldApiAuto: (name, internalLabel, value, fieldKind, options = {}) => apiRecipeStep(name, { internalLabel }, value, { ...options, fieldKind, dispatchDomEvents: "auto" }),
|
|
5344
5523
|
setNameApiAndDispatch: (name, internalLabel, value, options = {}) => liveFormFieldRecipes.setNameApi(name, internalLabel, value, { ...options, fieldKind: options.fieldKind ?? "name", dispatchDomEvents: "auto" }),
|