@rachelallyson/hero-hook-form 2.6.0 → 2.7.2

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.
@@ -20,25 +20,7 @@ function withRetry(operation, config = DEFAULT_CONFIG) {
20
20
  if (!config.retry) {
21
21
  return operation();
22
22
  }
23
- let attempts = 0;
24
- const maxAttempts = config.maxRetries + 1;
25
- const retryOperation = () => {
26
- attempts++;
27
- return operation().then(
28
- (result) => {
29
- return result;
30
- },
31
- (error) => {
32
- if (attempts < maxAttempts) {
33
- cy.log(`Retry attempt ${attempts}/${maxAttempts} for operation`);
34
- cy.wait(config.retryDelay);
35
- return retryOperation();
36
- }
37
- throw error;
38
- }
39
- );
40
- };
41
- return retryOperation();
23
+ return operation();
42
24
  }
43
25
  function findFieldByLabel(label) {
44
26
  return `label:contains("${label}")`;
@@ -128,6 +110,181 @@ function detectFieldType(value) {
128
110
  }
129
111
  return "text";
130
112
  }
113
+ function waitForFormReady(timeout = 5e3) {
114
+ return cy.get("form", { timeout }).should("exist").then(($form) => {
115
+ cy.get("form input, form textarea, form button", { timeout: 2e3 }).first().should("exist");
116
+ return cy.wrap($form);
117
+ });
118
+ }
119
+ function waitForReactUpdate(timeout = 2e3) {
120
+ return cy.get("form", { timeout }).should("exist").then(() => {
121
+ return cy.wait(100);
122
+ });
123
+ }
124
+ function waitForElementState(selector, state, timeout = 4e3) {
125
+ const element = cy.get(selector, { timeout });
126
+ switch (state) {
127
+ case "visible":
128
+ return element.should("be.visible");
129
+ case "hidden":
130
+ return element.should("be.hidden");
131
+ case "enabled":
132
+ return element.should("not.be.disabled");
133
+ case "disabled":
134
+ return element.should("be.disabled");
135
+ case "exist":
136
+ return element.should("exist");
137
+ default:
138
+ return element;
139
+ }
140
+ }
141
+ function waitForDropdownOpen(timeout = 5e3) {
142
+ cy.get('[role="dialog"]', { timeout }).should("exist");
143
+ cy.get('[role="option"]', { timeout }).should("exist");
144
+ return cy.get('[role="dialog"]');
145
+ }
146
+ function waitForDropdownClose(buttonSelector = 'button[aria-haspopup="listbox"]', timeout = 3e3) {
147
+ return cy.get(buttonSelector, { timeout }).first().should("have.attr", "aria-expanded", "false");
148
+ }
149
+ function getFormDataValue(fieldName) {
150
+ return cy.get("form").then(($form) => {
151
+ const formElement = $form[0];
152
+ if (!(formElement instanceof HTMLFormElement)) {
153
+ throw new Error("Expected HTMLFormElement");
154
+ }
155
+ const formData = new FormData(formElement);
156
+ return formData.get(fieldName);
157
+ });
158
+ }
159
+ function verifyFormDataValue(fieldName, expectedValue, timeout = 3e3) {
160
+ return getFormDataValue(fieldName).should("equal", String(expectedValue));
161
+ }
162
+ function findButtonNearLabel(labelText, buttonSelector = 'button[aria-haspopup="listbox"]') {
163
+ return cy.contains("label", labelText).then(($label) => {
164
+ const $container = $label.closest("div");
165
+ let $button = $container.find(buttonSelector);
166
+ if ($button.length > 0) {
167
+ return cy.wrap($button.first());
168
+ }
169
+ const $parent = $container.parent();
170
+ $button = $parent.find(buttonSelector);
171
+ if ($button.length > 0) {
172
+ return cy.wrap($button.first());
173
+ }
174
+ return cy.get(buttonSelector).first();
175
+ });
176
+ }
177
+ function waitForValidation(shouldHaveErrors = true, timeout = 3e3) {
178
+ if (shouldHaveErrors) {
179
+ return cy.get("body", { timeout }).then(($body) => {
180
+ const hasErrors = $body.find('[class*="error"], [class*="danger"], [class*="invalid"]').length > 0;
181
+ if (!hasErrors) {
182
+ cy.wait(500);
183
+ }
184
+ });
185
+ } else {
186
+ return cy.get('[class*="error"], [class*="danger"], [class*="invalid"]', { timeout }).should("not.exist");
187
+ }
188
+ }
189
+ function getFormDataArray(fieldName) {
190
+ return cy.get("form").then(($form) => {
191
+ const formElement = $form[0];
192
+ if (!(formElement instanceof HTMLFormElement)) {
193
+ throw new Error("Expected HTMLFormElement");
194
+ }
195
+ const formData = new FormData(formElement);
196
+ return Array.from(formData.getAll(fieldName));
197
+ });
198
+ }
199
+ function verifyFormDataArray(fieldName, expectedValues, exactMatch = false) {
200
+ return getFormDataArray(fieldName).then((values) => {
201
+ if (exactMatch) {
202
+ expect(values).to.deep.equal(expectedValues);
203
+ } else {
204
+ expectedValues.forEach((expected) => {
205
+ expect(values).to.include(expected);
206
+ });
207
+ expect(values.length).to.be.at.least(expectedValues.length);
208
+ }
209
+ });
210
+ }
211
+ function verifyFormDataFieldExists(fieldName) {
212
+ return cy.get("form").then(($form) => {
213
+ const formElement = $form[0];
214
+ if (!(formElement instanceof HTMLFormElement)) {
215
+ throw new Error("Expected HTMLFormElement");
216
+ }
217
+ const formData = new FormData(formElement);
218
+ expect(formData.has(fieldName)).to.be.true;
219
+ });
220
+ }
221
+ function verifyNameAttribute(fieldName, selector) {
222
+ if (selector) {
223
+ return cy.get(selector).then(($el) => {
224
+ const hasName = $el.attr("name") === fieldName;
225
+ if (hasName) {
226
+ cy.wrap($el).should("have.attr", "name", fieldName);
227
+ } else {
228
+ return verifyFormDataFieldExists(fieldName);
229
+ }
230
+ });
231
+ } else {
232
+ return verifyFormDataFieldExists(fieldName);
233
+ }
234
+ }
235
+ function verifyFormDataStructure(expectedData) {
236
+ return cy.get("form").then(($form) => {
237
+ const formElement = $form[0];
238
+ if (!(formElement instanceof HTMLFormElement)) {
239
+ throw new Error("Expected HTMLFormElement");
240
+ }
241
+ const formData = new FormData(formElement);
242
+ Object.entries(expectedData).forEach(([fieldName, expectedValue]) => {
243
+ if (Array.isArray(expectedValue)) {
244
+ const actualValues = Array.from(formData.getAll(fieldName));
245
+ expectedValue.forEach((val) => {
246
+ expect(actualValues).to.include(val);
247
+ });
248
+ expect(actualValues.length).to.be.at.least(expectedValue.length);
249
+ } else {
250
+ const actualValue = formData.get(fieldName);
251
+ if (typeof expectedValue === "string" && expectedValue.includes("*")) {
252
+ const pattern = expectedValue.replace(/\*/g, ".*");
253
+ expect(String(actualValue)).to.match(new RegExp(pattern));
254
+ } else {
255
+ expect(String(actualValue)).to.equal(String(expectedValue));
256
+ }
257
+ }
258
+ });
259
+ });
260
+ }
261
+ function verifyFormCleared(fieldNames) {
262
+ return cy.get("form").then(($form) => {
263
+ const formElement = $form[0];
264
+ if (!(formElement instanceof HTMLFormElement)) {
265
+ throw new Error("Expected HTMLFormElement");
266
+ }
267
+ const formData = new FormData(formElement);
268
+ fieldNames.forEach((fieldName) => {
269
+ const value = formData.get(fieldName);
270
+ expect(value === "" || value === null || value === void 0).to.be.true;
271
+ });
272
+ });
273
+ }
274
+ function verifyDropdownNameAttribute(fieldName, labelText) {
275
+ if (labelText) {
276
+ return findButtonNearLabel(labelText).then(($button) => {
277
+ const hasName = $button.attr("name") === fieldName;
278
+ if (hasName) {
279
+ cy.wrap($button).should("have.attr", "name", fieldName);
280
+ } else {
281
+ return verifyFormDataFieldExists(fieldName);
282
+ }
283
+ });
284
+ } else {
285
+ return verifyFormDataFieldExists(fieldName);
286
+ }
287
+ }
131
288
  var HERO_UI_SELECTORS, DEFAULT_CONFIG;
