Refactor email template rendering to support preview mode and improve CSS handling
All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good

This commit updates the email template rendering logic to conditionally apply a preview wrapper and associated CSS when the preview flag is set. It modifies the `renderEmailTemplate` and `renderTemplate` methods to ensure that the email content is correctly wrapped in a preview structure, while also ensuring that scaling is consistently applied. Additionally, tests are added to verify the correct behavior of the rendering process in both preview and non-preview scenarios, enhancing the overall functionality of the TemplateManager.
This commit is contained in:
Tom Butcher 2026-09-12 23:48:03 +01:00
parent c10281064e
commit 58cb444bf2
2 changed files with 92 additions and 14 deletions

View File

@ -203,11 +203,44 @@ describe('TemplateManager', () => {
);
expect(result.subject).toBe('rendered: Hello');
expect(result.html).toContain('max-width:640px');
expect(result.html).toContain('transformed: rendered: <Text>Body</Text>');
expect(result.html).toContain('width: 640px');
expect(result.html).toContain('class="preview"');
expect(ejs.render).toHaveBeenCalledWith(
'<div class="preview"><%- content %></div>',
expect.objectContaining({
content: 'transformed: rendered: <Text>Body</Text>',
}),
expect.objectContaining({ async: true })
);
expect(result).not.toHaveProperty('height');
expect(result).not.toHaveProperty('intellisense');
});
it('does not wrap sent email HTML in the preview template', async () => {
getObject.mockResolvedValue({
_reference: 'EMAIL1',
active: true,
global: true,
subject: 'Hello',
content: '<Text>Body</Text>',
});
const result = await templateManager.renderEmailTemplate(
'email-template-id',
undefined,
undefined,
{ name: 'Test' },
{},
false
);
expect(result.html).toContain('transformed: rendered: <Text>Body</Text>');
expect(ejs.render).not.toHaveBeenCalledWith(
'<div class="preview"><%- content %></div>',
expect.anything(),
expect.anything()
);
});
});
describe('renderTemplate', () => {
@ -230,6 +263,40 @@ describe('TemplateManager', () => {
expect(result.width).toBe(100);
expect(result.height).toBe(100);
expect(result).not.toHaveProperty('intellisense');
expect(ejs.render).toHaveBeenCalledWith(
'<html><%- content %></html>',
expect.objectContaining({
scale: '1',
scaledWidth: '100mm',
scaledPageWidth: '100mm',
}),
expect.anything()
);
});
it('does not apply zoom in preview HTML even when a scale is requested', async () => {
getObject.mockResolvedValue({
documentSize: { width: 100, height: 100, infiniteHeight: false },
global: false,
objectType: 'printer',
});
await templateManager.renderTemplate(
'temp-id',
'some content',
{ name: 'Test' },
2.5
);
expect(ejs.render).toHaveBeenCalledWith(
'<html><%- content %></html>',
expect.objectContaining({
scale: '1',
scaledWidth: '100mm',
scaledHeight: '100mm',
}),
expect.anything()
);
});
it('does not return intellisense when preview render throws', async () => {

View File

@ -786,18 +786,29 @@ export class TemplateManager {
pixelId != null
? `<img src="${config.app.urlApi}/emailmessages/${pixelId}/pixel" width="1" height="1" style="display:none" />`
: '';
const emailContent =
preview === true
? `<div style="max-width:${width}px;margin:0 auto">${transformed}</div>`
: transformed;
let emailContent = transformed;
let previewCss = '';
if (preview === true) {
emailContent = await ejs.render(previewTemplate, { content: transformed }, renderOptions);
previewCss = `
.previewWrapper {
width: ${width}px;
height: auto;
}
.previewDocument {
width: ${width}px;
height: auto;
overflow: visible;
}`;
}
const outerHtml = (css, content, pixel = undefined) => {
return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>${css}</style></head><body>${pixel != undefined ? pixel : ''}${content}</body></html>`;
};
return {
subject: renderedSubject,
html: outerHtml(baseCSS, emailContent),
htmlWithPixel: outerHtml(baseCSS, emailContent, renderedPixel),
html: outerHtml(`${baseCSS}${previewCss}`, emailContent),
htmlWithPixel: outerHtml(`${baseCSS}${previewCss}`, emailContent, renderedPixel),
};
} catch (error) {
logger.warn('Error whilst rendering email template:', error.message);
@ -841,7 +852,7 @@ export class TemplateManager {
return object;
}
async renderTemplate(id, content, data = {}, scale = 1, options = {}, preview = true) {
async renderTemplate(id, content, data = {}, _scale = 1, options = {}, preview = true) {
try {
const {
padding: paddingRequested = false,
@ -994,15 +1005,15 @@ export class TemplateManager {
content: innerHtml,
width: `${documentSize.width}mm`,
height: infiniteHeight ? 'fit-content' : `${documentSize.height}mm`,
scaledWidth: `${documentSize.width * scale}mm`,
scaledHeight: infiniteHeight ? 'auto' : `${documentSize.height * scale}mm`,
scaledPageWidth: `${pageWidth * scale}mm`,
scaledPageHeight: infiniteHeight ? 'auto' : `${pageHeight * scale}mm`,
scaledWidth: `${documentSize.width}mm`,
scaledHeight: infiniteHeight ? 'auto' : `${documentSize.height}mm`,
scaledPageWidth: `${pageWidth}mm`,
scaledPageHeight: infiniteHeight ? 'auto' : `${pageHeight}mm`,
paddingTop: `${padding.paddingTop}mm`,
paddingRight: `${padding.paddingRight}mm`,
paddingBottom: `${padding.paddingBottom}mm`,
paddingLeft: `${padding.paddingLeft}mm`,
scale: `${scale}`,
scale: '1',
baseCSS: baseCSS,
infiniteHeight: infiniteHeight,
pageWidth: `${pageWidth}mm`,