diff --git a/.github/agents/stencil-to-lit-migration-v1.agent.md b/.github/agents/stencil-to-lit-migration-v1.agent.md index b6fa378f8b1..9ef03eda241 100644 --- a/.github/agents/stencil-to-lit-migration-v1.agent.md +++ b/.github/agents/stencil-to-lit-migration-v1.agent.md @@ -244,6 +244,15 @@ When opening a PR for a functional component migration: Before completing a functional component migration, verify: +**Test quality (consensus-based patterns):** +- [ ] Icon properties tested using bracket notation (`iconElement?.['icon']`) +- [ ] Only conditional CSS classes tested (static classes skipped) +- [ ] Children content verified via text content (DOM checks optional) +- [ ] `tw`/`multiClassMap` used only for conditional/dynamic classes +- [ ] Interactive tests use Page API (`page.getByRole()`, not DOM API) +- [ ] Edge cases focus on security and common scenarios (pragmatic scope) + +**Migration equivalence:** - [ ] Functional component migrated to Lit with correct types - [ ] All imports use `@/*` path aliases (no `../` imports) - [ ] Original Stencil file renamed with `stencil-` prefix diff --git a/.github/prompts/generate-vitest-test-atomic-lit-functional-component.prompt.md b/.github/prompts/generate-vitest-test-atomic-lit-functional-component.prompt.md index 005362d102b..82d184afced 100644 --- a/.github/prompts/generate-vitest-test-atomic-lit-functional-component.prompt.md +++ b/.github/prompts/generate-vitest-test-atomic-lit-functional-component.prompt.md @@ -94,7 +94,9 @@ it('should handle click', async () => { ### 5. Essential Coverage -Test: (1) Basic rendering, (2) All props/defaults, (3) User interactions, (4) Visual attributes (classes, ARIA), (5) Children if applicable, (6) Error conditions. +Test: (1) Basic rendering, (2) All props/defaults, (3) User interactions, (4) Conditional visual attributes (CSS classes, ARIA), (5) Children if applicable, (6) Error conditions. + +**Note:** Static CSS classes and attributes are covered by visual regression tests. Focus unit tests on conditional logic. ### 6. Key Patterns @@ -135,6 +137,25 @@ it('should render localized text', async () => { }); ``` +**Icon properties:** +When components accept icon props (from `@/src/components/common/icon/icon`), test using bracket notation: +```typescript +it('should render with correct icon', async () => { + const element = await renderComponent({icon: ArrowUp}); + const iconElement = element.querySelector('atomic-icon'); + expect(iconElement?.['icon']).toBe(ArrowUp); +}); +``` + +**Children content:** +Verify children via text content. DOM structure verification is optional: +```typescript +it('should render children', async () => { + const element = await renderComponent({}, html`Child content`); + expect(element).toHaveTextContent('Child content'); // Sufficient +}); +``` + ### 7. Run Tests ```bash @@ -170,6 +191,38 @@ describe('when disabled', () => { }); ``` +**Conditional CSS classes:** +Test classes that change based on props/state. Skip static classes: +```typescript +// ✅ Test conditional classes +it('should apply active class when selected', async () => { + const element = await renderComponent({selected: true}); + expect(element.querySelector('button')).toHaveClass('bg-primary'); +}); + +it('should not apply active class when not selected', async () => { + const element = await renderComponent({selected: false}); + expect(element.querySelector('button')).not.toHaveClass('bg-primary'); +}); + +// ❌ Skip static classes (covered by visual tests) +it('should have rounded corners', async () => { + const element = await renderComponent(); + expect(element.querySelector('button')).toHaveClass('rounded-lg'); // Unnecessary +}); +``` + +**Avoid tw/multiClassMap in test helpers:** +When creating test fixtures, use plain class strings. Reserve `tw`/`multiClassMap` for testing the component's conditional logic, not test setup: +```typescript +// ✅ Good - plain strings in tests +const element = await renderComponent({class: 'test-class'}); + +// ❌ Avoid - unnecessary complexity in tests +const classNames = tw({'test-class': true}); +const element = await renderComponent({class: multiClassMap(classNames)}); +``` + ## Post-Execution Summary **Mandatory final todo:** Generate `.github/prompts/.executions/generate-vitest-test-atomic-lit-functional-component-[YYYY-MM-DD-HHmmss].prompt-execution.md` following `TEMPLATE.prompt-execution.md`. diff --git a/.github/prompts/migrate-stencil-functional-component-to-lit.prompt.md b/.github/prompts/migrate-stencil-functional-component-to-lit.prompt.md index 6f133b9da33..f4165ad2f16 100644 --- a/.github/prompts/migrate-stencil-functional-component-to-lit.prompt.md +++ b/.github/prompts/migrate-stencil-functional-component-to-lit.prompt.md @@ -84,20 +84,44 @@ html`` ## Tailwind Classes -**Use `tw` + `multiClassMap` for conditional classes:** +**Use `tw` + `multiClassMap` only for conditional or dynamic classes.** For components with only static classes, use plain class strings. + +**Conditional classes (use tw/multiClassMap):** ```typescript import {multiClassMap, tw} from '@/src/directives/multi-class-map'; const classNames = tw({ - 'rounded-lg border p-4': true, 'border-primary bg-primary-50': props.isHighlighted, 'border-neutral-dark bg-white': !props.isHighlighted, - [props.class ?? '']: Boolean(props.class), + [props.class ?? '']: Boolean(props.class), // Dynamic user classes }); +return html`