Files
stechuhr3/frontend/test/load-errors.test.cjs
2026-10-27 14:21:23 +01:00

108 lines
4.9 KiB
JavaScript

process.env.TZ = 'Europe/Berlin';
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { readFileSync } = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const { parse, compileScript } = require('@vue/compiler-sfc');
const { transformSync } = require('esbuild');
const vue = require('vue');
// Execute the real Vue setup and store code, with HTTP responses controlled by
// each test. No mounted app or browser globals are needed for these load paths.
function load(relativePath, dependencies = {}) {
const filename = path.resolve(__dirname, '..', relativePath);
let source = readFileSync(filename, 'utf8');
if (filename.endsWith('.vue')) {
source = compileScript(parse(source).descriptor, { id: 'load-error-test' }).content;
}
const code = transformSync(source, { format: 'cjs', loader: 'js' }).code;
const module = { exports: {} };
vm.runInThisContext(`(function(require, module, exports) { ${code}\n})`, { filename })(id => {
if (id in dependencies) return dependencies[id];
if (id === 'vue') return { ...vue, onMounted() {}, onBeforeUnmount() {} };
if (id === 'pinia') return { defineStore: (_name, setup) => setup };
if (id === '@/config/api') return { API_BASE_URL: '/api' };
if (id.endsWith('authStore')) return {
useAuthStore: () => ({ token: 'test-token', getAuthHeaders: () => ({}), clearAuth() {} })
};
throw new Error(`Unexpected import: ${id}`);
}, module, module.exports);
return module.exports;
}
function component(file, dependencies) {
return load(file, dependencies).default.setup({}, { expose() {} });
}
const ok = data => ({ ok: true, json: async () => data });
test('Wochenansicht fordert beim Öffnen aktuelle Woche an', async t => {
let requestedUrl;
t.mock.method(global, 'fetch', async url => {
requestedUrl = url;
return ok({ success: true, data: { weekStart: '2026-10-26', weekEnd: '2026-11-01', days: [] } });
});
const page = component('src/views/WeekOverview.vue');
await page.loadWeekData();
assert.equal(requestedUrl, '/api/week-overview?weekOffset=0');
assert.equal(page.weekRange.value, '26.10.2026 - 01.11.2026');
assert.equal(page.loadError.value, '');
});
test('Wochenansicht zeigt bei HTTP 500 Fehler und aktuelle Wochengrenzen statt Beispieldaten', async t => {
const RealDate = Date;
t.mock.method(global, 'Date', class extends RealDate {
constructor(...args) { super(...(args.length ? args : ['2026-10-27T14:00:00'])); }
});
t.mock.method(console, 'error', () => {});
t.mock.method(global, 'fetch', async () => ({ ok: false, status: 500 }));
const page = component('src/views/WeekOverview.vue');
await page.loadWeekData();
assert.equal(page.weekData.value, null);
assert.deepEqual(page.weekDays.value, []);
assert.equal(page.weekRange.value, '26.10.2026 - 01.11.2026');
assert.match(page.loadError.value, /nicht geladen/);
assert.equal(page.loading.value, false);
});
test('Ältere Wochenantwort überschreibt die zuletzt ausgewählte Woche nicht', async t => {
let finishOldRequest;
t.mock.method(global, 'fetch', url => url.endsWith('=0')
? new Promise(resolve => { finishOldRequest = resolve; })
: Promise.resolve(ok({ success: true, data: { weekStart: '2026-11-02', weekEnd: '2026-11-08', days: [] } })));
const page = component('src/views/WeekOverview.vue');
const oldRequest = page.loadWeekData();
page.weekOffset.value = 1;
await page.loadWeekData();
finishOldRequest(ok({ success: true, data: { weekStart: '2026-10-26', weekEnd: '2026-11-01', days: [] } }));
await oldRequest;
assert.equal(page.weekRange.value, '02.11.2026 - 08.11.2026');
});
test('Statistikfehler wird angezeigt, gültige Werte bleiben erhalten und Wiederholen funktioniert', async t => {
t.mock.method(console, 'error', () => {});
const stats = {
timestamp: new Date().toISOString(), currentlyWorked: '03:53:53', open: '03:06:07',
overtime: '+0:00', totalOvertime: '+5:49', weekWorktime: '3:53', nonWorkingHours: '8:00'
};
let failing = true;
t.mock.method(global, 'fetch', async () => failing ? { ok: false, status: 500 } : ok(stats));
const store = load('src/stores/timeStore.js');
const box = component('src/components/StatusBox.vue', { '../stores/timeStore': store });
await box.fetchStats();
assert.match(box.statsError.value, /nicht geladen/);
assert.equal(box.isReady.value, true);
assert.equal(box.displayRows.value['Überstunden (Woche)'], '—');
assert.equal('overtime' in box.displayRows.value, false);
assert.equal('totalOvertime' in box.displayRows.value, false);
failing = false;
await box.fetchStats();
assert.equal(box.statsError.value, '');
assert.equal(box.displayRows.value['Überstunden (Gesamt)'], '+5:49 h');
failing = true;
await box.fetchStats();
assert.match(box.statsError.value, /veraltet/);
assert.equal(box.displayRows.value['Überstunden (Gesamt)'], '+5:49 h');
});