Files

72 lines
2.5 KiB
TypeScript

import { expect, test } from "bun:test";
import { importMjml, renderEmail } from "../src/index.ts";
const parse = (columns: string) => {
let id = 0;
return importMjml(`<mjml><mj-body><mj-section>${columns}</mj-section></mj-body></mjml>`, {
subject: "Imported",
id: () => `n${++id}`,
});
};
test("imported columns form one native row with preserved order and exact width totals", async () => {
for (const widths of [
["", ""],
["33%", ""],
["", "", ""],
["25%", "75%"],
]) {
const result = parse(
widths
.map(
(width, index) =>
`<mj-column${width ? ` width="${width}"` : ""}><mj-text>Column ${index}</mj-text></mj-column>`,
)
.join(""),
);
expect(result.ok).toBe(true);
if (!result.ok) continue;
const row = result.value.root.slots.content[0]!.slots.content[0]!;
expect(row.type).toBe("layout.columns");
expect(row.slots.columns.map((column) => column.slots.content[0]!.props.text)).toEqual(
widths.map((_, index) => `Column ${index}`),
);
expect(
row.slots.columns.reduce(
(sum, column) => sum + Number.parseFloat(String(column.props.width)),
0,
),
).toBeCloseTo(100, 8);
const rendered = await renderEmail(result.value, { mjml: true });
expect(rendered.ok).toBe(true);
if (rendered.ok) expect(rendered.value.exports?.mjml).toContain("<mj-column");
}
});
test("import rejects invalid URLs, dimensions, widths, and native slot limits", () => {
for (const input of [
'<mj-column><mj-button href="javascript:alert(1)">Bad</mj-button></mj-column>',
'<mj-column><mj-image src="https://example.com/x.png" width="99999px" /></mj-column>',
'<mj-column width="75%"></mj-column><mj-column width="75%"></mj-column>',
'<mj-column width="400px"></mj-column>',
Array(5).fill("<mj-column><mj-text>X</mj-text></mj-column>").join(""),
])
expect(parse(input).ok).toBe(false);
});
test("import enforces depth limits and rejects repeated instance IDs", () => {
const nested =
"<mjml><mj-body>" +
"<mj-wrapper>".repeat(100) +
"<mj-section><mj-column><mj-text>X</mj-text></mj-column></mj-section>" +
"</mj-wrapper>".repeat(100) +
"</mj-body></mjml>";
expect(importMjml(nested, { subject: "Depth", id: () => "id" }).ok).toBe(false);
expect(
importMjml(
"<mjml><mj-body><mj-section><mj-column><mj-text>X</mj-text></mj-column></mj-section></mj-body></mjml>",
{ subject: "IDs", id: () => "same" },
).ok,
).toBe(false);
});