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>
121 lines
4.4 KiB
TypeScript
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');
|
|
});
|
|
});
|