Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,121 +5,70 @@ description: 'Generate comprehensive Vitest unit tests for an Atomic Lit functio

# Add Vitest Tests to Atomic Lit Functional Component

Generate comprehensive Vitest unit tests for a functional component that returns a Lit template (e.g., `renderButton`, `renderSortOption`). Follow the [Atomic testing instructions](../instructions/tests-atomic.instructions.md) and established codebase patterns.
Generate comprehensive Vitest unit tests for functional components (functions returning Lit `TemplateResult`, not classes). These are named `render[ComponentName]` (e.g., `renderButton`, `renderSortOption`), accept props, and are located in `src/components/common/` or similar.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this PR include changes to these two prompts?


**Working directory:** `packages/atomic`

## What Are Functional Components?

Functions that return Lit `TemplateResult` (not classes):
- Named `render[ComponentName]` (e.g., `renderButton`, `renderSortOption`)
- Accept props, return rendered HTML
- Used as building blocks in Web Components
- Located in `src/components/common/` or similar
**If component name not provided, ask for it.**

## Required Steps

**If component name not provided, ask for it.** Then:
**Use `manage_todo_list` tool:** Track progress through steps below. Mark ONE in-progress before working, mark completed immediately after finishing each step. Final todo must be execution summary generation.

**Checklist:**
- [ ] Analyze component (signature, props, children, events, dependencies)
- [ ] Create test file `{component-name}.spec.ts` in component directory
- [ ] Structure tests with describe blocks and helpers
- [ ] Implement test patterns (basic rendering, props, events, children)
- [ ] Ensure essential coverage (rendering, props, interactions, attributes, children, errors)
- [ ] Run tests with Vitest
- [ ] Generate execution summary (mandatory final todo)

### 1. Analyze the Component

Examine the component file for:
Examine for:
- **Function signature** - Props interface and types
- **Return type** - What it renders (buttons, inputs, containers)
- **Children handling** - Does it accept child content?
- **Event handlers** - What interactions does it support?
- **Return type** - What it renders
- **Children handling** - Accepts child content?
- **Event handlers** - Supported interactions
- **Conditional rendering** - Logic affecting output
- **Dependencies** - i18n, icons, other render functions

### 2. Create Test File

File: `{component-name}.spec.ts` in same directory as component.

### 3. Set Up Imports

```typescript
import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture';
import {createTestI18n} from '@/vitest-utils/testing-helpers/i18n-utils';
import {page} from '@vitest/browser/context';
import {html} from 'lit';
import {describe, it, vi, expect, beforeAll} from 'vitest';
import {render[ComponentName], type [ComponentName]Props} from './component-name';

// Mock dependencies if needed
vi.mock('@/src/utils/module', () => ({
utilFunction: vi.fn(),
}));
```

### 4. Structure Tests
`{component-name}.spec.ts` in same directory as component.

```typescript
describe('#render[ComponentName]', () => {
// Tests here
});
```

Nested describes: `'when [condition]'` for conditional scenarios
Test names: Always start with "should"
### 3. Structure Tests

### 5. Test Patterns by Component Type
Use `describe('#render[ComponentName]', () => {...})` with nested describes for conditions (`'when [condition]'`). Test names start with "should".

#### Simple Components (Buttons, Icons)
### 4. Test Patterns

**Basic setup with helper:**
```typescript
const renderComponent = async (props: Partial<ComponentProps> = {}) => {
const defaultProps: ComponentProps = {
text: 'Default',
onClick: vi.fn(),
};

const defaultProps: ComponentProps = {text: 'Default', onClick: vi.fn()};
return await renderFunctionFixture(
html`${renderComponentName({props: {...defaultProps, ...props}})}`
);
};

it('should render with correct attributes', async () => {
const element = await renderComponent({
text: 'Test',
disabled: true,
});

const button = element.querySelector('button');
expect(button).toHaveTextContent('Test');
expect(button).toHaveAttribute('disabled');
});
```

#### Components with Children

**Components with children:**
```typescript
const renderComponent = async (
props: Partial<ComponentProps> = {},
children = html`<span>Test</span>`
children = html`<span>Default</span>`
) => {
return await renderFunctionFixture(
html`${renderComponentName({props: {...defaultProps, ...props}})(children)}`
);
};

it('should render children correctly', async () => {
const element = await renderComponent(
{},
html`<div class="child">Content</div>`
);

expect(element.querySelector('.child')).toHaveTextContent('Content');
});
```

#### Components with i18n

**Components with i18n:**
```typescript
let i18n: Awaited<ReturnType<typeof createTestI18n>>;

beforeAll(async () => {
i18n = await createTestI18n();
});
beforeAll(async () => { i18n = await createTestI18n(); });

const renderComponent = async (props: Partial<ComponentProps> = {}) => {
return await renderFunctionFixture(
Expand All @@ -128,196 +77,103 @@ const renderComponent = async (props: Partial<ComponentProps> = {}) => {
};
```

#### Interactive Components (Page Locators)

**Interactive tests with locators:**
```typescript
const locators = {
get button() {
return page.getByRole('button');
},
option({selected}: {selected: boolean} = {selected: false}) {
return page.getByRole('option', {selected});
},
get button() { return page.getByRole('button'); },
option({selected = false}) { return page.getByRole('option', {selected}); },
};

it('should handle click', async () => {
const onClick = vi.fn();
await renderComponent({onClick});

await locators.button.click();

expect(onClick).toHaveBeenCalledOnce();
});
```

### 6. Essential Test Cases

Cover:

1. **Basic rendering** - Component renders without errors
2. **Props** - All properties work correctly, defaults applied
3. **User interactions** - Click handlers, form inputs, keyboard
4. **Visual attributes** - CSS classes, part attributes, ARIA
5. **Children** (if applicable) - Single, multiple, empty
6. **Error conditions** - Missing required props, invalid values

