All skills
bmad-labs avatar

/typescript-e2e-testing

@3bcc7a6
by bmad-labsbmad-labs/skills15 stars
4

E2E and integration testing for TypeScript/NestJS projects using Jest, supertest, and real infrastructure via Docker (Kafka, PostgreSQL, MongoDB, Redis) with the Given-When-Then pattern. Use whenever the user is working on `.e2e-spec.ts` files or anything under `test/e2e/`, or asks to set up, write, review, run, debug, or optimize E2E or integration tests — including flaky tests, docker-compose for tests, Kafka/Redpanda consumers, test isolation, or GWT compliance.

Use this Skill: https://skilld.dev/gh/bmad-labs/skills/typescript-e2e-testing

This session only. Nothing lands on disk.

referencescommondebugging.md

≈2.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

E2E Test Debugging Guide

Context Efficiency: Temp File Output

CRITICAL: Always redirect E2E test output to temp files. E2E output is verbose and bloats agent context.

IMPORTANT: Use unique session ID in filenames to prevent conflicts when multiple agents run.

# Initialize session (once at start of debugging)
export E2E_SESSION=$(date +%s)-$$

# Standard pattern - redirect to temp file only (no console output)
npm run test:e2e -- -t "{test name}" > /tmp/e2e-${E2E_SESSION}-debug.log 2>&1

# Read summary only
tail -50 /tmp/e2e-${E2E_SESSION}-debug.log

# Get failure details
grep -B 5 -A 20 "FAIL\|Error:" /tmp/e2e-${E2E_SESSION}-debug.log

# Cleanup when done
rm -f /tmp/e2e-${E2E_SESSION}-*.log /tmp/e2e-${E2E_SESSION}-*.md

Temp File Locations (with ${E2E_SESSION} unique per agent):

  • /tmp/e2e-${E2E_SESSION}-debug.log - Debug runs
  • /tmp/e2e-${E2E_SESSION}-output.log - General test output
  • /tmp/e2e-${E2E_SESSION}-verify.log - Verification runs
  • /tmp/e2e-${E2E_SESSION}-failures.md - Tracking file

VS Code Debugging

launch.json Configuration

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug E2E Tests",
      "program": "${workspaceFolder}/node_modules/.bin/jest",
      "args": [
        "--config", "test/jest-e2e.config.ts",
        "--runInBand",
        "--no-cache"
      ],
      "console": "integratedTerminal",
      "env": { "NODE_ENV": "test" }
    },
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Current Test File",
      "program": "${workspaceFolder}/node_modules/.bin/jest",
      "args": [
        "--config", "test/jest-e2e.config.ts",
        "--runInBand",
        "${relativeFile}"
      ],
      "console": "integratedTerminal"
    },
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Specific Test",
      "program": "${workspaceFolder}/node_modules/.bin/jest",
      "args": [
        "--config", "test/jest-e2e.config.ts",
        "--runInBand",
        "-t", "${input:testName}"
      ],
      "console": "integratedTerminal"
    }
  ],
  "inputs": [
    {
      "id": "testName",
      "type": "promptString",
      "description": "Test name pattern"
    }
  ]
}

Debugging Tips

  1. Set breakpoints in both test files and source code
  2. Disable coverage during debugging (add --coverage=false)
  3. Use debugger statement when breakpoints don't work
  4. Watch expressions: response.body, response.status, error.message

Command Line Debugging

All commands redirect to temp files only (no console output).

Node Inspector

# Basic debug mode (requires console for interactive debugging)
node --inspect-brk node_modules/.bin/jest --config test/jest-e2e.config.ts --runInBand

# With specific test file
node --inspect-brk node_modules/.bin/jest --config test/jest-e2e.config.ts --runInBand test/e2e/user.e2e-spec.ts

# With test name pattern
node --inspect-brk node_modules/.bin/jest --config test/jest-e2e.config.ts --runInBand -t "should create user"

Open Chrome → chrome://inspect to connect.

Verbose Output

# Verbose output - redirect then read summary (no console output)
npm run test:e2e -- --verbose > /tmp/e2e-${E2E_SESSION}-debug.log 2>&1
tail -100 /tmp/e2e-${E2E_SESSION}-debug.log

