Files
atomic-design-poc/src/app/brief/domain/org-template.machine.spec.ts
T
ehoandClaude Opus 4.8 5761b13dd2 style: format the repo with prettier (green format:check)
`npm run format:check` (a CI gate) had drifted red across 44 files — pre-existing
files plus recently-added ones committed without formatting. Ran `prettier --write .`;
no logic changes. Also regenerates documentation.json (compodoc reflects the reformatted
component sources).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:42 +02:00

145 lines
5.2 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { OrgTemplate, OrgTemplateAdminView } from './org-template';
import { OrgTemplateState, reduce } from './org-template.machine';
import { DocumentCategory } from '@shared/upload/upload.machine';
const template: OrgTemplate = {
subOrgId: 'cibg-registers',
orgName: 'CIBG',
returnAddress: 'Postbus 1\n2500 AA Den Haag',
footerContact: 'info@cibg.nl',
footerLegal: 'CIBG is onderdeel van VWS',
signatureName: 'A. de Vries',
signatureRole: 'Hoofd Registratie',
signatureClosing: 'Met vriendelijke groet,',
margins: { topMm: 25, rightMm: 20, bottomMm: 25, leftMm: 20 },
version: 3,
};
const view = (over: Partial<OrgTemplateAdminView> = {}): OrgTemplateAdminView => ({
draft: template,
publishedVersion: 3,
history: [],
unsentBriefs: 2,
...over,
});
const loaded = (): OrgTemplateState =>
reduce({ tag: 'loading' }, { tag: 'DraftLoaded', view: view() });
const logoCategory: DocumentCategory = {
categoryId: 'org-logo',
label: 'Logo',
description: '',
required: false,
acceptedTypes: ['image/png'],
maxSizeMb: 2,
multiple: false,
allowPostDelivery: false,
};
describe('org-template.machine', () => {
it('DraftLoaded moves to loaded with the draft, clean', () => {
const s = loaded();
expect(s.tag).toBe('loaded');
if (s.tag !== 'loaded') return;
expect(s.draft.orgName).toBe('CIBG');
expect(s.subOrgId).toBe('cibg-registers');
expect(s.unsentBriefs).toBe(2);
expect(s.dirty).toBe(false);
});
it('LoadFailed carries the reason', () => {
const s = reduce({ tag: 'loading' }, { tag: 'LoadFailed', reason: 'boom' });
expect(s).toEqual({ tag: 'failed', reason: 'boom' });
});
it('FieldEdited edits the draft and marks dirty', () => {
const s = reduce(loaded(), { tag: 'FieldEdited', field: 'orgName', value: 'CIBG Nieuw' });
expect(s.tag === 'loaded' && s.draft.orgName).toBe('CIBG Nieuw');
expect(s.tag === 'loaded' && s.dirty).toBe(true);
});
it('MarginEdited edits one edge and marks dirty', () => {
const s = reduce(loaded(), { tag: 'MarginEdited', edge: 'topMm', value: 40 });
expect(s.tag === 'loaded' && s.draft.margins.topMm).toBe(40);
expect(s.tag === 'loaded' && s.draft.margins.leftMm).toBe(20);
expect(s.tag === 'loaded' && s.dirty).toBe(true);
});
it('DraftSaved clears dirty when the saved draft is the current one', () => {
const edited = reduce(loaded(), { tag: 'FieldEdited', field: 'orgName', value: 'X' });
const savedDraft = edited.tag === 'loaded' ? edited.draft : template;
const s = reduce(edited, { tag: 'DraftSaved', savedDraft });
expect(s.tag === 'loaded' && s.dirty).toBe(false);
expect(s.tag === 'loaded' && s.draft.orgName).toBe('X');
});
it('DraftSaved keeps dirty when an edit landed during the save round-trip', () => {
const editing = reduce(loaded(), { tag: 'FieldEdited', field: 'orgName', value: 'X' });
const savedDraft = editing.tag === 'loaded' ? editing.draft : template;
// a further edit changes the draft reference before the save resolves
const raced = reduce(editing, { tag: 'FieldEdited', field: 'orgName', value: 'Y' });
const s = reduce(raced, { tag: 'DraftSaved', savedDraft });
expect(s.tag === 'loaded' && s.dirty).toBe(true);
});
it('edits are no-ops in non-loaded states', () => {
expect(
reduce({ tag: 'loading' }, { tag: 'FieldEdited', field: 'orgName', value: 'x' }),
).toEqual({
tag: 'loading',
});
});
it('a completed logo upload sets logoDocumentId + dirty', () => {
const withCat = reduce(loaded(), {
tag: 'Upload',
msg: { type: 'CategoriesLoaded', categories: [logoCategory] },
});
const selected = reduce(withCat, {
tag: 'Upload',
msg: {
type: 'FileSelected',
categoryId: 'org-logo',
localId: 'a',
fileName: 'l.png',
fileSizeMb: 0.1,
},
});
const done = reduce(selected, {
tag: 'Upload',
msg: { type: 'UploadComplete', localId: 'a', documentId: 'doc-1' },
});
expect(done.tag === 'loaded' && done.draft.logoDocumentId).toBe('doc-1');
expect(done.tag === 'loaded' && done.dirty).toBe(true);
});
it('removing the logo clears logoDocumentId + dirty', () => {
const withLogo = reduce(loaded(), {
tag: 'Upload',
msg: { type: 'UploadComplete', localId: 'a', documentId: 'doc-1' },
});
const removed = reduce(withLogo, {
tag: 'Upload',
msg: { type: 'UploadRemoved', localId: 'a' },
});
expect(removed.tag === 'loaded' && removed.draft.logoDocumentId).toBeUndefined();
expect(removed.tag === 'loaded' && removed.dirty).toBe(true);
});
it('DraftLoaded (sub-org switch) keeps the loaded logo category, drops uploads', () => {
const withCat = reduce(loaded(), {
tag: 'Upload',
msg: { type: 'CategoriesLoaded', categories: [logoCategory] },
});
const switched = reduce(withCat, {
tag: 'DraftLoaded',
view: view({ draft: { ...template, subOrgId: 'cibg-vakbekwaamheid' } }),
});
expect(switched.tag === 'loaded' && switched.upload.categories).toHaveLength(1);
expect(switched.tag === 'loaded' && switched.upload.uploads).toHaveLength(0);
expect(switched.tag === 'loaded' && switched.subOrgId).toBe('cibg-vakbekwaamheid');
});
});