66 * found in the LICENSE file at https://angular.dev/license
77 */
88
9- import { Injector , resource , signal } from '@angular/core' ;
9+ import { ApplicationRef , Injector , resource , signal } from '@angular/core' ;
1010import { TestBed } from '@angular/core/testing' ;
1111import {
1212 form ,
@@ -18,6 +18,12 @@ import {
1818} from '../../public_api' ;
1919
2020describe ( 'submit' , ( ) => {
21+ let appRef : ApplicationRef ;
22+
23+ beforeEach ( ( ) => {
24+ appRef = TestBed . inject ( ApplicationRef ) ;
25+ } ) ;
26+
2127 it ( 'fails fast on invalid form' , async ( ) => {
2228 const data = signal ( { first : '' , last : '' } ) ;
2329 const f = form (
@@ -60,10 +66,79 @@ describe('submit', () => {
6066 const submitSpy = jasmine . createSpy ( ) ;
6167 await submit ( f , submitSpy ) ;
6268
63- expect ( f ( ) . pending ( ) ) . toBe ( true ) ;
6469 expect ( submitSpy ) . toHaveBeenCalled ( ) ;
6570 } ) ;
6671
72+ it ( 'should cancel pending async validations on success' , async ( ) => {
73+ const data = signal ( '' ) ;
74+ const { promise, resolve} = promiseWithResolvers ( ) ;
75+ const f = form (
76+ data ,
77+ ( p ) => {
78+ validateAsync ( p , {
79+ params : ( { value} ) => value ( ) ,
80+ factory : ( params ) =>
81+ resource ( {
82+ params,
83+ loader : async ( { params} ) => {
84+ await promise ;
85+ return params ;
86+ } ,
87+ } ) ,
88+ onSuccess : ( ) => ( { kind : 'async' } ) ,
89+ onError : ( ) => ( { kind : 'async-error' } ) ,
90+ } ) ;
91+ } ,
92+ { injector : TestBed . inject ( Injector ) } ,
93+ ) ;
94+
95+ expect ( f ( ) . pending ( ) ) . withContext ( 'Async validator should be pending' ) . toBe ( true ) ;
96+
97+ await submit ( f , async ( ) => { } ) ;
98+ expect ( f ( ) . pending ( ) ) . withContext ( 'Submission should cancel pending validators' ) . toBe ( false ) ;
99+ expect ( f ( ) . valid ( ) ) . toBe ( true ) ;
100+
101+ resolve ( undefined ) ;
102+ await appRef . whenStable ( ) ;
103+ expect ( f ( ) . valid ( ) ) . toBe ( true ) ;
104+ } ) ;
105+
106+ it ( 'should cancel pending async validations on error' , async ( ) => {
107+ const data = signal ( '' ) ;
108+ const { promise, resolve} = promiseWithResolvers ( ) ;
109+ const f = form (
110+ data ,
111+ ( p ) => {
112+ validateAsync ( p , {
113+ params : ( { value} ) => value ( ) ,
114+ factory : ( params ) =>
115+ resource ( {
116+ params,
117+ loader : async ( { params} ) => {
118+ await promise ;
119+ return params ;
120+ } ,
121+ } ) ,
122+ onSuccess : ( ) => ( { kind : 'async' } ) ,
123+ onError : ( ) => ( { kind : 'async-error' } ) ,
124+ } ) ;
125+ } ,
126+ { injector : TestBed . inject ( Injector ) } ,
127+ ) ;
128+
129+ expect ( f ( ) . pending ( ) ) . withContext ( 'Async validator should be pending' ) . toBe ( true ) ;
130+
131+ await submit ( f , async ( ) => ( { kind : 'submit' } ) ) ;
132+ expect ( f ( ) . pending ( ) ) . withContext ( 'Submission should cancel pending validators' ) . toBe ( false ) ;
133+ expect ( f ( ) . errors ( ) ) . toEqual ( [ jasmine . objectContaining ( { kind : 'submit' } ) ] ) ;
134+
135+ resolve ( undefined ) ;
136+ await appRef . whenStable ( ) ;
137+ expect ( f ( ) . errors ( ) )
138+ . withContext ( 'Resolving the pending validator should not clear submission errors' )
139+ . toEqual ( [ jasmine . objectContaining ( { kind : 'submit' } ) ] ) ;
140+ } ) ;
141+
67142 it ( 'maps error to a field' , async ( ) => {
68143 const data = signal ( { first : '' , last : '' } ) ;
69144 const f = form (
0 commit comments