108 lines
4.9 KiB
JavaScript
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');
|
|
});
|