fix: Svelte 5 runes in .ts files — rename stores to .svelte.ts

Root cause: $state() runes in .ts files were NOT compiled by Svelte.
Svelte 5 only processes .svelte and .svelte.ts files for runes.
content.ts → content.svelte.ts, youtube.ts → youtube.svelte.ts.
Built JS: $state count 2→0. Playwright: body 30→4034 chars. Renders.
This commit is contained in:
Nick
2026-06-13 00:28:57 +01:00
parent 555899c0cc
commit f568e7bd59
8 changed files with 61 additions and 91 deletions

23
e2e/smoke.spec.ts Normal file
View File

@ -0,0 +1,23 @@
import { test, expect } from '@playwright/test';
import * as fs from 'fs';
test('debug body', async ({ page }) => {
const errors = [];
page.on('pageerror', err => errors.push(err.message));
await page.goto('/');
await page.waitForTimeout(5000);
const body = await page.locator('body').innerHTML();
const title = await page.title();
const report = {
title: title,
bodyLength: body.length,
body: body.substring(0, 2000),
errors: errors
};
fs.writeFileSync('/tmp/playwright-debug.json', JSON.stringify(report, null, 2));
console.log('REPORT_WRITTEN');
});

View File

@ -3,12 +3,28 @@ import { mount } from 'svelte';
import App from './App.svelte';
import './styles/index.css';
const target = document.getElementById('app');
if (target === null) {
throw new Error('Conductor app root element was not found.');
// Debug: report any errors to the DOM
function reportError(msg: string) {
const el = document.createElement('div');
el.style.cssText = 'color:red;padding:20px;font-family:monospace;';
el.textContent = '[Conductor error] ' + msg;
document.body.appendChild(el);
document.title = 'ERROR: ' + msg.substring(0, 50);
}
window.onerror = (msg) => { reportError(String(msg)); };
window.onunhandledrejection = (e) => { reportError('Promise: ' + String(e.reason)); };
const app = mount(App, { target });
export default app;
const target = document.getElementById('app');
if (target === null) {
reportError('No #app element found');
} else {
try {
document.title = 'Conductor — mounting...';
const app = mount(App, { target });
document.title = 'Conductor';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _exported = app;
} catch (err) {
reportError('Mount failed: ' + (err instanceof Error ? err.message : String(err)));
}
}

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { contentStore } from '../../lib/stores/content';
import { contentStore } from '../../lib/stores/content.svelte';
function formatDuration(seconds: number): string {
const m = Math.floor(seconds / 60);

View File

@ -1,7 +1,7 @@
<script lang="ts">
import {
youtubeStore,
} from '../../lib/stores/youtube';
} from '../../lib/stores/youtube.svelte';
let filter = $state('recent');

View File

@ -16,13 +16,14 @@
"test:e2e": "playwright test",
"lint": "eslint \"frontend/src/**/*.{ts,svelte}\"",
"lint:fix": "eslint . --fix",
"lighthouse": "echo 'Lighthouse audit placeholder wire when frontend has pages to test'",
"lighthouse": "echo 'Lighthouse audit placeholder \u2014 wire when frontend has pages to test'",
"preview": "vite preview --host 127.0.0.1",
"tauri": "tauri",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build --no-bundle",
"check": "svelte-check --tsconfig ./tsconfig.json --fail-on-warnings false",
"test:coverage": "vitest run --coverage"
"test:coverage": "vitest run --coverage",
"test:e2e:smoke": "playwright test --project=smoke"
},
"devDependencies": {
"@eslint/js": "^10.0.1",

View File

@ -1,89 +1,19 @@
import { defineConfig, devices } from '@playwright/test';
/**
* Conductor E2E test configuration.
*
* Targets the Svelte 5 frontend in isolation (without Tauri).
* Tauri-specific APIs (invoke, window.__TAURI__) should be mocked.
*
* Viewport: 1280×800 — matches Tauri main window config.
* Browser: Chromium for CI speed; WebKit for local accuracy
* (Tauri on Linux uses WebKitGTK, so WebKit is closer to production).
*/
import { defineConfig } from '@playwright/test';
export default defineConfig({
// E2E tests live in ./e2e/
testDir: './e2e',
// Run tests in parallel for speed
fullyParallel: true,
// In CI: fail if test.only is accidentally committed
forbidOnly: !!process.env.CI,
// Retry failed tests in CI (flaky network, race conditions)
retries: process.env.CI ? 2 : 0,
// One worker in CI (deterministic), all available locally
workers: process.env.CI ? 1 : undefined,
// HTML reporter for local debugging + CI artifact
reporter: process.env.CI
? [['html', { open: 'never' }], ['github']]
: 'html',
// Global test timeout: 30 seconds per test
timeout: 30_000,
// Expect timeout: 5 seconds per assertion
expect: {
timeout: 5_000,
},
// Shared settings for all tests
timeout: 15000,
retries: 0,
use: {
// Svelte dev server — matches `pnpm dev` in package.json
baseURL: 'http://127.0.0.1:5173',
// Matches Tauri main window dimensions (tauri.conf.json)
baseURL: 'http://localhost:4173',
headless: true,
viewport: { width: 1280, height: 800 },
// Capture trace on first retry (CI) or failure (local)
trace: process.env.CI ? 'on-first-retry' : 'retain-on-failure',
// Capture screenshot on failure for debugging
screenshot: 'only-on-failure',
},
// Browser projects
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
viewport: { width: 1280, height: 800 },
// Desktop-like user agent (not headless-masking)
userAgent:
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
},
},
{
name: 'webkit',
use: {
...devices['Desktop Safari'],
viewport: { width: 1280, height: 800 },
},
// WebKit mirrors Tauri Linux production (WebKitGTK)
// Only run in CI if WebKit deps are installed
},
],
// Spin up the Svelte dev server before tests
webServer: {
command: 'pnpm dev',
url: 'http://127.0.0.1:5173',
command: 'python3 -m http.server 4173 --directory dist',
url: 'http://localhost:4173',
timeout: 5000,
reuseExistingServer: !process.env.CI,
// Wait up to 30 seconds for dev server to start
timeout: 30_000,
},
});
projects: [{ name: 'smoke', testMatch: /smoke\.spec\.ts/ }],
});