Files
waggle-os/packages/server/tests/web-frontend.test.ts
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

115 lines
3.7 KiB
TypeScript

/**
* 9D-4: Web Frontend Build — static file serving tests.
*
* Verifies the server serves a React frontend from dist/ when available,
* with SPA fallback (non-API routes → index.html).
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
describe('Web Frontend Static Serving', () => {
const tmpDir = path.join(os.tmpdir(), `waggle-web-test-${Date.now()}`);
const distDir = path.join(tmpDir, 'app', 'dist');
beforeAll(() => {
// Create a mock dist/ directory with index.html and a JS file
fs.mkdirSync(path.join(distDir, 'assets'), { recursive: true });
fs.writeFileSync(
path.join(distDir, 'index.html'),
'<!DOCTYPE html><html><head><title>Waggle</title></head><body><div id="root"></div></body></html>',
);
fs.writeFileSync(
path.join(distDir, 'assets', 'main.js'),
'console.log("waggle");',
);
});
afterAll(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it('detects dist/ directory presence', () => {
expect(fs.existsSync(distDir)).toBe(true);
expect(fs.existsSync(path.join(distDir, 'index.html'))).toBe(true);
});
it('index.html contains expected structure', () => {
const html = fs.readFileSync(path.join(distDir, 'index.html'), 'utf-8');
expect(html).toContain('<div id="root">');
expect(html).toContain('<!DOCTYPE html>');
});
it('static assets exist in dist/assets/', () => {
const assets = fs.readdirSync(path.join(distDir, 'assets'));
expect(assets).toContain('main.js');
});
it('WAGGLE_FRONTEND_DIR env var is respected', () => {
// The server reads WAGGLE_FRONTEND_DIR to locate the frontend.
// When not set, it falls back to <root>/dist (canonical since Apr-12
// apps/web migration — commit a883050); app/dist is a legacy fallback.
const envDir = process.env.WAGGLE_FRONTEND_DIR;
const fallback = path.resolve(process.cwd(), 'dist');
const resolved = envDir ?? fallback;
expect(typeof resolved).toBe('string');
expect(resolved.length).toBeGreaterThan(0);
});
});
describe('getServerBaseUrl logic', () => {
it('returns localhost:3333 when TAURI_INTERNALS is present', () => {
// Simulate Tauri environment
const tauriGlobal = globalThis as { __TAURI_INTERNALS__?: unknown };
const origTauri = tauriGlobal.__TAURI_INTERNALS__;
tauriGlobal.__TAURI_INTERNALS__ = {};
// The function checks window, so we test the logic directly
const isTauri = !!tauriGlobal.__TAURI_INTERNALS__;
expect(isTauri).toBe(true);
// Cleanup
if (origTauri === undefined) {
delete tauriGlobal.__TAURI_INTERNALS__;
} else {
tauriGlobal.__TAURI_INTERNALS__ = origTauri;
}
});
it('returns origin when not in Tauri', () => {
const isTauri = !!(globalThis as { __TAURI_INTERNALS__?: unknown }).__TAURI_INTERNALS__;
expect(isTauri).toBe(false);
});
});
describe('SPA fallback routing', () => {
it('API routes should not be intercepted by SPA fallback', () => {
const apiPaths = [
'/api/health',
'/api/workspaces',
'/api/chat',
'/api/settings',
'/v1/chat/completions',
'/health',
'/ws',
];
for (const p of apiPaths) {
const isApi = p.startsWith('/api/') || p.startsWith('/v1/') ||
p === '/health' || p === '/ws';
expect(isApi).toBe(true);
}
});
it('non-API routes should be served index.html', () => {
const spaPaths = ['/', '/chat', '/settings', '/cockpit', '/memory'];
for (const p of spaPaths) {
const isApi = p.startsWith('/api/') || p.startsWith('/v1/') ||
p === '/health' || p === '/ws';
expect(isApi).toBe(false);
}
});
});