npm run test:e2e -- --verbose --expand > /tmp/e2e-${E2E_SESSION}-debug.log 2>&1
tail -100 /tmp/e2e-${E2E_SESSION}-debug.log

Single Test Execution

# Run specific file (no console output)
npm run test:e2e -- test/e2e/user.e2e-spec.ts > /tmp/e2e-${E2E_SESSION}-output.log 2>&1
tail -50 /tmp/e2e-${E2E_SESSION}-output.log

# Run tests matching pattern
npm run test:e2e -- -t "should create user" > /tmp/e2e-${E2E_SESSION}-output.log 2>&1
tail -50 /tmp/e2e-${E2E_SESSION}-output.log

# Run single describe block
npm run test:e2e -- -t "User API" > /tmp/e2e-${E2E_SESSION}-output.log 2>&1
tail -50 /tmp/e2e-${E2E_SESSION}-output.log

Log Analysis

Viewing Logs

# View application logs (limited output)
tail -100 logs/e2e-test.log

# Search for errors in test output
grep -i "error\|fail\|exception" /tmp/e2e-${E2E_SESSION}-output.log

# Search with context (5 lines before/after)
grep -B5 -A5 "FAIL" /tmp/e2e-${E2E_SESSION}-output.log

# Find specific request in app logs
grep "POST /users" logs/e2e-test.log | tail -20

Adding Debug Logs in Tests

it('should process order', async () => {
  console.log('=== DEBUG: Starting test ===');
  console.log('Order ID:', orderId);

  const response = await request(httpServer)
    .post('/orders')
    .send(orderData);

  console.log('=== DEBUG: Response ===');
  console.log('Status:', response.status);
  console.log('Body:', JSON.stringify(response.body, null, 2));
});

Request/Response Logging

function logRequest(response: request.Response): void {
  console.log('\n=== REQUEST ===');
  console.log(`${response.request.method} ${response.request.url}`);
  console.log('Body:', response.request._data);

  console.log('\n=== RESPONSE ===');
  console.log('Status:', response.status);
  console.log('Body:', JSON.stringify(response.body, null, 2));
}

// Usage
const response = await request(httpServer).post('/users').send(data);
logRequest(response);
expect(response.status).toBe(201);

Common Issues and Solutions

Test hangs indefinitely

// Cause 1: Unclosed database connection
afterAll(async () => {
  await dataSource?.destroy();
  await app?.close();
}, 30000);

// Cause 2: Unresolved promise - add timeout
const result = await Promise.race([
  someAsyncOperation(),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), 5000)
  ),
]);

// Cause 3: Kafka consumer not closed
afterAll(async () => {
  await producer?.disconnect();
  await consumer?.disconnect();
  await app?.close();
});

Tests pass individually but fail together

// Cause: Shared state between tests
beforeEach(async () => {
  await new Promise(r => setTimeout(r, 500));
  await userRepository.clear();
  await orderRepository.clear();
  jest.clearAllMocks();
});

Database constraint errors

// Clean in correct order (children first)
beforeEach(async () => {
  await orderItemRepository.delete({});
  await orderRepository.delete({});
  await userRepository.delete({});
});

// Or use CASCADE
await dataSource.query('SET session_replication_role = replica');
await dataSource.query('TRUNCATE users, orders CASCADE');
await dataSource.query('SET session_replication_role = DEFAULT');

Flaky async tests

// Solution: Poll until condition met
async function waitFor<T>(
  fn: () => Promise<T | null>,
  timeout = 10000,
  interval = 200
): Promise<T> {
  const startTime = Date.now();
  while (Date.now() - startTime < timeout) {
    const result = await fn();
    if (result) return result;
    await new Promise(r => setTimeout(r, interval));
  }
  throw new Error('Timeout waiting for condition');
}

const order = await waitFor(() =>
  orderRepository.findOne({ where: { id: event.orderId } })
);

Systematic Failure Resolution

CRITICAL: Fix ONE test at a time. NEVER run full suite repeatedly while debugging.

❌ WRONG: Run full suite → See 5 failures → Run full suite again → Still failures → ...
✅ RIGHT: Run full suite → See 5 failures → Fix test 1 → Verify → Fix test 2 → ... → Full suite ONCE

Step 1: Create Tracking File

List ALL failing tests from the initial run. Work through them one by one.

<!-- /tmp/e2e-${E2E_SESSION}-failures.md -->
# E2E Test Failures

