From 1d4c704dcbb80e9bb80ea3003486097078c05926 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Wed, 17 Jun 2026 09:14:51 -0500 Subject: [PATCH 01/15] fix(ion-button): sync disabled state in ion-button renderHiddenButton Closes #30968 --- core/src/components/button/button.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index 0e4741f4acd..b4aa82ec138 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -198,6 +198,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf * then do not append a new one again. */ if (formButtonEl !== null && formEl.contains(formButtonEl)) { + formButtonEl.disabled = this.disabled; return; } From 2964bc6a15552d19577bf0d92ff6f8558cd02272 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Wed, 17 Jun 2026 09:18:24 -0500 Subject: [PATCH 02/15] test(ion-button): add e2e test for async disabled state change (issue #30968) --- .../button/test/form-reference/button.e2e.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/core/src/components/button/test/form-reference/button.e2e.ts b/core/src/components/button/test/form-reference/button.e2e.ts index 5710f52da67..c8a0fe2039b 100644 --- a/core/src/components/button/test/form-reference/button.e2e.ts +++ b/core/src/components/button/test/form-reference/button.e2e.ts @@ -153,6 +153,42 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(submitEvent).toHaveReceivedEvent(); }); + + test('should submit the form when disabled state changes asynchronously', async ({ page }, testInfo) => { + testInfo.annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/30968', + }); + + await page.setContent( + ` +
+ + Submit +
+ `, + config + ); + + const submitEvent = await page.spyOnEvent('submit'); + const button = page.locator('ion-button'); + + // Simulate async disabled state change — e.g. async validator resolving + await button.evaluate((el: HTMLIonButtonElement) => { + el.disabled = true; + setTimeout(() => { + el.disabled = false; + }, 0); + }); + + // Wait for the async change to settle + await page.waitForTimeout(100); + + await page.click('ion-button'); + + expect(submitEvent).toHaveReceivedEvent(); + }); + }); test.describe(title('should throw a warning if the form cannot be found'), () => { From 31aff3187b2951ab19514f35a454e4af876d641f Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Wed, 17 Jun 2026 12:52:21 -0500 Subject: [PATCH 03/15] npm run lint.fix --- core/src/components/button/test/form-reference/button.e2e.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/components/button/test/form-reference/button.e2e.ts b/core/src/components/button/test/form-reference/button.e2e.ts index c8a0fe2039b..c803dd2c226 100644 --- a/core/src/components/button/test/form-reference/button.e2e.ts +++ b/core/src/components/button/test/form-reference/button.e2e.ts @@ -188,7 +188,6 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(submitEvent).toHaveReceivedEvent(); }); - }); test.describe(title('should throw a warning if the form cannot be found'), () => { From c235b7954e2775ae36629152cc84154c9ad30f56 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Thu, 18 Jun 2026 14:27:51 -0500 Subject: [PATCH 04/15] revert: fix(ion-button): sync disabled state in ion-button renderHiddenButton This reverts commit 1d4c704dcbb80e9bb80ea3003486097078c05926. Will retest changes without the fix and new test location --- core/src/components/button/button.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index b4aa82ec138..f8258af2239 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -195,10 +195,13 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf /** * If the form already has a rendered form button - * then do not append a new one again. + * then do not append a new one again. Sync the + * disabled state and type in it changes after button + * creation (e.g., runtime property updates). */ if (formButtonEl !== null && formEl.contains(formButtonEl)) { - formButtonEl.disabled = this.disabled; + // formButtonEl.disabled = this.disabled; + // formButtonEl.type = this.type; return; } From 5fb20dd4f9474ef979dd2fb4ebfa59bbcd52ed04 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Thu, 18 Jun 2026 14:43:25 -0500 Subject: [PATCH 05/15] revert: test(ion-button): add e2e test for async disabled state change (issue #30968) This reverts commit 2964bc6a15552d19577bf0d92ff6f8558cd02272. From 963ae43b5b679a1c7324f1e4443c70354c7abb93 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Sat, 20 Jun 2026 11:58:46 -0500 Subject: [PATCH 06/15] test(button): add e2e test for async disabled state change Imports AsyncValidatorFn in form.component.ts for new function Adds test to form.spec.ts to simulate disabled state --- .../test/base/e2e/src/lazy/form.spec.ts | 35 +++++++++++++++++++ .../base/src/app/lazy/form/form.component.ts | 16 +++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/packages/angular/test/base/e2e/src/lazy/form.spec.ts b/packages/angular/test/base/e2e/src/lazy/form.spec.ts index a5cacabe5e2..1fa34ae58e5 100644 --- a/packages/angular/test/base/e2e/src/lazy/form.spec.ts +++ b/packages/angular/test/base/e2e/src/lazy/form.spec.ts @@ -284,6 +284,41 @@ test.describe('Form', () => { }); }); + test.describe('async disabled state during init', () => { + test('should submit form when button disabled state resolves asynchronously during init', async ({ page }) => { + // Wait for the component to initialize. The async validator resolves during ngOnInit + await page.waitForTimeout(200); + + // Get the submit button + const submitButton = page.locator('ion-button[type="submit"]'); + + // Set up a promise that resolves when the form submits + const submitPromise = page.evaluate(() => { + return new Promise((resolve) => { + const form = document.querySelector('form'); + if (form) { + form.addEventListener('submit', (e) => { + e.preventDefault(); + resolve(); + }); + } + }); + }); + + // Click the submit button + await submitButton.click(); + + // Assert the form actually submitted + // This will timeout (fail) if the submit event never fires + await Promise.race([ + submitPromise, + new Promise((_, reject) => + setTimeout(() => reject(new Error('Form did not submit within 2 seconds')), 2000) + ), + ]); + }); + }); + // Helper functions async function testStatus(page: any, status: string) { await expect(page.locator('#status')).toHaveText(status); diff --git a/packages/angular/test/base/src/app/lazy/form/form.component.ts b/packages/angular/test/base/src/app/lazy/form/form.component.ts index 5e670482b47..128ba1f47e5 100644 --- a/packages/angular/test/base/src/app/lazy/form/form.component.ts +++ b/packages/angular/test/base/src/app/lazy/form/form.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { UntypedFormGroup, UntypedFormBuilder, Validators, UntypedFormControl, AbstractControl, ValidationErrors } from '@angular/forms'; +import { UntypedFormGroup, UntypedFormBuilder, Validators, UntypedFormControl, AbstractControl, ValidationErrors, AsyncValidatorFn } from '@angular/forms'; function otpRequiredLength(length: number) { return (control: AbstractControl): ValidationErrors | null => { @@ -10,6 +10,18 @@ function otpRequiredLength(length: number) { return null; }; } + +// async validation test +function asyncValidator() { + return (control: AbstractControl): Promise => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(null); + }, 100); + }); + }; +} + @Component({ selector: 'app-form', templateUrl: './form.component.html', @@ -26,7 +38,7 @@ export class FormComponent { datetime: ['2010-08-20', Validators.required], select: [undefined, Validators.required], toggle: [false], - input: ['', Validators.required], + input: ['', [Validators.required], [asyncValidator()]], input2: ['Default Value'], inputOtp: [null, [Validators.required, otpRequiredLength(4)]], inputOtpText: ['', [Validators.required, otpRequiredLength(4)]], From fb1d9f6c42d9940641dbe166ec21916a2cc00ebe Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Sat, 20 Jun 2026 12:01:23 -0500 Subject: [PATCH 07/15] fix(button): sync disabled state in renderHiddenButton --- core/src/components/button/button.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index f8258af2239..8a0a645177a 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -200,8 +200,8 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf * creation (e.g., runtime property updates). */ if (formButtonEl !== null && formEl.contains(formButtonEl)) { - // formButtonEl.disabled = this.disabled; - // formButtonEl.type = this.type; + formButtonEl.disabled = this.disabled; + formButtonEl.type = this.type; return; } From a96a6b5abbcf0df699a08794c8fae6083742f5f1 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Mon, 22 Jun 2026 10:16:19 -0500 Subject: [PATCH 08/15] revert: test(button): add e2e test for async disabled state change This reverts commit 963ae43b5b679a1c7324f1e4443c70354c7abb93 --- .../test/base/e2e/src/lazy/form.spec.ts | 35 ------------------- .../base/src/app/lazy/form/form.component.ts | 16 ++------- 2 files changed, 2 insertions(+), 49 deletions(-) diff --git a/packages/angular/test/base/e2e/src/lazy/form.spec.ts b/packages/angular/test/base/e2e/src/lazy/form.spec.ts index 1fa34ae58e5..a5cacabe5e2 100644 --- a/packages/angular/test/base/e2e/src/lazy/form.spec.ts +++ b/packages/angular/test/base/e2e/src/lazy/form.spec.ts @@ -284,41 +284,6 @@ test.describe('Form', () => { }); }); - test.describe('async disabled state during init', () => { - test('should submit form when button disabled state resolves asynchronously during init', async ({ page }) => { - // Wait for the component to initialize. The async validator resolves during ngOnInit - await page.waitForTimeout(200); - - // Get the submit button - const submitButton = page.locator('ion-button[type="submit"]'); - - // Set up a promise that resolves when the form submits - const submitPromise = page.evaluate(() => { - return new Promise((resolve) => { - const form = document.querySelector('form'); - if (form) { - form.addEventListener('submit', (e) => { - e.preventDefault(); - resolve(); - }); - } - }); - }); - - // Click the submit button - await submitButton.click(); - - // Assert the form actually submitted - // This will timeout (fail) if the submit event never fires - await Promise.race([ - submitPromise, - new Promise((_, reject) => - setTimeout(() => reject(new Error('Form did not submit within 2 seconds')), 2000) - ), - ]); - }); - }); - // Helper functions async function testStatus(page: any, status: string) { await expect(page.locator('#status')).toHaveText(status); diff --git a/packages/angular/test/base/src/app/lazy/form/form.component.ts b/packages/angular/test/base/src/app/lazy/form/form.component.ts index 128ba1f47e5..5e670482b47 100644 --- a/packages/angular/test/base/src/app/lazy/form/form.component.ts +++ b/packages/angular/test/base/src/app/lazy/form/form.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { UntypedFormGroup, UntypedFormBuilder, Validators, UntypedFormControl, AbstractControl, ValidationErrors, AsyncValidatorFn } from '@angular/forms'; +import { UntypedFormGroup, UntypedFormBuilder, Validators, UntypedFormControl, AbstractControl, ValidationErrors } from '@angular/forms'; function otpRequiredLength(length: number) { return (control: AbstractControl): ValidationErrors | null => { @@ -10,18 +10,6 @@ function otpRequiredLength(length: number) { return null; }; } - -// async validation test -function asyncValidator() { - return (control: AbstractControl): Promise => { - return new Promise((resolve) => { - setTimeout(() => { - resolve(null); - }, 100); - }); - }; -} - @Component({ selector: 'app-form', templateUrl: './form.component.html', @@ -38,7 +26,7 @@ export class FormComponent { datetime: ['2010-08-20', Validators.required], select: [undefined, Validators.required], toggle: [false], - input: ['', [Validators.required], [asyncValidator()]], + input: ['', Validators.required], input2: ['Default Value'], inputOtp: [null, [Validators.required, otpRequiredLength(4)]], inputOtpText: ['', [Validators.required, otpRequiredLength(4)]], From 09a65cea555208dc35629c6961be7ad8af02f970 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Tue, 23 Jun 2026 10:59:50 -0500 Subject: [PATCH 09/15] style: code comment Removing extra space in code comment on 2 lines --- core/src/components/button/button.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index 8a0a645177a..575d188b580 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -195,8 +195,8 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf /** * If the form already has a rendered form button - * then do not append a new one again. Sync the - * disabled state and type in it changes after button + * then do not append a new one again. Sync the + * disabled state and type in it changes after button * creation (e.g., runtime property updates). */ if (formButtonEl !== null && formEl.contains(formButtonEl)) { From 914a42c906c19faaaef257ef969e4268b9337d51 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Tue, 23 Jun 2026 11:01:42 -0500 Subject: [PATCH 10/15] test(button): add e2e test for async disabled state change Test gets disabled state and tests if they match for both initial state and after set-values is called to simulate filling out the form. --- .../test/base/e2e/src/lazy/form.spec.ts | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/angular/test/base/e2e/src/lazy/form.spec.ts b/packages/angular/test/base/e2e/src/lazy/form.spec.ts index a5cacabe5e2..196a8cf26a4 100644 --- a/packages/angular/test/base/e2e/src/lazy/form.spec.ts +++ b/packages/angular/test/base/e2e/src/lazy/form.spec.ts @@ -282,7 +282,52 @@ test.describe('Form', () => { await expect(control).toHaveClass(/ng-invalid/); }); - }); + + test('should keep hidden submit button disabled state in sync', async ({ page }) => { + + // Get the disabled state of both visible and hidden button + const getDisabledState = () => + page.evaluate(() => { + const visible = document.querySelector( + '#submit-button' + ) as HTMLIonButtonElement; + + const hidden = document.querySelector( + 'form button[type="submit"][style*="display: none"]' + ) as HTMLButtonElement; + + return { + visible: visible?.disabled, + hidden: hidden?.disabled, + }; + }); + + // Ensure disabled state of both visible and hidden button + // Should match each other and expected + const expectDisabledStatesMatch = async (expected: boolean) => { + const state = await getDisabledState(); + expect(state.visible).toBe(expected); + expect(state.hidden).toBe(expected); + expect(state.visible).toBe(state.hidden); + return state; + }; + + // Initial state - should be disabled and both match + await expectDisabledStatesMatch(true); + + // Set form values - should be enabled + await page.locator('#set-values').click(); + + // After set values - should be enabled and both match + await expectDisabledStatesMatch(false); + + // User clicks submit button + await page.locator('#submit-button').click(); + + // Form should submit successfully + await expect(page.locator('#submit')).toHaveText('true'); + }); + }); // Helper functions async function testStatus(page: any, status: string) { From 869ccd61039fd86fed74767a7a98586203ad45a0 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Tue, 23 Jun 2026 11:39:53 -0500 Subject: [PATCH 11/15] style Fixing typo in code comment for button.tsx --- core/src/components/button/button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index 575d188b580..a1e7f72bf01 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -196,7 +196,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf /** * If the form already has a rendered form button * then do not append a new one again. Sync the - * disabled state and type in it changes after button + * disabled state and type if it changes after button * creation (e.g., runtime property updates). */ if (formButtonEl !== null && formEl.contains(formButtonEl)) { From 2c78cd07badc6744cd5622b38e08b2877e7e8844 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Tue, 23 Jun 2026 11:50:28 -0500 Subject: [PATCH 12/15] revert: test(ion-button): add e2e test for async disabled state change This reverts commit 2964bc6a15552d19577bf0d92ff6f8558cd02272. --- .../button/test/form-reference/button.e2e.ts | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/core/src/components/button/test/form-reference/button.e2e.ts b/core/src/components/button/test/form-reference/button.e2e.ts index c803dd2c226..02e8ff9bbd0 100644 --- a/core/src/components/button/test/form-reference/button.e2e.ts +++ b/core/src/components/button/test/form-reference/button.e2e.ts @@ -154,42 +154,6 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(submitEvent).toHaveReceivedEvent(); }); - test('should submit the form when disabled state changes asynchronously', async ({ page }, testInfo) => { - testInfo.annotations.push({ - type: 'issue', - description: 'https://github.com/ionic-team/ionic-framework/issues/30968', - }); - - await page.setContent( - ` -
- - Submit -
- `, - config - ); - - const submitEvent = await page.spyOnEvent('submit'); - const button = page.locator('ion-button'); - - // Simulate async disabled state change — e.g. async validator resolving - await button.evaluate((el: HTMLIonButtonElement) => { - el.disabled = true; - setTimeout(() => { - el.disabled = false; - }, 0); - }); - - // Wait for the async change to settle - await page.waitForTimeout(100); - - await page.click('ion-button'); - - expect(submitEvent).toHaveReceivedEvent(); - }); - }); - test.describe(title('should throw a warning if the form cannot be found'), () => { test('form is a string selector', async ({ page }) => { const logs: string[] = []; From 7e25d02b25eb51aad7942d84790d32466e40cdd7 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Tue, 23 Jun 2026 11:50:28 -0500 Subject: [PATCH 13/15] revert: test(ion-button): add e2e test for async disabled state change This reverts commit 2964bc6a15552d19577bf0d92ff6f8558cd02272. --- .../button/test/form-reference/button.e2e.ts | 37 +------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/core/src/components/button/test/form-reference/button.e2e.ts b/core/src/components/button/test/form-reference/button.e2e.ts index c803dd2c226..8f4cfaaaa1e 100644 --- a/core/src/components/button/test/form-reference/button.e2e.ts +++ b/core/src/components/button/test/form-reference/button.e2e.ts @@ -153,43 +153,8 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(submitEvent).toHaveReceivedEvent(); }); - - test('should submit the form when disabled state changes asynchronously', async ({ page }, testInfo) => { - testInfo.annotations.push({ - type: 'issue', - description: 'https://github.com/ionic-team/ionic-framework/issues/30968', - }); - - await page.setContent( - ` -
- - Submit -
- `, - config - ); - - const submitEvent = await page.spyOnEvent('submit'); - const button = page.locator('ion-button'); - - // Simulate async disabled state change — e.g. async validator resolving - await button.evaluate((el: HTMLIonButtonElement) => { - el.disabled = true; - setTimeout(() => { - el.disabled = false; - }, 0); - }); - - // Wait for the async change to settle - await page.waitForTimeout(100); - - await page.click('ion-button'); - - expect(submitEvent).toHaveReceivedEvent(); - }); }); - + test.describe(title('should throw a warning if the form cannot be found'), () => { test('form is a string selector', async ({ page }) => { const logs: string[] = []; From 26b8aaea07d92011761a869ee77d7c8e96cb0bf3 Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Tue, 23 Jun 2026 12:32:28 -0500 Subject: [PATCH 14/15] style npm run lint --- core/src/components/button/test/form-reference/button.e2e.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/button/test/form-reference/button.e2e.ts b/core/src/components/button/test/form-reference/button.e2e.ts index 8f4cfaaaa1e..5710f52da67 100644 --- a/core/src/components/button/test/form-reference/button.e2e.ts +++ b/core/src/components/button/test/form-reference/button.e2e.ts @@ -154,7 +154,7 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(submitEvent).toHaveReceivedEvent(); }); }); - + test.describe(title('should throw a warning if the form cannot be found'), () => { test('form is a string selector', async ({ page }) => { const logs: string[] = []; From 4e9c622bc13208b01496ed83918e41c18c528f3a Mon Sep 17 00:00:00 2001 From: Zac-Smucker-Bryan Date: Wed, 24 Jun 2026 21:27:45 -0500 Subject: [PATCH 15/15] test(button): add e2e test for async type change Removes test for syncing disabled state of visible and hidden button that unreliably passed. Adds test for syncing type of visible and hidden button. --- .../test/base/e2e/src/lazy/form.spec.ts | 51 ++++++------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/packages/angular/test/base/e2e/src/lazy/form.spec.ts b/packages/angular/test/base/e2e/src/lazy/form.spec.ts index 196a8cf26a4..36b3709753d 100644 --- a/packages/angular/test/base/e2e/src/lazy/form.spec.ts +++ b/packages/angular/test/base/e2e/src/lazy/form.spec.ts @@ -283,49 +283,30 @@ test.describe('Form', () => { await expect(control).toHaveClass(/ng-invalid/); }); - test('should keep hidden submit button disabled state in sync', async ({ page }) => { - - // Get the disabled state of both visible and hidden button - const getDisabledState = () => + test('should keep hidden button type in sync with visible button', async ({ page }) => { + + // Get type of the hidden button + const getHiddenType = () => page.evaluate(() => { - const visible = document.querySelector( - '#submit-button' - ) as HTMLIonButtonElement; - const hidden = document.querySelector( - 'form button[type="submit"][style*="display: none"]' + 'form button[style*="display: none"]' ) as HTMLButtonElement; - return { - visible: visible?.disabled, - hidden: hidden?.disabled, - }; + return hidden?.type; }); - - // Ensure disabled state of both visible and hidden button - // Should match each other and expected - const expectDisabledStatesMatch = async (expected: boolean) => { - const state = await getDisabledState(); - expect(state.visible).toBe(expected); - expect(state.hidden).toBe(expected); - expect(state.visible).toBe(state.hidden); - return state; - }; - - // Initial state - should be disabled and both match - await expectDisabledStatesMatch(true); - - // Set form values - should be enabled - await page.locator('#set-values').click(); - // After set values - should be enabled and both match - await expectDisabledStatesMatch(false); + // Type should be submit to start + expect(await getHiddenType()).toBe('submit'); - // User clicks submit button - await page.locator('#submit-button').click(); + // Set type of visible button to reset + await page.locator('#submit-button').evaluate((el: HTMLIonButtonElement) => { + el.type = 'reset'; + }); - // Form should submit successfully - await expect(page.locator('#submit')).toHaveText('true'); + // Expect hidden button type to be reset + await expect + .poll(async () => await getHiddenType()) + .toBe('reset'); }); });