react-email
Version:
A live preview of your emails right in your browser.
151 lines (133 loc) • 4.35 kB
text/typescript
import { generate, type Rule } from 'css-tree';
import { setupTailwind } from '../tailwindcss/setup-tailwind.js';
import { extractRulesPerClass } from './extract-rules-per-class.js';
describe('extractRulesPerClass()', async () => {
function convertToComparable(map: Map<string, Rule>): Record<string, string> {
return Object.fromEntries(map.entries().map(([k, v]) => [k, generate(v)]));
}
it('works with just inlinable utilities', async () => {
const tailwind = await setupTailwind({});
const classes = ['text-center', 'bg-red-500'];
tailwind.addUtilities(classes);
const stylesheet = tailwind.getStyleSheet();
const { inlinable, nonInlinable } = extractRulesPerClass(
stylesheet,
classes,
);
expect(convertToComparable(inlinable)).toMatchInlineSnapshot(`
{
"bg-red-500": ".bg-red-500{background-color:var(--color-red-500)}",
"text-center": ".text-center{text-align:center}",
}
`);
expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot('{}');
});
it('handles non-inlinable utilities', async () => {
const tailwind = await setupTailwind({});
const classes = ['lg:w-1/2'];
tailwind.addUtilities(classes);
const stylesheet = tailwind.getStyleSheet();
const { inlinable, nonInlinable } = extractRulesPerClass(
stylesheet,
classes,
);
expect(convertToComparable(inlinable)).toMatchInlineSnapshot('{}');
expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
{
"lg:w-1/2": ".lg\\:w-1\\/2{@media (width>=64rem){width:calc(1/2*100%)}}",
}
`);
});
it('handles a mix of inlinable and non-inlinable utilities', async () => {
const tailwind = await setupTailwind({});
const classes = [
'text-center',
'bg-red-500',
'some-other-class', // should be ignored
'w-full',
'lg:w-1/2',
];
tailwind.addUtilities(classes);
const stylesheet = tailwind.getStyleSheet();
const { inlinable, nonInlinable } = extractRulesPerClass(
stylesheet,
classes,
);
expect(convertToComparable(inlinable)).toMatchInlineSnapshot(`
{
"bg-red-500": ".bg-red-500{background-color:var(--color-red-500)}",
"text-center": ".text-center{text-align:center}",
"w-full": ".w-full{width:100%}",
}
`);
expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
{
"lg:w-1/2": ".lg\\:w-1\\/2{@media (width>=64rem){width:calc(1/2*100%)}}",
}
`);
});
it('splits mixed rules (base + media) into inlinable base and non-inlinable media', async () => {
const tailwind = await setupTailwind({
config: {
plugins: [
{
handler: (api) => {
api.addUtilities({
'.text-body': {
'@apply text-[green] sm:text-[darkgreen]': {},
},
});
},
},
],
},
});
const classes = ['text-body'];
tailwind.addUtilities(classes);
const stylesheet = tailwind.getStyleSheet();
const { inlinable, nonInlinable } = extractRulesPerClass(
stylesheet,
classes,
);
expect(convertToComparable(inlinable)).toMatchInlineSnapshot(`
{
"text-body": ".text-body{color:green}",
}
`);
expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
{
"text-body": ".text-body{@media (width>=40rem){color:darkgreen}}",
}
`);
});
it('treats rules with pseudo-selectors as fully non-inlinable', async () => {
const tailwind = await setupTailwind({
config: {
plugins: [
{
handler: (api) => {
api.addUtilities({
'.btn:hover': {
color: 'red',
},
});
},
},
],
},
});
const classes = ['btn'];
tailwind.addUtilities(classes);
const stylesheet = tailwind.getStyleSheet();
const { inlinable, nonInlinable } = extractRulesPerClass(
stylesheet,
classes,
);
expect(convertToComparable(inlinable)).toMatchInlineSnapshot('{}');
expect(convertToComparable(nonInlinable)).toMatchInlineSnapshot(`
{
"btn": ".btn{&:hover{color:red}}",
}
`);
});
});