Files
lzt404 739c6e1711 feat(theme): support system appearance across desktop platforms
- Use Tauri native theme APIs with Linux desktop preference support
- Fall back to WebView theme detection for startup and browser preview
- Add a follow-system option to both standard and beginner modes
- Persist preferences and handle resume events and async race conditions
- Add translations and theme behavior and UI regression tests
2026-09-11 16:25:14 +08:00

86 lines
4.3 KiB
JavaScript

const { chromium } = require(process.env.PLAYWRIGHT_MODULE || 'playwright');
const assert = require('node:assert/strict');
(async () => {
const browser = await chromium.launch({ channel: 'msedge', headless: true });
try {
const page = await browser.newPage({ viewport: { width: 1280, height: 900 }, colorScheme: 'light' });
const errors = [];
page.on('pageerror', error => errors.push(String(error)));
page.setDefaultTimeout(10000);
const open = query => page.goto(`http://127.0.0.1:1422/tests/fixtures/theme.html?${query}`);
const theme = expected => page.waitForFunction(expected => document.documentElement.dataset.theme === expected, expected);
const sidebar = () => page.locator('.sidebar-theme-selector');
const select = index => sidebar().getByRole('button').nth(index).click();
const system = value => page.evaluate(value => window.themeFixture.setSystem(value), value);
const stored = () => page.evaluate(() => localStorage.getItem('easy-cli-proxy-api.theme'));
const nativeReady = () => page.waitForFunction(() => window.themeFixture?.calls.some(call => call.cmd.includes('background_color')));
for (const platform of ['windows', 'macos', 'linux']) {
console.log(`Checking ${platform}`);
await open(`reset&platform=${platform}`);
await nativeReady();
await theme('dark');
assert.equal(await sidebar().getByRole('button').nth(2).getAttribute('aria-pressed'), 'true');
assert.equal(await stored(), null);
await system('light');
await theme('light');
await select(1);
await theme('dark');
await system('light');
assert.equal(await stored(), 'dark');
await page.waitForFunction(() => window.themeFixture.appearance === 'dark');
await select(2);
await theme('light');
assert.equal(await stored(), 'system');
await open(`platform=${platform}&system=dark`);
await nativeReady();
await theme('dark');
assert.equal(await stored(), 'system');
await page.locator('.sidebar-easy-entry').click();
const easy = page.locator('.simple-mode-theme-group');
assert.equal(await easy.getByRole('button').count(), 3);
assert.equal(await easy.getByRole('button').nth(2).getAttribute('aria-pressed'), 'true');
await easy.getByRole('button').nth(0).click();
await theme('light');
await page.locator('.simple-mode-exit-btn').click();
assert.equal(await sidebar().getByRole('button').nth(0).getAttribute('aria-pressed'), 'true');
const writes = await page.evaluate(() => window.themeFixture.calls.filter(call => call.cmd === 'plugin:window|set_theme').length);
assert.ok(writes < 30, `${platform}: native theme echo loop (${writes} writes)`);
}
await open('reset&platform=browser');
await theme('light');
await page.emulateMedia({ colorScheme: 'dark' });
await theme('dark');
await select(0);
await theme('light');
await page.emulateMedia({ colorScheme: 'light' });
await page.emulateMedia({ colorScheme: 'dark' });
assert.equal(await page.locator('html').getAttribute('data-theme'), 'light');
await select(2);
await theme('dark');
await open('platform=browser');
await theme('dark');
assert.equal(await stored(), 'system');
for (const locale of ['zh-CN', 'zh-TW', 'en', 'ja']) for (const width of [1280, 640]) {
await page.setViewportSize({ width, height: 900 });
await open(`reset&platform=windows&locale=${locale}`);
await theme('dark');
const fits = await sidebar().getByRole('button').evaluateAll(buttons => buttons.every(button => button.scrollWidth <= button.clientWidth));
assert.ok(fits, `${locale} at ${width}px: theme label clipped`);
if (locale === 'zh-CN' && width === 1280 && process.env.THEME_SCREENSHOT_DIR) {
await page.screenshot({ path: `${process.env.THEME_SCREENSHOT_DIR}/theme-dark.png`, animations: 'disabled' });
await system('light');
await theme('light');
await page.screenshot({ path: `${process.env.THEME_SCREENSHOT_DIR}/theme-light.png`, animations: 'disabled' });
}
}
assert.deepEqual(errors, []);
console.log('Theme UI passed: Windows/macOS/Linux IPC, browser media, persistence, both selectors, 4 locales at 2 widths.');
} finally {
await browser.close();
}
})().catch(error => { console.error(error); process.exitCode = 1; });