chore: workspace eslint catalog, typescript dev dep, monorepo skill
- pnpm-workspace catalog: replace oxlint with eslint ^10.4.1. - Add typescript dev dep (root) so vitest typecheck (stdlib) resolves tsc. - Root vitest projects: configs/oxlint → configs/eslint. - Update monorepo skill + add component-test-auditor agent.
This commit is contained in:
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
description: "Audit and complete Vue/web component test suites. Use when the user asks to check test coverage, find missing test cases, fill gaps in component tests, verify a11y of a component, audit *.test.ts / a11y.test.ts files, or ensure a component is fully covered by vitest. Triggers: \"check tests\", \"complete tests\", \"test coverage\", \"missing test cases\", \"audit a11y\", \"проверь тесты\", \"допиши тесты\", \"покрытие тестов\"."
|
||||||
|
name: "Component Test Auditor"
|
||||||
|
tools: [read, search, edit, execute, todo]
|
||||||
|
model: "Claude Sonnet 4.5 (copilot)"
|
||||||
|
argument-hint: "Path to a component or test file to audit"
|
||||||
|
user-invocable: true
|
||||||
|
---
|
||||||
|
|
||||||
|
You are a specialist in Vue/web component test completeness auditing. Your single job: given a component, determine whether its test suite fully covers behavior, props, emits, slots, edge cases, and accessibility — then fill the gaps with additional vitest specs.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- DO NOT modify production component source. ONLY add or refine test files.
|
||||||
|
- DO NOT introduce new test frameworks. Use the existing `vitest` + `@vue/test-utils` setup found in the repo.
|
||||||
|
- DO NOT create redundant tests for behavior already covered. Each new spec must close a concrete gap.
|
||||||
|
- DO NOT skip running the suite — every change must be verified with vitest before reporting back.
|
||||||
|
- ONLY audit one component (or a small explicit list) per invocation. Stay focused.
|
||||||
|
|
||||||
|
## Required Skills
|
||||||
|
|
||||||
|
Always load these skills via `read_file` before acting:
|
||||||
|
|
||||||
|
1. `/Users/robonen/.agents/skills/vue-testing-best-practices/SKILL.md` — patterns for Vue component tests.
|
||||||
|
2. `/Users/robonen/.agents/skills/vitest/SKILL.md` — vitest API, mocking, coverage.
|
||||||
|
3. `/Users/robonen/.agents/skills/accessibility-a11y/SKILL.md` — WCAG checks, ARIA, keyboard nav. Load whenever the component has interactive behavior, focus management, ARIA attributes, or a sibling `a11y.test.ts` file exists.
|
||||||
|
4. `/Users/robonen/Projects/tools/.github/skills/monorepo/SKILL.md` — for repo-specific commands (`pnpm`, test scoping, workspace layout).
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
|
||||||
|
1. **Locate the component.** Resolve the target `.vue` / `.ts` source and its `__test__/` folder. Inspect sibling `*.test.ts`, `a11y.test.ts`, and any AGENTS.md / README.md for the package.
|
||||||
|
2. **Inventory the surface.** From the component source, enumerate: props (with defaults & types), emits, exposed methods, slots (named + scoped), `v-model`s, internal state machines, conditional branches, lifecycle effects, DOM/ARIA attributes, keyboard handlers.
|
||||||
|
3. **Inventory existing coverage.** Read all related test files and map each `it(...)` to the surface item it covers. Build a gap table.
|
||||||
|
4. **Decide on a11y scope.** If the component is interactive (focusable, ARIA roles, keyboard, form-related) → run the a11y skill checklist and ensure an `a11y.test.ts` exists with: role, accessible name, keyboard interaction, focus order, `aria-*` invariants, and (where applicable) `axe`-style assertions consistent with neighboring components.
|
||||||
|
5. **Write the missing specs.** Match the file/folder conventions used by neighbors (e.g. `src/<comp>/__test__/<Component>.test.ts` + `a11y.test.ts`). Mirror style (mount helpers, fixtures, naming). Keep specs deterministic and isolated.
|
||||||
|
6. **Run vitest.** Use `runTests` when available, otherwise `pnpm --filter <pkg> test -- <pattern>`. Iterate until green. If a failure reveals a real component bug, STOP and report it — do not silently fix production code.
|
||||||
|
7. **Optionally measure coverage.** If coverage is requested or unclear, run vitest in coverage mode scoped to the component file and report uncovered lines/branches.
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
Return a concise markdown report:
|
||||||
|
|
||||||
|
```
|
||||||
|
### Component: <name> (<path>)
|
||||||
|
|
||||||
|
**Surface inventory**
|
||||||
|
- props: ...
|
||||||
|
- emits: ...
|
||||||
|
- slots: ...
|
||||||
|
- interactive/a11y: yes/no — <reason>
|
||||||
|
|
||||||
|
**Existing coverage**
|
||||||
|
- <file>: N specs — covers X, Y
|
||||||
|
- a11y.test.ts: present/absent
|
||||||
|
|
||||||
|
**Gaps closed**
|
||||||
|
- + <spec name> — <what it covers>
|
||||||
|
- + <spec name> — <what it covers>
|
||||||
|
|
||||||
|
**Files changed**
|
||||||
|
- <path> (+N specs)
|
||||||
|
|
||||||
|
**Verification**
|
||||||
|
- vitest: PASS (M tests) | coverage: L% lines / B% branches
|
||||||
|
- a11y skill applied: yes/no
|
||||||
|
|
||||||
|
**Follow-ups (not done)**
|
||||||
|
- <anything out of scope or requiring human decision>
|
||||||
|
```
|
||||||
|
|
||||||
|
If no gaps exist, say so explicitly and do not touch any files.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: monorepo
|
name: monorepo
|
||||||
description: "Manage the @robonen/tools monorepo. Use when: installing dependencies, creating new packages, linting, building, testing, publishing, or scaffolding workspace packages. Covers pnpm catalogs, tsdown builds, oxlint presets, vitest projects, JSR/NPM publishing."
|
description: "Manage the @robonen/tools monorepo. Use when: installing dependencies, creating new packages, linting, building, testing, publishing, or scaffolding workspace packages. Covers pnpm catalogs, tsdown builds, eslint presets, vitest projects, JSR/NPM publishing."
|
||||||
---
|
---
|
||||||
|
|
||||||
# Monorepo Management
|
# Monorepo Management
|
||||||
@@ -13,7 +13,7 @@ This is a pnpm workspace monorepo (`@robonen/tools`) with shared configs, strict
|
|||||||
|
|
||||||
| Directory | Purpose | Examples |
|
| Directory | Purpose | Examples |
|
||||||
|-----------|---------|---------|
|
|-----------|---------|---------|
|
||||||
| `configs/*` | Shared tooling configs | `oxlint`, `tsconfig`, `tsdown` |
|
| `configs/*` | Shared tooling configs | `eslint`, `tsconfig`, `tsdown` |
|
||||||
| `core/*` | Platform-agnostic TS libraries | `stdlib`, `platform`, `encoding` |
|
| `core/*` | Platform-agnostic TS libraries | `stdlib`, `platform`, `encoding` |
|
||||||
| `vue/*` | Vue 3 packages | `primitives`, `toolkit` |
|
| `vue/*` | Vue 3 packages | `primitives`, `toolkit` |
|
||||||
| `docs` | Nuxt 4 documentation site | — |
|
| `docs` | Nuxt 4 documentation site | — |
|
||||||
@@ -35,7 +35,7 @@ pnpm -C <package-path> add -D <dep-name>
|
|||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
```bash
|
```bash
|
||||||
pnpm -C core/stdlib add -D oxlint
|
pnpm -C core/stdlib add -D eslint
|
||||||
pnpm -C vue/primitives add vue
|
pnpm -C vue/primitives add vue
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ Versions shared across multiple packages MUST use the pnpm catalog system. The c
|
|||||||
catalog:
|
catalog:
|
||||||
vitest: ^4.0.18
|
vitest: ^4.0.18
|
||||||
tsdown: ^0.21.0
|
tsdown: ^0.21.0
|
||||||
oxlint: ^1.2.0
|
eslint: ^10.4.1
|
||||||
vue: ^3.5.28
|
vue: ^3.5.28
|
||||||
# ... etc
|
# ... etc
|
||||||
```
|
```
|
||||||
@@ -57,7 +57,7 @@ In `package.json`, reference catalog versions with the `catalog:` protocol:
|
|||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"vitest": "catalog:",
|
"vitest": "catalog:",
|
||||||
"oxlint": "catalog:"
|
"eslint": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -72,7 +72,7 @@ Reference sibling packages with the workspace protocol:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@robonen/oxlint": "workspace:*",
|
"@robonen/eslint": "workspace:*",
|
||||||
"@robonen/tsconfig": "workspace:*",
|
"@robonen/tsconfig": "workspace:*",
|
||||||
"@robonen/tsdown": "workspace:*"
|
"@robonen/tsdown": "workspace:*"
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ Always run `pnpm install` at the root after editing `pnpm-workspace.yaml` or any
|
|||||||
|
|
||||||
## Creating a New Package
|
## Creating a New Package
|
||||||
|
|
||||||
> **Note:** The existing `bin/cli.ts` (`pnpm create`) is outdated — it generates Vite configs instead of tsdown and lacks oxlint/vitest setup. Follow the manual steps below instead.
|
> **Note:** The existing `bin/cli.ts` (`pnpm create`) is outdated — it generates Vite configs instead of tsdown and lacks eslint/vitest setup. Follow the manual steps below instead.
|
||||||
|
|
||||||
### 1. Create the directory
|
### 1. Create the directory
|
||||||
|
|
||||||
@@ -116,18 +116,17 @@ Choose the correct parent based on package type:
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint:check": "oxlint -c oxlint.config.ts",
|
"lint:check": "eslint .",
|
||||||
"lint:fix": "oxlint -c oxlint.config.ts --fix",
|
"lint:fix": "eslint . --fix",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"dev": "vitest dev",
|
"dev": "vitest dev",
|
||||||
"build": "tsdown"
|
"build": "tsdown"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@robonen/oxlint": "workspace:*",
|
"@robonen/eslint": "workspace:*",
|
||||||
"@robonen/tsconfig": "workspace:*",
|
"@robonen/tsconfig": "workspace:*",
|
||||||
"@robonen/tsdown": "workspace:*",
|
"@robonen/tsdown": "workspace:*",
|
||||||
"@stylistic/eslint-plugin": "catalog:",
|
"eslint": "catalog:",
|
||||||
"oxlint": "catalog:",
|
|
||||||
"tsdown": "catalog:"
|
"tsdown": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +138,6 @@ For Vue packages, also add:
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "catalog:",
|
"vue": "catalog:",
|
||||||
"@vue/shared": "catalog:"
|
"@vue/shared": "catalog:"
|
||||||
"@stylistic/eslint-plugin": "catalog:",
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/test-utils": "catalog:"
|
"@vue/test-utils": "catalog:"
|
||||||
@@ -215,16 +213,17 @@ export default defineConfig({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Create `oxlint.config.ts`
|
### 5. Create `eslint.config.ts`
|
||||||
|
|
||||||
Standard (node packages):
|
Standard (node packages):
|
||||||
```typescript
|
```typescript
|
||||||
import { defineConfig } from 'oxlint';
|
import { base, compose, imports, stylistic, typescript } from '@robonen/eslint';
|
||||||
import { compose, base, typescript, imports, stylistic } from '@robonen/oxlint';
|
|
||||||
|
|
||||||
export default defineConfig(compose(base, typescript, imports, stylistic));
|
export default compose(base, typescript, imports, stylistic);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> ESLint auto-discovers `eslint.config.ts` (loaded via `jiti`, which `@robonen/eslint` depends on). No `defineConfig` wrapper is needed — `compose()` returns the flat-config array directly.
|
||||||
|
|
||||||
### 6. Create `vitest.config.ts`
|
### 6. Create `vitest.config.ts`
|
||||||
|
|
||||||
Node package:
|
Node package:
|
||||||
@@ -292,7 +291,7 @@ pnpm -C <package-path> test
|
|||||||
|
|
||||||
## Linting
|
## Linting
|
||||||
|
|
||||||
Uses **oxlint** (not ESLint) with composable presets from `@robonen/oxlint`.
|
Uses **ESLint** (flat config) with composable presets from `@robonen/eslint`.
|
||||||
|
|
||||||
### Run linting
|
### Run linting
|
||||||
|
|
||||||
@@ -314,38 +313,29 @@ pnpm lint:fix
|
|||||||
|
|
||||||
| Preset | Purpose |
|
| Preset | Purpose |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| `base` | ESLint core + Oxc + Unicorn rules |
|
| `base` | ESLint core + Unicorn rules + global ignores |
|
||||||
| `typescript` | TypeScript rules (via overrides on `*.ts` files) |
|
| `typescript` | TypeScript rules (via `typescript-eslint`, on `*.ts` + `*.vue`) |
|
||||||
| `imports` | Import ordering, cycles, duplicates |
|
| `imports` | Import ordering, cycles, duplicates (`eslint-plugin-import-x`) |
|
||||||
| `stylistic` | Code style via `@stylistic/eslint-plugin` |
|
| `stylistic` | Code style via `@stylistic/eslint-plugin` |
|
||||||
| `vue` | Vue 3 Composition API rules |
|
| `vue` | Vue 3 Composition API rules (`eslint-plugin-vue`) |
|
||||||
| `vitest` | Test file rules |
|
| `vitest` | Test file rules (`@vitest/eslint-plugin`) |
|
||||||
| `node` | Node.js-specific rules |
|
| `node` | Node.js-specific rules (`eslint-plugin-n`) |
|
||||||
|
|
||||||
Compose presets in `oxlint.config.ts`:
|
Compose presets in `eslint.config.ts`:
|
||||||
```typescript
|
```typescript
|
||||||
import { defineConfig } from 'oxlint';
|
import { base, compose, imports, typescript } from '@robonen/eslint';
|
||||||
import { compose, base, typescript, imports } from '@robonen/oxlint';
|
|
||||||
|
|
||||||
export default defineConfig(compose(base, typescript, imports));
|
export default compose(base, typescript, imports);
|
||||||
|
```
|
||||||
|
|
||||||
**Recommended:** Include `stylistic` preset for code formatting:
|
**Recommended:** Include `stylistic` preset for code formatting:
|
||||||
```typescript
|
```typescript
|
||||||
import { defineConfig } from 'oxlint';
|
import { base, compose, imports, stylistic, typescript } from '@robonen/eslint';
|
||||||
import { compose, base, typescript, imports, stylistic } from '@robonen/oxlint';
|
|
||||||
|
|
||||||
export default defineConfig(compose(base, typescript, imports, stylistic));
|
export default compose(base, typescript, imports, stylistic);
|
||||||
```
|
```
|
||||||
|
|
||||||
When using `stylistic`, add `@stylistic/eslint-plugin` to devDependencies:
|
All plugins (including `@stylistic/eslint-plugin`) ship as dependencies of `@robonen/eslint`, so packages only need `@robonen/eslint` + `eslint` in devDependencies.
|
||||||
```json
|
|
||||||
{
|
|
||||||
"devDependencies": {
|
|
||||||
"@stylistic/eslint-plugin": "catalog:"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"jiti": "^2.6.1",
|
"jiti": "^2.6.1",
|
||||||
"jsdom": "catalog:",
|
"jsdom": "catalog:",
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
"vitest": "catalog:"
|
"vitest": "catalog:"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+6
-4
@@ -3,19 +3,21 @@ packages:
|
|||||||
- core/*
|
- core/*
|
||||||
- infra/*
|
- infra/*
|
||||||
- vue/*
|
- vue/*
|
||||||
|
- vue/*/playground
|
||||||
- docs
|
- docs
|
||||||
|
|
||||||
catalog:
|
catalog:
|
||||||
'@stylistic/eslint-plugin': ^5.10.0
|
'@stylistic/eslint-plugin': ^5.10.0
|
||||||
'@vitest/coverage-v8': ^4.0.18
|
'@vitest/browser': ^4.1.4
|
||||||
'@vitest/ui': ^4.0.18
|
'@vitest/coverage-v8': ^4.1.4
|
||||||
|
'@vitest/ui': ^4.1.4
|
||||||
'@vue/shared': ^3.5.29
|
'@vue/shared': ^3.5.29
|
||||||
'@vue/test-utils': ^2.4.6
|
'@vue/test-utils': ^2.4.6
|
||||||
|
eslint: ^10.4.1
|
||||||
jsdom: ^28.1.0
|
jsdom: ^28.1.0
|
||||||
nuxt: ^4.3.1
|
nuxt: ^4.3.1
|
||||||
oxlint: ^1.51.0
|
|
||||||
tsdown: ^0.21.0
|
tsdown: ^0.21.0
|
||||||
vitest: ^4.0.18
|
vitest: ^4.1.4
|
||||||
vue: ^3.5.29
|
vue: ^3.5.29
|
||||||
|
|
||||||
ignoredBuiltDependencies:
|
ignoredBuiltDependencies:
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config';
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
test: {
|
test: {
|
||||||
projects: [
|
projects: [
|
||||||
'configs/oxlint/vitest.config.ts',
|
'configs/eslint/vitest.config.ts',
|
||||||
'core/fetch/vitest.config.ts',
|
'core/fetch/vitest.config.ts',
|
||||||
'core/stdlib/vitest.config.ts',
|
'core/stdlib/vitest.config.ts',
|
||||||
'core/platform/vitest.config.ts',
|
'core/platform/vitest.config.ts',
|
||||||
|
|||||||
Reference in New Issue
Block a user