Legg til dagsplan + kommentar ved statusbytte
Dagsplan: tredje visning ved siden av tavle/tabell. To-kolonne planlegger der man plukker oppgaver fra backlog og bygger en ordnet plan for dagen (drag-sortering, kapasitet i timer, datovelger). Per bruker, per dato, lagret i plans.json; synkes mellom egne faner via plan-WS-melding. Kommentar ved statusbytte: når en oppgave markeres ferdig/blokkert (knapp, kanban-drag eller modal) tilbys en valgfri kommentar som festes til oppgaven (kind:status) og legges i aktivitetsloggen — atomisk i ett update-kall. 20 integrasjonstester (8 nye), alle grønne. README + CLAUDE.md oppdatert. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6b9dbf8254
commit
be166034de
8 changed files with 686 additions and 32 deletions
|
|
@ -189,6 +189,105 @@ test('replace erstatter alt og persisterer til disk', async () => {
|
|||
assert.equal(onDisk[0].title, 'Eneste oppgave');
|
||||
});
|
||||
|
||||
test('update med comment legger til status-kommentar og aktivitets-detalj', async () => {
|
||||
let res = await req('POST', '/api/tasks', { title: 'Kommentar-ved-status' });
|
||||
const { task } = await res.json();
|
||||
|
||||
res = await req('PUT', `/api/tasks/${task.id}`, { v: task.v, status: 'done', comment: 'Verifisert i lab, rullet ut' });
|
||||
assert.equal(res.status, 200);
|
||||
const { task: done } = await res.json();
|
||||
assert.equal(done.status, 'done');
|
||||
assert.equal(done.comments.length, 1);
|
||||
assert.equal(done.comments[0].kind, 'status');
|
||||
assert.equal(done.comments[0].status, 'done');
|
||||
assert.equal(done.comments[0].text, 'Verifisert i lab, rullet ut');
|
||||
|
||||
// Aktivitetsloggen skal nevne kommentaren
|
||||
res = await req('GET', '/api/state');
|
||||
const { activity } = await res.json();
|
||||
const entry = activity.find(a => a.taskId === task.id && a.action === 'done');
|
||||
assert.ok(entry.detail.some(d => d.includes('Verifisert i lab')));
|
||||
});
|
||||
|
||||
test('update uten comment legger ikke til kommentar', async () => {
|
||||
let res = await req('POST', '/api/tasks', { title: 'Ingen-kommentar' });
|
||||
const { task } = await res.json();
|
||||
res = await req('PUT', `/api/tasks/${task.id}`, { v: task.v, status: 'progress' });
|
||||
const { task: upd } = await res.json();
|
||||
assert.equal(upd.comments.length, 0);
|
||||
});
|
||||
|
||||
test('dagsplan: tom som standard, sett ordnet liste, hent tilbake', async () => {
|
||||
// Lag to oppgaver å planlegge
|
||||
const a = (await (await req('POST', '/api/tasks', { title: 'Plan A' })).json()).task;
|
||||
const b = (await (await req('POST', '/api/tasks', { title: 'Plan B' })).json()).task;
|
||||
|
||||
let res = await req('GET', '/api/plan?date=2026-06-15');
|
||||
assert.equal(res.status, 200);
|
||||
assert.deepEqual((await res.json()).items, []);
|
||||
|
||||
res = await req('PUT', '/api/plan', { date: '2026-06-15', items: [b.id, a.id] });
|
||||
assert.equal(res.status, 200);
|
||||
assert.deepEqual((await res.json()).items, [b.id, a.id]); // rekkefølge bevart
|
||||
|
||||
res = await req('GET', '/api/plan?date=2026-06-15');
|
||||
assert.deepEqual((await res.json()).items, [b.id, a.id]);
|
||||
});
|
||||
|
||||
test('dagsplan: er per bruker', async () => {
|
||||
const a = (await (await req('POST', '/api/tasks', { title: 'Privat plan' })).json()).task;
|
||||
await req('PUT', '/api/plan', { date: '2026-06-16', items: [a.id] }, { 'X-Remote-User': 'jon' });
|
||||
|
||||
// Annen bruker ser sin egen (tomme) plan
|
||||
const res = await req('GET', '/api/plan?date=2026-06-16', null, { 'X-Remote-User': 'kari' });
|
||||
assert.deepEqual((await res.json()).items, []);
|
||||
});
|
||||
|
||||
test('dagsplan: ugyldige/dupliserte/ukjente ider filtreres bort', async () => {
|
||||
const a = (await (await req('POST', '/api/tasks', { title: 'Gyldig plan-task' })).json()).task;
|
||||
const res = await req('PUT', '/api/plan', {
|
||||
date: '2026-06-17', items: [a.id, a.id, 't_finnesikke', 12345, null],
|
||||
});
|
||||
assert.deepEqual((await res.json()).items, [a.id]); // dedupet + kun eksisterende
|
||||
});
|
||||
|
||||
test('dagsplan: sletting av oppgave fjerner den fra planen', async () => {
|
||||
const a = (await (await req('POST', '/api/tasks', { title: 'Skal slettes fra plan' })).json()).task;
|
||||
await req('PUT', '/api/plan', { date: '2026-06-18', items: [a.id] });
|
||||
await req('DELETE', `/api/tasks/${a.id}`);
|
||||
const res = await req('GET', '/api/plan?date=2026-06-18');
|
||||
assert.deepEqual((await res.json()).items, []);
|
||||
});
|
||||
|
||||
test('dagsplan: ugyldig dato faller tilbake til i dag', async () => {
|
||||
const res = await req('GET', '/api/plan?date=tull');
|
||||
const data = await res.json();
|
||||
assert.match(data.date, /^\d{4}-\d{2}-\d{2}$/);
|
||||
});
|
||||
|
||||
test('websocket: dagsplan-endring pushes kun til samme bruker', async () => {
|
||||
const wsUrl = baseUrl.replace('http', 'ws') + '/ws';
|
||||
const mk = user => new Promise((resolve, reject) => {
|
||||
const c = new WebSocket(wsUrl, { headers: { 'X-Remote-User': user } });
|
||||
c.msgs = [];
|
||||
c.on('message', raw => c.msgs.push(JSON.parse(raw)));
|
||||
c.on('open', () => resolve(c));
|
||||
c.on('error', reject);
|
||||
});
|
||||
const jon = await mk('testuser'); // samme bruker som req() bruker
|
||||
const kari = await mk('kari');
|
||||
await new Promise(r => setTimeout(r, 150));
|
||||
|
||||
const a = (await (await req('POST', '/api/tasks', { title: 'WS-plan' })).json()).task;
|
||||
await req('PUT', '/api/plan', { date: '2026-06-19', items: [a.id] });
|
||||
await new Promise(r => setTimeout(r, 150));
|
||||
|
||||
assert.ok(jon.msgs.some(m => m.type === 'plan' && m.items.includes(a.id)), 'jon fikk plan-push');
|
||||
assert.ok(!kari.msgs.some(m => m.type === 'plan'), 'kari skal ikke få plan-push');
|
||||
|
||||
jon.close(); kari.close();
|
||||
});
|
||||
|
||||
test('cookie-auth: login setter cookie, ugyldig navn avvises', async () => {
|
||||
let res = await fetch(baseUrl + '/api/login', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue