Files
atomic-design-poc/apps/ssp/src/app/brief/application/org-template.store.spec.ts
T
ehoandClaude Sonnet 5 dd11eafe50 refactor: strip WP-/RB- ticket refs from apps and libs (RD-18)
204 WP-NN/RB-NN comments named a closed ticket instead of the code they
sit next to. git blame already records history and stays correct when
code moves; the comment does not. This sweep removes the reference and
keeps the sentence, across 95 files in apps/ and libs/ plus the
behaviour-spec generator's header text.

Eleven references stay: five story files justify an a11y disable per
the README's rule, and one line in a11y.mdx documents that convention.
Two sentences needed a rewrite, not a deletion, so the reference's
meaning survives its removal. behaviour-spec.mdx is regenerated, not
hand-edited.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 21:23:07 +02:00

121 lines
4.4 KiB
TypeScript

import { TestBed } from '@angular/core/testing';
import { describe, it, expect } from 'vitest';
import { Result, ok } from '@shared/kernel/fp';
import { BLOB_PRESENTER, BlobPresenter } from '@shared/application/blob-presenter';
import { UploadAdapter } from '@shared/infrastructure/upload.adapter';
import { UploadShellService } from '@shared/application/upload-shell.service';
import { OrgTemplate, OrgTemplateAdminView, SubOrgSummary } from '@brief/domain/org-template';
import { OrgTemplateAdapter } from '@brief/infrastructure/org-template.adapter';
import { OrgTemplateStore } from './org-template.store';
const template: OrgTemplate = {
subOrgId: 'cibg-registers',
orgName: 'CIBG — Registers',
returnAddress: 'Postbus 00000\n2500 AA Den Haag',
footerContact: 'info@voorbeeld.example',
footerLegal: 'KvK 00000000',
signatureName: 'A. de Vries',
signatureRole: 'Hoofd Registratie',
signatureClosing: 'Met vriendelijke groet,',
margins: { topMm: 25, rightMm: 25, bottomMm: 25, leftMm: 25 },
version: 1,
};
const view: OrgTemplateAdminView = {
draft: template,
publishedVersion: 1,
history: [],
unsentBriefs: 0,
};
const subOrgs: SubOrgSummary[] = [
{ subOrgId: 'cibg-registers', orgName: 'CIBG', publishedVersion: 1 },
];
/** A recording fake of BLOB_PRESENTER (TE-006) — records every call instead of
touching the DOM, so a spec can assert a command's success path directly. */
function fakeBlobPresenter() {
const opened: Blob[] = [];
const presenter: BlobPresenter = {
open: (blob) => opened.push(blob),
download: () => {
throw new Error('not used by OrgTemplateStore');
},
};
return { presenter, opened };
}
/** A no-op categories resource: the logo-upload sub-state is untouched by these
tests, so 'idle' (never resolved) keeps the constructor effect from dispatching. */
function fakeCategoriesResource(): ReturnType<UploadAdapter['categoriesResource']> {
const fake = { status: () => 'idle' as const, value: () => undefined };
return fake as unknown as ReturnType<UploadAdapter['categoriesResource']>;
}
function setup(
adapter: Partial<OrgTemplateAdapter>,
blobPresenter: BlobPresenter,
): OrgTemplateStore {
const uploadAdapter: Partial<UploadAdapter> = {
categoriesResource: () => fakeCategoriesResource(),
};
TestBed.configureTestingModule({
providers: [
{ provide: OrgTemplateAdapter, useValue: adapter },
{ provide: UploadAdapter, useValue: uploadAdapter },
{ provide: UploadShellService, useValue: {} },
{ provide: BLOB_PRESENTER, useValue: blobPresenter },
],
});
return TestBed.inject(OrgTemplateStore);
}
// --- TE-006: proefbrief() ends in BLOB_PRESENTER.open, not a raw
// window.open(URL.createObjectURL(...)) call, so both outcomes are assertable. ---
describe('OrgTemplateStore.proefbrief', () => {
it('opens the rendered proefbrief via BLOB_PRESENTER on success', async () => {
// Given a loaded sub-org template.
const { presenter, opened } = fakeBlobPresenter();
const blob = new Blob(['<html></html>'], { type: 'text/html' });
const store = setup(
{
list: (): Promise<Result<string, SubOrgSummary[]>> => Promise.resolve(ok(subOrgs)),
load: (): Promise<Result<string, OrgTemplateAdminView>> => Promise.resolve(ok(view)),
proefbrief: (): Promise<Result<string, Blob>> => Promise.resolve(ok(blob)),
},
presenter,
);
await store.load();
// When proefbrief() is called...
await store.proefbrief();
// Then the presenter receives exactly the rendered blob, and no error surfaces.
expect(opened).toEqual([blob]);
expect(store.lastError()).toBeNull();
});
it('surfaces the error without opening a tab on failure', async () => {
// Given a loaded sub-org template whose proefbrief call fails server-side.
const { presenter, opened } = fakeBlobPresenter();
const store = setup(
{
list: (): Promise<Result<string, SubOrgSummary[]>> => Promise.resolve(ok(subOrgs)),
load: (): Promise<Result<string, OrgTemplateAdminView>> => Promise.resolve(ok(view)),
proefbrief: (): Promise<Result<string, Blob>> =>
Promise.resolve({ ok: false, error: 'mislukt' }),
},
presenter,
);
await store.load();
// When proefbrief() is called...
await store.proefbrief();
// Then the presenter is never reached and the error is surfaced.
expect(opened).toHaveLength(0);
expect(store.lastError()).toBe('mislukt');
});
});