5. **Children** (if applicable) - Single, multiple, empty
6. **Error conditions** - Missing required props, invalid values

### 7. Example Test Patterns
### 5. Essential Coverage

#### Attributes & Classes
Test: (1) Basic rendering, (2) All props/defaults, (3) User interactions, (4) Visual attributes (classes, ARIA), (5) Children if applicable, (6) Error conditions.

```typescript
it('should apply CSS classes from props', async () => {
const element = await renderComponent({
style: 'primary',
class: 'custom',
});

const button = element.querySelector('button');
expect(button).toHaveClass('btn-primary', 'custom');
});
```

#### Event Handlers
### 6. Key Patterns

**Attributes & events:**
```typescript
it('should call onClick with correct arguments', async () => {
it('should apply classes and handle clicks', async () => {
const onClick = vi.fn();
await renderComponent({onClick, value: 'test'});

const button = page.getByRole('button');
await button.click();

expect(onClick).toHaveBeenCalledWith('test');
const element = await renderComponent({class: 'custom', onClick});

expect(element.querySelector('button')).toHaveClass('custom');
await page.getByRole('button').click();
expect(onClick).toHaveBeenCalledOnce();
});
```

#### Conditional Rendering

**Conditional rendering:**
```typescript
describe('when disabled is true', () => {
it('should render disabled button', async () => {
const element = await renderComponent({disabled: true});

const button = element.querySelector('button');
expect(button).toHaveAttribute('disabled');
expect(button).toHaveAttribute('aria-disabled', 'true');
});

it('should not call onClick when clicked', async () => {
describe('when disabled', () => {
it('should render disabled and not call onClick', async () => {
const onClick = vi.fn();
await renderComponent({disabled: true, onClick});

const element = await renderComponent({disabled: true, onClick});

expect(element.querySelector('button')).toHaveAttribute('disabled');
await page.getByRole('button').click();

expect(onClick).not.toHaveBeenCalled();
});
});
```

#### i18n Integration

**i18n:**
```typescript
it('should render localized text', async () => {
const customI18n = await createTestI18n();
customI18n.addResourceBundle('en', 'translation', {
'button.label': 'Custom Label',
});

const element = await renderComponent({
i18n: customI18n,
labelKey: 'button.label',
});

expect(element.querySelector('button')).toHaveTextContent('Custom Label');
customI18n.addResourceBundle('en', 'translation', {'key': 'Localized'});

const element = await renderComponent({i18n: customI18n, labelKey: 'key'});
expect(element).toHaveTextContent('Localized');
});
```

### 8. Run Tests
### 7. Run Tests

```bash
npx vitest ./src/components/path/component.spec.ts --run
```

## Key Guidelines
## Guidelines

**Mock external dependencies at file top** (before imports that use them):
**Mock dependencies before imports:**
```typescript
vi.mock('@/src/utils/module', () => ({utilFunction: vi.fn()}));
```

**Use `page` locators for interactive tests:**
**Use `page` locators for interactions:**
```typescript
const button = page.getByRole('button');
await button.click();
await page.getByRole('button').click();
```

**Prefer creation with desired props over mutation:**
**Prefer creation over mutation:**
```typescript
// ✅ Good
// ✅ Good - create with desired props
const element = await renderComponent({property: 'value'});

// ❌ Avoid (unless testing reactivity)
// ❌ Avoid - unless testing reactivity
element.property = 'value';
await element.updateComplete;
```

**Group related tests with describes:**
**Group related tests:**
```typescript
describe('when disabled is true', () => {
describe('when disabled', () => {
// All disabled-related tests
});
```

## Quick Reference

**Main utilities:**
- `renderFunctionFixture(template)` - Render functional components
- `createTestI18n()` - Test i18n instance
- `page.getByRole()` - Interactive element locators
- `vi.fn()` - Mock functions

**Naming:**
- Top describe: `'#render[ComponentName]'`
- Nested describes: `'when [condition]'`
- Tests: `'should [behavior]'`

**Common assertions:**
```typescript
expect(element).toBeDefined()
expect(button).toHaveTextContent('text')
expect(button).toHaveAttribute('disabled')
expect(button).toHaveClass('class-name')
expect(onClick).toHaveBeenCalledWith(arg)
await expect.element(locator).toBeInTheDocument()
```

Ask for the component name if not provided, then analyze and create comprehensive tests following these patterns.

## Post-Execution: Generate Summary

After completing test generation, generate execution summary:
## Post-Execution Summary

**1. Create summary file:**
- **Location:** `.github/prompts/.executions/generate-vitest-test-atomic-lit-functional-component-[YYYY-MM-DD-HHmmss].prompt-execution.md`
- **Structure:** Follow `.github/prompts/.executions/TEMPLATE.prompt-execution.md`
- **Purpose:** Structured feedback for prompt optimization
**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`.

**2. Include in summary:**
- Which similar functional component tests were used as reference (if any)
- Issues with understanding component signature or dependencies
- Ambiguities in prompt instructions that required interpretation
- Time-consuming operations (excessive file reads, searches)
- Missing instructions or unclear testing requirements
- Concrete suggestions for prompt improvements

**3. Inform user** about summary location and next steps (switch to "Prompt Engineer" chatmode for optimization)

**4. Mark complete** only after file created and user informed.

````
```
**Include:** Reference components used, type selection issues, ambiguous instructions, time-consuming operations, missing guidance, concrete improvement suggestions.

Ask for the component name if not provided, then analyze and create comprehensive tests following these patterns.
**Inform user** of summary location and suggest "Prompt Engineer" chatmode for optimization. Mark complete only after file created and user informed.
Loading
Loading