mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
test(forms): cover transformedValue without FormField context
Adds a test verifying that `transformedValue` exposes parse errors via the returned signal's `parseErrors()` property when no FormField context is present. This ensures that: - parse errors are still observable without DI-based field propagation - the model is not updated when `parse` omits `value` - valid input clears parse errors and updates the model This test protects the documented contract that DI-based error propagation is expected for FormValueControl usage, while standalone usage relies on explicit consumption of `parseErrors()`.
This commit is contained in:
parent
8c17721333
commit
71b8159b37
1 changed files with 53 additions and 0 deletions
|
|
@ -324,6 +324,59 @@ describe('parse errors', () => {
|
|||
await act(() => input.dispatchEvent(new Event('input')));
|
||||
expect(comp.f().errors()).toEqual([jasmine.objectContaining({kind: 'max'})]);
|
||||
});
|
||||
|
||||
it('should expose parseErrors when transformedValue is used without FormField', async () => {
|
||||
@Component({
|
||||
template: `
|
||||
<input [value]="raw()" (input)="raw.set($event.target.value)" />
|
||||
|
||||
@for (e of raw.parseErrors(); track $index) {
|
||||
<div class="error">{{ e.message }}</div>
|
||||
}
|
||||
`,
|
||||
})
|
||||
class TestCmp {
|
||||
readonly value = model<number | null>(5);
|
||||
|
||||
protected readonly raw = transformedValue(this.value, {
|
||||
parse: (rawValue: string) => {
|
||||
if (rawValue === '') return {value: null};
|
||||
const num = Number(rawValue);
|
||||
if (Number.isNaN(num)) {
|
||||
return {error: {kind: 'parse', message: `${rawValue} is not numeric`}};
|
||||
}
|
||||
return {value: num};
|
||||
},
|
||||
format: (val) => (val == null ? '' : String(val)),
|
||||
});
|
||||
}
|
||||
|
||||
const fixture = await act(() => TestBed.createComponent(TestCmp));
|
||||
const testEl = fixture.nativeElement as HTMLElement;
|
||||
const comp = fixture.componentInstance;
|
||||
|
||||
const input = testEl.querySelector('input') as HTMLInputElement;
|
||||
|
||||
// Invalid input: model unchanged, parse error exposed.
|
||||
input.value = 'abc';
|
||||
await act(() => input.dispatchEvent(new Event('input')));
|
||||
|
||||
expect(comp.value()).toBe(5);
|
||||
|
||||
let errors = Array.from(testEl.querySelectorAll('.error')).map((e) =>
|
||||
(e.textContent ?? '').trim(),
|
||||
);
|
||||
expect(errors).toEqual(['abc is not numeric']);
|
||||
|
||||
// Valid input: model updated, parse errors cleared.
|
||||
input.value = '42';
|
||||
await act(() => input.dispatchEvent(new Event('input')));
|
||||
|
||||
expect(comp.value()).toBe(42);
|
||||
|
||||
errors = Array.from(testEl.querySelectorAll('.error')).map((e) => (e.textContent ?? '').trim());
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@Component({
|
||||
|
|
|
|||
Loading…
Reference in a new issue