mirror of
https://github.com/pdfme/pdfme.git
synced 2026-06-03 11:56:09 -04:00
* feat(jsx): add pdfme jsx package * refactor(jsx): derive text props from schema types * fix(jsx): tighten mvp layout constraints * feat(jsx): measure text height with schema helpers * docs: update jsx md2pdf roadmap
151 lines
4.8 KiB
TypeScript
151 lines
4.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { join, dirname } from 'node:path';
|
|
import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { PDFDocument } from '@pdfme/pdf-lib';
|
|
import { a4BasePdf } from './helpers.js';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const CLI = join(__dirname, '..', 'dist', 'index.js');
|
|
const TMP = join(__dirname, '..', '.test-tmp-contract');
|
|
|
|
function runCli(
|
|
args: string[],
|
|
options: { input?: string; env?: NodeJS.ProcessEnv } = {},
|
|
): { stdout: string; stderr: string; exitCode: number } {
|
|
try {
|
|
const stdout = execFileSync('node', [CLI, ...args], {
|
|
encoding: 'utf8',
|
|
timeout: 30000,
|
|
input: options.input,
|
|
env: options.env,
|
|
});
|
|
return { stdout, stderr: '', exitCode: 0 };
|
|
} catch (error: any) {
|
|
return {
|
|
stdout: error.stdout ?? '',
|
|
stderr: error.stderr ?? '',
|
|
exitCode: error.status ?? 1,
|
|
};
|
|
}
|
|
}
|
|
|
|
describe('CLI contract failures', () => {
|
|
beforeAll(() => {
|
|
mkdirSync(TMP, { recursive: true });
|
|
});
|
|
|
|
afterAll(() => {
|
|
rmSync(TMP, { recursive: true, force: true });
|
|
});
|
|
|
|
it.each([
|
|
{ name: 'generate', args: ['generate', '--bogus', '--json'] },
|
|
{ name: 'validate', args: ['validate', '--bogus', '--json'] },
|
|
{ name: 'pdf2img', args: ['pdf2img', '--bogus', '--json'] },
|
|
{ name: 'pdf2size', args: ['pdf2size', '--bogus', '--json'] },
|
|
{ name: 'examples', args: ['examples', '--bogus', '--json'] },
|
|
])('returns structured EARG for unknown flags: $name', ({ args }) => {
|
|
const result = runCli(args);
|
|
|
|
expect(result.exitCode).toBe(1);
|
|
const parsed = JSON.parse(result.stdout);
|
|
expect(parsed.ok).toBe(false);
|
|
expect(parsed.error.code).toBe('EARG');
|
|
expect(parsed.error.message).toContain('Unknown argument');
|
|
});
|
|
|
|
it('reports malformed unified job JSON with structured EIO', () => {
|
|
const jobPath = join(TMP, 'malformed-job.json');
|
|
writeFileSync(jobPath, '{"template": ');
|
|
|
|
const result = runCli(['generate', jobPath, '-o', join(TMP, 'out.pdf'), '--json']);
|
|
|
|
expect(result.exitCode).toBe(3);
|
|
const parsed = JSON.parse(result.stdout);
|
|
expect(parsed.ok).toBe(false);
|
|
expect(parsed.error.code).toBe('EIO');
|
|
expect(parsed.error.message).toContain('Failed to parse JSON file');
|
|
});
|
|
|
|
it('reports malformed stdin JSON for validate with structured EIO', () => {
|
|
const result = runCli(['validate', '-', '--json'], {
|
|
input: '{"basePdf": ',
|
|
});
|
|
|
|
expect(result.exitCode).toBe(3);
|
|
const parsed = JSON.parse(result.stdout);
|
|
expect(parsed.ok).toBe(false);
|
|
expect(parsed.error.code).toBe('EIO');
|
|
expect(parsed.error.message).toContain('Failed to parse JSON from stdin');
|
|
});
|
|
|
|
it('returns structured EARG when generate has no input source', () => {
|
|
const result = runCli(['generate', '-o', join(TMP, 'no-input.pdf'), '--json']);
|
|
|
|
expect(result.exitCode).toBe(1);
|
|
const parsed = JSON.parse(result.stdout);
|
|
expect(parsed.ok).toBe(false);
|
|
expect(parsed.error.code).toBe('EARG');
|
|
expect(parsed.error.message).toContain('No input provided');
|
|
});
|
|
|
|
it('rejects invalid image format enum for generate', () => {
|
|
const jobPath = join(TMP, 'invalid-image-format.json');
|
|
writeFileSync(
|
|
jobPath,
|
|
JSON.stringify({
|
|
template: {
|
|
basePdf: a4BasePdf(),
|
|
schemas: [
|
|
[
|
|
{
|
|
name: 'title',
|
|
type: 'text',
|
|
position: { x: 20, y: 20 },
|
|
width: 100,
|
|
height: 10,
|
|
},
|
|
],
|
|
],
|
|
},
|
|
inputs: [{ title: 'Hello' }],
|
|
}),
|
|
);
|
|
|
|
const result = runCli([
|
|
'generate',
|
|
jobPath,
|
|
'--imageFormat',
|
|
'gif',
|
|
'-o',
|
|
join(TMP, 'invalid-image-format.pdf'),
|
|
'--json',
|
|
]);
|
|
|
|
expect(result.exitCode).toBe(1);
|
|
const parsed = JSON.parse(result.stdout);
|
|
expect(parsed.ok).toBe(false);
|
|
expect(parsed.error.code).toBe('EARG');
|
|
expect(parsed.error.message).toContain('--imageFormat');
|
|
});
|
|
|
|
it('rejects pdf2img output paths that are not directories', async () => {
|
|
const pdfPath = join(TMP, 'sample.pdf');
|
|
const outputPath = join(TMP, 'not-a-directory.txt');
|
|
const pdfDoc = await PDFDocument.create();
|
|
pdfDoc.addPage([595.28, 841.89]);
|
|
writeFileSync(pdfPath, await pdfDoc.save());
|
|
writeFileSync(outputPath, 'existing file');
|
|
|
|
const result = runCli(['pdf2img', pdfPath, '-o', outputPath, '--json']);
|
|
|
|
expect(result.exitCode).toBe(3);
|
|
const parsed = JSON.parse(result.stdout);
|
|
expect(parsed.ok).toBe(false);
|
|
expect(parsed.error.code).toBe('EIO');
|
|
expect(parsed.error.message).toContain('Output path must be a directory');
|
|
});
|
|
});
|