Files
feedBack/tests/browser/audio-engine-nav.spec.ts
T
byrongamatosandClaude Opus 4.8 ae0857bd97 feat(v3): promote Audio Engine to a first-class sidebar item
Add the bundled desktop `audio_engine` plugin (native audio I/O, VST
hosting, amp modeling, pitch detection) as a dedicated v3 sidebar entry
in the HOME group, immediately below Settings, via the existing
PROMOTED_PLUGINS mechanism in static/v3/shell.js. Gated on the plugin
being installed (renderPromotedNav checks /api/plugins), so it shows
only in desktop builds. Adds a new `audio` waveform icon; the displayed
label comes from the plugin manifest nav.label ("Audio").

Test: tests/browser/audio-engine-nav.spec.ts (placement after Settings,
inside the HOME group, links to the plugin screen).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 00:58:48 +02:00

49 lines
2.4 KiB
TypeScript

import { test, expect } from '@playwright/test';
// The Audio Engine plugin (`audio_engine`) is bundled only in the desktop
// build, so stub /api/plugins to make it present here. Asserts it renders as a
// promoted sidebar entry in the v3 "HOME" group, immediately AFTER the Settings
// entry, and routes to its plugin screen.
const STUB_PLUGINS = [
{ id: 'audio_engine', name: 'Audio Engine', nav: { label: 'Audio', screen: 'audio-engine', icon: '🎸' }, status: 'ready', has_screen: false, has_settings: false },
];
test('audio_engine is promoted into the HOME nav group after Settings', async ({ page }) => {
await page.route('**/api/plugins', route =>
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(STUB_PLUGINS) }));
await page.goto('/');
await page.waitForSelector('#v3-nav a[data-v3-nav]', { timeout: 15000 });
// The promoted slot is filled with the Audio Engine link.
const aeLink = page.locator('#v3-nav-audio-engine a[data-v3-nav="audio_engine"]');
await expect(aeLink).toHaveCount(1, { timeout: 10000 });
await expect(aeLink).toContainText('Audio');
// It sits inside the HOME group, immediately after the Settings entry.
const order = await page.evaluate(() => {
const nav = document.getElementById('v3-nav');
const links = Array.from(nav.querySelectorAll('a[data-v3-nav]'));
const keys = links.map(a => a.getAttribute('data-v3-nav'));
const settingsIdx = keys.indexOf('settings');
const aeIdx = keys.indexOf('audio_engine');
// renderSidebar emits one wrapper <div> per group as a direct child of
// #v3-nav: [ heading <div>GROUP</div>, items <div>…links…</div> ]. Walk up
// from the link to that group wrapper and read its heading (first child) —
// structural, so it doesn't depend on the heading's CSS classes.
function groupOf(el) {
let node = el;
while (node && node.parentElement && node.parentElement !== nav) node = node.parentElement;
const heading = node && node.firstElementChild;
return heading ? heading.textContent.trim() : null;
}
return { settingsIdx, aeIdx, group: aeIdx >= 0 ? groupOf(links[aeIdx]) : null };
});
expect(order.settingsIdx).toBeGreaterThanOrEqual(0);
expect(order.aeIdx).toBe(order.settingsIdx + 1);
expect(order.group).toBe('HOME');
// It targets the plugin's own screen.
await expect(aeLink).toHaveAttribute('href', '#/audio_engine');
});