@@ -16,15 +16,15 @@ limitations under the License.
1616import { Component } from '@angular/core' ;
1717import { fakeAsync , flush , TestBed , tick } from '@angular/core/testing' ;
1818import { provideMockActions } from '@ngrx/effects/testing' ;
19- import { Action , createAction , props , Store } from '@ngrx/store' ;
19+ import { Action , createAction , createSelector , props , Store } from '@ngrx/store' ;
2020import { MockStore , provideMockStore } from '@ngrx/store/testing' ;
2121import { of , ReplaySubject } from 'rxjs' ;
2222
2323import { State } from '../../app_state' ;
2424import * as actions from '../actions' ;
25- import { navigationRequested } from '../actions' ;
2625import { AppRootProvider , TestableAppRootProvider } from '../app_root' ;
2726import { Location } from '../location' ;
27+ import { DirtyUpdatesRegistryModule } from '../dirty_updates_registry_module' ;
2828import {
2929 NavigateToCompare ,
3030 NavigateToExperiments ,
@@ -33,7 +33,13 @@ import {
3333import { RouteRegistryModule } from '../route_registry_module' ;
3434import { getActiveRoute } from '../store/app_routing_selectors' ;
3535import { buildRoute , provideLocationTesting , TestableLocation } from '../testing' ;
36- import { Navigation , Route , RouteKind , SerializableQueryParams } from '../types' ;
36+ import {
37+ DirtyUpdates ,
38+ Navigation ,
39+ Route ,
40+ RouteKind ,
41+ SerializableQueryParams ,
42+ } from '../types' ;
3743
3844import { AppRoutingEffects , TEST_ONLY } from './app_routing_effects' ;
3945
@@ -45,6 +51,14 @@ const testNavToCompareAction = createAction(
4551 '[TEST] test nav to compare' ,
4652 props < NavigateToCompare [ 'routeParams' ] > ( )
4753) ;
54+ const testDirtyExperimentsSelector = createSelector (
55+ ( s ) => s ,
56+ ( state : any ) => {
57+ return {
58+ experimentIds : [ ] ,
59+ } as DirtyUpdates ;
60+ }
61+ ) ;
4862
4963describe ( 'app_routing_effects' , ( ) => {
5064 let effects : AppRoutingEffects ;
@@ -138,6 +152,10 @@ describe('app_routing_effects', () => {
138152 } ;
139153 }
140154
155+ function dirtyUpdatesFactory ( ) {
156+ return testDirtyExperimentsSelector ;
157+ }
158+
141159 await TestBed . configureTestingModule ( {
142160 imports : [
143161 RouteRegistryModule . registerRoutes ( routeFactory ) ,
@@ -147,6 +165,9 @@ describe('app_routing_effects', () => {
147165 ProgrammaticalNavigationModule . registerProgrammaticalNavigation (
148166 programmaticalCompareNavigationFactory
149167 ) ,
168+ DirtyUpdatesRegistryModule . registerDirtyUpdates < State > (
169+ dirtyUpdatesFactory
170+ ) ,
150171 ] ,
151172 providers : [
152173 provideMockActions ( action ) ,
@@ -157,6 +178,7 @@ describe('app_routing_effects', () => {
157178 provide : AppRootProvider ,
158179 useClass : TestableAppRootProvider ,
159180 } ,
181+ DirtyUpdatesRegistryModule ,
160182 ] ,
161183 } ) . compileComponents ( ) ;
162184
@@ -255,6 +277,108 @@ describe('app_routing_effects', () => {
255277 ] ) ;
256278 } ) ;
257279
280+ describe ( 'dispatchNavigating$ with dirty updates' , ( ) => {
281+ beforeEach ( ( ) => {
282+ const activeRoute = buildRoute ( {
283+ routeKind : RouteKind . EXPERIMENTS ,
284+ pathname : '/experiments' ,
285+ queryParams : [ ] ,
286+ navigationOptions : {
287+ replaceState : false ,
288+ } ,
289+ } ) ;
290+ const dirtyExperimentsFactory = ( ) => {
291+ return { experimentIds : [ 'otter' ] } ;
292+ } ;
293+
294+ store . overrideSelector ( getActiveRoute , activeRoute ) ;
295+ store . overrideSelector (
296+ testDirtyExperimentsSelector ,
297+ dirtyExperimentsFactory ( )
298+ ) ;
299+ store . refreshState ( ) ;
300+ } ) ;
301+
302+ it ( 'warns user when navigating away' , ( ) => {
303+ spyOn ( window , 'confirm' ) ;
304+ action . next (
305+ actions . navigationRequested ( {
306+ pathname : '/experiment/meow' ,
307+ } )
308+ ) ;
309+
310+ expect ( window . confirm ) . toHaveBeenCalledWith (
311+ 'You have unsaved edits, are you sure you want to discard them?'
312+ ) ;
313+ } ) ;
314+
315+ it ( 'noops if user cancels navigation' , ( ) => {
316+ spyOn ( window , 'confirm' ) . and . returnValue ( false ) ;
317+ action . next (
318+ actions . navigationRequested ( {
319+ pathname : '/experiment/meow' ,
320+ } )
321+ ) ;
322+
323+ expect ( window . confirm ) . toHaveBeenCalledTimes ( 1 ) ;
324+ expect ( actualActions ) . toEqual ( [ ] ) ;
325+ } ) ;
326+
327+ it (
328+ 'fires discardDirtyUpdates, navigating and navigated if user ' +
329+ 'discards dirty updates' ,
330+ fakeAsync ( ( ) => {
331+ spyOn ( window , 'confirm' ) . and . returnValue ( true ) ;
332+ action . next (
333+ actions . navigationRequested ( {
334+ pathname : '/experiment/meow' ,
335+ } )
336+ ) ;
337+
338+ expect ( window . confirm ) . toHaveBeenCalledTimes ( 1 ) ;
339+
340+ expect ( actualActions ) . toEqual ( [
341+ actions . discardDirtyUpdates ( ) ,
342+ actions . navigating ( {
343+ after : buildRoute ( {
344+ routeKind : RouteKind . EXPERIMENT ,
345+ params : { experimentId : 'meow' } ,
346+ pathname : '/experiment/meow' ,
347+ queryParams : [ ] ,
348+ } ) ,
349+ } ) ,
350+ ] ) ;
351+
352+ tick ( ) ;
353+ expect ( actualActions ) . toEqual ( [
354+ actions . discardDirtyUpdates ( ) ,
355+ actions . navigating ( {
356+ after : buildRoute ( {
357+ routeKind : RouteKind . EXPERIMENT ,
358+ params : { experimentId : 'meow' } ,
359+ pathname : '/experiment/meow' ,
360+ queryParams : [ ] ,
361+ } ) ,
362+ } ) ,
363+ actions . navigated ( {
364+ before : buildRoute ( {
365+ routeKind : RouteKind . EXPERIMENTS ,
366+ params : { } ,
367+ pathname : '/experiments' ,
368+ queryParams : [ ] ,
369+ } ) ,
370+ after : buildRoute ( {
371+ routeKind : RouteKind . EXPERIMENT ,
372+ params : { experimentId : 'meow' } ,
373+ pathname : '/experiment/meow' ,
374+ queryParams : [ ] ,
375+ } ) ,
376+ } ) ,
377+ ] ) ;
378+ } )
379+ ) ;
380+ } ) ;
381+
258382 describe ( 'order of events' , ( ) => {
259383 it (
260384 'dispatches navigating, waits (for UI to clear prev route page), ' +
@@ -1019,7 +1143,7 @@ describe('app_routing_effects', () => {
10191143 setAppRootAndSubscribe ( '/foo/bar/' ) ;
10201144
10211145 // Do note that this path name does not contain the appRoot.
1022- action . next ( navigationRequested ( { pathname : '/experiment/123' } ) ) ;
1146+ action . next ( actions . navigationRequested ( { pathname : '/experiment/123' } ) ) ;
10231147
10241148 expect ( actualActions ) . toEqual ( [
10251149 actions . navigating ( {
@@ -1046,7 +1170,7 @@ describe('app_routing_effects', () => {
10461170 . and . returnValue ( '/foo/bar/experiment/123' ) ;
10471171
10481172 // Do note that this path name does not contain the appRoot.
1049- action . next ( navigationRequested ( { pathname : '../experiment/123' } ) ) ;
1173+ action . next ( actions . navigationRequested ( { pathname : '../experiment/123' } ) ) ;
10501174
10511175 expect ( actualActions ) . toEqual ( [
10521176 actions . navigating ( {
0 commit comments