1818 */
1919
2020import React from 'react' ;
21- import { createMemoryHistory , History } from 'history' ;
21+ import { createMemoryHistory , History , createHashHistory } from 'history' ;
2222
2323import { AppRouter , AppNotFound } from '../ui' ;
2424import { EitherApp , MockedMounterMap , MockedMounterTuple } from '../test_types' ;
@@ -27,7 +27,15 @@ import { createRenderer, createAppMounter, createLegacyAppMounter } from './util
2727describe ( 'AppContainer' , ( ) => {
2828 let mounters : MockedMounterMap < EitherApp > ;
2929 let history : History ;
30- let navigate : ReturnType < typeof createRenderer > ;
30+ let update : ReturnType < typeof createRenderer > ;
31+
32+ const navigate = ( path : string ) => {
33+ history . push ( path ) ;
34+ return update ( ) ;
35+ } ;
36+
37+ const mockMountersToMounters = ( ) =>
38+ new Map ( [ ...mounters ] . map ( ( [ appId , { mounter } ] ) => [ appId , mounter ] ) ) ;
3139
3240 beforeEach ( ( ) => {
3341 mounters = new Map ( [
@@ -38,26 +46,26 @@ describe('AppContainer', () => {
3846 createAppMounter ( 'app3' , '<div>App 3</div>' , '/custom/path' ) ,
3947 ] as Array < MockedMounterTuple < EitherApp > > ) ;
4048 history = createMemoryHistory ( ) ;
41- navigate = createRenderer ( < AppRouter history = { history } mounters = { mounters } /> , history . push ) ;
49+ update = createRenderer ( < AppRouter history = { history } mounters = { mockMountersToMounters ( ) } /> ) ;
4250 } ) ;
4351
4452 it ( 'calls mount handler and returned unmount function when navigating between apps' , async ( ) => {
4553 const dom1 = await navigate ( '/app/app1' ) ;
4654 const app1 = mounters . get ( 'app1' ) ! ;
4755
48- expect ( app1 . mount ) . toHaveBeenCalled ( ) ;
56+ expect ( app1 . mounter . mount ) . toHaveBeenCalled ( ) ;
4957 expect ( dom1 ?. html ( ) ) . toMatchInlineSnapshot ( `
5058 "<div><div>
5159 basename: /app/app1
5260 html: <span>App 1</span>
5361 </div></div>"
5462 ` ) ;
5563
56- const app1Unmount = await app1 . mount . mock . results [ 0 ] . value ;
64+ const app1Unmount = await app1 . mounter . mount . mock . results [ 0 ] . value ;
5765 const dom2 = await navigate ( '/app/app2' ) ;
5866
5967 expect ( app1Unmount ) . toHaveBeenCalled ( ) ;
60- expect ( mounters . get ( 'app2' ) ! . mount ) . toHaveBeenCalled ( ) ;
68+ expect ( mounters . get ( 'app2' ) ! . mounter . mount ) . toHaveBeenCalled ( ) ;
6169 expect ( dom2 ?. html ( ) ) . toMatchInlineSnapshot ( `
6270 "<div><div>
6371 basename: /app/app2
@@ -70,29 +78,77 @@ describe('AppContainer', () => {
7078 mounters . set ( ...createAppMounter ( 'spaces' , '<div>Custom Space</div>' , '/spaces/fake-login' ) ) ;
7179 mounters . set ( ...createAppMounter ( 'login' , '<div>Login Page</div>' , '/fake-login' ) ) ;
7280 history = createMemoryHistory ( ) ;
73- navigate = createRenderer ( < AppRouter history = { history } mounters = { mounters } /> , history . push ) ;
81+ update = createRenderer ( < AppRouter history = { history } mounters = { mockMountersToMounters ( ) } /> ) ;
7482
7583 await navigate ( '/fake-login' ) ;
7684
77- expect ( mounters . get ( 'spaces' ) ! . mount ) . not . toHaveBeenCalled ( ) ;
78- expect ( mounters . get ( 'login' ) ! . mount ) . toHaveBeenCalled ( ) ;
85+ expect ( mounters . get ( 'spaces' ) ! . mounter . mount ) . not . toHaveBeenCalled ( ) ;
86+ expect ( mounters . get ( 'login' ) ! . mounter . mount ) . toHaveBeenCalled ( ) ;
7987 } ) ;
8088
8189 it ( 'should not mount when partial route path has higher specificity' , async ( ) => {
8290 mounters . set ( ...createAppMounter ( 'login' , '<div>Login Page</div>' , '/fake-login' ) ) ;
8391 mounters . set ( ...createAppMounter ( 'spaces' , '<div>Custom Space</div>' , '/spaces/fake-login' ) ) ;
8492 history = createMemoryHistory ( ) ;
85- navigate = createRenderer ( < AppRouter history = { history } mounters = { mounters } /> , history . push ) ;
93+ update = createRenderer ( < AppRouter history = { history } mounters = { mockMountersToMounters ( ) } /> ) ;
8694
8795 await navigate ( '/spaces/fake-login' ) ;
8896
89- expect ( mounters . get ( 'spaces' ) ! . mount ) . toHaveBeenCalled ( ) ;
90- expect ( mounters . get ( 'login' ) ! . mount ) . not . toHaveBeenCalled ( ) ;
97+ expect ( mounters . get ( 'spaces' ) ! . mounter . mount ) . toHaveBeenCalled ( ) ;
98+ expect ( mounters . get ( 'login' ) ! . mounter . mount ) . not . toHaveBeenCalled ( ) ;
99+ } ) ;
100+
101+ it ( 'should not remount when changing pages within app' , async ( ) => {
102+ const { mounter, unmount } = mounters . get ( 'app1' ) ! ;
103+ await navigate ( '/app/app1/page1' ) ;
104+ expect ( mounter . mount ) . toHaveBeenCalledTimes ( 1 ) ;
105+
106+ // Navigating to page within app does not trigger re-render
107+ await navigate ( '/app/app1/page2' ) ;
108+ expect ( mounter . mount ) . toHaveBeenCalledTimes ( 1 ) ;
109+ expect ( unmount ) . not . toHaveBeenCalled ( ) ;
110+ } ) ;
111+
112+ it ( 'should not remount when going back within app' , async ( ) => {
113+ const { mounter, unmount } = mounters . get ( 'app1' ) ! ;
114+ await navigate ( '/app/app1/page1' ) ;
115+ expect ( mounter . mount ) . toHaveBeenCalledTimes ( 1 ) ;
116+
117+ // Hitting back button within app does not trigger re-render
118+ await navigate ( '/app/app1/page2' ) ;
119+ history . goBack ( ) ;
120+ await update ( ) ;
121+ expect ( mounter . mount ) . toHaveBeenCalledTimes ( 1 ) ;
122+ expect ( unmount ) . not . toHaveBeenCalled ( ) ;
123+ } ) ;
124+
125+ it ( 'should not remount when when changing pages within app using hash history' , async ( ) => {
126+ history = createHashHistory ( ) ;
127+ update = createRenderer ( < AppRouter history = { history } mounters = { mockMountersToMounters ( ) } /> ) ;
128+
129+ const { mounter, unmount } = mounters . get ( 'app1' ) ! ;
130+ await navigate ( '/app/app1/page1' ) ;
131+ expect ( mounter . mount ) . toHaveBeenCalledTimes ( 1 ) ;
132+
133+ // Changing hash history does not trigger re-render
134+ await navigate ( '/app/app1/page2' ) ;
135+ expect ( mounter . mount ) . toHaveBeenCalledTimes ( 1 ) ;
136+ expect ( unmount ) . not . toHaveBeenCalled ( ) ;
137+ } ) ;
138+
139+ it ( 'should unmount when changing between apps' , async ( ) => {
140+ const { mounter, unmount } = mounters . get ( 'app1' ) ! ;
141+ await navigate ( '/app/app1/page1' ) ;
142+ expect ( mounter . mount ) . toHaveBeenCalledTimes ( 1 ) ;
143+
144+ // Navigating to other app triggers unmount
145+ await navigate ( '/app/app2/page1' ) ;
146+ expect ( unmount ) . toHaveBeenCalledTimes ( 1 ) ;
91147 } ) ;
92148
93149 it ( 'calls legacy mount handler' , async ( ) => {
94150 await navigate ( '/app/legacyApp1' ) ;
95- expect ( mounters . get ( 'legacyApp1' ) ! . mount . mock . calls [ 0 ] ) . toMatchInlineSnapshot ( `
151+ expect ( mounters . get ( 'legacyApp1' ) ! . mounter . mount . mock . calls [ 0 ] ) . toMatchInlineSnapshot ( `
96152 Array [
97153 Object {
98154 "appBasePath": "/app/legacyApp1",
@@ -104,7 +160,7 @@ describe('AppContainer', () => {
104160
105161 it ( 'handles legacy apps with subapps' , async ( ) => {
106162 await navigate ( '/app/baseApp' ) ;
107- expect ( mounters . get ( 'baseApp:legacyApp2' ) ! . mount . mock . calls [ 0 ] ) . toMatchInlineSnapshot ( `
163+ expect ( mounters . get ( 'baseApp:legacyApp2' ) ! . mounter . mount . mock . calls [ 0 ] ) . toMatchInlineSnapshot ( `
108164 Array [
109165 Object {
110166 "appBasePath": "/app/baseApp",
0 commit comments