[verified] fix: use system Chrome for PDF generation

This commit is contained in:
alexpolo1
2026-08-27 12:21:09 +02:00
parent ca8eb008d6
commit 1c44a514b4
2 changed files with 47 additions and 0 deletions

View File

@@ -114,6 +114,32 @@ describe('PdfGenerationService P0 contract', () => {
expect(html).not.toContain('Ingen materialelinjer registreret');
});
test('uses a configured Chrome executable for Puppeteer', () => {
const fs = require('fs');
const previousPath = process.env.PUPPETEER_EXECUTABLE_PATH;
process.env.PUPPETEER_EXECUTABLE_PATH = '/custom/chrome';
const exists = jest.spyOn(fs, 'existsSync').mockImplementation(filePath => filePath === '/custom/chrome');
expect(service.resolvePuppeteerExecutablePath()).toBe('/custom/chrome');
exists.mockRestore();
if (previousPath === undefined) delete process.env.PUPPETEER_EXECUTABLE_PATH;
else process.env.PUPPETEER_EXECUTABLE_PATH = previousPath;
});
test('discovers Chrome from PATH', () => {
const fs = require('fs');
const previousExecutable = process.env.PUPPETEER_EXECUTABLE_PATH;
const previousPath = process.env.PATH;
delete process.env.PUPPETEER_EXECUTABLE_PATH;
process.env.PATH = '/custom/bin';
const exists = jest.spyOn(fs, 'existsSync').mockImplementation(filePath => filePath === '/custom/bin/google-chrome');
expect(service.resolvePuppeteerExecutablePath()).toBe('/custom/bin/google-chrome');
exists.mockRestore();
if (previousPath === undefined) delete process.env.PATH;
else process.env.PATH = previousPath;
if (previousExecutable === undefined) delete process.env.PUPPETEER_EXECUTABLE_PATH;
else process.env.PUPPETEER_EXECUTABLE_PATH = previousExecutable;
});
test('converts Puppeteer Uint8Array output to a raw PDF Buffer', async () => {
const pdfBytes = new Uint8Array(Buffer.from('%PDF-1.7\nfixture'));
const close = jest.fn();
@@ -124,11 +150,13 @@ describe('PdfGenerationService P0 contract', () => {
}),
close
});
jest.spyOn(service, 'resolvePuppeteerExecutablePath').mockReturnValue('/resolved/chrome');
const result = await service.generateQuotePdf(buildData());
expect(Buffer.isBuffer(result)).toBe(true);
expect(result.subarray(0, 5).toString()).toBe('%PDF-');
expect(puppeteer.launch).toHaveBeenCalledWith(expect.objectContaining({ executablePath: '/resolved/chrome' }));
expect(close).toHaveBeenCalled();
});

View File

@@ -11,6 +11,23 @@ class PdfGenerationService {
this.maxPages = 3;
}
resolvePuppeteerExecutablePath() {
const browserNames = ['google-chrome-stable', 'google-chrome', 'chromium', 'chromium-browser'];
const pathCandidates = String(process.env.PATH || '')
.split(path.delimiter)
.filter(Boolean)
.flatMap(directory => browserNames.map(browserName => path.join(directory, browserName)));
const candidates = [
process.env.PUPPETEER_EXECUTABLE_PATH,
...pathCandidates,
'/usr/bin/google-chrome-stable',
'/usr/bin/google-chrome',
'/usr/bin/chromium',
'/usr/bin/chromium-browser'
];
return candidates.find(candidate => candidate && fs.existsSync(candidate));
}
// Convert image to base64
async imageToBase64(imagePath) {
try {
@@ -768,8 +785,10 @@ class PdfGenerationService {
const html = await this.generatePdfHtml(data);
// Launch puppeteer with better error handling
const executablePath = this.resolvePuppeteerExecutablePath();
browser = await puppeteer.launch({
headless: 'new',
...(executablePath ? { executablePath } : {}),
args: [
'--no-sandbox',
'--disable-setuid-sandbox',