Files
Webklar-Kundenbereich/runner/test/detect.test.js
T
2026-09-30 21:53:41 +00:00

146 lines
6.1 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, writeFileSync, mkdirSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { detectProject, detectFromDir } from '../src/detect.js';
const fake = (map) => ({
has: (p) => Object.hasOwn(map, p),
read: (p) => (Object.hasOwn(map, p) ? map[p] : null),
});
const pkg = (scripts) => JSON.stringify({ scripts });
test('HTML ohne package.json ⇒ static', () => {
const r = detectProject(fake({ 'index.html': '<html>' }), {});
assert.equal(r.kind, 'static');
assert.equal(r.buildCmd, null);
assert.equal(r.devCmd, null);
});
test('Vite mit build ⇒ build, npm', () => {
const r = detectProject(fake({ 'package.json': pkg({ build: 'vite build', dev: 'vite' }) }), { mode: 'auto' });
assert.equal(r.kind, 'build');
assert.equal(r.pm, 'npm');
assert.deepEqual(r.install, ['npm', 'install', '--include=dev', '--no-audit', '--no-fund']);
assert.deepEqual(r.buildCmd, ['npm', 'run', 'build']);
assert.deepEqual(r.outputDirs, ['dist', 'build', 'out', 'web-build']);
assert.equal(r.devPort, 5173);
});
test('npm mit Lock ⇒ npm ci', () => {
const r = detectProject(fake({ 'package.json': pkg({ build: 'x' }), 'package-lock.json': '{}' }), {});
assert.deepEqual(r.install, ['npm', 'ci', '--include=dev']);
});
test('pnpm-lock ⇒ pnpm', () => {
const r = detectProject(fake({ 'package.json': pkg({ build: 'x', dev: 'y' }), 'pnpm-lock.yaml': '' }), {});
assert.equal(r.pm, 'pnpm');
assert.deepEqual(r.install, ['pnpm', 'install', '--frozen-lockfile']);
assert.deepEqual(r.buildCmd, ['pnpm', 'run', 'build']);
assert.deepEqual(r.devCmd, ['pnpm', 'run', 'dev', '--host', '0.0.0.0', '--port', '5173']);
});
test('yarn Berry (.yarnrc.yml) ⇒ --immutable', () => {
const r = detectProject(fake({ 'package.json': pkg({ build: 'x' }), 'yarn.lock': '', '.yarnrc.yml': '' }), {});
assert.deepEqual(r.install, ['yarn', 'install', '--immutable']);
});
test('yarn.lock und bun.lockb/bun.lock', () => {
assert.deepEqual(detectProject(fake({ 'package.json': pkg({ build: 'x' }), 'yarn.lock': '' }), {}).install, ['yarn', 'install', '--frozen-lockfile']);
for (const lock of ['bun.lockb', 'bun.lock']) {
const r = detectProject(fake({ 'package.json': pkg({ build: 'x' }), [lock]: '' }), {});
assert.equal(r.pm, 'bun');
assert.deepEqual(r.install, ['bun', 'install', '--frozen-lockfile']);
assert.deepEqual(r.buildCmd, ['bun', 'run', 'build']);
}
});
test('nur dev-Skript ⇒ dev', () => {
const r = detectProject(fake({ 'package.json': pkg({ dev: 'vite' }) }), {});
assert.equal(r.kind, 'dev');
assert.equal(r.buildCmd, null);
assert.deepEqual(r.devCmd, ['npm', 'run', 'dev', '--', '--host', '0.0.0.0', '--port', '5173']);
});
test('index.html + build-Skript ⇒ build', () => {
const r = detectProject(fake({ 'index.html': 'x', 'package.json': pkg({ build: 'vite build' }) }), {});
assert.equal(r.kind, 'build');
});
test('index.html + nur dev-Skript ⇒ static (kein build)', () => {
const r = detectProject(fake({ 'index.html': 'x', 'package.json': pkg({ dev: 'vite' }) }), {});
assert.equal(r.kind, 'static');
});
test('mode dev erzwingt dev, auch mit build', () => {
const r = detectProject(fake({ 'package.json': pkg({ build: 'b', dev: 'd' }) }), { mode: 'dev' });
assert.equal(r.kind, 'dev');
assert.ok(r.devCmd);
});
test('mode dev ohne dev-Skript ⇒ Fehler', () => {
const r = detectProject(fake({ 'package.json': pkg({ build: 'b' }) }), { mode: 'dev' });
assert.equal(r.kind, 'dev');
assert.equal(r.error, 'Kein dev-Skript in package.json');
});
test('mode build ohne build-Skript ⇒ Fehler', () => {
const r = detectProject(fake({ 'index.html': 'x', 'package.json': pkg({ dev: 'd' }) }), { mode: 'build' });
assert.equal(r.kind, 'build');
assert.equal(r.error, 'Kein build-Skript in package.json');
});
test('devPort 3000 aus previewJson landet im devCmd', () => {
const r = detectProject(fake({ 'package.json': pkg({ dev: 'd' }) }), { previewJson: { devPort: 3000 } });
assert.equal(r.devPort, 3000);
assert.deepEqual(r.devCmd.slice(-2), ['--port', '3000']);
});
test('ungültiger devPort ⇒ 5173', () => {
for (const p of [80, 70000, 3000.5, '3000', null]) {
assert.equal(detectProject(fake({ 'package.json': pkg({ dev: 'd' }) }), { previewJson: { devPort: p } }).devPort, 5173);
}
});
test('previewJson.output überschreibt, unsicher wird ignoriert', () => {
const f = fake({ 'package.json': pkg({ build: 'b' }) });
assert.deepEqual(detectProject(f, { previewJson: { output: 'public/site' } }).outputDirs, ['public/site']);
for (const o of ['../x', '/etc', 'a/../b', '', 5]) {
assert.deepEqual(detectProject(f, { previewJson: { output: o } }).outputDirs, ['dist', 'build', 'out', 'web-build']);
}
});
test('index aus opts/previewJson: eigene Startseite ⇒ static', () => {
const f = fake({ 'site/home.html': 'x', 'package.json': pkg({ dev: 'd' }) });
const r = detectProject(f, { previewJson: { index: 'site/home.html' } });
assert.equal(r.kind, 'static');
assert.equal(r.index, 'site/home.html');
assert.equal(detectProject(f, { index: '../evil.html' }).kind, 'dev');
});
test('kaputtes package.json wirft nicht', () => {
const r = detectProject(fake({ 'package.json': '{kaputt' }), {});
assert.equal(r.kind, 'static');
assert.ok(r.hint);
});
test('weder index noch Skripte ⇒ static mit Hinweis', () => {
const r = detectProject(fake({ 'README.md': 'x' }), {});
assert.equal(r.kind, 'static');
assert.match(r.hint, /Hinweis|index|Startseite/i);
});
test('detectFromDir liest das Dateisystem', () => {
const d = mkdtempSync(join(tmpdir(), 'detect-'));
writeFileSync(join(d, 'package.json'), pkg({ build: 'vite build' }));
writeFileSync(join(d, 'pnpm-lock.yaml'), '');
writeFileSync(join(d, '.webklar-preview.json'), JSON.stringify({ output: 'out2' }));
assert.equal(detectFromDir(d, {}).pm, 'pnpm');
assert.deepEqual(detectFromDir(d, {}).outputDirs, ['out2']);
const e = mkdtempSync(join(tmpdir(), 'detect-'));
mkdirSync(join(e, 'index.html'));
assert.equal(detectFromDir(e, {}).kind, 'static');
assert.equal(detectFromDir(join(e, 'nix'), {}).kind, 'static');
});