Skip to content

Commit 0d9721a

Browse files
committed
test(forms): cover custom control listener order
Ensure reactive and template-driven custom controls update their directive-backed control before explicit valueChange listeners run.
1 parent b93c6a4 commit 0d9721a

2 files changed

Lines changed: 79 additions & 0 deletions

File tree

packages/forms/signals/test/web/reactive_fvc.spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,30 @@ describe('FormControlDirective with FVC', () => {
7171
expect(fixture.componentInstance.ctrl.value).toBe('from-fvc');
7272
});
7373

74+
it('should update FormControl value before template (valueChange) listener fires', () => {
75+
@Component({
76+
template: `<my-fvc-input [formControl]="ctrl" (valueChange)="onValueChange()" />`,
77+
imports: [MyFvcInput, ReactiveFormsModule],
78+
})
79+
class TestCmp {
80+
ctrl = new FormControl('initial');
81+
observedDuringValueChange: string | null | undefined;
82+
83+
onValueChange() {
84+
this.observedDuringValueChange = this.ctrl.value;
85+
}
86+
}
87+
88+
const fixture = act(() => TestBed.createComponent(TestCmp));
89+
const component = fixture.componentInstance;
90+
const fvc = fixture.debugElement.query(By.directive(MyFvcInput)).componentInstance;
91+
92+
act(() => fvc.value.set('from-fvc'));
93+
94+
expect(component.observedDuringValueChange).toBe('from-fvc');
95+
expect(component.ctrl.value).toBe('from-fvc');
96+
});
97+
7498
it('should fall back to CVA when no FVC pattern is present', () => {
7599
@Component({
76100
template: `<input [formControl]="ctrl" />`,
@@ -553,6 +577,36 @@ describe('FormControlName with FVC', () => {
553577
expect(fixture.componentInstance.form.controls.name.value).toBe('from-fvc');
554578
});
555579

580+
it('should update FormControl value before template (valueChange) listener fires', () => {
581+
@Component({
582+
template: `
583+
<form [formGroup]="form">
584+
<my-fvc-input formControlName="name" (valueChange)="onValueChange()" />
585+
</form>
586+
`,
587+
imports: [MyFvcInput, ReactiveFormsModule],
588+
})
589+
class TestCmp {
590+
form = new FormGroup({
591+
name: new FormControl('initial'),
592+
});
593+
observedDuringValueChange: string | null | undefined;
594+
595+
onValueChange() {
596+
this.observedDuringValueChange = this.form.controls.name.value;
597+
}
598+
}
599+
600+
const fixture = act(() => TestBed.createComponent(TestCmp));
601+
const component = fixture.componentInstance;
602+
const fvc = fixture.debugElement.query(By.directive(MyFvcInput)).componentInstance;
603+
604+
act(() => fvc.value.set('from-fvc'));
605+
606+
expect(component.observedDuringValueChange).toBe('from-fvc');
607+
expect(component.form.controls.name.value).toBe('from-fvc');
608+
});
609+
556610
it('should fall back to CVA when no FVC pattern is present', () => {
557611
@Component({
558612
template: `

packages/forms/signals/test/web/template_fvc.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,31 @@ describe('NgModel with FVC', () => {
7373
expect(fixture.componentInstance.val()).toBe('from-fvc');
7474
});
7575

76+
it('should update ngModel value before template (valueChange) listener fires', async () => {
77+
@Component({
78+
template: `<template-fvc-input [(ngModel)]="val" (valueChange)="onValueChange()" #model="ngModel" />`,
79+
imports: [TemplateFvcInput, FormsModule],
80+
})
81+
class TestCmp {
82+
val = signal('initial');
83+
observedDuringValueChange: string | undefined;
84+
@ViewChild('model') model!: NgModel;
85+
86+
onValueChange() {
87+
this.observedDuringValueChange = this.model.control.value;
88+
}
89+
}
90+
91+
const fixture = await actAsync(() => TestBed.createComponent(TestCmp));
92+
const component = fixture.componentInstance;
93+
const fvc = fixture.debugElement.query(By.directive(TemplateFvcInput)).componentInstance;
94+
95+
act(() => fvc.value.set('from-fvc'));
96+
97+
expect(component.observedDuringValueChange).toBe('from-fvc');
98+
expect(component.val()).toBe('from-fvc');
99+
});
100+
76101
it('should fall back to CVA when no FVC pattern is present', async () => {
77102
@Component({
78103
template: `<input [(ngModel)]="val" />`,

0 commit comments

Comments
 (0)