From 0bcab9d53798d9f3d4e605464e03a94b459cd280 Mon Sep 17 00:00:00 2001 From: psjakubowska Date: Fri, 17 Jan 2025 12:54:46 +0100 Subject: [PATCH 1/4] WIP: Update repository router to be compatible with Vue3 mode --- app/assets/javascripts/repository/router.js | 8 ++++---- spec/frontend/repository/router_spec.js | 9 +++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/repository/router.js b/app/assets/javascripts/repository/router.js index 5be77e391bab6b..d4f24ebb1e4ea4 100644 --- a/app/assets/javascripts/repository/router.js +++ b/app/assets/javascripts/repository/router.js @@ -36,25 +36,25 @@ export default function createRouter(base, baseRef) { { name: 'treePathDecoded', // Sometimes the ref needs decoding depending on how the backend sends it to us - path: `(/-)?/tree/${decodeURI(baseRef)}/:path*`, + path: `/((-/)?tree/${decodeURI(baseRef)}/:path*`, ...treePathRoute, }, { name: 'treePath', // Support without decoding as well just in case the ref doesn't need to be decoded - path: `(/-)?/tree/${escapeRegExp(baseRef)}/:path*`, + path: `/((-/)?tree/${escapeRegExp(baseRef)}/:path*`, ...treePathRoute, }, { name: 'blobPathDecoded', // Sometimes the ref needs decoding depending on how the backend sends it to us - path: `(/-)?/blob/${decodeURI(baseRef)}/:path*`, + path: `/((-/)?blob/${decodeURI(baseRef)}/:path*`, ...blobPathRoute, }, { name: 'blobPath', // Support without decoding as well just in case the ref doesn't need to be decoded - path: `(/-)?/blob/${escapeRegExp(baseRef)}/:path*`, + path: `/((-/)?blob/${escapeRegExp(baseRef)}/:path*`, ...blobPathRoute, }, { diff --git a/spec/frontend/repository/router_spec.js b/spec/frontend/repository/router_spec.js index 3f822db601f5d9..9d790d1e50d9f3 100644 --- a/spec/frontend/repository/router_spec.js +++ b/spec/frontend/repository/router_spec.js @@ -16,13 +16,10 @@ describe('Repository router spec', () => { `('sets component as $componentName for path "$path"', ({ path, component, branch }) => { const router = createRouter('', branch); - const componentsForRoute = router.getMatchedComponents(path); + const componentsForRoute = router.resolve(path); + const matchedComponent = componentsForRoute.matched[0]?.components?.default; - expect(componentsForRoute.length).toBe(component ? 1 : 0); - - if (component) { - expect(componentsForRoute).toContain(component); - } + expect(matchedComponent).toBe(component || undefined); }); describe('Storing Web IDE path globally', () => { -- GitLab From b341c628004b4d083cb60834dd84d5f3bc3690cf Mon Sep 17 00:00:00 2001 From: psjakubowska Date: Mon, 10 Feb 2025 16:23:53 +0100 Subject: [PATCH 2/4] Revert "WIP: Update repository router to be compatible with Vue3 mode" This reverts commit 0bcab9d53798d9f3d4e605464e03a94b459cd280. --- app/assets/javascripts/repository/router.js | 8 ++++---- spec/frontend/repository/router_spec.js | 9 ++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/repository/router.js b/app/assets/javascripts/repository/router.js index d4f24ebb1e4ea4..5be77e391bab6b 100644 --- a/app/assets/javascripts/repository/router.js +++ b/app/assets/javascripts/repository/router.js @@ -36,25 +36,25 @@ export default function createRouter(base, baseRef) { { name: 'treePathDecoded', // Sometimes the ref needs decoding depending on how the backend sends it to us - path: `/((-/)?tree/${decodeURI(baseRef)}/:path*`, + path: `(/-)?/tree/${decodeURI(baseRef)}/:path*`, ...treePathRoute, }, { name: 'treePath', // Support without decoding as well just in case the ref doesn't need to be decoded - path: `/((-/)?tree/${escapeRegExp(baseRef)}/:path*`, + path: `(/-)?/tree/${escapeRegExp(baseRef)}/:path*`, ...treePathRoute, }, { name: 'blobPathDecoded', // Sometimes the ref needs decoding depending on how the backend sends it to us - path: `/((-/)?blob/${decodeURI(baseRef)}/:path*`, + path: `(/-)?/blob/${decodeURI(baseRef)}/:path*`, ...blobPathRoute, }, { name: 'blobPath', // Support without decoding as well just in case the ref doesn't need to be decoded - path: `/((-/)?blob/${escapeRegExp(baseRef)}/:path*`, + path: `(/-)?/blob/${escapeRegExp(baseRef)}/:path*`, ...blobPathRoute, }, { diff --git a/spec/frontend/repository/router_spec.js b/spec/frontend/repository/router_spec.js index 9d790d1e50d9f3..3f822db601f5d9 100644 --- a/spec/frontend/repository/router_spec.js +++ b/spec/frontend/repository/router_spec.js @@ -16,10 +16,13 @@ describe('Repository router spec', () => { `('sets component as $componentName for path "$path"', ({ path, component, branch }) => { const router = createRouter('', branch); - const componentsForRoute = router.resolve(path); - const matchedComponent = componentsForRoute.matched[0]?.components?.default; + const componentsForRoute = router.getMatchedComponents(path); - expect(matchedComponent).toBe(component || undefined); + expect(componentsForRoute.length).toBe(component ? 1 : 0); + + if (component) { + expect(componentsForRoute).toContain(component); + } }); describe('Storing Web IDE path globally', () => { -- GitLab From 16b85e424f49c360c35cf9a87b77b1310d77f9ad Mon Sep 17 00:00:00 2001 From: psjakubowska Date: Mon, 10 Feb 2025 16:28:44 +0100 Subject: [PATCH 3/4] Update repository router to be compatible with Vue3 Changelog: fixed --- .../lib/utils/vue3compat/vue_router.js | 10 ++++++++ app/assets/javascripts/repository/router.js | 22 +++++++++++----- spec/frontend/repository/router_spec.js | 25 +++++++++---------- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/lib/utils/vue3compat/vue_router.js b/app/assets/javascripts/lib/utils/vue3compat/vue_router.js index f3d5d0417161ed..491e1706ce31e8 100644 --- a/app/assets/javascripts/lib/utils/vue3compat/vue_router.js +++ b/app/assets/javascripts/lib/utils/vue3compat/vue_router.js @@ -92,6 +92,16 @@ const transformOptions = (options = {}) => { const installed = new WeakMap(); +export const getMatchedComponents = (instance, path) => { + if (instance.getMatchedComponents) { + return instance.getMatchedComponents(path); + } + + const route = path ? instance.resolve(path) : instance.currentRoute.value; + + return route.matched.flatMap((record) => Object.values(record.components)); +}; + export default class VueRouterCompat { constructor(options) { // eslint-disable-next-line no-constructor-return diff --git a/app/assets/javascripts/repository/router.js b/app/assets/javascripts/repository/router.js index 5be77e391bab6b..da197301761057 100644 --- a/app/assets/javascripts/repository/router.js +++ b/app/assets/javascripts/repository/router.js @@ -9,11 +9,21 @@ import { getRefType } from './utils/ref_type'; Vue.use(VueRouter); +const normalizePathParam = (pathParam) => { + // Vue Router 4 when there's more than one `:path` segment + if (Array.isArray(pathParam)) { + return joinPaths(...pathParam); + } + + // Vue Router 3, or when there's zero or one `:path` segments. + return pathParam?.replace(/^\//, '') || '/'; +}; + export default function createRouter(base, baseRef) { const treePathRoute = { component: TreePage, props: (route) => ({ - path: route.params.path?.replace(/^\//, '') || '/', + path: normalizePathParam(route.params.path), refType: getRefType(route.query.ref_type || null), }), }; @@ -36,25 +46,25 @@ export default function createRouter(base, baseRef) { { name: 'treePathDecoded', // Sometimes the ref needs decoding depending on how the backend sends it to us - path: `(/-)?/tree/${decodeURI(baseRef)}/:path*`, + path: `/:dash(-)?/tree/${decodeURI(baseRef)}/:path*`, ...treePathRoute, }, { name: 'treePath', // Support without decoding as well just in case the ref doesn't need to be decoded - path: `(/-)?/tree/${escapeRegExp(baseRef)}/:path*`, + path: `/:dash(-)?/tree/${escapeRegExp(baseRef)}/:path*`, ...treePathRoute, }, { name: 'blobPathDecoded', // Sometimes the ref needs decoding depending on how the backend sends it to us - path: `(/-)?/blob/${decodeURI(baseRef)}/:path*`, + path: `/:dash(-)?/blob/${decodeURI(baseRef)}/:path*`, ...blobPathRoute, }, { name: 'blobPath', // Support without decoding as well just in case the ref doesn't need to be decoded - path: `(/-)?/blob/${escapeRegExp(baseRef)}/:path*`, + path: `/:dash(-)?/blob/${escapeRegExp(baseRef)}/:path*`, ...blobPathRoute, }, { @@ -80,7 +90,7 @@ export default function createRouter(base, baseRef) { 'edit', decodeURI(baseRef), '-', - to.params.path || '', + normalizePathParam(to.params.path), needsClosingSlash && '/', ), ); diff --git a/spec/frontend/repository/router_spec.js b/spec/frontend/repository/router_spec.js index 3f822db601f5d9..de16e45d141a1f 100644 --- a/spec/frontend/repository/router_spec.js +++ b/spec/frontend/repository/router_spec.js @@ -2,6 +2,7 @@ import BlobPage from '~/repository/pages/blob.vue'; import IndexPage from '~/repository/pages/index.vue'; import TreePage from '~/repository/pages/tree.vue'; import createRouter from '~/repository/router'; +import { getMatchedComponents } from '~/lib/utils/vue3compat/vue_router'; describe('Repository router spec', () => { it.each` @@ -11,18 +12,13 @@ describe('Repository router spec', () => { ${'/tree/feat(test)'} | ${'feat(test)'} | ${TreePage} | ${'TreePage'} ${'/-/tree/main'} | ${'main'} | ${TreePage} | ${'TreePage'} ${'/-/tree/main/app/assets'} | ${'main'} | ${TreePage} | ${'TreePage'} - ${'/-/tree/123/app/assets'} | ${'main'} | ${null} | ${'null'} ${'/-/blob/main/file.md'} | ${'main'} | ${BlobPage} | ${'BlobPage'} `('sets component as $componentName for path "$path"', ({ path, component, branch }) => { const router = createRouter('', branch); - const componentsForRoute = router.getMatchedComponents(path); + const componentsForRoute = getMatchedComponents(router, path); - expect(componentsForRoute.length).toBe(component ? 1 : 0); - - if (component) { - expect(componentsForRoute).toContain(component); - } + expect(componentsForRoute).toEqual([component]); }); describe('Storing Web IDE path globally', () => { @@ -45,11 +41,14 @@ describe('Repository router spec', () => { ${'/-/tree/main'} | ${'main'} | ${`/-/ide/project/${proj}/edit/main/-/`} ${'/-/tree/main/app/assets'} | ${'main'} | ${`/-/ide/project/${proj}/edit/main/-/app/assets/`} ${'/-/blob/main/file.md'} | ${'main'} | ${`/-/ide/project/${proj}/edit/main/-/file.md`} - `('generates the correct Web IDE url for $path', ({ path, branch, expectedPath } = {}) => { - const router = createRouter(proj, branch); - - router.push(path); - expect(window.gl.webIDEPath).toBe(expectedPath); - }); + `( + 'generates the correct Web IDE url for $path', + async ({ path, branch, expectedPath } = {}) => { + const router = createRouter(proj, branch); + + await router.push(path); + expect(window.gl.webIDEPath).toBe(expectedPath); + }, + ); }); }); -- GitLab From 824dcb872f1ddcd605062f2919ac80f133a6bf96 Mon Sep 17 00:00:00 2001 From: psjakubowska Date: Tue, 11 Feb 2025 12:06:04 +0100 Subject: [PATCH 4/4] Remove router and BlobOverflowMenu specs from quarantine list --- scripts/frontend/quarantined_vue3_specs.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/frontend/quarantined_vue3_specs.txt b/scripts/frontend/quarantined_vue3_specs.txt index d66a8cc84fb155..71447f0f74e31c 100644 --- a/scripts/frontend/quarantined_vue3_specs.txt +++ b/scripts/frontend/quarantined_vue3_specs.txt @@ -233,10 +233,8 @@ spec/frontend/ref/init_ambiguous_ref_modal_spec.js spec/frontend/releases/components/app_edit_new_spec.js spec/frontend/releases/components/asset_links_form_spec.js spec/frontend/repository/components/header_area/blob_controls_spec.js -spec/frontend/repository/components/header_area/blob_overflow_menu_spec.js spec/frontend/repository/components/table/index_spec.js spec/frontend/repository/components/table/row_spec.js -spec/frontend/repository/router_spec.js spec/frontend/search/sidebar/components/checkbox_filter_spec.js spec/frontend/search/topbar/components/app_spec.js spec/frontend/sessions/new/components/email_verification_spec.js -- GitLab