@redrockswebdevelopment/formstack-form-testing 0.1.25 → 0.1.26
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 +53 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +197 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -987,6 +987,22 @@ export function buildLiveFormMatrixValue(selections) {
|
|
|
987
987
|
}),
|
|
988
988
|
};
|
|
989
989
|
}
|
|
990
|
+
export function readLiveFormMatrixSelections(value) {
|
|
991
|
+
if (Array.isArray(value)) {
|
|
992
|
+
return value.filter((entry) => typeof entry === "string");
|
|
993
|
+
}
|
|
994
|
+
if (!value || typeof value !== "object")
|
|
995
|
+
return [];
|
|
996
|
+
const selections = value.value;
|
|
997
|
+
return Array.isArray(selections)
|
|
998
|
+
? selections.filter((entry) => typeof entry === "string")
|
|
999
|
+
: [];
|
|
1000
|
+
}
|
|
1001
|
+
export function normalizeLiveFormMatrixValue(selections) {
|
|
1002
|
+
if (Array.isArray(selections))
|
|
1003
|
+
return buildLiveFormMatrixValue(selections);
|
|
1004
|
+
return buildLiveFormMatrixValue(readLiveFormMatrixSelections(selections));
|
|
1005
|
+
}
|
|
990
1006
|
export async function readLiveFormFieldValue(page, options) {
|
|
991
1007
|
if (!hasEvaluate(page)) {
|
|
992
1008
|
throw new Error("readLiveFormFieldValue requires a Playwright page with evaluate().");
|
|
@@ -1959,6 +1975,104 @@ export async function waitForLiveFormSelectState(page, options) {
|
|
|
1959
1975
|
}
|
|
1960
1976
|
return latest;
|
|
1961
1977
|
}
|
|
1978
|
+
export async function readLiveFormMatrixState(page, options) {
|
|
1979
|
+
if (!hasEvaluate(page)) {
|
|
1980
|
+
throw new Error("readLiveFormMatrixState requires a Playwright page with evaluate().");
|
|
1981
|
+
}
|
|
1982
|
+
return page.evaluate((stateOptions) => {
|
|
1983
|
+
const normalizeText = (value) => (value ?? "").replace(/\s+/g, " ").trim();
|
|
1984
|
+
const controlSnapshot = (control) => ({
|
|
1985
|
+
tagName: control.tagName.toLowerCase(),
|
|
1986
|
+
type: control.getAttribute("type"),
|
|
1987
|
+
id: control.getAttribute("id"),
|
|
1988
|
+
name: control.getAttribute("name"),
|
|
1989
|
+
value: control.getAttribute("value"),
|
|
1990
|
+
currentValue: control.value,
|
|
1991
|
+
checked: control.checked,
|
|
1992
|
+
});
|
|
1993
|
+
const root = stateOptions.selector != null
|
|
1994
|
+
? document.querySelector(stateOptions.selector)
|
|
1995
|
+
: stateOptions.internalLabel != null
|
|
1996
|
+
? document.querySelector(`[data-internal-label="${CSS.escape(stateOptions.internalLabel)}"]`)
|
|
1997
|
+
: stateOptions.fieldId == null
|
|
1998
|
+
? null
|
|
1999
|
+
: document.querySelector(`#fsCell${CSS.escape(String(stateOptions.fieldId))}, #fsFieldCell${CSS.escape(String(stateOptions.fieldId))}, [data-testid="field-${CSS.escape(String(stateOptions.fieldId))}"]`);
|
|
2000
|
+
const controls = root
|
|
2001
|
+
? Array.from(root.querySelectorAll("input[type=\"radio\"], input[type=\"checkbox\"]"))
|
|
2002
|
+
: [];
|
|
2003
|
+
const inferCell = (control) => {
|
|
2004
|
+
const tableCell = control.closest("td, th");
|
|
2005
|
+
const tableRow = control.closest("tr");
|
|
2006
|
+
const table = control.closest("table");
|
|
2007
|
+
const rowHeader = tableRow
|
|
2008
|
+
? tableRow.querySelector("th") ??
|
|
2009
|
+
Array.from(tableRow.children).find((child) => !child.contains(control))
|
|
2010
|
+
: null;
|
|
2011
|
+
const columnIndex = tableCell?.cellIndex ?? -1;
|
|
2012
|
+
const headerRows = table ? Array.from(table.querySelectorAll("thead tr, tr")).filter((row) => !row.contains(control)) : [];
|
|
2013
|
+
const columnHeader = columnIndex >= 0
|
|
2014
|
+
? headerRows
|
|
2015
|
+
.map((row) => row.children.item(columnIndex))
|
|
2016
|
+
.find((cell) => cell && normalizeText(cell.textContent).length > 0) ?? null
|
|
2017
|
+
: null;
|
|
2018
|
+
const ariaLabel = control.getAttribute("aria-label");
|
|
2019
|
+
const label = control.id ? root?.querySelector(`label[for="${CSS.escape(control.id)}"]`) : null;
|
|
2020
|
+
const row = normalizeText(control.getAttribute("data-row-label")) ||
|
|
2021
|
+
normalizeText(rowHeader?.textContent) ||
|
|
2022
|
+
normalizeText(control.getAttribute("data-row")) ||
|
|
2023
|
+
null;
|
|
2024
|
+
const column = normalizeText(control.getAttribute("data-column-label")) ||
|
|
2025
|
+
normalizeText(columnHeader?.textContent) ||
|
|
2026
|
+
normalizeText(control.getAttribute("data-column")) ||
|
|
2027
|
+
normalizeText(ariaLabel) ||
|
|
2028
|
+
normalizeText(label?.textContent) ||
|
|
2029
|
+
normalizeText(control.value) ||
|
|
2030
|
+
null;
|
|
2031
|
+
const selection = row && column ? `${row} = ${column}` : null;
|
|
2032
|
+
return {
|
|
2033
|
+
row,
|
|
2034
|
+
column,
|
|
2035
|
+
value: control.value,
|
|
2036
|
+
selection,
|
|
2037
|
+
checked: control.checked,
|
|
2038
|
+
control: controlSnapshot(control),
|
|
2039
|
+
};
|
|
2040
|
+
};
|
|
2041
|
+
const cells = controls.map(inferCell);
|
|
2042
|
+
const selectedSelections = cells
|
|
2043
|
+
.filter((cell) => cell.checked && cell.selection)
|
|
2044
|
+
.map((cell) => cell.selection);
|
|
2045
|
+
const allSelections = cells
|
|
2046
|
+
.map((cell) => cell.selection)
|
|
2047
|
+
.filter((selection) => typeof selection === "string" && selection.length > 0);
|
|
2048
|
+
const expectedSelections = [...(stateOptions.expectedSelections ?? [])];
|
|
2049
|
+
const missingSelections = expectedSelections.filter((selection) => !selectedSelections.includes(selection));
|
|
2050
|
+
const unexpectedSelections = selectedSelections.filter((selection) => !expectedSelections.includes(selection));
|
|
2051
|
+
const exact = stateOptions.exact !== false;
|
|
2052
|
+
return {
|
|
2053
|
+
fieldMatched: Boolean(root),
|
|
2054
|
+
selectedSelections,
|
|
2055
|
+
allSelections,
|
|
2056
|
+
expectedSelections,
|
|
2057
|
+
missingSelections,
|
|
2058
|
+
unexpectedSelections,
|
|
2059
|
+
exact,
|
|
2060
|
+
matched: Boolean(root) && missingSelections.length === 0 && (!exact || unexpectedSelections.length === 0),
|
|
2061
|
+
cells,
|
|
2062
|
+
};
|
|
2063
|
+
}, options);
|
|
2064
|
+
}
|
|
2065
|
+
export async function waitForLiveFormMatrixState(page, options) {
|
|
2066
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
2067
|
+
const intervalMs = options.intervalMs ?? 100;
|
|
2068
|
+
const startedAt = Date.now();
|
|
2069
|
+
let latest = await readLiveFormMatrixState(page, options);
|
|
2070
|
+
while (!latest.matched && Date.now() - startedAt < timeoutMs) {
|
|
2071
|
+
await delay(intervalMs);
|
|
2072
|
+
latest = await readLiveFormMatrixState(page, options);
|
|
2073
|
+
}
|
|
2074
|
+
return latest;
|
|
2075
|
+
}
|
|
1962
2076
|
export async function dispatchLiveFormFieldDomEvents(page, options) {
|
|
1963
2077
|
if (!hasEvaluate(page)) {
|
|
1964
2078
|
throw new Error("dispatchLiveFormFieldDomEvents requires a Playwright page with evaluate().");
|
|
@@ -3635,6 +3749,50 @@ export async function setLiveFormSelectValue(page, options) {
|
|
|
3635
3749
|
(visibility?.matched ?? true),
|
|
3636
3750
|
};
|
|
3637
3751
|
}
|
|
3752
|
+
export async function setLiveFormMatrixValues(page, options) {
|
|
3753
|
+
const payload = normalizeLiveFormMatrixValue(options.selections);
|
|
3754
|
+
const selections = payload.value;
|
|
3755
|
+
const transaction = await performLiveFormFieldTransaction(page, {
|
|
3756
|
+
write: {
|
|
3757
|
+
...(options.write ?? {}),
|
|
3758
|
+
target: options.target,
|
|
3759
|
+
value: payload,
|
|
3760
|
+
notify: options.write?.notify ?? true,
|
|
3761
|
+
},
|
|
3762
|
+
fieldKind: "matrix",
|
|
3763
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3764
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3765
|
+
afterQuiet: options.afterQuiet ?? { quietMs: 200, timeoutMs: 5_000 },
|
|
3766
|
+
...(options.afterValidation === undefined ? {} : { afterValidation: options.afterValidation }),
|
|
3767
|
+
});
|
|
3768
|
+
const dom = await waitForLiveFormMatrixState(page, {
|
|
3769
|
+
...options.target,
|
|
3770
|
+
expectedSelections: selections,
|
|
3771
|
+
exact: options.exactDomSelection ?? true,
|
|
3772
|
+
...(options.write?.timeoutMs === undefined ? {} : { timeoutMs: options.write.timeoutMs }),
|
|
3773
|
+
});
|
|
3774
|
+
const visibilityTargets = [
|
|
3775
|
+
...(options.expectVisible ?? []).map((target) => ({ ...target, visible: true })),
|
|
3776
|
+
...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
|
|
3777
|
+
];
|
|
3778
|
+
const visibility = visibilityTargets.length > 0
|
|
3779
|
+
? await waitForLiveFormVisibility(page, { targets: visibilityTargets })
|
|
3780
|
+
: undefined;
|
|
3781
|
+
return {
|
|
3782
|
+
payload,
|
|
3783
|
+
selections,
|
|
3784
|
+
transaction,
|
|
3785
|
+
dom,
|
|
3786
|
+
...(visibility ? { visibility } : {}),
|
|
3787
|
+
ok: transaction.write.valueMatched &&
|
|
3788
|
+
(transaction.event?.matched ?? true) &&
|
|
3789
|
+
(transaction.domDispatch ? transaction.domDispatch.matchedControlCount >= selections.length : true) &&
|
|
3790
|
+
(transaction.quiet?.quiet ?? true) &&
|
|
3791
|
+
((transaction.validation?.inlineErrors.length ?? 0) === 0) &&
|
|
3792
|
+
dom.matched &&
|
|
3793
|
+
(visibility?.matched ?? true),
|
|
3794
|
+
};
|
|
3795
|
+
}
|
|
3638
3796
|
export async function answerProgressiveLiveFormField(page, options) {
|
|
3639
3797
|
const validationOptions = options.afterValidation === undefined ? undefined : typeof options.afterValidation === "object" ? options.afterValidation : {};
|
|
3640
3798
|
const checkboxTransaction = options.fieldKind === "checkbox"
|
|
@@ -3670,25 +3828,38 @@ export async function answerProgressiveLiveFormField(page, options) {
|
|
|
3670
3828
|
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3671
3829
|
})
|
|
3672
3830
|
: undefined;
|
|
3831
|
+
const matrixTransaction = !checkboxTransaction && !choiceTransaction && !selectTransaction && options.fieldKind === "matrix"
|
|
3832
|
+
? await setLiveFormMatrixValues(page, {
|
|
3833
|
+
target: options.target,
|
|
3834
|
+
selections: options.value,
|
|
3835
|
+
...(options.write === undefined ? {} : { write: options.write }),
|
|
3836
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3837
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3838
|
+
afterQuiet: options.afterQuiet ?? true,
|
|
3839
|
+
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3840
|
+
})
|
|
3841
|
+
: undefined;
|
|
3673
3842
|
const transaction = checkboxTransaction
|
|
3674
3843
|
? checkboxTransaction.transaction
|
|
3675
3844
|
: choiceTransaction
|
|
3676
3845
|
? choiceTransaction.transaction
|
|
3677
3846
|
: selectTransaction
|
|
3678
3847
|
? selectTransaction.transaction
|
|
3679
|
-
:
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3848
|
+
: matrixTransaction
|
|
3849
|
+
? matrixTransaction.transaction
|
|
3850
|
+
: await performLiveFormFieldTransaction(page, {
|
|
3851
|
+
write: {
|
|
3852
|
+
...(options.write ?? {}),
|
|
3853
|
+
target: options.target,
|
|
3854
|
+
value: options.value,
|
|
3855
|
+
notify: options.write?.notify ?? true,
|
|
3856
|
+
},
|
|
3857
|
+
...(options.fieldKind ? { fieldKind: options.fieldKind } : {}),
|
|
3858
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3859
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3860
|
+
afterQuiet: options.afterQuiet ?? true,
|
|
3861
|
+
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3862
|
+
});
|
|
3692
3863
|
const visibilityTargets = [
|
|
3693
3864
|
...(options.expectVisible ?? []),
|
|
3694
3865
|
...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
|
|
@@ -5150,7 +5321,19 @@ export const liveFormFieldRecipes = {
|
|
|
5150
5321
|
},
|
|
5151
5322
|
}),
|
|
5152
5323
|
setDateTimeApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormDateTimeValue(value), options),
|
|
5153
|
-
setMatrixApi: (name, internalLabel, selections, options) =>
|
|
5324
|
+
setMatrixApi: (name, internalLabel, selections, options = {}) => ({
|
|
5325
|
+
name,
|
|
5326
|
+
run: async (harness) => {
|
|
5327
|
+
await setLiveFormMatrixValues(harness.page, {
|
|
5328
|
+
target: { internalLabel },
|
|
5329
|
+
selections,
|
|
5330
|
+
write: { notify: options.notify ?? true },
|
|
5331
|
+
waitForEvent: options.waitForEvent ?? (options.notify ?? true),
|
|
5332
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
5333
|
+
afterQuiet: options.waitForQuiet ?? true,
|
|
5334
|
+
});
|
|
5335
|
+
},
|
|
5336
|
+
}),
|
|
5154
5337
|
setRatingApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormRatingValue(value), options),
|
|
5155
5338
|
setProductApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormProductValue(value), options),
|
|
5156
5339
|
setFieldApiAuto: (name, internalLabel, value, fieldKind, options = {}) => apiRecipeStep(name, { internalLabel }, value, { ...options, fieldKind, dispatchDomEvents: "auto" }),
|