132
289
  var init_utils = __esm({
133
290
  "src/cypress/utils.ts"() {
@@ -177,6 +334,19 @@ var init_utils = __esm({
177
334
  });
178
335
 
179
336
  // src/cypress/helpers.ts
337
+ function fillInputByName(name, value, options = {}) {
338
+ return withRetry(() => {
339
+ waitForFormReady();
340
+ const element = cy.get(`input[name="${name}"], textarea[name="${name}"]`, { timeout: DEFAULT_CONFIG.timeout }).should("exist").should("be.visible");
341
+ if (options.clear !== false) {
342
+ element.clear({ force: true });
343
+ }
344
+ return element.type(value, { force: true }).then(() => {
345
+ waitForReactUpdate();
346
+ return element;
347
+ });
348
+ }, DEFAULT_CONFIG);
349
+ }
180
350
  function fillInputByType(type, value, index = 0, options = {}) {
181
351
  const selector = type === "textarea" ? buildHeroUISelector("textarea") : buildHeroUISelector("input", type);
182
352
  const element = index > 0 ? cy.get(selector).eq(index) : cy.get(selector).first();
@@ -207,6 +377,15 @@ function fillInputByLabel(label, value, options = {}) {
207
377
  });
208
378
  }, DEFAULT_CONFIG);
209
379
  }