## Test 1: "should create user and publish event"
- **File**: `test/e2e/user.e2e-spec.ts:42`
- **Error**: `Timeout - Async callback was not invoked within 25000ms`
- **Status**: IN_PROGRESS
- **Notes**: Kafka consumer might not be ready

## Test 2: "should return 404 for missing user"
- **File**: `test/e2e/user.e2e-spec.ts:78`
- **Error**: `Expected 404, received 500`
- **Status**: PENDING

Step 2: Fix ONE Test at a Time

Run ONLY the specific test, NEVER the full suite:

# Run only the failing test (no console output)
npm run test:e2e -- -t "should create user and publish event" > /tmp/e2e-${E2E_SESSION}-debug.log 2>&1
tail -50 /tmp/e2e-${E2E_SESSION}-debug.log

# Check for errors in output
grep -i "error\|fail" /tmp/e2e-${E2E_SESSION}-debug.log

Step 3: Analyze Root Cause

  1. Check test setup: Is beforeAll/beforeEach complete?
  2. Check test isolation: Is state leaking from other tests?
  3. Check async operations: Are waits long enough?
  4. Check mocks: Are external dependencies mocked?
  5. Check database state: Is data being cleaned properly?

Step 4: Verify Fix

Run the SAME test 3-5 times to ensure stability:

# Run the fixed test multiple times (no console output)
for i in {1..5}; do
  npm run test:e2e -- -t "should create user" > /tmp/e2e-${E2E_SESSION}-run$i.log 2>&1
  if [ $? -eq 0 ]; then echo "Run $i: PASS"; else echo "Run $i: FAIL"; fi
done

Step 5: Update Tracking and Move to Next

## Test 1: "should create user and publish event"
- **Status**: FIXED ✅
- **Root Cause**: Kafka consumer group not ready
- **Fix**: Added 5s delay in beforeAll after startAllMicroservices()

Now move to Test 2. Repeat steps 2-5 for each failing test.

Step 6: Run Full Suite ONLY ONCE

Only after ALL individual tests pass:

npm run test:e2e > /tmp/e2e-${E2E_SESSION}-output.log 2>&1
tail -50 /tmp/e2e-${E2E_SESSION}-output.log

Step 7: Clean Up

rm /tmp/e2e-${E2E_SESSION}-failures.md
rm -f /tmp/e2e-*.log

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill provides comprehensive workflows and reference materials for E2E testing in TypeScript/NestJS projects. It uses standard development tools like Jest, Docker, and shell commands. No malicious patterns or security risks were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    30/103 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 3bcc7a6. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 5 months ago
  • TypeScript
  • jest
  • nestjs
  • e2e-testing
  • supertest
  • docker
  • kafka
  • postgres
  • mongodb
  • redis
  • integration-testing
  • gwt-pattern

README badge

README badge for bmad-labs/skills

Writes E2E and integration tests for NestJS projects using Jest and supertest against real Docker infrastructure (Kafka, PostgreSQL, MongoDB, Redis) following the Given-When-Then pattern. Covers test setup, writing, review, debugging, and optimization workflows with technology-specific helpers and isolation strategies for each service.

Generated from the current SKILL.md.

Does this skill work with NestJS only?
The skill is built for NestJS projects using Jest and supertest, but the patterns and workflows apply to any TypeScript backend with HTTP endpoints. Technology-specific helpers exist for Kafka, PostgreSQL, MongoDB, and Redis.
Do I need Docker running to use this skill?
Yes. The skill assumes real infrastructure via Docker (Kafka, PostgreSQL, MongoDB, Redis). Tests execute against actual services, not mocks.
What if my E2E tests are flaky or failing?
The Debugging E2E Test workflow provides a systematic protocol: fix one test at a time using isolated test runs, verify with 3-5 consecutive runs before moving to the next failure, then run the full suite only once all tests pass individually.
Does this skill cover GraphQL or gRPC testing?
Yes. The API testing reference includes examples for REST, GraphQL, and gRPC, plus external API mocking with MSW and Nock.
What is the Given-When-Then pattern and is it mandatory?
GWT is the mandatory test structure: Given describes setup state, When describes the action under test, Then describes expected outcomes. All E2E tests in this skill must follow this pattern.

Generated from the current SKILL.md. These answers refresh after source changes.