diff --git a/source/skills/critique/scripts/detect-antipatterns.mjs b/source/skills/critique/scripts/detect-antipatterns.mjs index 7c0e76d04..e4e8e0cca 100644 --- a/source/skills/critique/scripts/detect-antipatterns.mjs +++ b/source/skills/critique/scripts/detect-antipatterns.mjs @@ -1945,31 +1945,108 @@ const REGEX_ANALYZERS = [ }, ]; -function detectText(content, filePath) { - const findings = []; - const lines = content.split('\n'); +// --------------------------------------------------------------------------- +// Style block extraction (Vue/Svelte `; + const blocks = extractStyleBlocks(vue, '.vue'); + expect(blocks.length).toBe(1); + expect(blocks[0].content).toContain('border-left: 4px solid blue'); + expect(blocks[0].startLine).toBeGreaterThan(1); + }); + + test('extracts multiple +`; + const blocks = extractStyleBlocks(vue, '.vue'); + expect(blocks.length).toBe(2); + }); + + test('extracts `; + const blocks = extractStyleBlocks(svelte, '.svelte'); + expect(blocks.length).toBe(1); + expect(blocks[0].content).toContain('border-right: 4px solid'); + }); + + test('returns empty for non-Vue/Svelte files', () => { + const jsx = 'export function Card() { return
hi
; }'; + expect(extractStyleBlocks(jsx, '.jsx')).toHaveLength(0); + expect(extractStyleBlocks(jsx, '.tsx')).toHaveLength(0); + }); + + test('returns empty when no `; + const f = detectText(vue, 'Card.vue'); + expect(f.some(r => r.antipattern === 'side-tab')).toBe(true); + }); + + test('detects overused font in `; + const f = detectText(vue, 'App.vue'); + expect(f.some(r => r.antipattern === 'overused-font')).toBe(true); + }); + + test('detects bounce animation in `; + const f = detectText(vue, 'Card.vue'); + expect(f.some(r => r.antipattern === 'bounce-easing')).toBe(true); + }); + + test('detects gradient-text in `; + const f = detectText(vue, 'Hero.vue'); + expect(f.some(r => r.antipattern === 'gradient-text')).toBe(true); + }); + + test('detects Tailwind anti-patterns in