diff --git a/packages/kit/src/runtime/client/router.js b/packages/kit/src/runtime/client/router.js index 0d544d676c15..8716ff022395 100644 --- a/packages/kit/src/runtime/client/router.js +++ b/packages/kit/src/runtime/client/router.js @@ -371,7 +371,7 @@ export class Router { if (incorrect) { info.path = has_trailing_slash ? info.path.slice(0, -1) : info.path + '/'; - history.replaceState({}, '', `${this.base}${info.path}${location.search}`); + history.replaceState(history.state || {}, '', `${this.base}${info.path}${location.search}`); } } diff --git a/packages/kit/test/apps/basics/src/routes/routing/_tests.js b/packages/kit/test/apps/basics/src/routes/routing/_tests.js index 76ba9e711ee3..aa6535f7b932 100644 --- a/packages/kit/test/apps/basics/src/routes/routing/_tests.js +++ b/packages/kit/test/apps/basics/src/routes/routing/_tests.js @@ -230,6 +230,113 @@ export default function (test, is_dev) { assert.equal(page.url(), 'https://www.google.com/'); }); + + test('history index gets set on first render', '/routing/history/a', async ({ js, page }) => { + if (js) { + const state = await page.evaluate('history.state'); + assert.equal(state?.['sveltekit:index'], 0); + } + }); + + test('history index increases after navigating by clicking a link', '/routing/history/a', async ({ js, page, clicknav }) => { + if (js) { + await clicknav('[href="/routing/history/b"]'); + const state = await page.evaluate('history.state'); + assert.equal(state?.['sveltekit:index'], 1); + } + }); + + test('history index increases after navigating by using goto', '/routing/history/a', async ({ js, app, base, page }) => { + if (js) { + await app.goto(base + '/routing/history/b'); + const state = await page.evaluate('history.state'); + assert.equal(state?.['sveltekit:index'], 1); + } + }); + + test('history index stays after navigating by using goto with replaceState', '/routing/history/a', async ({ js, app, base, page }) => { + if (js) { + await app.goto(base + '/routing/history/b', { replaceState: true }); + const state = await page.evaluate('history.state'); + assert.equal(state?.['sveltekit:index'], 0); + } + }); + + test('history index stays after fixing tralingSlash', '/routing/history/a', async ({ js, app, base, page }) => { + if (js) { + await app.goto(base + '/routing/history/b/'); + const state = await page.evaluate('history.state'); + assert.equal(state?.['sveltekit:index'], 1); + } + }) + + test('history index decreases after navigating back', '/routing/history/a', async ({ js, clicknav, app, base, page }) => { + if (js) { + await clicknav('[href="/routing/history/b"]'); + await app.goto(base + '/routing/history/c'); + await page.goBack(); + const state1 = await page.evaluate('history.state'); + assert.equal(state1?.['sveltekit:index'], 1); + await clicknav('button'); + const state2 = await page.evaluate('history.state'); + assert.equal(state2?.['sveltekit:index'], 0); + await clicknav('[href="/routing/history/b"]'); + const state3 = await page.evaluate('history.state'); + assert.equal(state3?.['sveltekit:index'], 1); + } + }); + + test('history index survives a reload', '/routing/history/a', async ({ js, clicknav, app, base, page }) => { + if (js) { + await clicknav('[href="/routing/history/b"]'); + await page.reload({ waitUntil: 'networkidle' }); + await app.goto(base + '/routing/history/c'); + const state = await page.evaluate('history.state'); + assert.equal(state?.['sveltekit:index'], 2); + } + }); + + test('onBeforeNavigate can prevent navigation by clicking a link', '/routing/history/a', async ({ js, clicknav, page, app, base }) => { + if (js) { + await app.goto(base + '/routing/history/prevent-navigation'); + + try { + await clicknav('[href="/routing/history/b"]'); + assert.unreachable('should have thrown'); + } catch (err) { + assert.instance(err, Error); + assert.match(err.message, 'Timed out'); + } + + const state = await page.evaluate('history.state'); + assert.equal(state?.['sveltekit:index'], 1); + assert.equal(page.url(), base + '/routing/history/prevent-navigation'); + assert.equal(await page.innerHTML('pre'), 'true', 'onBeforeNavigate not triggered'); + } + }); + + test('onBeforeNavigate can prevent navigation by using goto', '/routing/history/a', async ({ js, page, app, base }) => { + if (js) { + await app.goto(base + '/routing/history/prevent-navigation-promise'); + await app.goto(base + '/routing/history/b'); + const state = await page.evaluate('history.state'); + assert.equal(state?.['sveltekit:index'], 1); + assert.equal(page.url(), base + '/routing/history/prevent-navigation-promise'); + assert.equal(await page.innerHTML('pre'), 'true', 'onBeforeNavigate not triggered'); + } + }); + + test('onBeforeNavigate can prevent navigation using the browser controls', '/routing/history/a', async ({ js, page, app, base }) => { + if (js) { + await app.goto(base + '/routing/history/prevent-navigation'); + await page.goBack(); + const state = await page.evaluate('history.state'); + assert.equal(state?.['sveltekit:index'], 1); + assert.equal(page.url(), base + '/routing/history/prevent-navigation'); + assert.equal(await page.innerHTML('pre'), 'true', 'onBeforeNavigate not triggered'); + } + }); + // skipping this test because it causes a bunch of failures locally test.skip('watch new route in dev', '/routing', async ({ page, base, js, watcher }) => { if (!is_dev || js) { diff --git a/packages/kit/test/apps/basics/src/routes/routing/history/a.svelte b/packages/kit/test/apps/basics/src/routes/routing/history/a.svelte new file mode 100644 index 000000000000..b666e502c7bc --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/history/a.svelte @@ -0,0 +1,2 @@ +

