From e707d5d6b9d1881cd08cbf7a7c66fa8083be54b5 Mon Sep 17 00:00:00 2001 From: lazerg Date: Thu, 29 Oct 2026 15:50:33 +0500 Subject: [PATCH] fix(cdk/testing): dispatch bubbling input and change events --- .../testbed/fake-events/type-in-element.ts | 6 +++--- src/cdk/testing/testbed/unit-test-element.ts | 2 +- src/cdk/testing/tests/cross-environment-tests.ts | 16 ++++++++++++++++ .../tests/harnesses/main-component-harness.ts | 1 + src/cdk/testing/tests/test-main-component.html | 3 ++- src/cdk/testing/tests/test-main-component.ts | 5 +++++ 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/cdk/testing/testbed/fake-events/type-in-element.ts b/src/cdk/testing/testbed/fake-events/type-in-element.ts index aff0b330dd7d..fd57af426585 100644 --- a/src/cdk/testing/testbed/fake-events/type-in-element.ts +++ b/src/cdk/testing/testbed/fake-events/type-in-element.ts @@ -163,7 +163,7 @@ export function typeInElement(element: HTMLElement, ...modifiersAndKeys: any[]) if (isInput && key.key && key.key.length === 1) { if (enterValueIncrementally) { (element as HTMLInputElement | HTMLTextAreaElement).value += key.key; - dispatchFakeEvent(element, 'input'); + dispatchFakeEvent(element, 'input', true); } } dispatchKeyboardEvent(element, 'keyup', key.keyCode, key.key, modifiers, key.code); @@ -171,7 +171,7 @@ export function typeInElement(element: HTMLElement, ...modifiersAndKeys: any[]) // Since we weren't dispatching `input` events while sending the keys, we have to do it now. if (!enterValueIncrementally) { - dispatchFakeEvent(element, 'input'); + dispatchFakeEvent(element, 'input', true); } } @@ -182,5 +182,5 @@ export function typeInElement(element: HTMLElement, ...modifiersAndKeys: any[]) export function clearElement(element: HTMLInputElement | HTMLTextAreaElement) { triggerFocus(element as HTMLElement); element.value = ''; - dispatchFakeEvent(element, 'input'); + dispatchFakeEvent(element, 'input', true); } diff --git a/src/cdk/testing/testbed/unit-test-element.ts b/src/cdk/testing/testbed/unit-test-element.ts index 9edb55433497..1a281464dc40 100644 --- a/src/cdk/testing/testbed/unit-test-element.ts +++ b/src/cdk/testing/testbed/unit-test-element.ts @@ -254,7 +254,7 @@ export class UnitTestElement implements TestElement { if (option.selected !== wasSelected) { hasChanged = true; - dispatchFakeEvent(this.element, 'change'); + dispatchFakeEvent(this.element, 'change', true); } } diff --git a/src/cdk/testing/tests/cross-environment-tests.ts b/src/cdk/testing/tests/cross-environment-tests.ts index df8df24b88fb..b4a1ad4ad59b 100644 --- a/src/cdk/testing/tests/cross-environment-tests.ts +++ b/src/cdk/testing/tests/cross-environment-tests.ts @@ -450,6 +450,12 @@ export function crossEnvironmentSpecs( expect(await value.text()).toBe('Input: Yi'); }); + it('should dispatch input events that bubble when sending keys', async () => { + const [input, bubbledEvent] = await parallel(() => [harness.input(), harness.bubbledEvent()]); + await input.sendKeys('Yi'); + expect(await bubbledEvent.text()).toBe('Bubbled event: input|input'); + }); + it('focuses the element before sending key', async () => { const input = await harness.input(); await input.sendKeys('Yi'); @@ -577,6 +583,16 @@ export function crossEnvironmentSpecs( expect(await changeEventCounter.text()).toBe('Change events: 2'); }); + it('should dispatch change events that bubble when selecting options', async () => { + const [select, bubbledEvent] = await parallel(() => [ + harness.multiSelect(), + harness.bubbledEvent(), + ]); + + await select.selectOptions(0); + expect(await bubbledEvent.text()).toBe('Bubbled event: change|multi-select'); + }); + it('should check if selector matches', async () => { const button = await harness.button(); expect(await button.matchesSelector('button:not(.fake-class)')).toBe(true); diff --git a/src/cdk/testing/tests/harnesses/main-component-harness.ts b/src/cdk/testing/tests/harnesses/main-component-harness.ts index 71d516ee5a28..93648d346eae 100644 --- a/src/cdk/testing/tests/harnesses/main-component-harness.ts +++ b/src/cdk/testing/tests/harnesses/main-component-harness.ts @@ -37,6 +37,7 @@ export class MainComponentHarness extends ComponentHarness { readonly multiSelect = this.locatorFor('#multi-select'); readonly multiSelectValue = this.locatorFor('#multi-select-value'); readonly multiSelectChangeEventCounter = this.locatorFor('#multi-select-change-counter'); + readonly bubbledEvent = this.locatorFor('#bubbled-event'); readonly numberInput = this.locatorFor('#number-input'); readonly numberInputValue = this.locatorFor('#number-input-value'); readonly contextmenuTestResult = this.locatorFor('.contextmenu-test-result'); diff --git a/src/cdk/testing/tests/test-main-component.html b/src/cdk/testing/tests/test-main-component.html index f7f87200aa36..2d6777a4ff5c 100644 --- a/src/cdk/testing/tests/test-main-component.html +++ b/src/cdk/testing/tests/test-main-component.html @@ -21,7 +21,7 @@

Main Component

{{asyncCounter}}
initial value -
+
{{specialKey}}
Input: {{input}}
@@ -52,6 +52,7 @@

Main Component

Multi-select: {{multiSelect}}
Change events: {{multiSelectChangeEventCount}}
+
Bubbled event: {{bubbledEvent}}
Basic event: {{basicEvent}}
diff --git a/src/cdk/testing/tests/test-main-component.ts b/src/cdk/testing/tests/test-main-component.ts index eba523cfe206..69317fb4f681 100644 --- a/src/cdk/testing/tests/test-main-component.ts +++ b/src/cdk/testing/tests/test-main-component.ts @@ -47,6 +47,7 @@ export class TestMainComponent implements OnDestroy { singleSelectChangeEventCount = 0; multiSelect: string[] = []; multiSelectChangeEventCount = 0; + bubbledEvent = ''; basicEvent = 0; customEventData: string | null = null; _shadowDomSupported = _supportsShadowDom(); @@ -115,6 +116,10 @@ export class TestMainComponent implements OnDestroy { .join('-'); } + onBubbledEvent(event: Event) { + this.bubbledEvent = `${event.type}|${(event.target as HTMLElement).id}`; + } + onCustomEvent(event: any) { this.customEventData = JSON.stringify({message: event.message, value: event.value}); }