@redrockswebdevelopment/formstack-form-testing 0.1.33 → 0.1.35

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.js CHANGED
@@ -23,8 +23,8 @@ export const liveFormFieldTypeCoverage = {
23
23
  apiWrite: true,
24
24
  domInteraction: true,
25
25
  domAssertion: true,
26
- reason: "Compound address values can be written through Live Form API, while layout and subfield UX should be asserted through DOM controls.",
27
- recommendedHelpers: ["buildLiveFormAddressValue", "setLiveFormFieldValueTransaction", "fillAddress", "readLiveFormDomFieldState"],
26
+ reason: "Compound address values can be written through Live Form API, while subfield UX should be asserted for address line, city, state, ZIP, and country controls.",
27
+ recommendedHelpers: ["buildLiveFormAddressValue", "setLiveFormAddressValue", "readLiveFormAddressState", "fillAddress"],
28
28
  },
29
29
  checkbox: {
30
30
  kind: "checkbox",
@@ -150,8 +150,8 @@ export const liveFormFieldTypeCoverage = {
150
150
  apiWrite: true,
151
151
  domInteraction: true,
152
152
  domAssertion: true,
153
- reason: "Product API state can be set, but visible quantity and amount UI can diverge by product configuration.",
154
- recommendedHelpers: ["buildLiveFormProductValue", "setLiveFormFieldValueTransaction", "fillProduct", "readLiveFormDomFieldState"],
153
+ reason: "Product API state can be set, but visible quantity, fixed amount, and selected product UI can diverge by product configuration.",
154
+ recommendedHelpers: ["buildLiveFormProductValue", "setLiveFormProductValue", "readLiveFormProductState", "fillProduct"],
155
155
  },
156
156
  radio: {
157
157
  kind: "radio",
@@ -901,6 +901,16 @@ export function buildLiveFormAddressValue(value, current = {}) {
901
901
  ...value,
902
902
  };
903
903
  }
904
+ export function normalizeLiveFormAddressValue(value, current = {}) {
905
+ return buildLiveFormAddressValue({
906
+ ...(value.address === undefined ? {} : { address: String(value.address) }),
907
+ ...(value.address2 === undefined ? {} : { address2: String(value.address2) }),
908
+ ...(value.city === undefined ? {} : { city: String(value.city) }),
909
+ ...(value.state === undefined ? {} : { state: String(value.state) }),
910
+ ...(value.zip === undefined ? {} : { zip: String(value.zip) }),
911
+ ...(value.country === undefined ? {} : { country: String(value.country) }),
912
+ }, current);
913
+ }
904
914
  export function buildLiveFormChoiceValue(value, otherValue = "") {
905
915
  return { value, otherValue };
906
916
  }
@@ -1030,6 +1040,13 @@ export function buildLiveFormProductValue(value, current = {}) {
1030
1040
  ...value,
1031
1041
  };
1032
1042
  }
1043
+ export function normalizeLiveFormProductValue(value, current = {}) {
1044
+ return buildLiveFormProductValue({
1045
+ ...(value.fixedAmount === undefined ? {} : { fixedAmount: String(value.fixedAmount) }),
1046
+ ...(value.quantity === undefined ? {} : { quantity: String(value.quantity) }),
1047
+ ...("value" in value && value.value !== undefined ? { value: String(value.value) } : {}),
1048
+ }, current);
1049
+ }
1033
1050
  export function buildLiveFormRatingValue(value) {
1034
1051
  return { value: String(value) };
1035
1052
  }
@@ -1827,6 +1844,217 @@ export async function waitForLiveFormDomFieldState(page, options) {
1827
1844
  }
1828
1845
  return latest;
1829
1846
  }
