mirror of
https://github.com/mudler/LocalAI.git
synced 2026-10-28 09:08:54 -04:00
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>
67 lines
3.2 KiB
JavaScript
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)
|
|
})
|
|
})
|