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 `) .join('\n'); expect(checkFormControls(source, filename)).toEqual([]); expect(checkFormControls('', filename)).toEqual([]); }); it('ignores comments, script strings and shared components', () => { const source = '', 'src/lib/ui/components/primitives/Input.svelte') ).toEqual([]); expect( checkFormControls('', 'src/lib/ui/components/primitives/Select.svelte') ).toEqual([]); expect( checkFormControls('', 'src/lib/ui/components/primitives/Field.svelte') ).toHaveLength(1); expect(checkFormControls('', 'src/lib/ui/components/Input.svelte')).toHaveLength( 1 ); }); });