@redrockswebdevelopment/formstack-form-testing 0.1.23 → 0.1.25
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 +87 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +294 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -904,9 +904,35 @@ export function buildLiveFormAddressValue(value, current = {}) {
|
|
|
904
904
|
export function buildLiveFormChoiceValue(value, otherValue = "") {
|
|
905
905
|
return { value, otherValue };
|
|
906
906
|
}
|
|
907
|
+
export function readLiveFormChoiceValue(value) {
|
|
908
|
+
if (typeof value === "string")
|
|
909
|
+
return value;
|
|
910
|
+
if (!value || typeof value !== "object")
|
|
911
|
+
return null;
|
|
912
|
+
const selected = value.value;
|
|
913
|
+
return typeof selected === "string" && selected.length > 0 ? selected : null;
|
|
914
|
+
}
|
|
915
|
+
export function normalizeLiveFormChoiceValue(value, otherValue = "") {
|
|
916
|
+
if (typeof value === "string")
|
|
917
|
+
return buildLiveFormChoiceValue(value, otherValue);
|
|
918
|
+
return buildLiveFormChoiceValue(value.value, typeof value.otherValue === "string" ? value.otherValue : otherValue);
|
|
919
|
+
}
|
|
907
920
|
export function buildLiveFormSelectValue(value) {
|
|
908
921
|
return { value };
|
|
909
922
|
}
|
|
923
|
+
export function readLiveFormSelectValue(value) {
|
|
924
|
+
if (typeof value === "string")
|
|
925
|
+
return value;
|
|
926
|
+
if (!value || typeof value !== "object")
|
|
927
|
+
return null;
|
|
928
|
+
const selected = value.value;
|
|
929
|
+
return typeof selected === "string" && selected.length > 0 ? selected : null;
|
|
930
|
+
}
|
|
931
|
+
export function normalizeLiveFormSelectValue(value) {
|
|
932
|
+
if (typeof value === "string")
|
|
933
|
+
return buildLiveFormSelectValue(value);
|
|
934
|
+
return buildLiveFormSelectValue(value.value);
|
|
935
|
+
}
|
|
910
936
|
export function buildLiveFormCheckboxValue(values, otherValue = null) {
|
|
911
937
|
return Object.fromEntries([...values.map((value, index) => [String(index), value]), ["otherValue", otherValue]]);
|
|
912
938
|
}
|
|
@@ -1815,6 +1841,124 @@ export async function diagnoseLiveFormCommitControl(page, options) {
|
|
|
1815
1841
|
};
|
|
1816
1842
|
}, options);
|
|
1817
1843
|
}
|
|
1844
|
+
export async function readLiveFormChoiceState(page, options) {
|
|
1845
|
+
if (!hasEvaluate(page)) {
|
|
1846
|
+
throw new Error("readLiveFormChoiceState requires a Playwright page with evaluate().");
|
|
1847
|
+
}
|
|
1848
|
+
return page.evaluate((stateOptions) => {
|
|
1849
|
+
const controlSnapshot = (control) => ({
|
|
1850
|
+
tagName: control.tagName.toLowerCase(),
|
|
1851
|
+
type: control.getAttribute("type"),
|
|
1852
|
+
id: control.getAttribute("id"),
|
|
1853
|
+
name: control.getAttribute("name"),
|
|
1854
|
+
value: control.getAttribute("value"),
|
|
1855
|
+
currentValue: control.value,
|
|
1856
|
+
checked: control.checked,
|
|
1857
|
+
});
|
|
1858
|
+
const root = stateOptions.selector != null
|
|
1859
|
+
? document.querySelector(stateOptions.selector)
|
|
1860
|
+
: stateOptions.internalLabel != null
|
|
1861
|
+
? document.querySelector(`[data-internal-label="${CSS.escape(stateOptions.internalLabel)}"]`)
|
|
1862
|
+
: stateOptions.fieldId == null
|
|
1863
|
+
? null
|
|
1864
|
+
: document.querySelector(`#fsCell${CSS.escape(String(stateOptions.fieldId))}, #fsFieldCell${CSS.escape(String(stateOptions.fieldId))}, [data-testid="field-${CSS.escape(String(stateOptions.fieldId))}"]`);
|
|
1865
|
+
const controls = root
|
|
1866
|
+
? Array.from(root.querySelectorAll("input[type=\"radio\"]")).map(controlSnapshot)
|
|
1867
|
+
: [];
|
|
1868
|
+
const selectedControls = controls.filter((control) => control.checked === true);
|
|
1869
|
+
const selectedValue = selectedControls.length === 1
|
|
1870
|
+
? selectedControls[0]?.currentValue ?? selectedControls[0]?.value ?? null
|
|
1871
|
+
: null;
|
|
1872
|
+
const allValues = controls
|
|
1873
|
+
.map((control) => control.currentValue ?? control.value ?? "")
|
|
1874
|
+
.filter((value) => value.length > 0);
|
|
1875
|
+
return {
|
|
1876
|
+
fieldMatched: Boolean(root),
|
|
1877
|
+
selectedValue,
|
|
1878
|
+
allValues,
|
|
1879
|
+
...(stateOptions.expectedValue === undefined ? {} : { expectedValue: stateOptions.expectedValue }),
|
|
1880
|
+
matched: Boolean(root) &&
|
|
1881
|
+
selectedControls.length === 1 &&
|
|
1882
|
+
(stateOptions.expectedValue === undefined || selectedValue === stateOptions.expectedValue),
|
|
1883
|
+
controls,
|
|
1884
|
+
};
|
|
1885
|
+
}, options);
|
|
1886
|
+
}
|
|
1887
|
+
export async function waitForLiveFormChoiceState(page, options) {
|
|
1888
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
1889
|
+
const intervalMs = options.intervalMs ?? 100;
|
|
1890
|
+
const startedAt = Date.now();
|
|
1891
|
+
let latest = await readLiveFormChoiceState(page, options);
|
|
1892
|
+
while (!latest.matched && Date.now() - startedAt < timeoutMs) {
|
|
1893
|
+
await delay(intervalMs);
|
|
1894
|
+
latest = await readLiveFormChoiceState(page, options);
|
|
1895
|
+
}
|
|
1896
|
+
return latest;
|
|
1897
|
+
}
|
|
1898
|
+
export async function readLiveFormSelectState(page, options) {
|
|
1899
|
+
if (!hasEvaluate(page)) {
|
|
1900
|
+
throw new Error("readLiveFormSelectState requires a Playwright page with evaluate().");
|
|
1901
|
+
}
|
|
1902
|
+
return page.evaluate((stateOptions) => {
|
|
1903
|
+
const controlSnapshot = (control) => ({
|
|
1904
|
+
tagName: control.tagName.toLowerCase(),
|
|
1905
|
+
type: control.getAttribute("type"),
|
|
1906
|
+
id: control.getAttribute("id"),
|
|
1907
|
+
name: control.getAttribute("name"),
|
|
1908
|
+
value: control.getAttribute("value"),
|
|
1909
|
+
currentValue: control.value,
|
|
1910
|
+
checked: null,
|
|
1911
|
+
});
|
|
1912
|
+
const root = stateOptions.selector != null
|
|
1913
|
+
? document.querySelector(stateOptions.selector)
|
|
1914
|
+
: stateOptions.internalLabel != null
|
|
1915
|
+
? document.querySelector(`[data-internal-label="${CSS.escape(stateOptions.internalLabel)}"]`)
|
|
1916
|
+
: stateOptions.fieldId == null
|
|
1917
|
+
? null
|
|
1918
|
+
: document.querySelector(`#fsCell${CSS.escape(String(stateOptions.fieldId))}, #fsFieldCell${CSS.escape(String(stateOptions.fieldId))}, [data-testid="field-${CSS.escape(String(stateOptions.fieldId))}"]`);
|
|
1919
|
+
const selects = root
|
|
1920
|
+
? Array.from(root.querySelectorAll("select"))
|
|
1921
|
+
: [];
|
|
1922
|
+
const controls = selects.map(controlSnapshot);
|
|
1923
|
+
const primary = selects[0] ?? null;
|
|
1924
|
+
const options = primary
|
|
1925
|
+
? Array.from(primary.options).map((option) => ({
|
|
1926
|
+
value: option.value,
|
|
1927
|
+
text: option.textContent?.replace(/\s+/g, " ").trim() ?? "",
|
|
1928
|
+
selected: option.selected,
|
|
1929
|
+
}))
|
|
1930
|
+
: [];
|
|
1931
|
+
const selected = options.find((option) => option.selected) ?? null;
|
|
1932
|
+
const selectedValue = primary ? primary.value : null;
|
|
1933
|
+
const selectedText = selected?.text ?? null;
|
|
1934
|
+
const allValues = options
|
|
1935
|
+
.map((option) => option.value)
|
|
1936
|
+
.filter((value) => value.length > 0);
|
|
1937
|
+
return {
|
|
1938
|
+
fieldMatched: Boolean(root),
|
|
1939
|
+
selectedValue,
|
|
1940
|
+
selectedText,
|
|
1941
|
+
allValues,
|
|
1942
|
+
...(stateOptions.expectedValue === undefined ? {} : { expectedValue: stateOptions.expectedValue }),
|
|
1943
|
+
matched: Boolean(root) &&
|
|
1944
|
+
primary !== null &&
|
|
1945
|
+
(stateOptions.expectedValue === undefined || selectedValue === stateOptions.expectedValue),
|
|
1946
|
+
controls,
|
|
1947
|
+
options,
|
|
1948
|
+
};
|
|
1949
|
+
}, options);
|
|
1950
|
+
}
|
|
1951
|
+
export async function waitForLiveFormSelectState(page, options) {
|
|
1952
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
1953
|
+
const intervalMs = options.intervalMs ?? 100;
|
|
1954
|
+
const startedAt = Date.now();
|
|
1955
|
+
let latest = await readLiveFormSelectState(page, options);
|
|
1956
|
+
while (!latest.matched && Date.now() - startedAt < timeoutMs) {
|
|
1957
|
+
await delay(intervalMs);
|
|
1958
|
+
latest = await readLiveFormSelectState(page, options);
|
|
1959
|
+
}
|
|
1960
|
+
return latest;
|
|
1961
|
+
}
|
|
1818
1962
|
export async function dispatchLiveFormFieldDomEvents(page, options) {
|
|
1819
1963
|
if (!hasEvaluate(page)) {
|
|
1820
1964
|
throw new Error("dispatchLiveFormFieldDomEvents requires a Playwright page with evaluate().");
|
|
@@ -3405,6 +3549,92 @@ export async function setLiveFormCheckboxValues(page, options) {
|
|
|
3405
3549
|
(commitControl?.ready ?? true),
|
|
3406
3550
|
};
|
|
3407
3551
|
}
|
|
3552
|
+
export async function setLiveFormChoiceValue(page, options) {
|
|
3553
|
+
const payload = normalizeLiveFormChoiceValue(options.value);
|
|
3554
|
+
const value = payload.value;
|
|
3555
|
+
const transaction = await performLiveFormFieldTransaction(page, {
|
|
3556
|
+
write: {
|
|
3557
|
+
...(options.write ?? {}),
|
|
3558
|
+
target: options.target,
|
|
3559
|
+
value: payload,
|
|
3560
|
+
notify: options.write?.notify ?? true,
|
|
3561
|
+
},
|
|
3562
|
+
fieldKind: "radio",
|
|
3563
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3564
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3565
|
+
afterQuiet: options.afterQuiet ?? { quietMs: 200, timeoutMs: 5_000 },
|
|
3566
|
+
...(options.afterValidation === undefined ? {} : { afterValidation: options.afterValidation }),
|
|
3567
|
+
});
|
|
3568
|
+
const dom = await waitForLiveFormChoiceState(page, {
|
|
3569
|
+
...options.target,
|
|
3570
|
+
expectedValue: value,
|
|
3571
|
+
...(options.write?.timeoutMs === undefined ? {} : { timeoutMs: options.write.timeoutMs }),
|
|
3572
|
+
});
|
|
3573
|
+
const visibilityTargets = [
|
|
3574
|
+
...(options.expectVisible ?? []).map((target) => ({ ...target, visible: true })),
|
|
3575
|
+
...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
|
|
3576
|
+
];
|
|
3577
|
+
const visibility = visibilityTargets.length > 0
|
|
3578
|
+
? await waitForLiveFormVisibility(page, { targets: visibilityTargets })
|
|
3579
|
+
: undefined;
|
|
3580
|
+
return {
|
|
3581
|
+
payload,
|
|
3582
|
+
value,
|
|
3583
|
+
transaction,
|
|
3584
|
+
dom,
|
|
3585
|
+
...(visibility ? { visibility } : {}),
|
|
3586
|
+
ok: transaction.write.valueMatched &&
|
|
3587
|
+
(transaction.event?.matched ?? true) &&
|
|
3588
|
+
(transaction.domDispatch ? transaction.domDispatch.matchedControlCount === 1 : true) &&
|
|
3589
|
+
(transaction.quiet?.quiet ?? true) &&
|
|
3590
|
+
((transaction.validation?.inlineErrors.length ?? 0) === 0) &&
|
|
3591
|
+
dom.matched &&
|
|
3592
|
+
(visibility?.matched ?? true),
|
|
3593
|
+
};
|
|
3594
|
+
}
|
|
3595
|
+
export async function setLiveFormSelectValue(page, options) {
|
|
3596
|
+
const payload = normalizeLiveFormSelectValue(options.value);
|
|
3597
|
+
const value = payload.value;
|
|
3598
|
+
const transaction = await performLiveFormFieldTransaction(page, {
|
|
3599
|
+
write: {
|
|
3600
|
+
...(options.write ?? {}),
|
|
3601
|
+
target: options.target,
|
|
3602
|
+
value: payload,
|
|
3603
|
+
notify: options.write?.notify ?? true,
|
|
3604
|
+
},
|
|
3605
|
+
fieldKind: "select",
|
|
3606
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3607
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3608
|
+
afterQuiet: options.afterQuiet ?? { quietMs: 200, timeoutMs: 5_000 },
|
|
3609
|
+
...(options.afterValidation === undefined ? {} : { afterValidation: options.afterValidation }),
|
|
3610
|
+
});
|
|
3611
|
+
const dom = await waitForLiveFormSelectState(page, {
|
|
3612
|
+
...options.target,
|
|
3613
|
+
expectedValue: value,
|
|
3614
|
+
...(options.write?.timeoutMs === undefined ? {} : { timeoutMs: options.write.timeoutMs }),
|
|
3615
|
+
});
|
|
3616
|
+
const visibilityTargets = [
|
|
3617
|
+
...(options.expectVisible ?? []).map((target) => ({ ...target, visible: true })),
|
|
3618
|
+
...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
|
|
3619
|
+
];
|
|
3620
|
+
const visibility = visibilityTargets.length > 0
|
|
3621
|
+
? await waitForLiveFormVisibility(page, { targets: visibilityTargets })
|
|
3622
|
+
: undefined;
|
|
3623
|
+
return {
|
|
3624
|
+
payload,
|
|
3625
|
+
value,
|
|
3626
|
+
transaction,
|
|
3627
|
+
dom,
|
|
3628
|
+
...(visibility ? { visibility } : {}),
|
|
3629
|
+
ok: transaction.write.valueMatched &&
|
|
3630
|
+
(transaction.event?.matched ?? true) &&
|
|
3631
|
+
(transaction.domDispatch ? transaction.domDispatch.matchedControlCount >= 1 : true) &&
|
|
3632
|
+
(transaction.quiet?.quiet ?? true) &&
|
|
3633
|
+
((transaction.validation?.inlineErrors.length ?? 0) === 0) &&
|
|
3634
|
+
dom.matched &&
|
|
3635
|
+
(visibility?.matched ?? true),
|
|
3636
|
+
};
|
|
3637
|
+
}
|
|
3408
3638
|
export async function answerProgressiveLiveFormField(page, options) {
|
|
3409
3639
|
const validationOptions = options.afterValidation === undefined ? undefined : typeof options.afterValidation === "object" ? options.afterValidation : {};
|
|
3410
3640
|
const checkboxTransaction = options.fieldKind === "checkbox"
|
|
@@ -3418,21 +3648,47 @@ export async function answerProgressiveLiveFormField(page, options) {
|
|
|
3418
3648
|
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3419
3649
|
})
|
|
3420
3650
|
: undefined;
|
|
3421
|
-
const
|
|
3422
|
-
?
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
target: options.target,
|
|
3427
|
-
value: options.value,
|
|
3428
|
-
notify: options.write?.notify ?? true,
|
|
3429
|
-
},
|
|
3430
|
-
...(options.fieldKind ? { fieldKind: options.fieldKind } : {}),
|
|
3651
|
+
const choiceTransaction = !checkboxTransaction && options.fieldKind === "radio"
|
|
3652
|
+
? await setLiveFormChoiceValue(page, {
|
|
3653
|
+
target: options.target,
|
|
3654
|
+
value: options.value,
|
|
3655
|
+
...(options.write === undefined ? {} : { write: options.write }),
|
|
3431
3656
|
waitForEvent: options.waitForEvent ?? true,
|
|
3432
3657
|
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3433
3658
|
afterQuiet: options.afterQuiet ?? true,
|
|
3434
3659
|
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3435
|
-
})
|
|
3660
|
+
})
|
|
3661
|
+
: undefined;
|
|
3662
|
+
const selectTransaction = !checkboxTransaction && !choiceTransaction && options.fieldKind === "select"
|
|
3663
|
+
? await setLiveFormSelectValue(page, {
|
|
3664
|
+
target: options.target,
|
|
3665
|
+
value: options.value,
|
|
3666
|
+
...(options.write === undefined ? {} : { write: options.write }),
|
|
3667
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3668
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3669
|
+
afterQuiet: options.afterQuiet ?? true,
|
|
3670
|
+
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3671
|
+
})
|
|
3672
|
+
: undefined;
|
|
3673
|
+
const transaction = checkboxTransaction
|
|
3674
|
+
? checkboxTransaction.transaction
|
|
3675
|
+
: choiceTransaction
|
|
3676
|
+
? choiceTransaction.transaction
|
|
3677
|
+
: selectTransaction
|
|
3678
|
+
? selectTransaction.transaction
|
|
3679
|
+
: await performLiveFormFieldTransaction(page, {
|
|
3680
|
+
write: {
|
|
3681
|
+
...(options.write ?? {}),
|
|
3682
|
+
target: options.target,
|
|
3683
|
+
value: options.value,
|
|
3684
|
+
notify: options.write?.notify ?? true,
|
|
3685
|
+
},
|
|
3686
|
+
...(options.fieldKind ? { fieldKind: options.fieldKind } : {}),
|
|
3687
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3688
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3689
|
+
afterQuiet: options.afterQuiet ?? true,
|
|
3690
|
+
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3691
|
+
});
|
|
3436
3692
|
const visibilityTargets = [
|
|
3437
3693
|
...(options.expectVisible ?? []),
|
|
3438
3694
|
...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
|
|
@@ -3635,7 +3891,7 @@ export function normalizeLiveFormSampleValue(kind = "unknown", value) {
|
|
|
3635
3891
|
if (value instanceof Date)
|
|
3636
3892
|
return value.toISOString().slice(0, 10);
|
|
3637
3893
|
if (["radio", "rating", "select"].includes(kind) && typeof value === "string") {
|
|
3638
|
-
return { value };
|
|
3894
|
+
return kind === "radio" ? normalizeLiveFormChoiceValue(value) : { value };
|
|
3639
3895
|
}
|
|
3640
3896
|
if (kind === "checkbox" && (Array.isArray(value) || (value && typeof value === "object"))) {
|
|
3641
3897
|
return normalizeLiveFormCheckboxValue(value);
|
|
@@ -4854,8 +5110,32 @@ export const liveFormFieldRecipes = {
|
|
|
4854
5110
|
}),
|
|
4855
5111
|
setNameApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormNameValue(value), options),
|
|
4856
5112
|
setAddressApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormAddressValue(value), options),
|
|
4857
|
-
setChoiceApi: (name, internalLabel, value, options) =>
|
|
4858
|
-
|
|
5113
|
+
setChoiceApi: (name, internalLabel, value, options = {}) => ({
|
|
5114
|
+
name,
|
|
5115
|
+
run: async (harness) => {
|
|
5116
|
+
await setLiveFormChoiceValue(harness.page, {
|
|
5117
|
+
target: { internalLabel },
|
|
5118
|
+
value,
|
|
5119
|
+
write: { notify: options.notify ?? true },
|
|
5120
|
+
waitForEvent: options.waitForEvent ?? (options.notify ?? true),
|
|
5121
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
5122
|
+
afterQuiet: options.waitForQuiet ?? true,
|
|
5123
|
+
});
|
|
5124
|
+
},
|
|
5125
|
+
}),
|
|
5126
|
+
setSelectApi: (name, internalLabel, value, options = {}) => ({
|
|
5127
|
+
name,
|
|
5128
|
+
run: async (harness) => {
|
|
5129
|
+
await setLiveFormSelectValue(harness.page, {
|
|
5130
|
+
target: { internalLabel },
|
|
5131
|
+
value,
|
|
5132
|
+
write: { notify: options.notify ?? true },
|
|
5133
|
+
waitForEvent: options.waitForEvent ?? (options.notify ?? true),
|
|
5134
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
5135
|
+
afterQuiet: options.waitForQuiet ?? true,
|
|
5136
|
+
});
|
|
5137
|
+
},
|
|
5138
|
+
}),
|
|
4859
5139
|
setCheckboxesApi: (name, internalLabel, values, options = {}) => ({
|
|
4860
5140
|
name,
|
|
4861
5141
|
run: async (harness) => {
|