380
+ function fillTextareaByName(name, value, options = {}) {
381
+ return withRetry(() => {
382
+ const element = cy.get(`textarea[name="${name}"]`);
383
+ if (options.clear !== false) {
384
+ element.clear({ force: true });
385
+ }
386
+ return element.type(value, { force: true });
387
+ }, DEFAULT_CONFIG);
388
+ }
210
389
  function fillTextarea(value, index = 0, options = {}) {
211
390
  const selector = HERO_UI_SELECTORS.textarea;
212
391
  return withRetry(() => {
@@ -217,6 +396,37 @@ function fillTextarea(value, index = 0, options = {}) {
217
396
  return element.type(value, { force: true });
218
397
  }, DEFAULT_CONFIG);
219
398
  }
399
+ function selectDropdownByName(name, optionValue) {
400
+ const optionSelector = HERO_UI_SELECTORS.dropdown.option;
401
+ return withRetry(() => {
402
+ waitForFormReady();
403
+ cy.get("body").then(($body) => {
404
+ const buttonWithName = $body.find(`button[aria-haspopup="listbox"][name="${name}"]`).length > 0;
405
+ if (buttonWithName) {
406
+ cy.get(`button[aria-haspopup="listbox"][name="${name}"]`, { timeout: DEFAULT_CONFIG.timeout }).should("be.visible").click({ force: true });
407
+ } else {
408
+ const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
409
+ findButtonNearLabel(capitalizedName).should("be.visible").click({ force: true });
410
+ }
411
+ });
412
+ waitForDropdownOpen(5e3);
413
+ cy.get('[role="dialog"]').within(() => {
414
+ cy.get(optionSelector, { timeout: 6e3 }).should("exist");
415
+ if (optionValue) {
416
+ cy.get(optionSelector).contains(optionValue).click({ force: true });
417
+ } else {
418
+ cy.get(optionSelector).first().click({ force: true });
419
+ }
420
+ });
421
+ cy.get("body").then(($body) => {
422
+ const buttonWithName = $body.find(`button[aria-haspopup="listbox"][name="${name}"]`).length > 0;
423
+ const buttonSelector = buttonWithName ? `button[aria-haspopup="listbox"][name="${name}"]` : 'button[aria-haspopup="listbox"]';
424
+ waitForDropdownClose(buttonSelector, 3e3);
425
+ });
426
+ waitForReactUpdate();
427
+ return cy.get('button[aria-haspopup="listbox"]').first();
428
+ }, DEFAULT_CONFIG);
429
+ }
220
430
  function selectDropdownOption(optionValue, dropdownIndex = 0) {
221
431
  const triggerSelector = HERO_UI_SELECTORS.dropdown.trigger;
222
432
  const optionSelector = HERO_UI_SELECTORS.dropdown.option;
@@ -246,6 +456,15 @@ function selectDropdownByLabel(label, optionValue) {
246
456
  });
247
457
  }, DEFAULT_CONFIG);
248
458
  }
459
+ function checkCheckboxByName(name) {
460
+ return withRetry(() => {
461
+ waitForFormReady();
462
+ return cy.get(`input[type="checkbox"][name="${name}"]`, { timeout: DEFAULT_CONFIG.timeout }).should("exist").check({ force: true }).then(() => {
463
+ waitForReactUpdate();
464
+ return cy.get(`input[type="checkbox"][name="${name}"]`);
465
+ });
466
+ }, DEFAULT_CONFIG);
467
+ }
249
468
  function checkCheckbox(index = 0) {
250
469
  const selector = HERO_UI_SELECTORS.checkbox.input;
251
470
  const element = index > 0 ? cy.get(selector).eq(index) : cy.get(selector).first();
@@ -258,6 +477,11 @@ function checkCheckboxByLabel(label) {
258
477
  return cy.contains("label", label).closest("div").find('input[type="checkbox"]').check({ force: true });
259
478
  }, DEFAULT_CONFIG);
260
479
  }
480
+ function checkSwitchByName(name) {
481
+ return withRetry(() => {
482
+ return cy.get(`input[type="checkbox"][role="switch"][name="${name}"], input[type="checkbox"][name="${name}"][aria-label*="switch"]`).check({ force: true });
483
+ }, DEFAULT_CONFIG);
484
+ }
261
485
  function checkSwitch(index = 0) {
262
486
  const selector = HERO_UI_SELECTORS.switch.input;
263
487
  const element = index > 0 ? cy.get(selector).eq(index) : cy.get(selector).first();
@@ -265,6 +489,15 @@ function checkSwitch(index = 0) {
265
489
  return element.check();
266
490
  }, DEFAULT_CONFIG);
267
491
  }
492
+ function uncheckCheckboxByName(name) {
493
+ return withRetry(() => {
494
+ waitForFormReady();
495
+ return cy.get(`input[type="checkbox"][name="${name}"]`, { timeout: DEFAULT_CONFIG.timeout }).should("exist").uncheck({ force: true }).then(() => {
496
+ waitForReactUpdate();
497
+ return cy.get(`input[type="checkbox"][name="${name}"]`);
498
+ });
499
+ }, DEFAULT_CONFIG);
500
+ }
268
501
  function uncheckCheckbox(index = 0) {
269
502
  const selector = HERO_UI_SELECTORS.checkbox.input;
270
503
  const element = index > 0 ? cy.get(selector).eq(index) : cy.get(selector).first();
@@ -272,6 +505,15 @@ function uncheckCheckbox(index = 0) {
272
505
  return element.uncheck();
273
506
  }, DEFAULT_CONFIG);
274
507
  }
