mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-01 01:59:01 -05:00
The Face recognition, Voice recognition, Voice library, Fine-tune and Quantize pages now live only in their add-ons. The router still kept a built-in copy of each one and showed it when the add-on was not installed, which meant two pages to maintain for the same job. Replace the MovedPage wrapper with a plain redirect. The old addresses (/app/face, /app/voice, /app/voice-library, /app/fine-tune and the rest) keep working and always open the add-on route. When the add-on is not installed or not ready, the add-on shell shows its "not available" page. The model of /app/face/:model and /app/voice/:model still travels as ?model=. Delete the six page files, the components, styles and helpers only they used, the biometrics translation namespace, and the end-to-end specs that tested the built-in copies. Drop the two add-on parity tests that compared the add-on page with the deleted core page. Server APIs and feature flags are unchanged. Assisted-by: Claude:claude-sonnet-5-5 [Claude Code] Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
59 lines
2.9 KiB
JavaScript
59 lines
2.9 KiB
JavaScript
import { test, expect } from './coverage-fixtures.js'
|
|
import { installStudioAddons } from './studio-addon.js'
|
|
|
|
// A standing guard against the two defects an earlier automated edit left
|
|
// scattered through the pages: icons that render nothing at all (here, an svg
|
|
// whose sprite target is missing or that has no size), and controls left with
|
|
// the user agent's own chrome, which is a pale grey button on a dark ground.
|
|
const ROUTES = [
|
|
'/app', '/app/chat', '/app/models', '/app/studio', '/app/talk',
|
|
'/app/agents', '/app/skills', '/app/collections', '/app/agent-jobs',
|
|
'/app/models?view=installed', '/app/backends', '/app/activity', '/app/operate',
|
|
'/app/settings', '/app/traces', '/app/usage', '/app/nodes', '/app/p2p',
|
|
'/app/account',
|
|
]
|
|
|
|
test('no page renders a dead icon or a default-chrome control', async ({ page }) => {
|
|
// Studio and Talk are add-ons; /app/studio and /app/talk open them.
|
|
await installStudioAddons(page)
|
|
// One test walks every route, so its budget has to scale with the list rather
|
|
// than sit on Playwright's per-test default of 30s. At 25 routes that default
|
|
// allows ~1.2s per navigation, which holds on a developer machine and does
|
|
// not on a loaded CI runner: the suite went red on the commit that added this
|
|
// spec, timing out mid-loop at waitForTimeout rather than at any single goto,
|
|
// which is what cumulative slowness looks like as opposed to one hung route.
|
|
// Six seconds a route absorbs a slow runner and still fails promptly if a
|
|
// route really does hang.
|
|
test.setTimeout(ROUTES.length * 6_000)
|
|
|
|
const findings = []
|
|
for (const route of ROUTES) {
|
|
await page.goto(route)
|
|
await page.waitForTimeout(400)
|
|
const found = await page.evaluate(() => {
|
|
const out = []
|
|
for (const el of document.querySelectorAll('button, a')) {
|
|
if (el.getBoundingClientRect().width === 0) continue
|
|
const cs = getComputedStyle(el)
|
|
if (cs.borderTopStyle === 'outset' || cs.backgroundColor === 'rgb(239, 239, 239)') {
|
|
out.push(`default-chrome: "${(el.textContent || '').trim().slice(0, 24)}" [${el.className}]`)
|
|
}
|
|
}
|
|
for (const svg of document.querySelectorAll('svg[data-icon]')) {
|
|
const use = svg.querySelector('use')
|
|
const target = use && document.querySelector(use.getAttribute('href'))
|
|
const box = svg.getBoundingClientRect()
|
|
if (use && !target) out.push(`dead-icon: no sprite symbol for [${svg.dataset.icon}]`)
|
|
if (box.width > 0 && (box.width < 4 || box.height < 4)) out.push(`dead-icon: [${svg.dataset.icon}] is ${box.width}x${box.height}`)
|
|
}
|
|
// A leftover icon-font element draws nothing now that the font is gone.
|
|
for (const i of document.querySelectorAll('i[class*="fa-"], .fas, .far, .fab')) {
|
|
out.push(`dead-icon: font class left on [${i.className}]`)
|
|
}
|
|
return [...new Set(out)]
|
|
})
|
|
for (const f of found) findings.push(`${route} — ${f}`)
|
|
}
|
|
expect(findings).toEqual([])
|
|
})
|