1847
+ export async function readLiveFormProductState(page, options) {
1848
+ if (!hasEvaluate(page)) {
1849
+ throw new Error("readLiveFormProductState requires a Playwright page with evaluate().");
1850
+ }
1851
+ return page.evaluate((stateOptions) => {
1852
+ const normalizeText = (value) => (value ?? "").replace(/\s+/g, " ").trim();
1853
+ const inferPart = (control) => {
1854
+ const label = control.id ? document.querySelector(`label[for="${CSS.escape(control.id)}"]`) : null;
1855
+ const haystack = [
1856
+ control.name,
1857
+ control.id,
1858
+ control.className,
1859
+ control.getAttribute("placeholder"),
1860
+ control.getAttribute("aria-label"),
1861
+ label?.textContent,
1862
+ ].map(normalizeText).join(" ").toLowerCase();
1863
+ if (/\b(quantity|qty|\[quantity\]|-quantity$|_quantity$)/i.test(haystack))
1864
+ return "quantity";
1865
+ if (/\b(fixed amount|fixedamount|amount|price|\[fixedamount\]|-fixedamount$|_fixedamount$)/i.test(haystack))
1866
+ return "fixedAmount";
1867
+ if (/\b(product|option|value|\[value\]|-value$|_value$)/i.test(haystack))
1868
+ return "value";
1869
+ return null;
1870
+ };
1871
+ const root = stateOptions.selector != null
1872
+ ? document.querySelector(stateOptions.selector)
1873
+ : stateOptions.internalLabel != null
1874
+ ? document.querySelector(`[data-internal-label="${CSS.escape(stateOptions.internalLabel)}"]`)
1875
+ : stateOptions.fieldId == null
1876
+ ? null
1877
+ : document.querySelector(`#fsCell${CSS.escape(String(stateOptions.fieldId))}, #fsFieldCell${CSS.escape(String(stateOptions.fieldId))}, [data-testid="field-${CSS.escape(String(stateOptions.fieldId))}"]`);
1878
+ const nativeControls = root
1879
+ ? Array.from(root.querySelectorAll("input, textarea, select"))
1880
+ : [];
1881
+ const controls = nativeControls.map((control) => {
1882
+ const selected = control instanceof HTMLSelectElement
1883
+ ? control.selectedIndex >= 0
1884
+ : control instanceof HTMLInputElement && ["checkbox", "radio"].includes(control.type)
1885
+ ? control.checked
1886
+ : null;
1887
+ return {
1888
+ tagName: control.tagName.toLowerCase(),
1889
+ type: control.getAttribute("type"),
1890
+ id: control.getAttribute("id"),
1891
+ name: control.getAttribute("name"),
1892
+ value: control.getAttribute("value"),
1893
+ currentValue: "value" in control ? control.value : "",
1894
+ checked: control instanceof HTMLInputElement && ["checkbox", "radio"].includes(control.type) ? control.checked : null,
1895
+ part: inferPart(control),
1896
+ text: control instanceof HTMLSelectElement
1897
+ ? normalizeText(control.selectedOptions.item(0)?.textContent)
1898
+ : normalizeText(control.textContent),
1899
+ selected,
1900
+ disabled: "disabled" in control ? Boolean(control.disabled) : null,
1901
+ };
1902
+ });
1903
+ const activeControls = controls.filter((control) => control.disabled !== true &&
1904
+ ((control.tagName === "select" && (control.currentValue ?? "").length > 0) ||
1905
+ (["checkbox", "radio"].includes(control.type ?? "") ? control.checked === true : (control.currentValue ?? "").length > 0)));
1906
+ const findPart = (part) => activeControls.find((control) => control.part === part)?.currentValue ?? null;
1907
+ const selectedValue = findPart("value") ?? activeControls.find((control) => ["checkbox", "radio"].includes(control.type ?? "") && control.checked === true)?.currentValue ?? null;
1908
+ const quantity = findPart("quantity");
1909
+ const fixedAmount = findPart("fixedAmount");
1910
+ const amountText = root
1911
+ ? Array.from(root.querySelectorAll("[class*=\"amount\"], [class*=\"price\"], [data-testid*=\"amount\"], [data-testid*=\"price\"]"))
1912
+ .map((element) => normalizeText(element.textContent))
1913
+ .find((text) => text.length > 0 && /[$€£]|\d/.test(text)) ?? null
1914
+ : null;
1915
+ const expected = stateOptions.expectedValue ?? {};
1916
+ const expectedEntries = Object.entries(expected).filter((entry) => typeof entry[1] === "string" && entry[1].length > 0);
1917
+ const selectedParts = {
1918
+ value: selectedValue,
1919
+ quantity,
1920
+ fixedAmount,
1921
+ };
1922
+ const renderedExpectedEntries = expectedEntries.filter(([part]) => selectedParts[part] !== null);
1923
+ const unavailableParts = expectedEntries
1924
+ .filter(([part]) => selectedParts[part] === null)
1925
+ .map(([part]) => part);
1926
+ const missingParts = renderedExpectedEntries
1927
+ .filter(([part]) => selectedParts[part] === "")
1928
+ .map(([part]) => part);
1929
+ const mismatchedParts = renderedExpectedEntries
1930
+ .filter(([part, value]) => selectedParts[part] !== "" && selectedParts[part] !== value)
1931
+ .map(([part]) => part);
1932
+ const renderedValueAvailable = renderedExpectedEntries.length > 0 || activeControls.length > 0 || amountText !== null;
1933
+ return {
1934
+ fieldMatched: Boolean(root),
1935
+ selectedValue,
1936
+ quantity,
1937
+ fixedAmount,
1938
+ amountText,
1939
+ ...(stateOptions.expectedValue === undefined ? {} : { expectedValue: stateOptions.expectedValue }),
1940
+ renderedValueAvailable,
1941
+ unavailableParts,
1942
+ missingParts,
1943
+ mismatchedParts,
1944
+ matched: Boolean(root) && missingParts.length === 0 && mismatchedParts.length === 0,
1945
+ controls,
1946
+ };
1947
+ }, options);
1948
+ }
1949
+ export async function waitForLiveFormProductState(page, options) {
1950
+ const timeoutMs = options.timeoutMs ?? 10_000;
1951
+ const intervalMs = options.intervalMs ?? 100;
1952
+ const startedAt = Date.now();
1953
+ let latest = await readLiveFormProductState(page, options);
1954
+ while (!latest.matched && Date.now() - startedAt < timeoutMs) {
1955
+ await delay(intervalMs);
1956
+ latest = await readLiveFormProductState(page, options);
1957
+ }
1958
+ return latest;
1959
+ }
1960
+ export async function readLiveFormAddressState(page, options) {
1961
+ if (!hasEvaluate(page)) {
1962
+ throw new Error("readLiveFormAddressState requires a Playwright page with evaluate().");
1963
+ }
1964
+ return page.evaluate((stateOptions) => {
1965
+ const normalizeText = (value) => (value ?? "").replace(/\s+/g, " ").trim();
1966
+ const inferPart = (control) => {
1967
+ const label = control.id ? document.querySelector(`label[for="${CSS.escape(control.id)}"]`) : null;
1968
+ const haystack = [
1969
+ control.name,
1970
+ control.id,
1971
+ control.className,
1972
+ control.getAttribute("placeholder"),
1973
+ control.getAttribute("aria-label"),
1974
+ label?.textContent,
1975
+ ].map(normalizeText).join(" ").toLowerCase();
1976
+ if (/\b(address line 2|address 2|address2|line 2|street 2|\[address2\]|-address2$|_address2$)/i.test(haystack))
1977
+ return "address2";
1978
+ if (/\b(address line 1|address 1|address|street|line 1|\[address\]|-address$|_address$)/i.test(haystack))
1979
+ return "address";
1980
+ if (/\b(city|\[city\]|-city$|_city$)/i.test(haystack))
1981
+ return "city";
1982
+ if (/\b(state|province|region|\[state\]|-state$|_state$)/i.test(haystack))
1983
+ return "state";
1984
+ if (/\b(zip|postal|postcode|post code|\[zip\]|-zip$|_zip$)/i.test(haystack))
1985
+ return "zip";
1986
+ if (/\b(country|\[country\]|-country$|_country$)/i.test(haystack))
1987
+ return "country";
1988
+ return null;
1989
+ };
1990
+ const root = stateOptions.selector != null
1991
+ ? document.querySelector(stateOptions.selector)
1992
+ : stateOptions.internalLabel != null
1993
+ ? document.querySelector(`[data-internal-label="${CSS.escape(stateOptions.internalLabel)}"]`)
1994
+ : stateOptions.fieldId == null
1995
+ ? null
1996
+ : document.querySelector(`#fsCell${CSS.escape(String(stateOptions.fieldId))}, #fsFieldCell${CSS.escape(String(stateOptions.fieldId))}, [data-testid="field-${CSS.escape(String(stateOptions.fieldId))}"]`);
1997
+ const nativeControls = root
1998
+ ? Array.from(root.querySelectorAll("input, textarea, select"))
1999
+ : [];
2000
+ const controls = nativeControls.map((control) => ({
2001
+ tagName: control.tagName.toLowerCase(),
2002
+ type: control.getAttribute("type"),
2003
+ id: control.getAttribute("id"),
2004
+ name: control.getAttribute("name"),
2005
+ value: control.getAttribute("value"),
2006
+ currentValue: "value" in control ? control.value : "",
2007
+ checked: control instanceof HTMLInputElement && ["checkbox", "radio"].includes(control.type) ? control.checked : null,
2008
+ part: inferPart(control),
2009
+ text: control instanceof HTMLSelectElement
2010
+ ? normalizeText(control.selectedOptions.item(0)?.textContent)
2011
+ : normalizeText(control.textContent),
2012
+ disabled: "disabled" in control ? Boolean(control.disabled) : null,
2013
+ }));
2014
+ const selectedParts = {};
2015
+ for (const control of controls) {
2016
+ if (control.part && control.disabled !== true && typeof control.currentValue === "string" && control.currentValue.length > 0) {
2017
+ selectedParts[control.part] = control.currentValue;
2018
+ }
2019
+ }
2020
+ const expected = stateOptions.expectedValue ?? {};
2021
+ const expectedEntries = Object.entries(expected).filter((entry) => typeof entry[1] === "string" && entry[1].length > 0);
2022
+ const availableParts = new Set(controls.filter((control) => control.part && control.disabled !== true).map((control) => control.part));
2023
+ const renderedExpectedEntries = expectedEntries.filter(([part]) => availableParts.has(part));
2024
+ const unavailableParts = expectedEntries
2025
+ .filter(([part]) => !availableParts.has(part))
2026
+ .map(([part]) => part);
2027
+ const missingParts = renderedExpectedEntries
2028
+ .filter(([part]) => selectedParts[part] === undefined || selectedParts[part] === "")
2029
+ .map(([part]) => part);
2030
+ const mismatchedParts = renderedExpectedEntries
2031
+ .filter(([part, value]) => selectedParts[part] !== undefined && selectedParts[part] !== "" && selectedParts[part] !== value)
2032
+ .map(([part]) => part);
2033
+ const renderedValueAvailable = Object.keys(selectedParts).length > 0;
2034
+ return {
2035
+ fieldMatched: Boolean(root),
2036
+ selectedParts,
2037
+ ...(stateOptions.expectedValue === undefined ? {} : { expectedValue: stateOptions.expectedValue }),
2038
+ renderedValueAvailable,
2039
+ unavailableParts,
2040
+ missingParts,
2041
+ mismatchedParts,
2042
+ matched: Boolean(root) && missingParts.length === 0 && mismatchedParts.length === 0,
2043
+ controls,
2044
+ };
2045
+ }, options);
2046
+ }
2047
+ export async function waitForLiveFormAddressState(page, options) {
2048
+ const timeoutMs = options.timeoutMs ?? 10_000;
2049
+ const intervalMs = options.intervalMs ?? 100;
2050
+ const startedAt = Date.now();
2051
+ let latest = await readLiveFormAddressState(page, options);
2052
+ while (!latest.matched && Date.now() - startedAt < timeoutMs) {
2053
+ await delay(intervalMs);
2054
+ latest = await readLiveFormAddressState(page, options);
2055
+ }
2056
+ return latest;
2057
+ }
1830
2058
  export async function readLiveFormCheckboxState(page, options) {
1831
2059
  if (!hasEvaluate(page)) {
1832
2060
  throw new Error("readLiveFormCheckboxState requires a Playwright page with evaluate().");
@@ -4081,6 +4309,88 @@ export async function setLiveFormMatrixValues(page, options) {
4081
4309
  (visibility?.matched ?? true),
4082
4310
  };
4083
4311
  }
4312
+ export async function setLiveFormProductValue(page, options) {
4313
+ const payload = normalizeLiveFormProductValue(options.value, options.current);
4314
+ const transaction = await performLiveFormFieldTransaction(page, {
4315
+ write: {
4316
+ ...(options.write ?? {}),
4317
+ target: options.target,
4318
+ value: payload,
4319
+ notify: options.write?.notify ?? true,
4320
+ },
4321
+ fieldKind: "product",
4322
+ waitForEvent: options.waitForEvent ?? true,
4323
+ dispatchDomEvents: options.dispatchDomEvents ?? "auto",
4324
+ afterQuiet: options.afterQuiet ?? { quietMs: 200, timeoutMs: 5_000 },
4325
+ ...(options.afterValidation === undefined ? {} : { afterValidation: options.afterValidation }),
4326
+ });
4327
+ const dom = await waitForLiveFormProductState(page, {
4328
+ ...options.target,
4329
+ expectedValue: payload,
4330
+ ...(options.write?.timeoutMs === undefined ? {} : { timeoutMs: options.write.timeoutMs }),
4331
+ });
4332
+ const visibilityTargets = [
4333
+ ...(options.expectVisible ?? []).map((target) => ({ ...target, visible: true })),
4334
+ ...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
4335
+ ];
4336
+ const visibility = visibilityTargets.length > 0
4337
+ ? await waitForLiveFormVisibility(page, { targets: visibilityTargets })
4338
+ : undefined;
4339
+ return {
4340
+ payload,
4341
+ transaction,
4342
+ dom,
4343
+ ...(visibility ? { visibility } : {}),
4344
+ ok: transaction.write.valueMatched &&
4345
+ (transaction.event?.matched ?? true) &&
4346
+ (transaction.domDispatch ? transaction.domDispatch.matchedControlCount >= 1 : true) &&
4347
+ (transaction.quiet?.quiet ?? true) &&
4348
+ ((transaction.validation?.inlineErrors.length ?? 0) === 0) &&
4349
+ dom.matched &&
4350
+ (visibility?.matched ?? true),
4351
+ };
4352
+ }
4353
+ export async function setLiveFormAddressValue(page, options) {
4354
+ const payload = normalizeLiveFormAddressValue(options.value, options.current);
4355
+ const transaction = await performLiveFormFieldTransaction(page, {
4356
+ write: {
4357
+ ...(options.write ?? {}),
4358
+ target: options.target,
4359
+ value: payload,
4360
+ notify: options.write?.notify ?? true,
4361
+ },
4362
+ fieldKind: "address",
4363
+ waitForEvent: options.waitForEvent ?? true,
4364
+ dispatchDomEvents: options.dispatchDomEvents ?? "auto",
4365
+ afterQuiet: options.afterQuiet ?? { quietMs: 200, timeoutMs: 5_000 },
4366
+ ...(options.afterValidation === undefined ? {} : { afterValidation: options.afterValidation }),
4367
+ });
4368
+ const dom = await waitForLiveFormAddressState(page, {
4369
+ ...options.target,
4370
+ expectedValue: payload,
4371
+ ...(options.write?.timeoutMs === undefined ? {} : { timeoutMs: options.write.timeoutMs }),
4372
+ });
4373
+ const visibilityTargets = [
4374
+ ...(options.expectVisible ?? []).map((target) => ({ ...target, visible: true })),
4375
+ ...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
4376
+ ];
4377
+ const visibility = visibilityTargets.length > 0
4378
+ ? await waitForLiveFormVisibility(page, { targets: visibilityTargets })
4379
+ : undefined;
4380
+ return {
4381
+ payload,
4382
+ transaction,
4383
+ dom,
4384
+ ...(visibility ? { visibility } : {}),
4385
+ ok: transaction.write.valueMatched &&
4386
+ (transaction.event?.matched ?? true) &&
4387
+ (transaction.domDispatch ? transaction.domDispatch.matchedControlCount >= 1 : true) &&
4388
+ (transaction.quiet?.quiet ?? true) &&
4389
+ ((transaction.validation?.inlineErrors.length ?? 0) === 0) &&
4390
+ dom.matched &&
4391
+ (visibility?.matched ?? true),
4392
+ };
4393
+ }
4084
4394
  export async function setLiveFormRatingValue(page, options) {
4085
4395
  const payload = normalizeLiveFormRatingValue(options.value);
4086
4396
  const value = payload.value;
@@ -4211,7 +4521,29 @@ export async function answerProgressiveLiveFormField(page, options) {
4211
4521
  ...(validationOptions ? { afterValidation: validationOptions } : {}),
4212
4522
  })
4213
4523
  : undefined;
4214
- const ratingTransaction = !checkboxTransaction && !choiceTransaction && !selectTransaction && !matrixTransaction && options.fieldKind === "rating"
4524
+ const addressTransaction = !checkboxTransaction && !choiceTransaction && !selectTransaction && !matrixTransaction && options.fieldKind === "address"
4525
+ ? await setLiveFormAddressValue(page, {
4526
+ target: options.target,
4527
+ value: options.value,
4528
+ ...(options.write === undefined ? {} : { write: options.write }),
4529
+ waitForEvent: options.waitForEvent ?? true,
4530
+ dispatchDomEvents: options.dispatchDomEvents ?? "auto",
4531
+ afterQuiet: options.afterQuiet ?? true,
4532
+ ...(validationOptions ? { afterValidation: validationOptions } : {}),
4533
+ })
4534
+ : undefined;
4535
+ const productTransaction = !checkboxTransaction && !choiceTransaction && !selectTransaction && !matrixTransaction && !addressTransaction && options.fieldKind === "product"
4536
+ ? await setLiveFormProductValue(page, {
4537
+ target: options.target,
4538
+ value: options.value,
4539
+ ...(options.write === undefined ? {} : { write: options.write }),
4540
+ waitForEvent: options.waitForEvent ?? true,
4541
+ dispatchDomEvents: options.dispatchDomEvents ?? "auto",
4542
+ afterQuiet: options.afterQuiet ?? true,
4543
+ ...(validationOptions ? { afterValidation: validationOptions } : {}),
4544
+ })
4545
+ : undefined;
4546
+ const ratingTransaction = !checkboxTransaction && !choiceTransaction && !selectTransaction && !matrixTransaction && !addressTransaction && !productTransaction && options.fieldKind === "rating"
4215
4547
  ? await setLiveFormRatingValue(page, {
4216
4548
  target: options.target,
4217
4549
  value: options.value,
@@ -4241,23 +4573,27 @@ export async function answerProgressiveLiveFormField(page, options) {
4241
4573
  ? selectTransaction.transaction
4242
4574
  : matrixTransaction
4243
4575
  ? matrixTransaction.transaction
4244
- : ratingTransaction
4245
- ? ratingTransaction.transaction
4246
- : dateTimeTransaction
4247
- ? dateTimeTransaction.transaction
4248
- : await performLiveFormFieldTransaction(page, {
4249
- write: {
4250
- ...(options.write ?? {}),
4251
- target: options.target,
4252
- value: options.value,
4253
- notify: options.write?.notify ?? true,
4254
- },
4255
- ...(options.fieldKind ? { fieldKind: options.fieldKind } : {}),
4256
- waitForEvent: options.waitForEvent ?? true,
4257
- dispatchDomEvents: options.dispatchDomEvents ?? "auto",
4258
- afterQuiet: options.afterQuiet ?? true,
4259
- ...(validationOptions ? { afterValidation: validationOptions } : {}),
4260
- });
4576
+ : addressTransaction
4577
+ ? addressTransaction.transaction
4578
+ : productTransaction
4579
+ ? productTransaction.transaction
4580
+ : ratingTransaction
4581
+ ? ratingTransaction.transaction
4582
+ : dateTimeTransaction
4583
+ ? dateTimeTransaction.transaction
4584
+ : await performLiveFormFieldTransaction(page, {
4585
+ write: {
4586
+ ...(options.write ?? {}),
4587
+ target: options.target,
4588
+ value: options.value,
4589
+ notify: options.write?.notify ?? true,
4590
+ },
4591
+ ...(options.fieldKind ? { fieldKind: options.fieldKind } : {}),
4592
+ waitForEvent: options.waitForEvent ?? true,
4593
+ dispatchDomEvents: options.dispatchDomEvents ?? "auto",
4594
+ afterQuiet: options.afterQuiet ?? true,
4595
+ ...(validationOptions ? { afterValidation: validationOptions } : {}),
4596
+ });
4261
4597
  const visibilityTargets = [
4262
4598
  ...(options.expectVisible ?? []),
4263
4599
  ...(options.expectHidden ?? []).map((target) => ({ ...target, visible: false })),
@@ -5962,7 +6298,19 @@ export const liveFormFieldRecipes = {
5962
6298
  },
5963
6299
  }),
5964
6300
  setNameApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormNameValue(value), options),
5965
- setAddressApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormAddressValue(value), options),
6301
+ setAddressApi: (name, internalLabel, value, options = {}) => ({
6302
+ name,
6303
+ run: async (harness) => {
6304
+ await setLiveFormAddressValue(harness.page, {
6305
+ target: { internalLabel },
6306
+ value,
6307
+ write: { notify: options.notify ?? true },
6308
+ waitForEvent: options.waitForEvent ?? (options.notify ?? true),
6309
+ dispatchDomEvents: options.dispatchDomEvents ?? "auto",
6310
+ afterQuiet: options.waitForQuiet ?? true,
6311
+ });
6312
+ },
6313
+ }),
5966
6314
  setChoiceApi: (name, internalLabel, value, options = {}) => ({
5967
6315
  name,
5968
6316
  run: async (harness) => {
@@ -6041,7 +6389,19 @@ export const liveFormFieldRecipes = {
6041
6389
  });
6042
6390
  },
6043
6391
  }),