508
+ function uncheckSwitchByName(name) {
509
+ return withRetry(() => {
510
+ waitForFormReady();
511
+ return cy.get(`input[type="checkbox"][role="switch"][name="${name}"], input[type="checkbox"][name="${name}"][aria-label*="switch"]`, { timeout: DEFAULT_CONFIG.timeout }).should("exist").uncheck({ force: true }).then(() => {
512
+ waitForReactUpdate();
513
+ return cy.get(`input[role="switch"][name="${name}"]`);
514
+ });
515
+ }, DEFAULT_CONFIG);
516
+ }
275
517
  function uncheckSwitch(index = 0) {
276
518
  const selector = HERO_UI_SELECTORS.switch.input;
277
519
  const element = index > 0 ? cy.get(selector).eq(index) : cy.get(selector).first();
@@ -286,6 +528,99 @@ function moveSlider(value, index = 0) {
286
528
  return element.invoke("val", value).trigger("change");
287
529
  }, DEFAULT_CONFIG);
288
530
  }
531
+ function moveSliderByName(name, value) {
532
+ return withRetry(() => {
533
+ waitForFormReady();
534
+ const slider = cy.get(`input[type="range"][name="${name}"]`, { timeout: DEFAULT_CONFIG.timeout }).should("exist");
535
+ return slider.then(($slider) => {
536
+ const sliderElement = $slider[0];
537
+ if (!(sliderElement instanceof HTMLInputElement)) {
538
+ throw new Error("Expected HTMLInputElement for slider");
539
+ }
540
+ const min = Number(sliderElement.min || 0);
541
+ const max = Number(sliderElement.max || 100);
542
+ const clampedValue = Math.max(min, Math.min(max, value));
543
+ cy.wrap($slider).invoke("val", clampedValue).trigger("input", { force: true, bubbles: true }).then(() => {
544
+ sliderElement.value = String(clampedValue);
545
+ const inputEvent = new Event("input", { bubbles: true, cancelable: true });
546
+ sliderElement.dispatchEvent(inputEvent);
547
+ cy.wrap($slider).trigger("change", { force: true, bubbles: true }).then(() => {
548
+ const changeEvent = new Event("change", { bubbles: true, cancelable: true });
549
+ sliderElement.dispatchEvent(changeEvent);
550
+ waitForReactUpdate(1500);
551
+ return cy.wrap($slider);
552
+ });
553
+ });
554
+ });
555
+ }, DEFAULT_CONFIG);
556
+ }
557
+ function selectRadioByName(name, value) {
558
+ return withRetry(() => {
559
+ waitForFormReady();
560
+ return cy.get(`input[type="radio"][name="${name}"][value="${value}"]`, { timeout: DEFAULT_CONFIG.timeout }).should("exist").check({ force: true }).then(() => {
561
+ waitForReactUpdate();
562
+ return cy.get(`input[type="radio"][name="${name}"][value="${value}"]`);
563
+ });
564
+ }, DEFAULT_CONFIG);
565
+ }
566
+ function checkCheckboxInGroupByName(name, index = 0) {
567
+ return withRetry(() => {
568
+ waitForFormReady();
569
+ const checkboxes = cy.get(`input[type="checkbox"][name="${name}"]`, { timeout: DEFAULT_CONFIG.timeout }).should("exist");
570
+ const targetCheckbox = index > 0 ? checkboxes.eq(index) : checkboxes.first();
571
+ return targetCheckbox.should("exist").check({ force: true }).then(() => {
572
+ waitForReactUpdate();
573
+ return targetCheckbox;
574
+ });
575
+ }, DEFAULT_CONFIG);
576
+ }
577
+ function selectAutocompleteByName(name, optionValue) {
578
+ return withRetry(() => {
579
+ waitForFormReady();
580
+ const input = cy.get(`input[name="${name}"]`, { timeout: DEFAULT_CONFIG.timeout }).should("exist").should("be.visible");
581
+ input.clear({ force: true });
582
+ input.focus();
583
+ input.click({ force: true });
584
+ waitForReactUpdate(300);
585
+ input.type(optionValue, { force: true, delay: 50 });
586
+ cy.wait(500);
587
+ cy.get('[role="listbox"], [role="combobox"], [role="dialog"]', { timeout: 5e3 }).should("exist");
588
+ cy.wait(200);
589
+ cy.get('[role="option"]', { timeout: 3e3 }).should("exist").contains(optionValue, { matchCase: false }).first().should("be.visible").click({ force: true });
590
+ waitForReactUpdate();
591
+ return cy.get(`input[name="${name}"]`);
592
+ }, DEFAULT_CONFIG);
593
+ }
594
+ function fillDateInputByName(name) {
595
+ return withRetry(() => {
596
+ return cy.get("body").then(($body) => {
597
+ const hasSpinbutton = $body.find('input[type="text"][role="spinbutton"]').length > 0;
598
+ const hasDateInput = $body.find('[data-slot="date-input"]').length > 0;
599
+ const hasDateField = $body.find(`[data-field-name="${name}"]`).length > 0;
600
+ if (hasSpinbutton || hasDateInput || hasDateField) {
601
+ if (hasSpinbutton) {
602
+ return cy.get('input[type="text"][role="spinbutton"]').first();
603
+ } else if (hasDateInput) {
604
+ return cy.get('[data-slot="date-input"]').first();
605
+ } else {
606
+ return cy.get(`[data-field-name="${name}"]`).first();
607
+ }
608
+ } else {
609
+ cy.log("DateInput component structure not found, but name attribute is set on component");
610
+ return cy.get("body");
611
+ }
612
+ });
613
+ }, DEFAULT_CONFIG);
614
+ }
615
+ function selectFileByName(name, filePath) {
616
+ return withRetry(() => {
617
+ waitForFormReady();
618
+ return cy.get(`input[type="file"][name="${name}"]`, { timeout: DEFAULT_CONFIG.timeout }).should("exist").selectFile(filePath, { force: true }).then(() => {
619
+ waitForReactUpdate();
620
+ return cy.get(`input[type="file"][name="${name}"]`);
621
+ });
622
+ }, DEFAULT_CONFIG);
623
+ }
289
624
  function expectValidationError(message) {
290
625
  return cy.contains(message).should("be.visible");
291
626
  }
