Repository: mapbox/batfish
Pull request #189 (2017-10-09 22:58:48)

[WIP] Fragment links


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index adf19fc..cbbcbed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Changelog
 
+## 0.11.3
+
+- [Fix] Fragments in the URL take precedence over saved scroll positions in the history.
+
 ## 0.11.2
 
 - [Fix] Development server does not quit the process when there are Webpack compilation errors.
diff --git a/examples/fragment-urls/README.md b/examples/fragment-urls/README.md
new file mode 100644
index 0000000..e777071
--- /dev/null
+++ b/examples/fragment-urls/README.md
@@ -0,0 +1,3 @@
+# Fragment URLs
+
+- Demonstrates how fragment URLs work on initial page and dynamic linking, for both the development server and the static build.
diff --git a/examples/fragment-urls/package.json b/examples/fragment-urls/package.json
new file mode 100644
index 0000000..a78255f
--- /dev/null
+++ b/examples/fragment-urls/package.json
@@ -0,0 +1,7 @@
+{
+  "private": true,
+  "scripts": {
+    "batfish": "../../bin/example-batfish"
+  },
+  "dependencies": {}
+}
diff --git a/examples/fragment-urls/src/page-shell.js b/examples/fragment-urls/src/page-shell.js
new file mode 100644
index 0000000..3be2cea
--- /dev/null
+++ b/examples/fragment-urls/src/page-shell.js
@@ -0,0 +1,19 @@
+import React from 'react';
+
+export default class PageShell extends React.Component {
+  render() {
+    return (
+      <div>
+        <ul>
+          <li>
+            <a href="/">one</a>
+          </li>
+          <li>
+            <a href="/two">two</a>
+          </li>
+        </ul>
+        {this.props.children}
+      </div>
+    );
+  }
+}
diff --git a/examples/fragment-urls/src/pages/index.js b/examples/fragment-urls/src/pages/index.js
new file mode 100644
index 0000000..c05f422
--- /dev/null
+++ b/examples/fragment-urls/src/pages/index.js
@@ -0,0 +1,181 @@
+import React from 'react';
+import PageShell from '../page-shell';
+
+export default class One extends React.Component {
+  render() {
+    return (
+      <PageShell>
+        <ul>
+          <li>
+            <a href="#lorem-ipsum">lorem-ipsum</a>
+          </li>
+          <li>
+            <a href="#nam-rhoncus">nam-rhoncus</a>
+          </li>
+          <li>
+            <a href="#integer-ut">integer-ut</a>
+          </li>
+          <li>
+            <a href="#praesent-vel">praesent-vel</a>
+          </li>
+          <li>
+            <a href="#ut-posuere">ut-posuere</a>
+          </li>
+          <li>
+            <a href="#suspendisse-ut">suspendisse-ut</a>
+          </li>
+          <li>
+            <a href="#nam-vel">nam-vel</a>
+          </li>
+          <li>
+            <a href="#ut-venenatis">ut-venenatis</a>
+          </li>
+          <li>
+            <a href="#in-efficitur">in-efficitur</a>
+          </li>
+          <li>
+            <a href="#quisque-cursus">quisque-cursus</a>
+          </li>
+        </ul>
+
+        <h2 id="lorem-ipsum">Lorem ipsum</h2>
+        <p>
+          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla
+          interdum auctor erat rutrum consectetur. Ut sit amet mi pulvinar,
+          mattis ex quis, tincidunt nunc. Mauris quis lacinia quam. Praesent
+          congue neque dui, nec maximus diam ullamcorper vitae. Quisque odio
+          nunc, aliquet at luctus vel, egestas eu felis. Vivamus vitae justo
+          maximus, egestas massa quis, varius nisi. Phasellus bibendum tortor
+          non massa bibendum, sit amet dapibus augue venenatis. In hac habitasse
+          platea dictumst. Suspendisse accumsan, felis eu pharetra dignissim,
+          sem ante tristique augue, ornare tristique risus ex imperdiet velit.
+          Proin nulla neque, luctus eu arcu eget, facilisis consectetur arcu.
+          Proin mollis sem sit amet ante imperdiet elementum. Curabitur in erat
+          tincidunt, pharetra libero sed, vestibulum ipsum. Duis ut consequat
+          libero, eu auctor quam. Maecenas sit amet ex nec orci iaculis
+          pharetra. Praesent non augue metus. Ut rhoncus placerat felis.
+        </p>
+
+        <h2 id="nam-rhoncus">Nam rhoncus</h2>
+        <p>
+          Nam rhoncus metus efficitur nisi pulvinar tincidunt. Aliquam erat
+          volutpat. Ut a elementum ipsum, ac posuere nulla. Nam lobortis ac nisl
+          et varius. Curabitur nec commodo lacus. Proin consectetur posuere
+          velit vel pulvinar. Nullam condimentum eleifend ante, nec egestas eros
+          tristique id. Aliquam at ornare dui. Fusce auctor tellus pharetra
+          suscipit volutpat. In sed enim eu quam condimentum sollicitudin non
+          tristique elit.
+        </p>
+
+        <h2 id="integer-ut">Integer ut</h2>
+        <p>
+          Integer ut justo lectus. Praesent feugiat, erat non pretium luctus,
+          dolor tellus gravida neque, a eleifend mi ex a neque. Cras massa
+          velit, bibendum eget consectetur in, pretium eget libero. Suspendisse
+          leo lorem, eleifend sed sapien sed, ultrices mattis mauris. Sed ac
+          tortor a magna pretium pharetra. Mauris risus dolor, accumsan eget
+          auctor id, porta quis quam. Class aptent taciti sociosqu ad litora
+          torquent per conubia nostra, per inceptos himenaeos. Pellentesque nec
+          mi quis dolor placerat accumsan. Phasellus congue mi sem, ut posuere
+          lacus tempus ut.
+        </p>
+
+        <h2 id="praesent-vel">Praesent vel</h2>
+        <p>
+          Praesent vel nibh eu sapien fermentum dictum id id lectus. Sed risus
+          nulla, tristique a mollis non, tincidunt eget eros. Duis metus dolor,
+          cursus ut blandit a, ullamcorper ut nisl. Aliquam eu ex at nulla
+          blandit vehicula. Quisque quis tellus quis turpis pharetra gravida.
+          Sed eros justo, faucibus quis nunc nec, viverra sagittis felis. Ut
+          pharetra egestas leo sit amet vestibulum. Nunc cursus ex non velit
+          facilisis eleifend. Pellentesque eget orci pretium, pharetra magna
+          efficitur, viverra lorem. Nam ullamcorper libero est, sit amet
+          ultricies purus sagittis ac.
+        </p>
+
+        <h2 id="ut-posuere">Ut posuere</h2>
+        <p>
+          Ut posuere tellus eu est dapibus convallis. Proin posuere finibus arcu
+          sed sagittis. Aenean sed aliquet urna, nec ullamcorper leo. Vestibulum
+          vel nulla dictum, interdum ligula id, egestas eros. Vestibulum
+          porttitor vel sem in ornare. Nam nunc ex, condimentum vitae imperdiet
+          non, dapibus at sapien. Curabitur dictum velit at dolor tincidunt, a
+          auctor tellus placerat. Lorem ipsum dolor sit amet, consectetur
+          adipiscing elit. Maecenas condimentum blandit ipsum, ac mollis arcu
+          euismod ac. Nullam ligula tortor, gravida sit amet rhoncus id, viverra
+          vitae diam. Vivamus interdum leo sed vestibulum egestas. Sed fermentum
+          faucibus metus, sed congue metus pulvinar id.
+        </p>
+
+        <h2 id="suspendisse-ut">Suspendisse ut</h2>
+        <p>
+          Suspendisse ut ligula ut est suscipit imperdiet. Phasellus tempus nec
+          sem ac sodales. Praesent id venenatis ex. Aliquam posuere quam ac
+          cursus consequat. Nulla eu ex scelerisque, blandit velit in, molestie
+          elit. Aenean non justo at quam condimentum consectetur ac id augue.
+          Nulla facilisi. Pellentesque pellentesque auctor erat. Suspendisse
+          interdum pretium diam eget condimentum. Nullam vehicula nunc vel
+          mauris sagittis, a consectetur risus vehicula. Fusce a ullamcorper
+          arcu. Aenean auctor ante euismod orci finibus venenatis. Quisque leo
+          risus, commodo vel nisi eget, ultricies iaculis ex. Duis lacinia
+          euismod molestie. Praesent sit amet magna non odio porta pharetra.
+        </p>
+
+        <h2 id="nam-vel">Nam vel</h2>
+        <p>
+          Nam vel ante at augue aliquam eleifend eget vel leo. Phasellus tempor,
+          nisi sed suscipit sagittis, arcu tellus consectetur lectus, quis
+          ornare massa turpis ut mi. Nulla ultrices sapien nunc, vel vulputate
+          sapien aliquet vel. Ut molestie metus ac dolor eleifend, sit amet
+          lacinia quam gravida. Curabitur a aliquet tellus, at laoreet quam.
+          Integer nec auctor enim. Nulla risus eros, feugiat id ligula at,
+          vulputate scelerisque velit. Duis ut faucibus lacus, vitae fringilla
+          turpis. Proin congue condimentum magna fringilla tincidunt. Cras
+          lacinia, orci ac sollicitudin blandit, nisi erat rhoncus nunc, eu
+          elementum justo nibh vel metus. Vestibulum sagittis tristique laoreet.
+        </p>
+
+        <h2 id="ut-venenatis">Ut venenatis</h2>
+        <p>
+          Ut venenatis, orci a venenatis volutpat, tortor sapien consectetur
+          arcu, sit amet sagittis enim lorem eu libero. Nam eu pellentesque
+          magna. Mauris eget scelerisque arcu, sit amet tristique mauris. Class
+          aptent taciti sociosqu ad litora torquent per conubia nostra, per
+          inceptos himenaeos. Fusce tincidunt enim id faucibus commodo. Aliquam
+          a tellus ut magna mollis lacinia. Sed ut mauris suscipit nunc lacinia
+          euismod a eu ipsum. Nunc id neque justo. Fusce ut justo ac nibh
+          pretium tristique a in erat.
+        </p>
+
+        <h2 id="in-efficitur">In efficitur</h2>
+        <p>
+          In efficitur laoreet dui. Fusce et turpis mi. Cras lacinia euismod est
+          et condimentum. Proin sit amet vestibulum nisi. Nullam vel arcu
+          libero. Proin tempor lacus et metus condimentum hendrerit. Ut
+          consequat, lorem eu aliquam rutrum, enim sem maximus lacus, a
+          pellentesque arcu ex sed purus. Sed finibus dapibus urna non pulvinar.
+          Vestibulum ante ipsum primis in faucibus orci luctus et ultrices
+          posuere cubilia Curae; Ut gravida sem orci, sit amet rutrum sapien
+          dapibus ut. Donec maximus suscipit nisi, ac elementum tortor maximus
+          ut. Vestibulum cursus arcu lacus, rutrum vulputate tortor consectetur
+          vel. Sed id augue maximus orci maximus vehicula cursus eget ligula.
+          Duis purus nisl, dignissim at purus quis, cursus molestie velit.
+          Aenean ultrices metus mi, ut feugiat diam efficitur eget. Duis at
+          tellus vel augue fermentum pretium a sit amet est.
+        </p>
+
+        <h2 id="quisque-cursus">Quisque cursus</h2>
+        <p>
+          Quisque cursus enim et consequat feugiat. Vivamus et ex a orci cursus
+          molestie nec et dui. Donec ac viverra diam, convallis aliquet lorem.
+          Nullam efficitur nibh lorem, ac suscipit felis rhoncus non. In
+          elementum cursus pulvinar. Nullam sollicitudin rhoncus lacus, nec
+          dictum magna pulvinar aliquet. Nullam sit amet congue quam, non
+          ultricies ipsum. Integer nec nulla a felis semper aliquet quis sed
+          metus. Fusce aliquet ac massa non maximus. Suspendisse tincidunt
+          imperdiet pharetra.
+        </p>
+      </PageShell>
+    );
+  }
+}
diff --git a/examples/fragment-urls/src/pages/two.js b/examples/fragment-urls/src/pages/two.js
new file mode 100644
index 0000000..6ba51da
--- /dev/null
+++ b/examples/fragment-urls/src/pages/two.js
@@ -0,0 +1,181 @@
+import React from 'react';
+import PageShell from '../page-shell';
+
+export default class Two extends React.Component {
+  render() {
+    return (
+      <PageShell>
+        <ul>
+          <li>
+            <a href="#two-lorem-ipsum">two-lorem-ipsum</a>
+          </li>
+          <li>
+            <a href="#two-nam-rhoncus">two-nam-rhoncus</a>
+          </li>
+          <li>
+            <a href="#two-integer-ut">two-integer-ut</a>
+          </li>
+          <li>
+            <a href="#two-praesent-vel">two-praesent-vel</a>
+          </li>
+          <li>
+            <a href="#two-ut-posuere">two-ut-posuere</a>
+          </li>
+          <li>
+            <a href="#two-suspendisse-ut">two-suspendisse-ut</a>
+          </li>
+          <li>
+            <a href="#two-nam-vel">two-nam-vel</a>
+          </li>
+          <li>
+            <a href="#two-ut-venenatis">two-ut-venenatis</a>
+          </li>
+          <li>
+            <a href="#two-in-efficitur">two-in-efficitur</a>
+          </li>
+          <li>
+            <a href="#two-quisque-cursus">two-quisque-cursus</a>
+          </li>
+        </ul>
+
+        <h2 id="two-lorem-ipsum">TWO Lorem ipsum</h2>
+        <p>
+          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla
+          interdum auctor erat rutrum consectetur. Ut sit amet mi pulvinar,
+          mattis ex quis, tincidunt nunc. Mauris quis lacinia quam. Praesent
+          congue neque dui, nec maximus diam ullamcorper vitae. Quisque odio
+          nunc, aliquet at luctus vel, egestas eu felis. Vivamus vitae justo
+          maximus, egestas massa quis, varius nisi. Phasellus bibendum tortor
+          non massa bibendum, sit amet dapibus augue venenatis. In hac habitasse
+          platea dictumst. Suspendisse accumsan, felis eu pharetra dignissim,
+          sem ante tristique augue, ornare tristique risus ex imperdiet velit.
+          Proin nulla neque, luctus eu arcu eget, facilisis consectetur arcu.
+          Proin mollis sem sit amet ante imperdiet elementum. Curabitur in erat
+          tincidunt, pharetra libero sed, vestibulum ipsum. Duis ut consequat
+          libero, eu auctor quam. Maecenas sit amet ex nec orci iaculis
+          pharetra. Praesent non augue metus. Ut rhoncus placerat felis.
+        </p>
+
+        <h2 id="two-nam-rhoncus">TWO Nam rhoncus</h2>
+        <p>
+          Nam rhoncus metus efficitur nisi pulvinar tincidunt. Aliquam erat
+          volutpat. Ut a elementum ipsum, ac posuere nulla. Nam lobortis ac nisl
+          et varius. Curabitur nec commodo lacus. Proin consectetur posuere
+          velit vel pulvinar. Nullam condimentum eleifend ante, nec egestas eros
+          tristique id. Aliquam at ornare dui. Fusce auctor tellus pharetra
+          suscipit volutpat. In sed enim eu quam condimentum sollicitudin non
+          tristique elit.
+        </p>
+
+        <h2 id="two-integer-ut">TWO Integer ut</h2>
+        <p>
+          Integer ut justo lectus. Praesent feugiat, erat non pretium luctus,
+          dolor tellus gravida neque, a eleifend mi ex a neque. Cras massa
+          velit, bibendum eget consectetur in, pretium eget libero. Suspendisse
+          leo lorem, eleifend sed sapien sed, ultrices mattis mauris. Sed ac
+          tortor a magna pretium pharetra. Mauris risus dolor, accumsan eget
+          auctor id, porta quis quam. Class aptent taciti sociosqu ad litora
+          torquent per conubia nostra, per inceptos himenaeos. Pellentesque nec
+          mi quis dolor placerat accumsan. Phasellus congue mi sem, ut posuere
+          lacus tempus ut.
+        </p>
+
+        <h2 id="two-praesent-vel">TWO Praesent vel</h2>
+        <p>
+          Praesent vel nibh eu sapien fermentum dictum id id lectus. Sed risus
+          nulla, tristique a mollis non, tincidunt eget eros. Duis metus dolor,
+          cursus ut blandit a, ullamcorper ut nisl. Aliquam eu ex at nulla
+          blandit vehicula. Quisque quis tellus quis turpis pharetra gravida.
+          Sed eros justo, faucibus quis nunc nec, viverra sagittis felis. Ut
+          pharetra egestas leo sit amet vestibulum. Nunc cursus ex non velit
+          facilisis eleifend. Pellentesque eget orci pretium, pharetra magna
+          efficitur, viverra lorem. Nam ullamcorper libero est, sit amet
+          ultricies purus sagittis ac.
+        </p>
+
+        <h2 id="two-ut-posuere">TWO Ut posuere</h2>
+        <p>
+          Ut posuere tellus eu est dapibus convallis. Proin posuere finibus arcu
+          sed sagittis. Aenean sed aliquet urna, nec ullamcorper leo. Vestibulum
+          vel nulla dictum, interdum ligula id, egestas eros. Vestibulum
+          porttitor vel sem in ornare. Nam nunc ex, condimentum vitae imperdiet
+          non, dapibus at sapien. Curabitur dictum velit at dolor tincidunt, a
+          auctor tellus placerat. Lorem ipsum dolor sit amet, consectetur
+          adipiscing elit. Maecenas condimentum blandit ipsum, ac mollis arcu
+          euismod ac. Nullam ligula tortor, gravida sit amet rhoncus id, viverra
+          vitae diam. Vivamus interdum leo sed vestibulum egestas. Sed fermentum
+          faucibus metus, sed congue metus pulvinar id.
+        </p>
+
+        <h2 id="two-suspendisse-ut">TWO Suspendisse ut</h2>
+        <p>
+          Suspendisse ut ligula ut est suscipit imperdiet. Phasellus tempus nec
+          sem ac sodales. Praesent id venenatis ex. Aliquam posuere quam ac
+          cursus consequat. Nulla eu ex scelerisque, blandit velit in, molestie
+          elit. Aenean non justo at quam condimentum consectetur ac id augue.
+          Nulla facilisi. Pellentesque pellentesque auctor erat. Suspendisse
+          interdum pretium diam eget condimentum. Nullam vehicula nunc vel
+          mauris sagittis, a consectetur risus vehicula. Fusce a ullamcorper
+          arcu. Aenean auctor ante euismod orci finibus venenatis. Quisque leo
+          risus, commodo vel nisi eget, ultricies iaculis ex. Duis lacinia
+          euismod molestie. Praesent sit amet magna non odio porta pharetra.
+        </p>
+
+        <h2 id="two-nam-vel">TWO Nam vel</h2>
+        <p>
+          Nam vel ante at augue aliquam eleifend eget vel leo. Phasellus tempor,
+          nisi sed suscipit sagittis, arcu tellus consectetur lectus, quis
+          ornare massa turpis ut mi. Nulla ultrices sapien nunc, vel vulputate
+          sapien aliquet vel. Ut molestie metus ac dolor eleifend, sit amet
+          lacinia quam gravida. Curabitur a aliquet tellus, at laoreet quam.
+          Integer nec auctor enim. Nulla risus eros, feugiat id ligula at,
+          vulputate scelerisque velit. Duis ut faucibus lacus, vitae fringilla
+          turpis. Proin congue condimentum magna fringilla tincidunt. Cras
+          lacinia, orci ac sollicitudin blandit, nisi erat rhoncus nunc, eu
+          elementum justo nibh vel metus. Vestibulum sagittis tristique laoreet.
+        </p>
+
+        <h2 id="two-ut-venenatis">TWO Ut venenatis</h2>
+        <p>
+          Ut venenatis, orci a venenatis volutpat, tortor sapien consectetur
+          arcu, sit amet sagittis enim lorem eu libero. Nam eu pellentesque
+          magna. Mauris eget scelerisque arcu, sit amet tristique mauris. Class
+          aptent taciti sociosqu ad litora torquent per conubia nostra, per
+          inceptos himenaeos. Fusce tincidunt enim id faucibus commodo. Aliquam
+          a tellus ut magna mollis lacinia. Sed ut mauris suscipit nunc lacinia
+          euismod a eu ipsum. Nunc id neque justo. Fusce ut justo ac nibh
+          pretium tristique a in erat.
+        </p>
+
+        <h2 id="two-in-efficitur">TWO In efficitur</h2>
+        <p>
+          In efficitur laoreet dui. Fusce et turpis mi. Cras lacinia euismod est
+          et condimentum. Proin sit amet vestibulum nisi. Nullam vel arcu
+          libero. Proin tempor lacus et metus condimentum hendrerit. Ut
+          consequat, lorem eu aliquam rutrum, enim sem maximus lacus, a
+          pellentesque arcu ex sed purus. Sed finibus dapibus urna non pulvinar.
+          Vestibulum ante ipsum primis in faucibus orci luctus et ultrices
+          posuere cubilia Curae; Ut gravida sem orci, sit amet rutrum sapien
+          dapibus ut. Donec maximus suscipit nisi, ac elementum tortor maximus
+          ut. Vestibulum cursus arcu lacus, rutrum vulputate tortor consectetur
+          vel. Sed id augue maximus orci maximus vehicula cursus eget ligula.
+          Duis purus nisl, dignissim at purus quis, cursus molestie velit.
+          Aenean ultrices metus mi, ut feugiat diam efficitur eget. Duis at
+          tellus vel augue fermentum pretium a sit amet est.
+        </p>
+
+        <h2 id="two-quisque-cursus">TWO Quisque cursus</h2>
+        <p>
+          Quisque cursus enim et consequat feugiat. Vivamus et ex a orci cursus
+          molestie nec et dui. Donec ac viverra diam, convallis aliquet lorem.
+          Nullam efficitur nibh lorem, ac suscipit felis rhoncus non. In
+          elementum cursus pulvinar. Nullam sollicitudin rhoncus lacus, nec
+          dictum magna pulvinar aliquet. Nullam sit amet congue quam, non
+          ultricies ipsum. Integer nec nulla a felis semper aliquet quis sed
+          metus. Fusce aliquet ac massa non maximus. Suspendisse tincidunt
+          imperdiet pharetra.
+        </p>
+      </PageShell>
+    );
+  }
+}
diff --git a/package.json b/package.json
index 2f756d9..4bc37a3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@mapbox/batfish",
-  "version": "0.11.2",
+  "version": "0.11.3",
   "description": "The React-powered static-site generator you didn't know you wanted",
   "main": "dist/node/index.js",
   "bin": {
diff --git a/src/webpack/change-page.js b/src/webpack/change-page.js
index e3b047a..62070f2 100644
--- a/src/webpack/change-page.js
+++ b/src/webpack/change-page.js
@@ -45,10 +45,10 @@ export function changePage(
       setRouterState(nextState, () => {
         if (options.scrollToTop) {
           win.scrollTo(0, 0);
+        } else if (nextLocation.hash) {
+          scrollToFragment();
         } else if (scrollRestorer.getSavedScroll()) {
           scrollRestorer.restoreScroll();
-        } else {
-          scrollToFragment();
         }
         if (onFinish) onFinish();
         _invokeRouteChangeEndCallbacks(nextLocation.pathname);
diff --git a/src/webpack/router.js b/src/webpack/router.js
index ebc5681..831100c 100644
--- a/src/webpack/router.js
+++ b/src/webpack/router.js
@@ -60,17 +60,15 @@ class Router extends React.PureComponent<Props, RouterState> {
 
   componentDidMount() {
     scrollRestorer.start({ autoRestore: false });
-    scrollRestorer.restoreScroll();
 
-    // Only on the dev server do we need to scroll to fragments on the initial
-    // load. With static HTML pages, the browser should take care of this
-    // for us.
-    if (process.env.DEV_SERVER) {
+    const win = getWindow();
+    if (!win.location.hash) {
+      scrollRestorer.restoreScroll();
+    } else {
       scrollToFragment();
     }
 
     routeTo._setRouteToHandler(this.routeTo);
-    const win = getWindow();
     win.addEventListener('popstate', event => {
       event.preventDefault();
       changePage(


Test patch:
diff --git a/test/change-page.test.js b/test/change-page.test.js
index e81ea8b..24c92fa 100644
--- a/test/change-page.test.js
+++ b/test/change-page.test.js
@@ -116,7 +116,7 @@ describe('changePage', () => {
     const wait = new Promise(resolve => {
       setTimeout(resolve, 500);
     });
-    _invokeRouteChangeStartCallbacks.mockReturnValueOnce(wait);
+    _invokeRouteChangeStartCallbacks.mockReturnValue(wait);
     changePage(nextLocation, setRouterState);
     jest.runTimersToTime(400);
     expect(setRouterState).not.toHaveBeenCalled();
@@ -187,7 +187,7 @@ describe('changePage', () => {
   });
 
   test('setRouterState callback restores saved scroll', () => {
-    scrollRestorer.getSavedScroll.mockReturnValueOnce({});
+    scrollRestorer.getSavedScroll.mockReturnValue({});
     return changePage(nextLocation, setRouterState).then(() => {
       expect(scrollRestorer.restoreScroll).not.toHaveBeenCalled();
       const callback = setRouterState.mock.calls[0][1];
@@ -197,8 +197,13 @@ describe('changePage', () => {
     });
   });
 
-  test('setRouterState callback does not restore scroll if none is saved', () => {
+  test('setRouterState scrolls to fragment if there is a hash', () => {
+    nextLocation = {
+      hash: '#foo'
+    };
+    scrollRestorer.getSavedScroll.mockReturnValue({});
     return changePage(nextLocation, setRouterState).then(() => {
+      expect(scrollRestorer.restoreScroll).not.toHaveBeenCalled();
       const callback = setRouterState.mock.calls[0][1];
       callback();
       expect(mockWindow.scrollTo).not.toHaveBeenCalled();
@@ -206,14 +211,14 @@ describe('changePage', () => {
     });
   });
 
-  test('setRouterState callback tries scrolling to fragment if no other scrolling happens', () => {
+  test('setRouterState callback does not restore scroll if none is saved', () => {
+    scrollRestorer.getSavedScroll.mockReturnValue(null);
     return changePage(nextLocation, setRouterState).then(() => {
-      expect(scrollToFragment).not.toHaveBeenCalled();
       const callback = setRouterState.mock.calls[0][1];
       callback();
       expect(mockWindow.scrollTo).not.toHaveBeenCalled();
       expect(scrollRestorer.restoreScroll).not.toHaveBeenCalled();
-      expect(scrollToFragment).toHaveBeenCalled();
+      expect(scrollToFragment).not.toHaveBeenCalled();
     });
   });