6044
- setProductApi: (name, internalLabel, value, options) => apiRecipeStep(name, { internalLabel }, buildLiveFormProductValue(value), options),
6392
+ setProductApi: (name, internalLabel, value, options = {}) => ({
6393
+ name,
6394
+ run: async (harness) => {
6395
+ await setLiveFormProductValue(harness.page, {
6396
+ target: { internalLabel },
6397
+ value,
6398
+ write: { notify: options.notify ?? true },
6399
+ waitForEvent: options.waitForEvent ?? (options.notify ?? true),
6400
+ dispatchDomEvents: options.dispatchDomEvents ?? "auto",
6401
+ afterQuiet: options.waitForQuiet ?? true,
6402
+ });
6403
+ },
6404
+ }),
6045
6405
  setFieldApiAuto: (name, internalLabel, value, fieldKind, options = {}) => apiRecipeStep(name, { internalLabel }, value, { ...options, fieldKind, dispatchDomEvents: "auto" }),
6046
6406
  setNameApiAndDispatch: (name, internalLabel, value, options = {}) => liveFormFieldRecipes.setNameApi(name, internalLabel, value, { ...options, fieldKind: options.fieldKind ?? "name", dispatchDomEvents: "auto" }),
6047
6407
  setChoiceApiAndDispatch: (name, internalLabel, value, options = {}) => liveFormFieldRecipes.setChoiceApi(name, internalLabel, value, { ...options, fieldKind: options.fieldKind ?? "radio", dispatchDomEvents: "auto" }),