From 878dda1d370c6e60e76488f70a225c826aa84ed2 Mon Sep 17 00:00:00 2001 From: Thomas Durieux <5577568+tdurieux@users.noreply.github.com> Date: Mon, 14 Sep 2026 14:32:00 +0000 Subject: [PATCH] fix: restore legacy hashbang repository links --- public/script/main.js | 5 +++++ test/vue-ui.test.js | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/public/script/main.js b/public/script/main.js index 24db02e..89a1c6b 100644 --- a/public/script/main.js +++ b/public/script/main.js @@ -30,6 +30,11 @@ export function safeUrl(value) { } export function mountApplication(target = "#app", options = {}) { + // AngularJS links stored the repository route in the fragment. Normalize it + // before web history reads the URL, replacing the entry so Back still works. + if (!options.history && window.location.pathname === "/" && /^#!\/r\/[^/?#]+/.test(window.location.hash)) { + window.history.replaceState(window.history.state, "", window.location.hash.slice(2)); + } const events = new Map(); const http = createHttp(options.fetch); let root; diff --git a/test/vue-ui.test.js b/test/vue-ui.test.js index 045b708..ca81797 100644 --- a/test/vue-ui.test.js +++ b/test/vue-ui.test.js @@ -75,6 +75,31 @@ describe("Vue 3 UI", function () { let ui; afterEach(() => ui?.close()); + for (const path of [ + "/r/submission-artifact-604F/", + "/r/submission-artifact-604F", + "/r/submission-artifact-604F/src/hello%20world.js?raw=1&value=a%2Fb#L12", + ]) { + it(`opens a legacy hashbang repository link at ${path}`, async function () { + ui = await browser("/#!" + path); + expect(ui.app.router.currentRoute.value.fullPath).to.equal(path); + expect(ui.app.router.currentRoute.value.params.repoId).to.equal("submission-artifact-604F"); + expect(ui.window.location.href).to.equal("http://localhost" + path); + expect(ui.window.history.length).to.equal(1); + expect(ui.requests.some(request => request.url.pathname.startsWith("/api/repo/submission-artifact-604F/"))).to.equal(true); + expect(ui.errors).to.deep.equal([]); + }); + } + + for (const path of ["/r/test/#L12", "/#ordinary-anchor", "/#!//example.com/r/test/"]) { + it(`leaves a nonlegacy URL unchanged: ${path}`, async function () { + ui = await browser(path); + expect(ui.window.location.href).to.equal("http://localhost" + path); + expect(ui.app.router.currentRoute.value.fullPath).to.equal(path); + expect(ui.errors).to.deep.equal([]); + }); + } + it("renders every public and administrative route", async function () { ui = await browser(); for (const route of ["/faq", "/anonymize", "/gist-anonymize", "/pull-request-anonymize", "/status/test", "/404", "/r/test/", "/repository/test/", "/pr/test/", "/gist/test/"]) {