From cd14c6ecbcdd17bcfad399481aeca3c7f0968ba1 Mon Sep 17 00:00:00 2001 From: Paulina Sedlak-Jakubowska <psedlak-jakubowska@gitlab.com> Date: Tue, 11 Feb 2025 19:44:58 +0000 Subject: [PATCH] WIP: Update repository router to be compatible with Vue3 mode --- .../lib/utils/vue3compat/vue_router.js | 10 ++++++++ app/assets/javascripts/repository/router.js | 22 +++++++++++----- scripts/frontend/quarantined_vue3_specs.txt | 2 -- spec/frontend/repository/router_spec.js | 25 +++++++++---------- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/lib/utils/vue3compat/vue_router.js b/app/assets/javascripts/lib/utils/vue3compat/vue_router.js index f3d5d0417161e..491e1706ce31e 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 5be77e391bab6..da19730176105 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/scripts/frontend/quarantined_vue3_specs.txt b/scripts/frontend/quarantined_vue3_specs.txt index d66a8cc84fb15..71447f0f74e31 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 diff --git a/spec/frontend/repository/router_spec.js b/spec/frontend/repository/router_spec.js index 3f822db601f5d..de16e45d141a1 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