@@ -293,14 +628,37 @@ function expectNoValidationErrors() {
293
628
  return cy.get("body").should("not.contain", "error").and("not.contain", "invalid");
294
629
  }
295
630
  function expectFieldError(fieldLabel, errorMessage) {
296
- const fieldContainer = cy.contains("label", fieldLabel).closest("div");
297
- if (errorMessage) {
298
- return fieldContainer.should("contain", errorMessage);
299
- }
300
- return fieldContainer.should(($div) => {
301
- const text = $div.text();
302
- const hasError = text.includes("error") || text.includes("invalid") || text.includes("required") || $div.find('[class*="error"], [class*="invalid"], [class*="danger"]').length > 0;
303
- expect(hasError, "Field should have an error").to.be.true;
631
+ return cy.get("body").then(($body) => {
632
+ const labels = $body.find("label");
633
+ let $matchingLabel = null;
634
+ labels.each((index, label) => {
635
+ const labelText = Cypress.$(label).text().toLowerCase();
636
+ if (labelText.includes(fieldLabel.toLowerCase())) {
637
+ $matchingLabel = Cypress.$(label);
638
+ return false;
639
+ }
640
+ });
641
+ if (!$matchingLabel || $matchingLabel.length === 0) {
642
+ return cy.contains("label", new RegExp(fieldLabel, "i")).closest("div").then(($container) => {
643
+ if (errorMessage) {
644
+ return cy.wrap($container).should("contain", errorMessage);
645
+ }
646
+ return cy.wrap($container).should(($div) => {
647
+ const text = $div.text();
648
+ const hasError = text.includes("error") || text.includes("invalid") || text.includes("required") || $div.find('[class*="error"], [class*="invalid"], [class*="danger"]').length > 0;
649
+ expect(hasError, "Field should have an error").to.be.true;
650
+ });
651
+ });
652
+ }
653
+ const fieldContainer = cy.wrap($matchingLabel).closest("div");
654
+ if (errorMessage) {
655
+ return fieldContainer.should("contain", errorMessage);
656
+ }
657
+ return fieldContainer.should(($div) => {
658
+ const text = $div.text();
659
+ const hasError = text.includes("error") || text.includes("invalid") || text.includes("required") || $div.find('[class*="error"], [class*="invalid"], [class*="danger"]').length > 0;
660
+ expect(hasError, "Field should have an error").to.be.true;
661
+ });
304
662
  });
305
663
  }
306
664
  function expectFieldValid(fieldLabel) {
@@ -318,8 +676,11 @@ function triggerValidation(submitButton = false) {
318
676
  }
319
677
  function submitForm() {
320
678
  return withRetry(() => {
321
- cy.get("form").should("exist");
322
- cy.get(HERO_UI_SELECTORS.button.submit).first().should("be.visible").should("not.be.disabled").click();
679
+ waitForFormReady();
680
+ waitForElementState(HERO_UI_SELECTORS.button.submit, "visible", DEFAULT_CONFIG.timeout);
681
+ waitForElementState(HERO_UI_SELECTORS.button.submit, "enabled", DEFAULT_CONFIG.timeout);
682
+ cy.get(HERO_UI_SELECTORS.button.submit).first().click();
683
+ waitForReactUpdate();
323
684
  return cy.get("form");
324
685
  }, DEFAULT_CONFIG);
325
686
  }
@@ -337,23 +698,27 @@ function submitAndExpectSuccess(successIndicator) {
337
698
  }
338
699
  function submitAndExpectErrors(errorMessage, formIndex = 0) {
339
700
  return withRetry(() => {
340
- cy.get("form").should("exist");
341
- cy.get(HERO_UI_SELECTORS.button.submit).eq(formIndex).should("be.visible").should("not.be.disabled").click();
342
- cy.get("form").should("exist");
343
- cy.wait(500);
701
+ waitForFormReady();
702
+ waitForElementState(HERO_UI_SELECTORS.button.submit, "visible", DEFAULT_CONFIG.timeout);
703
+ waitForElementState(HERO_UI_SELECTORS.button.submit, "enabled", DEFAULT_CONFIG.timeout);
704
+ cy.get(HERO_UI_SELECTORS.button.submit).eq(formIndex).click();
705
+ cy.get("form", { timeout: DEFAULT_CONFIG.timeout }).should("exist");
706
+ waitForValidation(true, 3e3);
344
707
  if (errorMessage) {
345
- cy.get("body").then(($body) => {
708
+ cy.get("body", { timeout: 2e3 }).then(($body) => {
346
709
  if ($body.text().includes(errorMessage)) {
347
- cy.contains(errorMessage).should("be.visible");
710
+ cy.contains(errorMessage, { timeout: 3e3 }).should("be.visible");
348
711
  } else {
349
712
  cy.log("Expected error message not found:", errorMessage);
350
- cy.get('[class*="text-danger"], [class*="text-red"], [class*="error"]').then(($errors) => {
351
- cy.log("Found validation errors:", $errors.length);
352
- $errors.each((index, error) => {
353
- cy.log(`Error ${index}:`, error.textContent);
354
- });
713
+ cy.get('[class*="text-danger"], [class*="text-red"], [class*="error"], [class*="invalid"]', { timeout: 2e3 }).then(($errors) => {
714
+ if ($errors.length > 0) {
715
+ cy.log("Found validation errors:", $errors.length);
716
+ $errors.each((index, error) => {
717
+ cy.log(`Error ${index}:`, error.textContent);
718
+ });
719
+ }
355
720
  });
356
- cy.contains(errorMessage).should("be.visible");
721
+ cy.contains(errorMessage, { timeout: 3e3 }).should("be.visible");
357
722
  }
358
723
  });
359
724
  }
@@ -361,12 +726,37 @@ function submitAndExpectErrors(errorMessage, formIndex = 0) {
361
726
  }, DEFAULT_CONFIG);
362
727
  }
363
728
  function resetForm() {
364
- return cy.get("body").then(($body) => {
365
- if ($body.find(HERO_UI_SELECTORS.button.reset).length > 0) {
366
- return cy.get(HERO_UI_SELECTORS.button.reset).click();
367
- }
368
- return cy.get("input, textarea").each(($el) => {
369
- cy.wrap($el).clear();
729
+ return waitForFormReady().then(() => {
730
+ return cy.get("body").then(($body) => {
731
+ if ($body.find(HERO_UI_SELECTORS.button.reset).length > 0) {
732
+ cy.get(HERO_UI_SELECTORS.button.reset, { timeout: DEFAULT_CONFIG.timeout }).should("be.visible").click();
733
+ waitForReactUpdate();
734
+ return cy.get("form");
735
+ }
736
+ cy.get("input, textarea", { timeout: DEFAULT_CONFIG.timeout }).each(($el) => {
737
+ const type = $el.attr("type");
738
+ const tagName = $el.prop("tagName")?.toLowerCase();
739
+ if (tagName === "textarea" || (type === "text" || type === "email" || type === "tel" || type === "password" || type === "url" || type === "search" || type === "number" || !type)) {
740
+ cy.wrap($el).clear({ force: true });
741
+ }
742
+ });
743
+ cy.get('input[type="checkbox"]').each(($el) => {
744
+ const role = $el.attr("role");
745
+ if (role !== "switch" && $el.attr("type") === "checkbox") {
746
+ cy.wrap($el).uncheck({ force: true });
747
+ }
748
+ });
749
+ cy.get('input[role="switch"]').each(($el) => {
750
+ cy.wrap($el).uncheck({ force: true });
751
+ });
752
+ cy.get("form").then(($form) => {
753
+ const formElement = $form[0];
754
+ if (formElement instanceof HTMLFormElement) {
755
+ formElement.reset();
756
+ }
757
+ });
758
+ waitForReactUpdate();
759
+ return cy.get("form");
370
760
  });
371
761
  });
372
762
  }
@@ -379,11 +769,24 @@ function interceptFormSubmission(method, url, alias) {
379
769
  }, DEFAULT_CONFIG);
380
770
  }
381
771
  function verifyFormExists() {
382
- return cy.get("form").should("exist");
772
+ return waitForFormReady();
383
773
  }
384
774
  function verifyFieldExists(selector) {
385
775
  return cy.get(selector).should("exist");
386
776
  }
777
+ function verifyFieldValueByName(name, value) {
778
+ return withRetry(() => {
779
+ waitForFormReady();
780
+ cy.get(`input[name="${name}"], textarea[name="${name}"], select[name="${name}"]`, { timeout: DEFAULT_CONFIG.timeout }).should("exist").then(($el) => {
781
+ const actualValue = $el.val();
782
+ if (actualValue === value) {
783
+ return cy.wrap($el).should("have.value", value);
784
+ } else {
785
+ return verifyFormDataValue(name, value);
786
+ }
787
+ });
788
+ }, DEFAULT_CONFIG);
789
+ }
387
790
  function verifyFieldValue(type, value, index = 0) {
388
791
  const selector = type === "textarea" ? buildHeroUISelector("textarea") : buildHeroUISelector("input", type);
389
792
  const element = index > 0 ? cy.get(selector).eq(index) : cy.get(selector).first();
@@ -400,28 +803,52 @@ function fillCompleteForm(formData) {
400
803
  Object.entries(formData).forEach(([key, value]) => {
401
804
  if (value === null || value === void 0) return;
402
805
  const fieldType = detectFieldType(value);
403
- switch (fieldType) {
404
- case "text":
405
- case "email":
406
- case "tel":
407
- case "password":
408
- case "number":
409
- fillInputByType(fieldType, String(value));
410
- break;
411
- case "checkbox":
412
- if (value) {
413
- checkCheckbox();
414
- }
415
- break;
416
- default:
417
- cy.get("body").then(($body) => {
418
- if ($body.find(`label:contains("${key}")`).length > 0) {
419
- fillInputByLabel(key, String(value));
806
+ cy.get("body").then(($body) => {
807
+ const hasNameAttribute = $body.find(`[name="${key}"]`).length > 0;
808
+ if (hasNameAttribute) {
809
+ const fieldElement = cy.get(`[name="${key}"]`);
810
+ fieldElement.then(($el) => {
811
+ const tagName = $el.prop("tagName")?.toLowerCase();
812
+ const inputType = $el.attr("type");
813
+ const role = $el.attr("role");
814
+ if (tagName === "textarea") {
815
+ fillTextareaByName(key, String(value));
816
+ } else if (tagName === "select") {
817
+ selectDropdownByName(key, String(value));
818
+ } else if (inputType === "checkbox" || inputType === "radio") {
819
+ const isSwitch = role === "switch" || $el.attr("aria-label")?.includes("switch");
820
+ if (value) {
821
+ isSwitch ? checkSwitchByName(key) : checkCheckboxByName(key);
822
+ } else {
823
+ isSwitch ? uncheckSwitchByName(key) : uncheckCheckboxByName(key);
824
+ }
420
825
  } else {
421
- fillInputByType("text", String(value));
826
+ fillInputByName(key, String(value));
422
827
  }
423
828
  });
424
- }
829
+ } else {
830
+ switch (fieldType) {
831
+ case "text":
832
+ case "email":
833
+ case "tel":
834
+ case "password":
835
+ case "number":
836
+ fillInputByType(fieldType, String(value));
837
+ break;
838
+ case "checkbox":
839
+ if (value) {
840
+ checkCheckbox();
841
+ }
842
+ break;
843
+ default:
844
+ if ($body.find(`label:contains("${key}")`).length > 0) {
845
+ fillInputByLabel(key, String(value));
846
+ } else {
847
+ fillInputByType("text", String(value));
848
+ }
849
+ }
850
+ }
851
+ });
425
852
  });
426
853
  return cy.get("form");
427
854
  });
@@ -702,6 +1129,20 @@ var init_commands = __esm({
702
1129
  "src/cypress/commands.ts"() {
703
1130
  "use strict";
704
1131
  init_helpers();
1132
+ init_utils();
1133
+ Cypress.Commands.add("fillInputByName", fillInputByName);
1134
+ Cypress.Commands.add("fillTextareaByName", fillTextareaByName);
1135
+ Cypress.Commands.add("selectDropdownByName", selectDropdownByName);
1136
+ Cypress.Commands.add("checkCheckboxByName", checkCheckboxByName);
1137
+ Cypress.Commands.add("checkSwitchByName", checkSwitchByName);
1138
+ Cypress.Commands.add("uncheckCheckboxByName", uncheckCheckboxByName);
1139
+ Cypress.Commands.add("uncheckSwitchByName", uncheckSwitchByName);
1140
+ Cypress.Commands.add("moveSliderByName", moveSliderByName);
1141
+ Cypress.Commands.add("selectRadioByName", selectRadioByName);
1142
+ Cypress.Commands.add("checkCheckboxInGroupByName", checkCheckboxInGroupByName);
1143
+ Cypress.Commands.add("selectAutocompleteByName", selectAutocompleteByName);
1144
+ Cypress.Commands.add("fillDateInputByName", fillDateInputByName);
1145
+ Cypress.Commands.add("selectFileByName", selectFileByName);
705
1146
  Cypress.Commands.add("fillInputByType", fillInputByType);
706
1147
  Cypress.Commands.add("fillInputByPlaceholder", fillInputByPlaceholder);
707
1148
  Cypress.Commands.add("fillInputByLabel", fillInputByLabel);
@@ -723,6 +1164,7 @@ var init_commands = __esm({
723
1164
  Cypress.Commands.add("submitAndExpectErrors", submitAndExpectErrors);
724
1165
  Cypress.Commands.add("verifyFormExists", verifyFormExists);
725
1166
  Cypress.Commands.add("verifyFieldExists", verifyFieldExists);
1167
+ Cypress.Commands.add("verifyFieldValueByName", verifyFieldValueByName);
726
1168
  Cypress.Commands.add("verifyFieldValue", verifyFieldValue);
727
1169
  Cypress.Commands.add("verifyFieldCount", verifyFieldCount);
728
1170
  Cypress.Commands.add("getFormData", getFormData);
@@ -751,9 +1193,50 @@ var init_commands = __esm({
751
1193
  cy.log("Form state:", formData);
752
1194
  });
753
1195
  });
754
- Cypress.Commands.add("waitForFormReady", () => {
755
- cy.get("form").should("exist");
756
- cy.get("input, textarea, select").should("be.visible");
1196
+ Cypress.Commands.add("waitForFormReady", (timeout) => {
1197
+ return waitForFormReady(timeout);
1198
+ });
1199
+ Cypress.Commands.add("waitForReactUpdate", (timeout) => {
1200
+ return waitForReactUpdate(timeout);
1201
+ });
1202
+ Cypress.Commands.add("waitForElementState", (selector, state, timeout) => {
1203
+ return waitForElementState(selector, state, timeout);
1204
+ });
1205
+ Cypress.Commands.add("waitForDropdownOpen", (timeout) => {
1206
+ return waitForDropdownOpen(timeout);
1207
+ });
1208
+ Cypress.Commands.add("waitForDropdownClose", (buttonSelector, timeout) => {
1209
+ return waitForDropdownClose(buttonSelector, timeout);
1210
+ });
1211
+ Cypress.Commands.add("getFormDataValue", (fieldName) => {
1212
+ return getFormDataValue(fieldName);
1213
+ });
1214
+ Cypress.Commands.add("verifyFormDataValue", (fieldName, expectedValue, timeout) => {
1215
+ return verifyFormDataValue(fieldName, expectedValue, timeout);
1216
+ });
1217
+ Cypress.Commands.add("waitForValidation", (shouldHaveErrors, timeout) => {
1218
+ return waitForValidation(shouldHaveErrors, timeout);
1219
+ });
1220
+ Cypress.Commands.add("getFormDataArray", (fieldName) => {
1221
+ return getFormDataArray(fieldName);
1222
+ });
1223
+ Cypress.Commands.add("verifyFormDataArray", (fieldName, expectedValues, exactMatch) => {
1224
+ return verifyFormDataArray(fieldName, expectedValues, exactMatch);
1225
+ });
1226
+ Cypress.Commands.add("verifyFormDataFieldExists", (fieldName) => {
1227
+ return verifyFormDataFieldExists(fieldName);
1228
+ });
1229
+ Cypress.Commands.add("verifyNameAttribute", (fieldName, selector) => {
1230
+ return verifyNameAttribute(fieldName, selector);
1231
+ });
1232
+ Cypress.Commands.add("verifyFormDataStructure", (expectedData) => {
1233
+ return verifyFormDataStructure(expectedData);
1234
+ });
1235
+ Cypress.Commands.add("verifyFormCleared", (fieldNames) => {
1236
+ return verifyFormCleared(fieldNames);
1237
+ });
1238
+ Cypress.Commands.add("verifyDropdownNameAttribute", (fieldName, labelText) => {
1239
+ return verifyDropdownNameAttribute(fieldName, labelText);
757
1240
  });
758
1241
  Cypress.Commands.add("clearForm", () => {
759
1242
  cy.get("input, textarea").each(($el) => {
@@ -872,7 +1355,10 @@ export {
872
1355
  buildHeroUISelector,
873
1356
  checkCheckbox,
874
1357
  checkCheckboxByLabel,
1358
+ checkCheckboxByName,
1359
+ checkCheckboxInGroupByName,
875
1360
  checkSwitch,
1361
+ checkSwitchByName,
876
1362
  detectFieldType,
877
1363
  dynamicFormHelpers,
878
1364
  elementExists,
@@ -882,24 +1368,35 @@ export {
882
1368
  expectValidationError,
883
1369
  extractFormData,
884
1370
  fillCompleteForm,
1371
+ fillDateInputByName,
885
1372
  fillInputByLabel,
1373
+ fillInputByName,
886
1374
  fillInputByPlaceholder,
887
1375
  fillInputByType,
888
1376
  fillTextarea,
1377
+ fillTextareaByName,
1378
+ findButtonNearLabel,
889
1379
  findFieldByLabel,
890
1380
  findFieldByPlaceholder,
891
1381
  findFieldByType,
892
1382
  forceClickWithRetry,
893
1383
  formSubmissionHelpers,
894
1384
  getFormData,
1385
+ getFormDataArray,
1386
+ getFormDataValue,
895
1387
  interceptFormSubmission,
896
1388
  logFormState,
897
1389
  moveSlider,
1390
+ moveSliderByName,
898
1391
  performanceHelpers,
899
1392
  registerHeroFormCommands,
900
1393
  resetForm,
1394
+ selectAutocompleteByName,
901
1395
  selectDropdownByLabel,
1396
+ selectDropdownByName,
902
1397
  selectDropdownOption,
1398
+ selectFileByName,
1399
+ selectRadioByName,
903
1400
  submitAndExpectErrors,
904
1401
  submitAndExpectSuccess,
905
1402
  submitForm,
@@ -915,11 +1412,27 @@ export {
915
1412
  typeInferenceHelpers,
916
1413
  typeWithClear,
917
1414
  uncheckCheckbox,
1415
+ uncheckCheckboxByName,
918
1416
  uncheckSwitch,
1417
+ uncheckSwitchByName,
1418
+ verifyDropdownNameAttribute,
919
1419
  verifyFieldCount,
920
1420
  verifyFieldExists,
921
1421
  verifyFieldValue,
1422
+ verifyFieldValueByName,
1423
+ verifyFormCleared,
1424
+ verifyFormDataArray,
1425
+ verifyFormDataFieldExists,
1426
+ verifyFormDataStructure,
1427
+ verifyFormDataValue,
922
1428
  verifyFormExists,
1429
+ verifyNameAttribute,
1430
+ waitForDropdownClose,
1431
+ waitForDropdownOpen,
1432
+ waitForElementState,
1433
+ waitForFormReady,
1434
+ waitForReactUpdate,
923
1435
  waitForStable,
1436
+ waitForValidation,
924
1437
  withRetry
925
1438
  };