diff --git a/package.json b/package.json index 7deed9f8e0..bd6000ad60 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,8 @@ "inquirer": "6.2.2", "jest": "24.7.1", "jest-diff": "24.7.0", + "jest-environment-jsdom": "24.7.1", + "jest-environment-jsdom-global": "1.2.0", "jest-watch-typeahead": "0.3.0", "jsdom-global": "3.0.2", "mversion": "1.13.0", diff --git a/scripts/jest/config.js b/scripts/jest/config.js index 70028cd63c..0cf1aaaf6d 100644 --- a/scripts/jest/config.js +++ b/scripts/jest/config.js @@ -2,6 +2,7 @@ module.exports = { rootDir: process.cwd(), + testEnvironment: 'jest-environment-jsdom-global', testPathIgnorePatterns: [ '/node_modules/', '/dist*', diff --git a/src/lib/__tests__/RoutingManager-test.js b/src/lib/__tests__/RoutingManager-test.js index 42345286de..0b6aa27b21 100644 --- a/src/lib/__tests__/RoutingManager-test.js +++ b/src/lib/__tests__/RoutingManager-test.js @@ -1,5 +1,7 @@ +/* globals jsdom */ import instantsearch from '../main'; import RoutingManager from '../RoutingManager'; +import historyRouter from '../routers/history'; const runAllMicroTasks = () => new Promise(setImmediate); @@ -545,4 +547,42 @@ describe('RoutingManager', () => { }); }); }); + + describe('windowTitle', () => { + test('should update the window title with URL query params on first render', async () => { + jsdom.reconfigure({ + url: 'https://website.com/?query=query', + }); + + const setWindowTitle = jest.spyOn(window.document, 'title', 'set'); + const searchClient = createFakeSearchClient(); + const stateMapping = createFakeStateMapping(); + const router = historyRouter({ + windowTitle(routeState) { + return `Searching for "${routeState.query}"`; + }, + }); + + const search = instantsearch({ + indexName: 'instant_search', + searchClient, + routing: { + router, + stateMapping, + }, + }); + + const fakeSearchBox = createFakeSearchBox(); + + search.addWidget(fakeSearchBox); + search.start(); + + await runAllMicroTasks(); + + expect(setWindowTitle).toHaveBeenCalledTimes(1); + expect(setWindowTitle).toHaveBeenLastCalledWith('Searching for "query"'); + + setWindowTitle.mockRestore(); + }); + }); }); diff --git a/src/lib/routers/history.js b/src/lib/routers/history.js index 34d3f91699..d81d7ceed5 100644 --- a/src/lib/routers/history.js +++ b/src/lib/routers/history.js @@ -15,6 +15,12 @@ function defaultParseURL({ qsModule, location }) { return qsModule.parse(location.search.slice(1)); } +function setWindowTitle(title) { + if (title) { + window.document.title = title; + } +} + class BrowserHistory { /** * Initializes a new storage provider that will sync the search state in the URL @@ -44,6 +50,10 @@ class BrowserHistory { this.writeDelay = writeDelay; this._createURL = createURL; this.parseURL = parseURL; + + const title = this.windowTitle && this.windowTitle(this.read()); + + setWindowTitle(title); } /** @@ -60,7 +70,8 @@ class BrowserHistory { } this.writeTimer = setTimeout(() => { - if (title) window.document.title = title; + setWindowTitle(title); + window.history.pushState(routeState, title || '', url); this.writeTimer = undefined; }, this.writeDelay); diff --git a/yarn.lock b/yarn.lock index 382c2ed674..eb56e8501c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7256,7 +7256,12 @@ jest-each@^24.7.1: jest-util "^24.7.1" pretty-format "^24.7.0" -jest-environment-jsdom@^24.7.1: +jest-environment-jsdom-global@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom-global/-/jest-environment-jsdom-global-1.2.0.tgz#dd5b16fe0a0566ee40010d8632be5adf5a5ccb65" + integrity sha512-41cDl0OxzmFY/cnW0COUN+lnt2N2ks1r3V4fAKOnlZ9xIrGy0PNPan+Bz8HP+uQy/8bGV6T7J4Oi7X+h43It6g== + +jest-environment-jsdom@24.7.1, jest-environment-jsdom@^24.7.1: version "24.7.1" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz#a40e004b4458ebeb8a98082df135fd501b9fbbd6" integrity sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==