Files
LocalAI/core/http/react-ui/e2e/not-found.spec.js
T
mudler-agentandEttore Di Giacinto b9b5434692 test(ui): stub the pin routes in the add-on e2e fixtures (#12882)
test(ui): stub the add-on routes in the add-on e2e fixtures

The add-on owner now runs as its own workload. With no owner running the
server reports the add-ons feature off and answers 5xx on
/api/addons/builtin and /api/addons/pins. The add-on pages are gated on
that flag, and the navigation shell calls both routes, which the browser
logs as failed requests. Specs that draw add-on pages assert no console
errors, so they failed on master.

Stub those routes on the browser context: the feature flag is switched on
with the server's other flags kept, and the pin routes answer with an empty
set. A spec's own page routes still take precedence. Studio's old-address
and 404 specs call the same stub, since they expect add-ons to be on. The
server behaviour is unchanged.

Assisted-by: Claude Code:claude-opus-5-5

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-10-24 22:46:09 +02:00

67 lines
3.2 KiB
JavaScript

import { test, expect } from './coverage-fixtures.js'
import { mockAccess } from './access-fixtures.js'
import { stubAddonRoutes } from './addons-fixtures.js'
test.describe('404', () => {
test('names the address, offers a way home and lists the places the viewer can go', async ({ page }) => {
await stubAddonRoutes(page)
await page.goto('/app/no-such-page')
const nf = page.getByTestId('not-found')
await expect(nf.getByRole('heading', { level: 1 })).toHaveText('This page does not exist')
await expect(nf).toContainText('/app/no-such-page')
await expect(nf.getByRole('link', { name: 'Go home' })).toHaveAttribute('href', '/app')
const places = nf.getByRole('navigation', { name: 'Where to go' })
for (const name of ['Home', 'Models', 'Chat', 'Add-ons', 'Build', 'Operate']) {
await expect(places.getByRole('link', { name })).toBeVisible()
}
// Settings is a tab of the Operate hub, so the places do not list it.
await expect(places.getByRole('link', { name: 'Settings' })).toHaveCount(0)
})
test('has no search, because the app has none to offer', async ({ page }) => {
await page.goto('/app/no-such-page')
await expect(page.getByTestId('not-found').getByRole('searchbox')).toHaveCount(0)
await expect(page.getByTestId('not-found')).not.toContainText('Press /')
})
test('the links go where they say', async ({ page }) => {
await page.goto('/app/no-such-page')
await page.getByTestId('not-found').getByRole('link', { name: 'Chat' }).click()
await expect(page).toHaveURL(/\/app\/chat$/)
await page.goto('/app/no-such-page')
await page.getByTestId('not-found').getByRole('link', { name: 'Go home' }).click()
await expect(page).toHaveURL(/\/app\/?$/)
})
test('a person who is not an admin is not offered admin places', async ({ page }) => {
await mockAccess(page, { status: 'member' })
await page.goto('/app/no-such-page')
const places = page.getByTestId('not-found').getByRole('navigation', { name: 'Where to go' })
await expect(places.getByRole('link', { name: 'Chat' })).toBeVisible()
await expect(places.getByRole('link', { name: 'Models' })).toHaveCount(0)
await expect(places.getByRole('link', { name: 'Operate' })).toHaveCount(0)
})
test('an address outside the app gets the same page', async ({ page }) => {
await page.goto('/nowhere/at/all')
await expect(page.getByTestId('not-found')).toContainText('/nowhere/at/all')
await expect(page.getByTestId('not-found').getByRole('link', { name: 'Go home' })).toBeVisible()
})
test('fits a phone', async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 })
await page.goto('/app/no-such-page')
await expect(page.getByTestId('not-found')).toBeVisible()
expect(await page.evaluate(() => document.documentElement.scrollWidth > window.innerWidth)).toBe(false)
})
})
test.describe('404: reduced motion', () => {
test('hover transitions are switched off', async ({ page }) => {
await page.emulateMedia({ reducedMotion: 'reduce' })
await page.goto('/app/no-such-page')
const seconds = await page.locator('.nf-place').first().evaluate(el => parseFloat(getComputedStyle(el).transitionDuration))
expect(seconds).toBeLessThan(0.001)
})
})