import { describe, expect, it } from 'vitest';
import { checkFormControls, checkMediaWidths } from '../../scripts/check-ui-styles.mjs';
const filename = 'src/routes/example/+page.svelte';
describe('shared responsive width guard', () => {
/** @param {string} query */
const styles = (query) => ``;
it.each([360, 640, 960, 1100])('allows the shared %i px breakpoint', (width) => {
for (const feature of ['width', 'min-width', 'max-width']) {
expect(checkMediaWidths(styles(`(${feature}: ${width}px)`), filename)).toEqual([]);
}
});
it.each([440, 480, 500, 540, 600, 620, 700, 720, 760, 800, 850, 1000])(
'rejects the legacy %i px breakpoint in a new component',
(width) => {
expect(checkMediaWidths(styles(`(max-width: ${width}px)`), filename)).toEqual([
`${filename}:2: use a shared media width (360px, 640px, 960px, 1100px): ${width}px`
]);
}
);
it('checks every width in compound, comma-separated and range queries', () => {
expect(
checkMediaWidths(
styles('screen and (min-width: 600px) and (max-width: 960px), (width <= 720px)'),
filename
)
).toHaveLength(2);
expect(checkMediaWidths(styles('(360px < width <= 1100px)'), filename)).toEqual([]);
expect(checkMediaWidths(styles('(600px= width)'), filename)).toHaveLength(1);
expect(checkMediaWidths(styles('(max-width: 40rem)'), filename)).toHaveLength(1);
});
it('limits migration exceptions to the recorded file and widths', () => {
const legacyFile = 'src/routes/leaderboard/+page.svelte';
expect(checkMediaWidths(styles('(max-width: 800px)'), legacyFile)).toEqual([]);
expect(checkMediaWidths(styles('(max-width: 1000px)'), legacyFile)).toEqual([]);
expect(checkMediaWidths(styles('(max-width: 640px)'), legacyFile)).toEqual([]);
expect(checkMediaWidths(styles('(max-width: 600px)'), legacyFile)).toHaveLength(1);
expect(checkMediaWidths(styles('(max-width: 800px)'), filename)).toHaveLength(1);
});
it('checks nested rules and standalone CSS with accurate line numbers', () => {
const cssFile = 'src/lib/ui/example.css';
expect(
checkMediaWidths(
'@supports (display: grid) {\n@media (max-width: 700px) { a { display: grid; } }\n}',
cssFile
)
).toEqual([`${cssFile}:2: use a shared media width (360px, 640px, 960px, 1100px): 700px`]);
});
it('ignores comments, script strings, declaration values and non-width media features', () => {
expect(
checkMediaWidths(
`
`,
filename
)
).toEqual([]);
expect(checkMediaWidths('
No styles
', filename)).toEqual([]);
});
});
describe('shared form control guard', () => {
it.each([
'',
' type="text"',
' type="search"',
' type="password"',
' type="email"',
' type="url"',
' type="tel"',
' type="number"',
' type="time"',
' type={visible ? "text" : "password"}'
])('rejects raw input%s with a useful location', (attributes) => {
expect(checkFormControls(`
\n\n
`, filename)).toEqual([
`${filename}:2: use Field with Input instead of raw `
]);
});
it('finds selects nested in blocks without confusing event expressions for markup', () => {
const source =
'{#if ready}{/if}';
expect(checkFormControls(source, filename)).toEqual([
`${filename}:1: use Field with Select instead of raw