feat: Complete fleet — 94 skills across 10+ domains
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>
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
---
|
||||
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)
|
||||
Reference in New Issue
Block a user