Agent skill
cypress-api
API testing patterns with Cypress for CRUD operations. Covers BaseAPIController, entity controllers, test structure, and BDD documentation. Use this skill when writing API tests or creating new entity API controllers.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/cypress-api
SKILL.md
Cypress API Testing Skill
Patterns and tools for writing API tests with Cypress.
Architecture Overview
contents/themes/{theme}/tests/cypress/
├── e2e/
│ └── api/ # API tests (this skill)
│ ├── entities/
│ │ ├── {entity}-crud.cy.ts # Entity CRUD tests
│ │ └── {entity}-crud.bdd.md # BDD documentation
│ └── {feature}/
│ └── {feature}.cy.ts
├── src/
│ └── controllers/
│ ├── BaseAPIController.js # Base class (DO NOT MODIFY)
│ └── {Entity}APIController.js # Entity-specific controllers
└── fixtures/
└── entities.json # AUTO-GENERATED entity config
When to Use This Skill
- Writing API tests for entity CRUD operations
- Creating entity API controllers
- Testing authentication (API key, session)
- Validating API responses and error handling
- Testing pagination and filtering
Test Tag System
Permanent Tags (COMMIT)
| Tag | Purpose | When to Use |
|---|---|---|
@api |
API tests | Tests in e2e/api/ |
@feat-{entity} |
Entity-specific | @feat-tasks, @feat-customers |
@crud |
CRUD operations | Tests covering Create/Read/Update/Delete |
@smoke |
Critical path tests | Must-pass tests |
@regression |
Full test suite | All tests |
@security |
Security tests | Auth validation tests |
Temporary Tags (NEVER COMMIT)
| Tag | Purpose | When to Use |
|---|---|---|
@in-develop |
Tests being fixed | During iteration loop |
@scope-{session} |
Session tests | All tests for current session |
CRITICAL: code-reviewer (Phase 16) BLOCKS if temporary tags remain.
Test ID Convention
{ENTITY_SLUG_UPPER}_API_{NUMBER}
Examples:
- TASKS_API_001 → List entities
- TASKS_API_010 → Create entity
- TASKS_API_020 → Get by ID
- TASKS_API_030 → Update entity
- TASKS_API_040 → Delete entity
- TASKS_API_100 → Integration/lifecycle test
Number ranges:
- 001-009: LIST operations
- 010-019: CREATE operations
- 020-029: GET BY ID operations
- 030-039: UPDATE operations
- 040-049: DELETE operations
- 100+: Integration tests
BaseAPIController Pattern
Key Methods
class BaseAPIController {
constructor(baseUrl, apiKey, teamId, entityConfig) {
this.baseUrl = baseUrl
this.apiKey = apiKey
this.teamId = teamId
this.entitySlug = entityConfig.slug
}
// CRUD Methods
list(options = {}) // GET /api/v1/{entity}
getById(id, options = {}) // GET /api/v1/{entity}/{id}
create(data, options = {}) // POST /api/v1/{entity}
update(id, data, options = {}) // PATCH /api/v1/{entity}/{id}
delete(id, options = {}) // DELETE /api/v1/{entity}/{id}
// Validation Methods
validateSuccessResponse(response, expectedStatus)
validatePaginatedResponse(response)
validateBaseEntityFields(entity)
// Utility Methods
setApiKey(key) // Change API key
setTeamId(teamId) // Change team context
getHeaders() // Get request headers
}
Entity Controller Structure
import BaseAPIController from './BaseAPIController'
import entitiesConfig from '../fixtures/entities.json'
const { slug } = entitiesConfig.entities.tasks
class TasksAPIController extends BaseAPIController {
constructor(baseUrl, apiKey, teamId) {
super(baseUrl, apiKey, teamId, { slug })
}
// Semantic aliases
getTasks(options = {}) {
return this.list(options)
}
getTaskById(id, options = {}) {
return this.getById(id, options)
}
createTask(data, options = {}) {
return this.create(data, options)
}
updateTask(id, data, options = {}) {
return this.update(id, data, options)
}
deleteTask(id, options = {}) {
return this.delete(id, options)
}
// Data generators
generateRandomTaskData(overrides = {}) {
return {
title: `Test Task ${Date.now()}`,
description: 'Generated test task',
status: 'active',
...overrides
}
}
// Entity-specific validation
validateTaskObject(entity, allowMetas = false) {
this.validateBaseEntityFields(entity)
expect(entity).to.have.property('title')
expect(entity).to.have.property('status')
}
}
Authentication
Dual Auth System
// API Key Authentication (primary for API tests)
const api = new TasksAPIController(baseUrl, SUPERADMIN_API_KEY, TEAM_ID)
// Headers sent:
// - Authorization: Bearer {apiKey}
// - x-team-id: {teamId}
// Session Authentication (for UAT tests)
// Uses cy.session() - see cypress-e2e skill
Test Users & API Keys
// Test API keys (from fixtures or environment)
const SUPERADMIN_API_KEY = 'sk_test_...' // Full access
const MEMBER_API_KEY = 'sk_member_...' // Limited access
// Team IDs
const TEAM_ID = 'team-tmt-001'
// For session-based tests, see cypress-e2e skill for login helpers
Auth Test Patterns
// Test without API key
it('should reject request without API key', () => {
const originalApiKey = api.apiKey
api.setApiKey(null)
api.list().then((response) => {
expect(response.status).to.eq(401)
expect(response.body.success).to.be.false
})
api.setApiKey(originalApiKey)
})
// Test without team context
it('should reject request without x-team-id', () => {
const originalTeamId = api.teamId
api.setTeamId(null)
api.list().then((response) => {
expect(response.status).to.eq(400)
expect(response.body.code).to.eq('TEAM_CONTEXT_REQUIRED')
})
api.setTeamId(originalTeamId)
})
HTTP Status Codes
| Code | Meaning | When Used |
|---|---|---|
| 200 | OK | Successful GET, PATCH, DELETE |
| 201 | Created | Successful POST |
| 400 | Bad Request | Validation error, missing x-team-id |
| 401 | Unauthorized | Missing/invalid API key |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Entity doesn't exist |
| 409 | Conflict | Duplicate entry, constraint violation |
| 500 | Server Error | Internal error |
Error Codes
| Code | Description |
|---|---|
TEAM_CONTEXT_REQUIRED |
Missing x-team-id header |
MISSING_API_KEY |
No authentication provided |
INVALID_API_KEY |
API key doesn't exist or expired |
AUTHENTICATION_REQUIRED |
Must provide auth credentials |
INSUFFICIENT_PERMISSIONS |
User lacks required scope |
VALIDATION_ERROR |
Request body validation failed |
NOT_FOUND |
Entity doesn't exist |
CONFLICT |
Duplicate or constraint violation |
Test Structure
describe('{Entity} API - CRUD Operations', {
tags: ['@api', '@feat-{entity}', '@crud', '@regression']
}, () => {
let api: any
let createdEntities: any[] = []
const SUPERADMIN_API_KEY = '...'
const TEAM_ID = 'team-tmt-001'
const BASE_URL = Cypress.config('baseUrl')
before(() => {
api = new EntityAPIController(BASE_URL, SUPERADMIN_API_KEY, TEAM_ID)
})
beforeEach(() => {
allure.epic('API')
allure.feature('Entities')
})
afterEach(() => {
// CRITICAL: Cleanup created entities
createdEntities.forEach((entity) => {
if (entity?.id) {
api.delete(entity.id)
}
})
createdEntities = []
})
// Tests...
})
BDD Documentation Format (API)
API tests use YAML frontmatter + ## @test sections:
---
feature: {Entity} API
priority: high
tags: [api, feat-{entity}, crud, regression]
grepTags: ["@api", "@feat-{entity}"]
coverage: N tests
---
# {Entity} API
> API tests for /api/v1/{entity} endpoints.
## Endpoints Covered
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/v1/{entity}` | GET | List entities |
| `/api/v1/{entity}` | POST | Create entity |
| `/api/v1/{entity}/{id}` | GET | Get entity by ID |
| `/api/v1/{entity}/{id}` | PATCH | Update entity |
| `/api/v1/{entity}/{id}` | DELETE | Delete entity |
---
## @test {ENTITY}_API_001: List entities
### Metadata
- **Priority:** Critical
- **Type:** Smoke
- **Tags:** api, {entity}, list
- **Grep:** `@smoke @feat-{entity}`
\`\`\`gherkin:en
Scenario: List entities with valid API key
Given I have a valid superadmin API key
And I have x-team-id header set
When I make a GET request to /api/v1/{entity}
Then the response status should be 200
And the response body should have success true
And the data should contain paginated results
\`\`\`
\`\`\`gherkin:es
Scenario: Listar entidades con API key válida
Given tengo una API key de superadmin válida
And tengo el header x-team-id configurado
When hago una solicitud GET a /api/v1/{entity}
Then el status de respuesta debería ser 200
And el body debería tener success true
And los datos deberían contener resultados paginados
\`\`\`
### Expected Results
- Status: 200 OK
- Response contains paginated data array
- Info contains total, page, limit
---
## Response Format
### Success Response (200)
\`\`\`json
{
"success": true,
"data": [...],
"info": {
"total": 10,
"page": 1,
"limit": 10
}
}
\`\`\`
### Error Response (401)
\`\`\`json
{
"success": false,
"error": {
"message": "Authentication required",
"code": "AUTHENTICATION_REQUIRED"
}
}
\`\`\`
---
## Test Summary
| Test ID | Endpoint | Description | Tags |
|---------|----------|-------------|------|
| {ENTITY}_API_001 | GET / | List with valid auth | `@smoke` |
| {ENTITY}_API_010 | POST / | Create with valid data | `@smoke` |
| {ENTITY}_API_020 | GET /{id} | Get by valid ID | |
| {ENTITY}_API_030 | PATCH /{id} | Update with valid data | |
| {ENTITY}_API_040 | DELETE /{id} | Delete by valid ID | |
Scripts
Generate API Controller
# Generate API controller from template
python3 .claude/skills/cypress-api/scripts/generate-api-controller.py \
--entity tasks \
--theme default
# Preview without writing
python3 .claude/skills/cypress-api/scripts/generate-api-controller.py \
--entity tasks \
--theme default \
--dry-run
Generate API Test
# Generate API test file + BDD documentation
python3 .claude/skills/cypress-api/scripts/generate-api-test.py \
--entity tasks \
--theme default \
--with-bdd
# Preview without writing
python3 .claude/skills/cypress-api/scripts/generate-api-test.py \
--entity tasks \
--theme default \
--with-bdd \
--dry-run
Test Execution Commands
# Run specific API test file
pnpm cy:run --spec "contents/themes/default/tests/cypress/e2e/api/entities/tasks-crud.cy.ts"
# Run all API tests
pnpm cy:run --env grepTags="@api"
# Run entity-specific API tests
pnpm cy:run --env grepTags="@feat-tasks,@api"
# Run smoke API tests
pnpm cy:run --env grepTags="@api,@smoke"
# Run @in-develop tests only (during iteration)
pnpm cy:run --env grepTags="@in-develop"
Anti-Patterns
// NEVER: Hardcoded entity slugs
const endpoint = '/api/v1/tasks'
// CORRECT: From entities.json
const { slug } = entitiesConfig.entities.tasks
const endpoint = `/api/v1/${slug}`
// NEVER: Fixed timeouts
cy.wait(3000)
// CORRECT: Use promise chaining
api.create(data).then((response) => {
// Handle response
})
// NEVER: Skip cleanup
afterEach(() => {
// Missing cleanup!
})
// CORRECT: Always cleanup created entities
afterEach(() => {
createdEntities.forEach((entity) => {
if (entity?.id) api.delete(entity.id)
})
createdEntities = []
})
// NEVER: Modify shared state without restoring
api.setApiKey(null)
// Tests continue with null key...
// CORRECT: Always restore original state
const original = api.apiKey
api.setApiKey(null)
// Test...
api.setApiKey(original)
Checklist
Before finalizing API tests:
- Uses entity API controller (extends BaseAPIController)
- Uses correct test ID convention (
{ENTITY}_API_{NUMBER}) - Includes cleanup in
afterEach - Tests authentication (401 without key)
- Tests team context (400 without x-team-id)
- Includes permanent tags (
@api,@feat-{entity},@crud) - NO temporary tags (
@in-develop,@scope-{session}) - Has corresponding
.bdd.mddocumentation file - BDD uses YAML frontmatter +
## @testformat - BDD is bilingual (
:enand:esgherkin blocks)
Related Skills
cypress-e2e- UAT testing patternscypress-selectors- Selector architecturebetter-auth- Authentication patterns
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?