Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
209 lines
5.5 KiB
Markdown
209 lines
5.5 KiB
Markdown
---
|
|
title: Choose Browser-Based Runner for Style and DOM Event Testing
|
|
impact: MEDIUM
|
|
impactDescription: Node-based runners cannot test real CSS behavior, native DOM events, cookies, or computed styles
|
|
type: capability
|
|
tags: [vue3, testing, component-testing, vitest, browser, jsdom]
|
|
---
|
|
|
|
# Choose Browser-Based Runner for Style and DOM Event Testing
|
|
|
|
**Impact: MEDIUM** - Node-based test runners (Vitest with jsdom/happy-dom) simulate the DOM but cannot test real CSS rendering, native browser events, cookies, computed styles, or cross-browser behavior. Use browser-based runners when these matter.
|
|
|
|
Use Vitest for most component tests (fast), but use Vitest Browser Mode when testing visual/DOM-dependent features.
|
|
|
|
## Task Checklist
|
|
|
|
- [ ] Use Vitest (node) for logic-focused component tests
|
|
- [ ] Use Vitest Browser Mode for style-dependent tests
|
|
- [ ] Use Vitest Browser Mode for native events (focus, drag, resize)
|
|
- [ ] Use Vitest Browser Mode for cookies and computed CSS styles
|
|
- [ ] Accept slower speed tradeoff for browser accuracy
|
|
|
|
## When to Use Each Approach
|
|
|
|
### Node-Based Runner (Vitest + happy-dom/jsdom)
|
|
Best for:
|
|
- Pure logic testing
|
|
- State management
|
|
- Event emission
|
|
- Props/slots behavior
|
|
- Most component interactions
|
|
- Fast CI/CD pipelines
|
|
|
|
```javascript
|
|
// vitest.config.js
|
|
export default defineConfig({
|
|
test: {
|
|
environment: 'happy-dom', // or 'jsdom'
|
|
}
|
|
})
|
|
```
|
|
|
|
```javascript
|
|
// Fast but limited - fine for most tests
|
|
test('button emits click event', async () => {
|
|
const wrapper = mount(Button)
|
|
await wrapper.trigger('click')
|
|
expect(wrapper.emitted('click')).toBeTruthy()
|
|
})
|
|
```
|
|
|
|
### Vitest Browser Mode
|
|
Required for:
|
|
- CSS computed styles verification
|
|
- CSS transitions/animations
|
|
- Real focus/blur behavior
|
|
- Drag and drop
|
|
- Cookie operations
|
|
- Viewport-dependent behavior
|
|
- Cross-browser validation
|
|
|
|
## Vitest Browser Mode Setup
|
|
|
|
```bash
|
|
npm install -D @vitest/browser playwright
|
|
```
|
|
|
|
```javascript
|
|
// vitest.config.js
|
|
import { defineConfig } from 'vitest/config'
|
|
|
|
export default defineConfig({
|
|
test: {
|
|
browser: {
|
|
enabled: true,
|
|
name: 'chromium',
|
|
provider: 'playwright',
|
|
},
|
|
},
|
|
})
|
|
```
|
|
|
|
```javascript
|
|
// Button.browser.test.js
|
|
import { render } from 'vitest-browser-vue'
|
|
import Button from './Button.vue'
|
|
|
|
test('has correct hover styling', async () => {
|
|
const { getByRole } = render(Button, { props: { label: 'Click me' } })
|
|
|
|
const button = getByRole('button')
|
|
|
|
// Check initial style
|
|
await expect.element(button).toHaveStyle({
|
|
backgroundColor: 'rgb(59, 130, 246)' // blue
|
|
})
|
|
})
|
|
|
|
test('maintains focus after click', async () => {
|
|
const { getByRole } = render(Button)
|
|
|
|
const button = getByRole('button')
|
|
await button.click()
|
|
|
|
await expect.element(button).toHaveFocus()
|
|
})
|
|
```
|
|
|
|
## Examples: What Each Runner Can/Cannot Test
|
|
|
|
### Styles - Browser Required
|
|
```javascript
|
|
// Node runner: CANNOT verify actual CSS
|
|
test('danger button has red background', () => {
|
|
const wrapper = mount(Button, { props: { variant: 'danger' } })
|
|
// This only checks class exists, not actual color
|
|
expect(wrapper.classes()).toContain('bg-red-500')
|
|
})
|
|
|
|
// Vitest Browser Mode: CAN verify computed styles
|
|
test('danger button renders red', async () => {
|
|
const { getByRole } = render(Button, { props: { variant: 'danger' } })
|
|
await expect.element(getByRole('button')).toHaveStyle({
|
|
backgroundColor: 'rgb(239, 68, 68)'
|
|
})
|
|
})
|
|
```
|
|
|
|
### Computed CSS Styles - Browser Required
|
|
```javascript
|
|
// Node runner: CANNOT get real computed styles
|
|
test('button has correct padding', () => {
|
|
const wrapper = mount(Button)
|
|
// getComputedStyle returns empty/default values in jsdom
|
|
const style = window.getComputedStyle(wrapper.element)
|
|
// style.padding will be empty string, not actual computed value
|
|
})
|
|
|
|
// Vitest Browser Mode: Real computed styles
|
|
test('button has correct padding', async () => {
|
|
const { getByRole } = render(Button)
|
|
const button = getByRole('button')
|
|
|
|
await expect.element(button).toHaveStyle({
|
|
padding: '12px 24px'
|
|
})
|
|
})
|
|
```
|
|
|
|
### Native Events - Browser Required
|
|
```javascript
|
|
// Node runner: Synthetic events only
|
|
test('handles drag and drop', async () => {
|
|
const wrapper = mount(DraggableList)
|
|
// trigger('dragstart') is synthetic - may not work as expected
|
|
await wrapper.find('.item').trigger('dragstart')
|
|
})
|
|
|
|
// Vitest Browser Mode: Real native events via userEvent
|
|
import { userEvent } from '@vitest/browser/context'
|
|
|
|
test('reorders items on drag', async () => {
|
|
const { getByTestId } = render(DraggableList)
|
|
|
|
const item = getByTestId('item-1')
|
|
const target = getByTestId('item-3')
|
|
|
|
await userEvent.dragAndDrop(item, target)
|
|
|
|
// Assert reordering
|
|
})
|
|
```
|
|
|
|
## Recommended Testing Strategy
|
|
|
|
```javascript
|
|
// vitest.config.js - Separate test configurations
|
|
|
|
export default defineConfig({
|
|
test: {
|
|
// Default: Node environment for speed
|
|
environment: 'happy-dom',
|
|
|
|
// Browser tests in separate directory
|
|
include: ['src/**/*.test.{js,ts}'],
|
|
},
|
|
})
|
|
|
|
// Run browser tests separately
|
|
// npx vitest --browser.enabled
|
|
```
|
|
|
|
### Directory Structure
|
|
```
|
|
tests/
|
|
├── unit/ # Fast node-based tests
|
|
│ ├── Button.test.js
|
|
│ └── useCounter.test.js
|
|
├── component/ # Slower browser-based tests
|
|
│ ├── Button.browser.test.js
|
|
│ └── DragDrop.browser.test.js
|
|
└── e2e/ # Full E2E tests (Playwright)
|
|
└── user-flow.spec.ts
|
|
```
|
|
|
|
## Reference
|
|
- [Vue.js Testing - Component Testing](https://vuejs.org/guide/scaling-up/testing#component-testing)
|
|
- [Vitest Browser Mode](https://vitest.dev/guide/browser.html)
|