257 lines
8.2 KiB
TypeScript
257 lines
8.2 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
import {
|
|
observeRuntime,
|
|
startNestedStaticServer,
|
|
type NestedStaticServer,
|
|
} from './browser-helpers';
|
|
|
|
test.describe('static application and Toolbox shell', () => {
|
|
test('runs standalone, stays lazy and supports keyboard-operated shell controls', async ({
|
|
page,
|
|
}) => {
|
|
const runtime = observeRuntime(page);
|
|
|
|
await page.goto('/');
|
|
|
|
await expect(page).toHaveTitle('Audio & Video Tools');
|
|
await expect(
|
|
page.getByRole('heading', {
|
|
level: 1,
|
|
name: 'Audio & Video Tools',
|
|
})
|
|
).toBeVisible();
|
|
await expect(page.locator('[data-toolbox-context]')).toHaveAttribute(
|
|
'data-toolbox-context',
|
|
'standalone'
|
|
);
|
|
await expect(page.getByRole('main')).toBeVisible();
|
|
await expect(
|
|
page.getByRole('tab', { name: 'Quick Convert' })
|
|
).toHaveAttribute('aria-selected', 'true');
|
|
await expect(
|
|
page.getByRole('button', { name: 'Choose files' })
|
|
).toBeEnabled();
|
|
|
|
expect(
|
|
runtime.requests.some((url) => url.includes('/vendor/ffmpeg/'))
|
|
).toBe(false);
|
|
expect(
|
|
runtime.requests.some((url) => /\/assets\/worker-[^/]+\.js$/u.test(url))
|
|
).toBe(false);
|
|
|
|
const helpButton = page.getByRole('button', { name: 'Help' });
|
|
await helpButton.focus();
|
|
await page.keyboard.press('Enter');
|
|
const helpDialog = page.getByRole('dialog', {
|
|
name: 'Audio & Video Tools',
|
|
});
|
|
await expect(helpDialog).toBeVisible();
|
|
await expect(
|
|
helpDialog.getByRole('heading', {
|
|
level: 2,
|
|
name: 'Audio & Video Tools',
|
|
})
|
|
).toBeVisible();
|
|
await page.keyboard.press('Escape');
|
|
await expect(helpDialog).toBeHidden();
|
|
|
|
const personalizeButton = page.getByRole('button', {
|
|
name: 'Personalize',
|
|
});
|
|
await personalizeButton.focus();
|
|
await page.keyboard.press('Enter');
|
|
await expect(
|
|
page.getByRole('heading', {
|
|
level: 2,
|
|
name: 'Personalize your toolbox',
|
|
})
|
|
).toBeVisible();
|
|
await page.getByRole('button', { name: 'Dark' }).click();
|
|
await expect(page.locator('.toolbox-shell').first()).toHaveAttribute(
|
|
'data-toolbox-theme',
|
|
'dark'
|
|
);
|
|
await page.keyboard.press('Escape');
|
|
await expect(personalizeButton).toBeFocused();
|
|
|
|
const networkUrls = runtime.requests.filter((url) => /^https?:/u.test(url));
|
|
expect(
|
|
networkUrls.every(
|
|
(url) => new URL(url).origin === 'http://127.0.0.1:4173'
|
|
)
|
|
).toBe(true);
|
|
expect(runtime.failedRequests).toEqual([]);
|
|
expect(runtime.pageErrors).toEqual([]);
|
|
expect(runtime.consoleErrors).toEqual([]);
|
|
});
|
|
|
|
test('keeps preview primary and exposes accessible Edit drawers on narrow screens', async ({
|
|
page,
|
|
}) => {
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await page.goto('/');
|
|
await page.getByRole('tab', { name: 'Edit' }).click();
|
|
|
|
await expect(page.getByRole('heading', { name: 'Preview' })).toBeVisible();
|
|
const mediaDrawer = page.locator('#edit-media-drawer');
|
|
const inspectorDrawer = page.locator('#edit-inspector-drawer');
|
|
await expect(mediaDrawer).toBeHidden();
|
|
await expect(inspectorDrawer).toBeHidden();
|
|
|
|
const sources = page.getByRole('button', { name: 'Sources' });
|
|
await sources.click();
|
|
await expect(mediaDrawer).toBeVisible();
|
|
await expect(
|
|
mediaDrawer.getByRole('button', { name: 'Close media bin' })
|
|
).toBeFocused();
|
|
await page.keyboard.press('Escape');
|
|
await expect(mediaDrawer).toBeHidden();
|
|
await expect(sources).toBeFocused();
|
|
|
|
const inspector = page.getByRole('button', { name: 'Inspector' });
|
|
await inspector.click();
|
|
await expect(inspectorDrawer).toBeVisible();
|
|
const closeInspector = inspectorDrawer.getByRole('button', {
|
|
name: 'Close inspector',
|
|
});
|
|
await expect(closeInspector).toBeFocused();
|
|
await closeInspector.click();
|
|
await expect(inspectorDrawer).toBeHidden();
|
|
await expect(inspector).toBeFocused();
|
|
});
|
|
});
|
|
|
|
test.describe('nested static deployment and Toolbox context', () => {
|
|
let nestedServer: NestedStaticServer;
|
|
|
|
test.beforeAll(async () => {
|
|
nestedServer = await startNestedStaticServer();
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
await nestedServer.close();
|
|
});
|
|
|
|
test('loads a valid same-origin catalog and all runtime assets below the nested path', async ({
|
|
page,
|
|
}) => {
|
|
const runtime = observeRuntime(page);
|
|
await page.goto(`${nestedServer.appUrl}?toolbox=../toolbox.catalog.json`);
|
|
|
|
await expect(page.locator('[data-toolbox-context]')).toHaveAttribute(
|
|
'data-toolbox-context',
|
|
'connected'
|
|
);
|
|
await expect(
|
|
page.getByRole('link', { name: 'add·ideas Toolbox' })
|
|
).toHaveAttribute('href', `${nestedServer.origin}/deep/nested/`);
|
|
|
|
await page.getByRole('button', { name: 'Apps' }).click();
|
|
const appSwitcher = page.getByRole('navigation', {
|
|
name: 'Toolbox applications',
|
|
});
|
|
await expect(
|
|
appSwitcher.getByRole('link', { name: 'Browser Test Tool' })
|
|
).toBeVisible();
|
|
await expect(
|
|
appSwitcher.getByRole('link', { name: 'Audio & Video Tools' })
|
|
).toBeVisible();
|
|
await page.keyboard.press('Escape');
|
|
|
|
expect(
|
|
runtime.requests.some((url) =>
|
|
/\/deep\/nested\/av\/assets\/index-[^/]+\.js$/u.test(url)
|
|
)
|
|
).toBe(true);
|
|
expect(
|
|
runtime.requests.some((url) =>
|
|
/\/deep\/nested\/av\/assets\/index-[^/]+\.css$/u.test(url)
|
|
)
|
|
).toBe(true);
|
|
expect(
|
|
runtime.requests.some((url) => url.includes('/vendor/ffmpeg/'))
|
|
).toBe(false);
|
|
|
|
await page.getByText('Engine & browser').click();
|
|
await page.getByLabel('Engine mode').selectOption('force-single-thread');
|
|
await page.getByRole('button', { name: 'Initialize engine' }).click();
|
|
|
|
await expect(
|
|
page
|
|
.locator('.engine-pill')
|
|
.filter({ hasText: 'Single-threaded compatibility mode' })
|
|
).toBeVisible({ timeout: 120_000 });
|
|
await expect(page.getByText('0.12.15 / 0.12.10')).toBeVisible();
|
|
await expect(
|
|
page.getByText('Encoders').locator('..').locator('dd')
|
|
).not.toHaveText('0');
|
|
await expect(
|
|
page.getByText('Filters').locator('..').locator('dd')
|
|
).not.toHaveText('0');
|
|
|
|
const coreScript = runtime.responses.find((response) =>
|
|
response.url.endsWith(
|
|
'/deep/nested/av/vendor/ffmpeg/0.12.10/st/ffmpeg-core.js'
|
|
)
|
|
);
|
|
const coreWasm = runtime.responses.find((response) =>
|
|
response.url.endsWith(
|
|
'/deep/nested/av/vendor/ffmpeg/0.12.10/st/ffmpeg-core.wasm'
|
|
)
|
|
);
|
|
expect(coreScript).toMatchObject({
|
|
status: 200,
|
|
contentType: 'text/javascript; charset=utf-8',
|
|
});
|
|
expect(coreWasm).toMatchObject({
|
|
status: 200,
|
|
contentType: 'application/wasm',
|
|
});
|
|
expect(
|
|
runtime.responses.some((response) =>
|
|
/\/deep\/nested\/av\/assets\/worker-[^/]+\.js$/u.test(response.url)
|
|
)
|
|
).toBe(true);
|
|
|
|
const networkUrls = runtime.requests.filter((url) => /^https?:/u.test(url));
|
|
expect(
|
|
networkUrls.every((url) => new URL(url).origin === nestedServer.origin)
|
|
).toBe(true);
|
|
expect(runtime.failedRequests).toEqual([]);
|
|
expect(runtime.pageErrors).toEqual([]);
|
|
expect(runtime.consoleErrors).toEqual([]);
|
|
});
|
|
|
|
test('rejects an invalid same-origin catalog and remains usable standalone', async ({
|
|
page,
|
|
}) => {
|
|
const runtime = observeRuntime(page);
|
|
await page.goto(`${nestedServer.appUrl}?toolbox=../invalid-catalog.json`);
|
|
|
|
await expect
|
|
.poll(() => runtime.consoleWarnings.length, { timeout: 10_000 })
|
|
.toBeGreaterThan(0);
|
|
await expect(page.locator('[data-toolbox-context]')).toHaveAttribute(
|
|
'data-toolbox-context',
|
|
'standalone'
|
|
);
|
|
await expect(
|
|
page.getByRole('button', { name: 'Choose files' })
|
|
).toBeEnabled();
|
|
expect(
|
|
runtime.consoleWarnings.some((warning) =>
|
|
warning.includes('Toolbox context unavailable; continuing standalone.')
|
|
)
|
|
).toBe(true);
|
|
expect(
|
|
runtime.requests.some(
|
|
(url) =>
|
|
url === `${nestedServer.origin}/deep/nested/invalid-catalog.json`
|
|
)
|
|
).toBe(true);
|
|
expect(runtime.failedRequests).toEqual([]);
|
|
expect(runtime.pageErrors).toEqual([]);
|
|
expect(runtime.consoleErrors).toEqual([]);
|
|
});
|
|
});
|