mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 18:55:42 +00:00
- Introduced StampUpload component for uploading company stamp images. - Integrated stamp upload in contract signing modal, supporting PNG and JPG formats. - Implemented validation for file type and size (max 5 MB). - Added visual feedback for drag-and-drop functionality. - Updated contract-related pages to handle duplicate contract alerts and pricing notices. - Enhanced contract expiry management with a nightly sweep service. - Added unit tests for new features and updated existing tests for contract handling.
82 lines
3.4 KiB
TypeScript
82 lines
3.4 KiB
TypeScript
import { Readable } from 'stream';
|
|
|
|
import { ContractTransitionService } from './contract-transition.service';
|
|
|
|
/**
|
|
* A stamp may be uploaded as JPEG/WebP while a drawn signature is always PNG.
|
|
* The type must survive the round-trip: data URL in → stored object extension
|
|
* → data URL out. Getting this wrong labels JPEG bytes as image/png in the
|
|
* contract PDF and leaves the seal to browser content-sniffing.
|
|
*/
|
|
describe('ContractTransitionService signature/stamp asset typing', () => {
|
|
const pngPixel =
|
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==';
|
|
const jpegPixel = `data:image/jpeg;base64,${Buffer.from('fake-jpeg').toString('base64')}`;
|
|
|
|
/** Minimal service instance — only filesService/minioService are exercised. */
|
|
const build = () => {
|
|
const uploaded: Array<{ code: string; mimetype: string; name: string }> = [];
|
|
const filesService = {
|
|
upsertByCode: jest.fn(({ code, file }) => {
|
|
uploaded.push({ code, mimetype: file.mimetype, name: file.originalname });
|
|
return Promise.resolve({ id: `file-${code}`, url: `https://minio/x/${file.originalname}` });
|
|
}),
|
|
};
|
|
const minioService = {
|
|
getObjectNameFromUrl: (url: string) => url.split('/').pop() ?? '',
|
|
getFileStream: () => Promise.resolve(Readable.from(Buffer.from('bytes'))),
|
|
};
|
|
const service = Object.create(
|
|
ContractTransitionService.prototype,
|
|
) as ContractTransitionService;
|
|
Object.assign(service, { filesService, minioService });
|
|
return { service, uploaded };
|
|
};
|
|
|
|
const contract = { id: 'c-1', reference: 'CTR-2026-00001' };
|
|
|
|
it('stores a drawn PNG signature as image/png', async () => {
|
|
const { service, uploaded } = build();
|
|
await (service as never as {
|
|
uploadSignatureAsset: (c: unknown, code: string, b64: string) => Promise<unknown>;
|
|
}).uploadSignatureAsset(contract, 'signature_customer', pngPixel);
|
|
|
|
expect(uploaded[0].mimetype).toBe('image/png');
|
|
expect(uploaded[0].name).toBe('signature-customer-CTR-2026-00001.png');
|
|
});
|
|
|
|
it('keeps an uploaded JPEG stamp as image/jpeg, not image/png', async () => {
|
|
const { service, uploaded } = build();
|
|
await (service as never as {
|
|
uploadSignatureAsset: (c: unknown, code: string, b64: string) => Promise<unknown>;
|
|
}).uploadSignatureAsset(contract, 'stamp_customer', jpegPixel);
|
|
|
|
expect(uploaded[0].mimetype).toBe('image/jpeg');
|
|
expect(uploaded[0].name).toBe('stamp-customer-CTR-2026-00001.jpg');
|
|
});
|
|
|
|
it('inlines a stored .jpg back as a data:image/jpeg URI', async () => {
|
|
const { service } = build();
|
|
const inline = (service as never as {
|
|
inlineImageUrl: (url?: string | null) => Promise<string | null | undefined>;
|
|
}).inlineImageUrl.bind(service);
|
|
|
|
await expect(inline('https://minio/x/stamp-customer-CTR.jpg')).resolves.toMatch(
|
|
/^data:image\/jpeg;base64,/,
|
|
);
|
|
await expect(inline('https://minio/x/signature-customer-CTR.png')).resolves.toMatch(
|
|
/^data:image\/png;base64,/,
|
|
);
|
|
});
|
|
|
|
it('passes through empty and already-inlined values untouched', async () => {
|
|
const { service } = build();
|
|
const inline = (service as never as {
|
|
inlineImageUrl: (url?: string | null) => Promise<string | null | undefined>;
|
|
}).inlineImageUrl.bind(service);
|
|
|
|
await expect(inline(null)).resolves.toBeNull();
|
|
await expect(inline(pngPixel)).resolves.toBe(pngPixel);
|
|
});
|
|
});
|