@@ -13,6 +13,63 @@ import {
13
13
import getHtml from "../../react-router/__tests__/utils/getHtml" ;
14
14
15
15
describe ( `ScrollRestoration` , ( ) => {
16
+ it ( "restores the scroll position for a page when re-visited" , ( ) => {
17
+ const consoleWarnMock = jest
18
+ . spyOn ( console , "warn" )
19
+ . mockImplementation ( ( ) => { } ) ;
20
+
21
+ let testWindow = getWindowImpl ( "/base" ) ;
22
+ const mockScroll = jest . fn ( ) ;
23
+ window . scrollTo = mockScroll ;
24
+
25
+ let router = createBrowserRouter (
26
+ [
27
+ {
28
+ path : "/" ,
29
+ Component ( ) {
30
+ return (
31
+ < >
32
+ < Outlet />
33
+ < ScrollRestoration
34
+ getKey = { ( location ) => "test1-" + location . pathname }
35
+ />
36
+ </ >
37
+ ) ;
38
+ } ,
39
+ children : testPages ,
40
+ } ,
41
+ ] ,
42
+ { basename : "/base" , window : testWindow }
43
+ ) ;
44
+ let { container } = render ( < RouterProvider router = { router } /> ) ;
45
+
46
+ expect ( getHtml ( container ) ) . toMatch ( "On page 1" ) ;
47
+
48
+ // simulate scrolling
49
+ Object . defineProperty ( window , "scrollY" , { writable : true , value : 100 } ) ;
50
+
51
+ // leave page
52
+ window . dispatchEvent ( new Event ( "pagehide" ) ) ;
53
+ fireEvent . click ( screen . getByText ( "Go to page 2" ) ) ;
54
+ expect ( getHtml ( container ) ) . toMatch ( "On page 2" ) ;
55
+
56
+ // return to page
57
+ window . dispatchEvent ( new Event ( "pagehide" ) ) ;
58
+ fireEvent . click ( screen . getByText ( "Go to page 1" ) ) ;
59
+
60
+ expect ( getHtml ( container ) ) . toMatch ( "On page 1" ) ;
61
+
62
+ // check scroll activity
63
+ expect ( mockScroll . mock . calls ) . toEqual ( [
64
+ [ 0 , 0 ] ,
65
+ [ 0 , 0 ] ,
66
+ [ 0 , 100 ] , // restored
67
+ ] ) ;
68
+
69
+ expect ( consoleWarnMock ) . not . toHaveBeenCalled ( ) ;
70
+ consoleWarnMock . mockRestore ( ) ;
71
+ } ) ;
72
+
16
73
it ( "removes the basename from the location provided to getKey" , ( ) => {
17
74
let getKey = jest . fn ( ( ) => "mykey" ) ;
18
75
let testWindow = getWindowImpl ( "/base" ) ;
@@ -64,8 +121,99 @@ describe(`ScrollRestoration`, () => {
64
121
// @ts -expect-error
65
122
expect ( getKey . mock . calls [ 2 ] [ 0 ] . pathname ) . toBe ( "/page" ) ; // restore
66
123
} ) ;
124
+
125
+ it ( "fails gracefully if sessionStorage is not available" , ( ) => {
126
+ const consoleWarnMock = jest
127
+ . spyOn ( console , "warn" )
128
+ . mockImplementation ( ( ) => { } ) ;
129
+
130
+ let testWindow = getWindowImpl ( "/base" ) ;
131
+ const mockScroll = jest . fn ( ) ;
132
+ window . scrollTo = mockScroll ;
133
+
134
+ jest . spyOn ( window , "sessionStorage" , "get" ) . mockImplementation ( ( ) => {
135
+ throw new Error ( "denied" ) ;
136
+ } ) ;
137
+
138
+ let router = createBrowserRouter (
139
+ [
140
+ {
141
+ path : "/" ,
142
+ Component ( ) {
143
+ return (
144
+ < >
145
+ < Outlet />
146
+ < ScrollRestoration
147
+ getKey = { ( location ) => "test2-" + location . pathname }
148
+ />
149
+ </ >
150
+ ) ;
151
+ } ,
152
+ children : testPages ,
153
+ } ,
154
+ ] ,
155
+ { basename : "/base" , window : testWindow }
156
+ ) ;
157
+ let { container } = render ( < RouterProvider router = { router } /> ) ;
158
+
159
+ expect ( getHtml ( container ) ) . toMatch ( "On page 1" ) ;
160
+
161
+ // simulate scrolling
162
+ Object . defineProperty ( window , "scrollY" , { writable : true , value : 100 } ) ;
163
+
164
+ // leave page
165
+ window . dispatchEvent ( new Event ( "pagehide" ) ) ;
166
+ fireEvent . click ( screen . getByText ( "Go to page 2" ) ) ;
167
+ expect ( getHtml ( container ) ) . toMatch ( "On page 2" ) ;
168
+
169
+ // return to page
170
+ window . dispatchEvent ( new Event ( "pagehide" ) ) ;
171
+ fireEvent . click ( screen . getByText ( "Go to page 1" ) ) ;
172
+
173
+ expect ( getHtml ( container ) ) . toMatch ( "On page 1" ) ;
174
+
175
+ // check scroll activity
176
+ expect ( mockScroll . mock . calls ) . toEqual ( [
177
+ [ 0 , 0 ] ,
178
+ [ 0 , 0 ] ,
179
+ [ 0 , 100 ] , // restored (still possible because the user hasn't left the page)
180
+ ] ) ;
181
+
182
+ expect ( consoleWarnMock ) . toHaveBeenCalledWith (
183
+ expect . stringContaining (
184
+ "Failed to save scroll positions in sessionStorage"
185
+ )
186
+ ) ;
187
+
188
+ consoleWarnMock . mockRestore ( ) ;
189
+ } ) ;
67
190
} ) ;
68
191
192
+ const testPages = [
193
+ {
194
+ index : true ,
195
+ Component ( ) {
196
+ return (
197
+ < p >
198
+ On page 1< br />
199
+ < Link to = "/page" > Go to page 2</ Link >
200
+ </ p >
201
+ ) ;
202
+ } ,
203
+ } ,
204
+ {
205
+ path : "page" ,
206
+ Component ( ) {
207
+ return (
208
+ < p >
209
+ On page 2< br />
210
+ < Link to = "/" > Go to page 1</ Link >
211
+ </ p >
212
+ ) ;
213
+ } ,
214
+ } ,
215
+ ] ;
216
+
69
217
function getWindowImpl ( initialUrl : string ) : Window {
70
218
// Need to use our own custom DOM in order to get a working history
71
219
const dom = new JSDOM ( `<!DOCTYPE html>` , { url : "http://localhost/" } ) ;
0 commit comments