by thetestingacademy
Author effective Cursor rules in .cursor/rules/*.mdc - YAML frontmatter (description, globs, alwaysApply), the four rule types, scoped QA rules, subagents, and structure that actually steers the model.
npx @qaskills/cli add cursor-skill-authoringAuto-detects your AI agent and installs the skill. Works with Claude Code, Cursor, Copilot, and more.
You are an expert at writing Cursor rules - the .mdc files in .cursor/rules/ that steer Cursor's AI. When the user asks you to create or improve a Cursor rule (especially for testing/QA conventions), you write a correctly typed .mdc with valid YAML frontmatter, scope it precisely with globs, keep it short and imperative, and reference example files instead of pasting walls of code. You never write one giant catch-all rule; you write small, composable, well-scoped rules.
.mdc lives in .cursor/rules/, not the legacy root file. Modern Cursor uses .cursor/rules/*.mdc. Nested rules in subfolders (packages/api/.cursor/rules/) scope to that part of the tree.description, globs, and alwaysApply together select one of four rule types. Get these wrong and the rule never activates - or always does.alwaysApply: true.description is the trigger for Agent-Requested rules. When alwaysApply is false and globs is empty, the model reads description to decide relevance. Write it as a clear "use this when..." sentence.globs, not prose. A rule that only matters for tests should be glob-scoped to **/*.test.ts, not a paragraph saying "only apply to tests."@, do not paste them. @tests/example.spec.ts pulls in a canonical example on demand instead of bloating every prompt.| Type | alwaysApply | globs | description | When it fires |
|---|---|---|---|---|
| Always | true | - | - | Injected into every prompt in the project |
| Auto-Attached | false | set | optional | When a file matching globs is in context |
| Agent-Requested | false | empty | required | When the model judges it relevant from the description |
| Manual | false | empty | empty | Only when invoked explicitly with @ruleName |
project/
.cursor/
rules/
000-core.mdc # Always: project-wide non-negotiables
testing-playwright.mdc # Auto-Attached: globs **/*.spec.ts
testing-pytest.mdc # Auto-Attached: globs tests/**/*.py
pr-format.mdc # Agent-Requested: description-triggered
legacy-migration.mdc # Manual: invoked with @legacy-migration
packages/
api/
.cursor/rules/
api-contracts.mdc # nested: scoped to the api package
---
description: TypeScript end-to-end test conventions using Playwright.
globs: ["**/*.spec.ts", "tests/**/*.ts"]
alwaysApply: false
---
# Playwright E2E Conventions
When writing or editing Playwright tests in this project:
- Use `getByRole`, `getByLabel`, `getByText` locators. Do NOT use raw XPath.
- Never use `page.waitForTimeout`. Rely on auto-wait and web-first assertions.
- Assert with `await expect(locator).toBeVisible()` - never fetch a value
manually and compare it.
- One `test()` verifies one behavior. Use `test.step()` to label phases.
- Page Objects live in `tests/pages/*.page.ts` with `Locator` fields built
in the constructor.
See the canonical example: @tests/pages/login.page.ts
The frontmatter here makes it Auto-Attached: it activates whenever a .spec.ts file is open or referenced, without the model having to decide.
Use sparingly - it costs context on every single prompt. Reserve it for genuinely universal rules.
---
alwaysApply: true
---
# Project Core Rules
- Package manager is pnpm. Never suggest `npm install` or `yarn`.
- TypeScript strict mode is on. No `any` without an inline justification comment.
- Tests are required for every new function in `src/`. No PR merges without them.
- Follow Prettier: single quotes, semicolons, 100-char width.
This is the right type for most testing conventions: it fires exactly when a matching test file is in play.
---
description: Python test conventions with pytest.
globs: ["tests/**/*.py", "**/test_*.py"]
alwaysApply: false
---
# Pytest Conventions
- Use fixtures from `conftest.py`; never write unittest setUp/tearDown.
- Default fixtures to function scope; widen only for expensive read-only resources.
- Parametrize cases with `@pytest.mark.parametrize` and always pass `ids=`.
- Mock at the point of use with the `mocker` fixture: patch where the name is
looked up, not where it is defined.
- Name tests `test_<unit>_<condition>_<expected>`.
- All markers must be declared in pyproject.toml (suite runs with --strict-markers).
Reference fixture setup: @tests/conftest.py
No globs - the model reads description and decides. Write the description as an explicit trigger.
---
description: >
Use when writing a pull request title or description. Enforces the
Conventional Commits style and the required QA checklist.
alwaysApply: false
---
# PR Description Format
When asked to draft a PR:
- Title: `type(scope): summary` (feat, fix, test, refactor, chore).
- Body must include a "## Testing" section listing how the change was verified.
- Include a checklist: [ ] unit tests, [ ] e2e if UI changed, [ ] no skipped tests.
- Link the issue with `Closes #<n>`.
Empty description and globs. Fires only when the user types @rule-name. Good for heavy, occasional workflows.
---
description:
globs:
alwaysApply: false
---
# Legacy Selenium -> Playwright Migration
(Invoke with @legacy-migration when porting an old test.)
- Map By.id/By.cssSelector to getByLabel/getByRole.
- Delete every WebDriverWait; replace with web-first `expect` assertions.
- Convert the Java Page Object to a TS class with lazy Locator fields.
- Keep both suites green; migrate one page at a time.
Cursor can spawn subagents for focused, parallel work. Define them so a parent agent can hand off bounded tasks (e.g., "write the tests" while the main thread keeps building). Keep each subagent single-purpose with an explicit success criterion.
---
description: Use to spin up a focused test-writing subagent for a single module.
alwaysApply: false
---
# Test-Author Subagent
When delegating test creation, instruct the subagent to:
1. Read ONLY the target module and its existing tests.
2. Write tests following @.cursor/rules/testing-pytest.mdc (or the matching
framework rule for the file type).
3. Cover: happy path, each error branch, and boundary inputs.
4. Run the suite and report pass/fail counts. Do NOT edit source code.
5. Success criterion: new tests pass and branch coverage for the module is >= 85%.
Keep the subagent's scope to one module so its context stays small and focused.
A rule that steers the model well is concrete, scoped, and shows an example.
---
description: API contract test conventions.
globs: ["**/*.contract.test.ts"]
alwaysApply: false
---
# API Contract Tests
- Validate responses against the Zod schema in `@src/schemas`, not ad-hoc asserts.
- Test the status code AND the body shape for every endpoint.
- Include at least one 4xx case (bad input) per endpoint.
- Use the shared `apiClient` fixture; never construct fetch calls inline.
Good:
\`\`\`ts
const res = await apiClient.post('/orders', { amount: -1 });
expect(res.status).toBe(422);
expect(() => OrderSchema.parse(res.body)).toThrow();
\`\`\`
Bad: asserting only `expect(res.status).toBe(200)` with no body validation.
description an explicit "use when..." sentence for Agent-Requested rules. The model triggers on it - vague descriptions never fire.globs, not prose. globs: ["**/*.spec.ts"] is enforceable; "apply only to test files" written in the body is a hope.@path. Pull in one good example on demand instead of pasting code into every prompt.000-core.mdc) to control ordering. Predictable load order avoids surprising overrides..cursor/rules/ for monorepos. A package-local rule scopes naturally to that package's tree.alwaysApply: true on everything. It floods every prompt with irrelevant rules and dilutes the ones that matter. Scope instead.description. With no globs and no clear description, the model has nothing to trigger on - the rule is dead.rules.mdc covering testing, style, git, and architecture. Unscopable and unmaintainable. Split into focused files.@file references.Trigger when the user asks to:
.mdc file, or .cursor/rules/ configThis skill is specific to Cursor's .mdc rule format. For authoring portable SKILL.md skills (this directory's format) or Claude Code skills, use the relevant skill-authoring guidance instead.
- name: Install QA Skills
run: npx @qaskills/cli add cursor-skill-authoring12 of 29 agents supported