import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { describe, expect, it } from 'vitest'; const projectRoot = process.cwd(); const indexSource = readFileSync(join(projectRoot, 'src/index.ts'), 'utf8'); const html = readFileSync(join(projectRoot, 'index.html'), 'utf8'); const escapeRegex = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const hasClass = (className: string, tagName?: string) => { const tagPattern = tagName ? `<${tagName}\\b[^>]*` : '<[a-z][^>]*'; return new RegExp( `${tagPattern}class="[^"]*\\b${escapeRegex(className)}\\b[^"]*"`, 'i' ).test(html); }; const hasId = (id: string) => new RegExp(`\\bid="${escapeRegex(id)}"`, 'i').test(html); const hasTag = (tagName: string) => new RegExp(`<${escapeRegex(tagName)}(?:\\s|>|/)`, 'i').test(html); const selectorExists = (selector: string) => { const idSelector = /^#(?[\w-]+)$/.exec(selector); if (idSelector?.groups?.id) { return hasId(idSelector.groups.id); } const classSelector = /^\.([\w-]+)$/.exec(selector); if (classSelector?.[1]) { return hasClass(classSelector[1]); } const tagClassSelector = /^(?[a-z]+)\.(?[\w-]+)$/.exec(selector); if (tagClassSelector?.groups) { return hasClass(tagClassSelector.groups.className, tagClassSelector.groups.tagName); } if (/^[a-z]+$/.test(selector)) { return hasTag(selector); } throw new Error(`Unsupported selector contract syntax: ${selector}`); }; describe('index DOM selector contract', () => { it('keeps every boot-time required selector target present in index.html', () => { const selectors = Array.from( indexSource.matchAll(/queryRequiredElements?\(\s*'([^']+)'\s*,/g), (match) => match[1] ); expect(selectors.length).toBeGreaterThan(0); expect(selectors.filter((selector) => !selectorExists(selector))).toEqual([]); }); it('keeps the three color swatches expected by the palette UI', () => { const colorSwatchCount = Array.from( html.matchAll(/class="[^"]*\bcolor-swatch\b[^"]*"/g) ).length; expect(colorSwatchCount).toBe(3); }); });