a

+b diff --git a/packages/kit/test/apps/basics/src/routes/routing/history/b.svelte b/packages/kit/test/apps/basics/src/routes/routing/history/b.svelte new file mode 100644 index 000000000000..2a2e83150868 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/history/b.svelte @@ -0,0 +1,2 @@ +

b

+ diff --git a/packages/kit/test/apps/basics/src/routes/routing/history/c.svelte b/packages/kit/test/apps/basics/src/routes/routing/history/c.svelte new file mode 100644 index 000000000000..9bf75e6d887e --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/history/c.svelte @@ -0,0 +1,2 @@ +

c

+ diff --git a/packages/kit/test/apps/basics/src/routes/routing/history/prevent-navigation-promise.svelte b/packages/kit/test/apps/basics/src/routes/routing/history/prevent-navigation-promise.svelte new file mode 100644 index 000000000000..ea2aaf745698 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/history/prevent-navigation-promise.svelte @@ -0,0 +1,13 @@ + + +

prevent navigation promise

+b +
{triggered}
diff --git a/packages/kit/test/apps/basics/src/routes/routing/history/prevent-navigation.svelte b/packages/kit/test/apps/basics/src/routes/routing/history/prevent-navigation.svelte new file mode 100644 index 000000000000..ef218b321d8e --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/routing/history/prevent-navigation.svelte @@ -0,0 +1,13 @@ + + +

prevent navigation

+b +
{triggered}
diff --git a/packages/kit/test/test.js b/packages/kit/test/test.js index dba8011f2984..6b65261bb96a 100644 --- a/packages/kit/test/test.js +++ b/packages/kit/test/test.js @@ -102,9 +102,10 @@ async function setup({ port }) { app: { /** * @param {string} url + * @param {{ replaceState?: boolean; noScroll?: boolean }} opts * @returns {Promise} */ - goto: (url) => pages.js.evaluate((url) => goto(url), url), + goto: (url, opts = {}) => pages.js.evaluate(({ url, opts }) => goto(url, opts), { url, opts }), /** * @param {string} url