Angular Jest中NumberLimitDirective指令单元测试preventDefault未生效问题
Let's break down why your unit test isn't behaving as expected and fix it step by step.
The Root Cause
Your test is failing because two key issues with how you're handling the keyboard event:
- The
KeyboardEventyou're creating isn't cancelable — by default, nativeKeyboardEventinstances havecancelable: false, so callingevent.preventDefault()on them does nothing. - You're checking
defaultPreventedsynchronously before the event handler finishes — Angular'sHostListenerprocesses events asynchronously in some cases, so you need to wait for the event handling to complete before checking if the event was canceled.
Step-by-Step Fixes
1. Create a Cancelable Keyboard Event
When you instantiate your keypress event, explicitly set cancelable: true so preventDefault() can actually block the event:
async function testInput(inputElement: any, text: number | string) { const keypressEvent = new KeyboardEvent('keypress', { key: `${text}`, cancelable: true // Critical: allows preventDefault() to work }); await sendKeyDown(inputElement, keypressEvent); // Wait for event processing if (!keypressEvent.defaultPrevented) { await sendInput(inputElement, text); } }
2. Wait for Asynchronous Event Handling
Notice we added await before sendKeyDown — your sendKeyDown function returns fixture.whenStable(), so we need to wait for that promise to resolve before checking if the event was canceled. This ensures Angular has finished running the directive's onKeyPress handler.
3. Fix the Undefined current Variable in the Directive
Looking at your directive code, there's a missing variable definition that would break it in production (even if you said it works now, this is likely a paste error):
@HostListener('keypress', ['$event']) public onKeyPress(event) { const current = this.el.nativeElement.value; // Add this line! const next: string = current.concat(event.key); const regexp = RegExp(this.setRegexp(), 'g'); if (next && !String(next).match(regexp)) { event.preventDefault(); } }
Without defining current, the directive would throw a ReferenceError — fixing this ensures both production and test environments behave consistently.
4. Adjust Your Test Expectation (If Needed)
Your test expects the final value to be 01, but let's check the default regex logic:
- Default
integerLimit: 10,fractionalLimit: 0creates the regex^[0-9]{0,10}$ - This allows digits only, so inputting
0(initial value) →1(allowed) →,(blocked) →0(allowed) would result in010, not01.
If you intended to block leading zeros or single0inputs, you'd need to adjust the regex insetRegexp(), but that's a separate issue from the test failure.
Updated Test Code Snippet
Here's how your modified testInput and test case would look:
async function testInput(inputElement: any, text: number | string) { const keypressEvent = new KeyboardEvent('keypress', { key: `${text}`, cancelable: true }); await sendKeyDown(inputElement, keypressEvent); if (!keypressEvent.defaultPrevented) { await sendInput(inputElement, text); } } test('Check valid input for default input.', async () => { fixture.detectChanges(); const testLocator = fixture.debugElement.query(By.css('#default')); expect(testLocator).not.toBe(null); await testInput(testLocator, 1); await testInput(testLocator, ','); await testInput(testLocator, 0); expect(testLocator.nativeElement.value).toBe('010'); // Updated expectation to match regex logic });
Final Notes
- Always ensure test events are cancelable when testing
preventDefault()logic - Use
awaitwithfixture.whenStable()to wait for Angular's change detection and event processing to finish - Double-check for missing variable definitions in your directive code to avoid inconsistencies between environments
内容的提问来源于stack exchange,提问作者Max

