Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
import { generateUrl } from '@nextcloud/router'
import hljs from 'highlight.js'
import { createApp, h } from 'vue'
import { createRouter, createWebHashHistory } from 'vue-router'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
import AuditTrailWidget from './components/widgets/AuditTrailWidget.vue'
import ThemePreviewWidget from './components/widgets/ThemePreviewWidget.vue'
Expand Down Expand Up @@ -284,8 +284,33 @@ function routesFromManifest(manifest) {
return routes
}

/**
* The router base for THIS page load.
*
* ⚠️ `generateUrl('/apps/opencatalogi')` alone is not enough. Nextcloud serves
* the app under BOTH `/apps/opencatalogi/...` and
* `/index.php/apps/opencatalogi/...`, but `generateUrl()` returns only the form
* the instance is configured for. A visitor arriving on the other form — a
* bookmark, an emailed deep link, an integration that hardcodes `/index.php` —
* falls outside the router base, vue-router cannot resolve the path, and the
* catch-all redirects to `/`. They land on the dashboard with no error: the
* deep link is silently swallowed.
*
* Hash routing never had this, because the route travelled in the fragment and
* the path prefix was irrelevant. This app's own e2e suite uses BOTH spellings
* — `/index.php/apps/opencatalogi` in `_nav.ts` and the visual spec,
* `/apps/opencatalogi` in `docs-screenshots.spec.ts` — so either would break
* without this.
*
* @return {string} The base path vue-router should strip from the URL.
*/
function routerBase() {
const match = window.location.pathname.match(/^(.*\/apps\/opencatalogi)(?:\/|$)/)
return match ? match[1] : generateUrl('/apps/opencatalogi')
}

const router = createRouter({
history: createWebHashHistory(generateUrl('/apps/opencatalogi')),
history: createWebHistory(routerBase()),
routes: routesFromManifest(resolvedManifest),
})

Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/spec-coverage/_nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export function fatalErrors(errors: string[]): string[] {
* @param route The in-app route, leading slash included (e.g. '/glossary').
*/
export async function navToRoute(page: Page, route: string): Promise<void> {
await page.goto(`${APP}/#${route}`, { waitUntil: 'domcontentloaded' })
await page.goto(`${APP}${route}`, { waitUntil: 'domcontentloaded' })
await page.waitForTimeout(1500)
await dismissOverlays(page)
await expect(page.locator('[data-testid="cn-nav"]').first()).toBeVisible({
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/spec-coverage/catalog-detail-page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ test.describe('catalog-detail-page', () => {
})
expect(id, 'a catalog id must be resolvable from the list').toBeTruthy()

await page.goto(`${APP}/#/catalogi/${id}`, {
await page.goto(`${APP}/catalogi/${id}`, {
waitUntil: 'domcontentloaded',
})
await page.waitForTimeout(1500)
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/spec-coverage/gate19.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ async function openIndexRoute(page: Page, route: string): Promise<void> {
* @param route The in-app route (e.g. '/catalogi/123').
*/
async function gotoHash(page: Page, route: string): Promise<void> {
await page.goto(`${APP}/#${route}`, { waitUntil: 'domcontentloaded' })
await page.goto(`${APP}${route}`, { waitUntil: 'domcontentloaded' })
await page.waitForTimeout(1500)
await dismissOverlays(page)
}
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/spec-coverage/page-components.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ async function gotoHash(
page: import('@playwright/test').Page,
route: string,
): Promise<void> {
await page.goto(`${APP}/#${route}`, { waitUntil: 'domcontentloaded' })
await page.goto(`${APP}${route}`, { waitUntil: 'domcontentloaded' })
await page.waitForTimeout(1500)
await dismissOverlays(page)
}
Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/spec-coverage/usage-analytics-page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ test.describe('usage-analytics', () => {

// The Publications index for the catalog (hash route — path-form
// gotos boot the Dashboard in this hash-mode SPA).
await page.goto(`${APP}/#/publications/${slug}`, {
await page.goto(`${APP}/publications/${slug}`, {
waitUntil: 'domcontentloaded',
})
await page.waitForTimeout(1500)
Expand All @@ -163,7 +163,7 @@ test.describe('usage-analytics', () => {
).toBeVisible({ timeout: 15000 })

// The publication detail page for a real publication.
await page.goto(`${APP}/#/publications/${slug}/${pubId}`, {
await page.goto(`${APP}/publications/${slug}/${pubId}`, {
waitUntil: 'domcontentloaded',
})
await page.waitForTimeout(1500)
Expand Down Expand Up @@ -208,7 +208,7 @@ test.describe('usage-analytics', () => {
const slug = await resolveCatalogSlug(request)
const pubId = await resolvePublicationId(request)
await bootApp(page)
await page.goto(`${APP}/#/publications/${slug}/${pubId}`, {
await page.goto(`${APP}/publications/${slug}/${pubId}`, {
waitUntil: 'domcontentloaded',
})
await page.waitForTimeout(1500)
Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/visual/opencatalogi.visual.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const APP = '/index.php/apps/opencatalogi'

test.describe('OpenCatalogi — visual baselines', () => {
test('dashboard', async ({ page }) => {
await shootSurface(page, `${APP}/#/`, 'dashboard.png')
await shootSurface(page, `${APP}/`, 'dashboard.png')
})

test('publications list', async ({ page, request }) => {
Expand All @@ -54,10 +54,10 @@ test.describe('OpenCatalogi — visual baselines', () => {

// Boot the SPA, then take the in-app hash route (path-form gotos boot
// the Dashboard in this hash-mode SPA; see tests/e2e/spec-coverage/_nav.ts).
await page.goto(`${APP}/#/`, { waitUntil: 'domcontentloaded' })
await page.goto(`${APP}/`, { waitUntil: 'domcontentloaded' })
await dismissSupportDialog(page)
await waitForContentReady(page)
await page.goto(`${APP}/#/publications/${slug}`, {
await page.goto(`${APP}/publications/${slug}`, {
waitUntil: 'domcontentloaded',
})
await page.waitForTimeout(1500)
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/workflows/federation-search.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ test.describe('Federation search page', () => {
}
})

await page.goto(`${APP}/#/search`, { waitUntil: 'domcontentloaded' })
await page.goto(`${APP}/search`, { waitUntil: 'domcontentloaded' })
await dismissSupportDialog(page)

// The page must actually mount a search surface. Asserting on the
Expand Down Expand Up @@ -139,7 +139,7 @@ test.describe('Federation search page', () => {
}
})

await page.goto(`${APP}/#/search`, { waitUntil: 'domcontentloaded' })
await page.goto(`${APP}/search`, { waitUntil: 'domcontentloaded' })
await dismissSupportDialog(page)

const searchInput = appSearchBox(page)
Expand Down
Loading