@redrockswebdevelopment/formstack-form-testing 0.1.25 → 0.1.27
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 +201 -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,108 @@ 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
|
|
2032
|
+
? `${row} = ${column}`
|
|
2033
|
+
: control.value.length > 0
|
|
2034
|
+
? control.value
|
|
2035
|
+
: null;
|
|
2036
|
+
return {
|
|
2037
|
+
row,
|
|
2038
|
+
column,
|
|
2039
|
+
value: control.value,
|
|
2040
|
+
selection,
|
|
2041
|
+
checked: control.checked,
|
|
2042
|
+
control: controlSnapshot(control),
|
|
2043
|
+
};
|
|
2044
|
+
};
|
|
2045
|
+
const cells = controls.map(inferCell);
|
|
2046
|
+
const selectedSelections = cells
|
|
2047
|
+
.filter((cell) => cell.checked && cell.selection)
|
|
2048
|
+
.map((cell) => cell.selection);
|
|
2049
|
+
const allSelections = cells
|
|
2050
|
+
.map((cell) => cell.selection)
|
|
2051
|
+
.filter((selection) => typeof selection === "string" && selection.length > 0);
|
|
2052
|
+
const expectedSelections = [...(stateOptions.expectedSelections ?? [])];
|
|
2053
|
+
const missingSelections = expectedSelections.filter((selection) => !selectedSelections.includes(selection));
|
|
2054
|
+
const unexpectedSelections = selectedSelections.filter((selection) => !expectedSelections.includes(selection));
|
|
2055
|
+
const exact = stateOptions.exact !== false;
|
|
2056
|
+
return {
|
|
2057
|
+
fieldMatched: Boolean(root),
|
|
2058
|
+
selectedSelections,
|
|
2059
|
+
allSelections,
|
|
2060
|
+
expectedSelections,
|
|
2061
|
+
missingSelections,
|
|
2062
|
+
unexpectedSelections,
|
|
2063
|
+
exact,
|
|
2064
|
+
matched: Boolean(root) && missingSelections.length === 0 && (!exact || unexpectedSelections.length === 0),
|
|
2065
|
+
cells,
|
|
2066
|
+
};
|
|
2067
|
+
}, options);
|
|
2068
|
+
}
|
|
2069
|
+
export async function waitForLiveFormMatrixState(page, options) {
|
|
2070
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
2071
|
+
const intervalMs = options.intervalMs ?? 100;
|
|
2072
|
+
const startedAt = Date.now();
|
|
2073
|
+
let latest = await readLiveFormMatrixState(page, options);
|
|
2074
|
+
while (!latest.matched && Date.now() - startedAt < timeoutMs) {
|
|
2075
|
+
await delay(intervalMs);
|
|
2076
|
+
latest = await readLiveFormMatrixState(page, options);
|
|
2077
|
+
}
|
|
2078
|
+
return latest;
|
|
2079
|
+
}
|
|
1962
2080
|
export async function dispatchLiveFormFieldDomEvents(page, options) {
|
|
1963
2081
|
if (!hasEvaluate(page)) {
|
|
1964
2082
|
throw new Error("dispatchLiveFormFieldDomEvents requires a Playwright page with evaluate().");
|
|
@@ -3635,6 +3753,50 @@ export async function setLiveFormSelectValue(page, options) {
|
|
|
3635
3753
|
(visibility?.matched ?? true),
|
|
3636
3754
|
};
|
|
3637
3755
|
}
|
|
3756
|
+
export async function setLiveFormMatrixValues(page, options) {
|
|
3757
|
+
const payload = normalizeLiveFormMatrixValue(options.selections);
|
|
3758
|
+
const selections = payload.value;
|
|
3759
|
+
const transaction = await performLiveFormFieldTransaction(page, {
|
|
3760
|
+
write: {
|
|
3761
|
+
...(options.write ?? {}),
|
|
3762
|
+
target: options.target,
|
|
3763
|
+
value: payload,
|
|
3764
|
+
notify: options.write?.notify ?? true,
|
|
3765
|
+
},
|
|
3766
|
+
fieldKind: "matrix",
|
|
3767
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3768
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3769
|
+
afterQuiet: options.afterQuiet ?? { quietMs: 200, timeoutMs: 5_000 },
|
|
3770
|
+
...(options.afterValidation === undefined ? {} : { afterValidation: options.afterValidation }),
|
|
3771
|
+
});
|
|
3772
|
+
const dom = await waitForLiveFormMatrixState(page, {
|
|
3773
|
+
...options.target,
|
|
3774
|
+
expectedSelections: selections,
|
|
3775
|
+
exact: options.exactDomSelection ?? true,
|
|
3776
|
+
...(options.write?.timeoutMs === undefined ? {} : { timeoutMs: options.write.timeoutMs }),
|
|
3777
|
+
});
|
|
3778
|
+
const visibilityTargets = [
|
|
3779
|
+
...(options.expectVisible ?? []).map((target) => ({ ...target, visible: true })),
|
|
3780
|
+
...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
|
|
3781
|
+
];
|
|
3782
|
+
const visibility = visibilityTargets.length > 0
|
|
3783
|
+
? await waitForLiveFormVisibility(page, { targets: visibilityTargets })
|
|
3784
|
+
: undefined;
|
|
3785
|
+
return {
|
|
3786
|
+
payload,
|
|
3787
|
+
selections,
|
|
3788
|
+
transaction,
|
|
3789
|
+
dom,
|
|
3790
|
+
...(visibility ? { visibility } : {}),
|
|
3791
|
+
ok: transaction.write.valueMatched &&
|
|
3792
|
+
(transaction.event?.matched ?? true) &&
|
|
3793
|
+
(transaction.domDispatch ? transaction.domDispatch.matchedControlCount >= selections.length : true) &&
|
|
3794
|
+
(transaction.quiet?.quiet ?? true) &&
|
|
3795
|
+
((transaction.validation?.inlineErrors.length ?? 0) === 0) &&
|
|
3796
|
+
dom.matched &&
|
|
3797
|
+
(visibility?.matched ?? true),
|
|
3798
|
+
};
|
|
3799
|
+
}
|
|
3638
3800
|
export async function answerProgressiveLiveFormField(page, options) {
|
|
3639
3801
|
const validationOptions = options.afterValidation === undefined ? undefined : typeof options.afterValidation === "object" ? options.afterValidation : {};
|
|
3640
3802
|
const checkboxTransaction = options.fieldKind === "checkbox"
|
|
@@ -3670,25 +3832,38 @@ export async function answerProgressiveLiveFormField(page, options) {
|
|
|
3670
3832
|
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3671
3833
|
})
|
|
3672
3834
|
: undefined;
|
|
3835
|
+
const matrixTransaction = !checkboxTransaction && !choiceTransaction && !selectTransaction && options.fieldKind === "matrix"
|
|
3836
|
+
? await setLiveFormMatrixValues(page, {
|
|
3837
|
+
target: options.target,
|
|
3838
|
+
selections: options.value,
|
|
3839
|
+
...(options.write === undefined ? {} : { write: options.write }),
|
|
3840
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3841
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3842
|
+
afterQuiet: options.afterQuiet ?? true,
|
|
3843
|
+
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3844
|
+
})
|
|
3845
|
+
: undefined;
|
|
3673
3846
|
const transaction = checkboxTransaction
|
|
3674
3847
|
? checkboxTransaction.transaction
|
|
3675
3848
|
: choiceTransaction
|
|
3676
3849
|
? choiceTransaction.transaction
|
|
3677
3850
|
: selectTransaction
|
|
3678
3851
|
? selectTransaction.transaction
|
|
3679
|
-
:
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3852
|
+
: matrixTransaction
|
|
3853
|
+
? matrixTransaction.transaction
|
|
3854
|
+
: await performLiveFormFieldTransaction(page, {
|
|
3855
|
+
write: {
|
|
3856
|
+
...(options.write ?? {}),
|
|
3857
|
+
target: options.target,
|
|
3858
|
+
value: options.value,
|
|
3859
|
+
notify: options.write?.notify ?? true,
|
|
3860
|
+
},
|
|
3861
|
+
...(options.fieldKind ? { fieldKind: options.fieldKind } : {}),
|
|
3862
|
+
waitForEvent: options.waitForEvent ?? true,
|
|
3863
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
3864
|
+
afterQuiet: options.afterQuiet ?? true,
|
|
3865
|
+
...(validationOptions ? { afterValidation: validationOptions } : {}),
|
|
3866
|
+
});
|
|
3692
3867
|
const visibilityTargets = [
|
|
3693
3868
|
...(options.expectVisible ?? []),
|
|
3694
3869
|
...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
|
|
@@ -5150,7 +5325,19 @@ export const liveFormFieldRecipes = {
|
|
|
5150
5325
|
},
|
|
5151
5326
|
}),
|
|
5152
5327
|
setDateTimeApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormDateTimeValue(value), options),
|
|
5153
|
-
setMatrixApi: (name, internalLabel, selections, options) =>
|
|
5328
|
+
setMatrixApi: (name, internalLabel, selections, options = {}) => ({
|
|
5329
|
+
name,
|
|
5330
|
+
run: async (harness) => {
|
|
5331
|
+
await setLiveFormMatrixValues(harness.page, {
|
|
5332
|
+
target: { internalLabel },
|
|
5333
|
+
selections,
|
|
5334
|
+
write: { notify: options.notify ?? true },
|
|
5335
|
+
waitForEvent: options.waitForEvent ?? (options.notify ?? true),
|
|
5336
|
+
dispatchDomEvents: options.dispatchDomEvents ?? "auto",
|
|
5337
|
+
afterQuiet: options.waitForQuiet ?? true,
|
|
5338
|
+
});
|
|
5339
|
+
},
|
|
5340
|
+
}),
|
|
5154
5341
|
setRatingApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormRatingValue(value), options),
|
|
5155
5342
|
setProductApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormProductValue(value), options),
|
|
5156
5343
|
setFieldApiAuto: (name, internalLabel, value, fieldKind, options = {}) => apiRecipeStep(name, { internalLabel }, value, { ...options, fieldKind, dispatchDomEvents: "auto" }),
|