~/resources/configssection live
$ qa open configs --category all

//Configs

Configure it once.

Starter configs for Cypress, Playwright, Jest, ESLint, GitHub Actions, and more — copy, paste, adapt to your project.

>search configs…⌘K
27
Configs
9
Categories
All frameworks27 configs

Categories

// 🧪 CYPRESS · 3 CONFIGS

cypress.config.ts — TypeScript starter

CypressTS

Cypress 13+ E2E config with TypeScript, baseUrl, specPattern, viewport sizing, video disabled, screenshots on failure, and an API URL environment variable.

// Config file

import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3000',
    specPattern: 'cypress/e2e/**/*.cy.{ts,tsx}',
    supportFile: 'cypress/support/e2e.ts',
    viewportWidth: 1280,
    viewportHeight: 720,
    video: false,
    screenshotOnRunFailure: true,
    defaultCommandTimeout: 8000,
    requestTimeout: 10000,
    env: {
      apiUrl: 'http://localhost:3001/api',
    },
  },
});

// Setup

Place at the root of your project as cypress.config.ts. Run npm install --save-dev cypress typescript ts-node. Add a cypress/tsconfig.json that extends your root tsconfig.json and includes cypress/**/*.

// How to use

npx cypress open for interactive mode; npx cypress run for headless CI. Override baseUrl per environment with CYPRESS_baseUrl=https://staging.example.com npx cypress run.

cypress/support/commands.ts — custom commands

CypressTS

TypeScript custom commands template with proper module augmentation for Cypress.Chainable — includes a session-cached login command and a dataCy selector helper.

// Config file

// Extend Cypress types so commands are fully typed
declare global {
  namespace Cypress {
    interface Chainable {
      login(email: string, password: string): Chainable<void>;
      dataCy(selector: string): Chainable<JQuery<HTMLElement>>;
    }
  }
}

// Caches the authenticated session so login only runs once per spec
Cypress.Commands.add('login', (email: string, password: string) => {
  cy.session([email, password], () => {
    cy.visit('/login');
    cy.get('[data-testid="email-input"]').type(email);
    cy.get('[data-testid="password-input"]').type(password, { log: false });
    cy.get('[data-testid="submit-btn"]').click();
    cy.url().should('not.include', '/login');
  });
});

// Select elements via data-cy attribute
Cypress.Commands.add('dataCy', (selector: string) => {
  return cy.get(`[data-cy="${selector}"]`);
});

export {};

// Setup

Place at cypress/support/commands.ts and import it from cypress/support/e2e.ts with import './commands'. Ensure your root tsconfig.json includes cypress/support/**/* in the include array.

// How to use

Call cy.login('user@example.com', 'password') in a beforeEach — Cypress caches the session and skips the network round-trip on subsequent runs. Use cy.dataCy('submit-btn') anywhere you'd otherwise write cy.get('[data-cy="submit-btn"]').

cypress.config.ts — React component testing

CypressTS

Cypress component testing config for React with Vite bundler — targets *.cy.tsx files under src/, with a dedicated component support file.

// Config file

import { defineConfig } from 'cypress';

export default defineConfig({
  component: {
    devServer: {
      framework: 'react',
      bundler: 'vite',
    },
    specPattern: 'src/**/*.cy.{ts,tsx}',
    supportFile: 'cypress/support/component.ts',
    viewportWidth: 1280,
    viewportHeight: 720,
    screenshotOnRunFailure: true,
  },
});

// Setup

Run npm install --save-dev cypress — the Vite dev server is bundled in Cypress 10+, no extra package needed. Create cypress/support/component.ts to register any global setup (e.g. the mount command). Switch bundler to 'webpack' if your project uses webpack instead of Vite.

// How to use

npx cypress open --component opens the component runner; npx cypress run --component runs headlessly. Component specs live alongside your source files — name them MyComponent.cy.tsx.

// 🎭 PLAYWRIGHT · 3 CONFIGS

playwright.config.ts — multi-browser starter

PlaywrightTS

Playwright 1.40+ config targeting Chromium, Firefox, and WebKit in parallel — HTML reporter, trace on retry, screenshot on failure, and CI-aware retry count.

// Config file

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  retries: process.env.CI ? 1 : 0,
  workers: process.env.CI ? 2 : undefined,
  reporter: [['html', { open: 'never' }]],
  use: {
    baseURL: process.env.BASE_URL ?? 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

// Setup

Place at the root of your project as playwright.config.ts. Run npm install --save-dev @playwright/test then npx playwright install to download browser binaries. Set BASE_URL in your environment for non-localhost targets.

// How to use

npx playwright test runs all three browsers in parallel; npx playwright test --project=chromium targets a single browser. View the HTML report with npx playwright show-report after a run.

playwright.config.ts — auth storage state

PlaywrightTS

Playwright config with authentication reuse — a setup project writes storageState, and the authenticated project depends on it so login runs only once per suite.

// Config file

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  retries: process.env.CI ? 1 : 0,
  use: {
    baseURL: process.env.BASE_URL ?? 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    {
      name: 'setup',
      testMatch: /.*\.setup\.ts/,
    },
    {
      name: 'authenticated',
      use: {
        ...devices['Desktop Chrome'],
        storageState: '.auth/user.json',
      },
      dependencies: ['setup'],
    },
    {
      name: 'unauthenticated',
      use: { ...devices['Desktop Chrome'] },
    },
  ],
});

// Setup

Create the .auth/ directory (add it to .gitignore). Write a tests/auth.setup.ts file that logs in and calls page.context().storageState({ path: '.auth/user.json' }). Pair this config with the playwright-global-setup-login config if you prefer global setup over fixture-based setup.

// How to use

Tests in the authenticated project automatically receive the saved cookies and localStorage — no manual login needed per test. Run npx playwright test as normal; the setup project executes first automatically.

global-setup.ts — programmatic login

PlaywrightTS

Playwright global setup that logs in via the UI, then writes cookies and localStorage to `.auth/user.json` for reuse across all authenticated tests.

// Config file

import { chromium, type FullConfig } from '@playwright/test';
import path from 'node:path';
import fs from 'node:fs';

const AUTH_FILE = path.join(process.cwd(), '.auth/user.json');

export default async function globalSetup(_config: FullConfig): Promise<void> {
  fs.mkdirSync(path.dirname(AUTH_FILE), { recursive: true });

  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();

  const baseURL = process.env.BASE_URL ?? 'http://localhost:3000';

  await page.goto(`${baseURL}/login`);
  await page.getByLabel('Email').fill(process.env.TEST_EMAIL ?? 'qa@example.com');
  await page.getByLabel('Password').fill(process.env.TEST_PASSWORD ?? 'password');
  await page.getByRole('button', { name: 'Sign in' }).click();
  await page.waitForURL('**/dashboard');

  await context.storageState({ path: AUTH_FILE });
  await browser.close();
}

// Setup

Reference this file from playwright.config.ts with globalSetup: './global-setup.ts'. Set TEST_EMAIL and TEST_PASSWORD as environment variables (use a .env.test file with dotenv). Add .auth/ to .gitignore — it contains session tokens.

// How to use

Global setup runs once before any test worker starts. All tests that use storageState: '.auth/user.json' share the same session without re-authenticating. If the login flow changes, update the selectors here and the stored state regenerates on the next run.

// 🃏 JEST / VITEST · 2 CONFIGS

jest.config.js — TypeScript + React

Jest / VitestJS

Jest 29+ config for a TypeScript React project — ts-jest preset, jsdom environment, module name mapping for CSS and image imports, path alias support, and coverage collection.

// Config file

/** @type {import('jest').Config} */
const config = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterFramework: ['<rootDir>/src/setupTests.ts'],
  moduleNameMapper: {
    '\\.(css|scss|sass|less)$': '<rootDir>/__mocks__/fileMock.js',
    '\\.(png|jpg|jpeg|gif|svg|webp)$': '<rootDir>/__mocks__/fileMock.js',
    '^@/(.*)$': '<rootDir>/src/$1',
  },
  transform: {
    '^.+\\.tsx?$': ['ts-jest', { tsconfig: { jsx: 'react-jsx' } }],
  },
  testMatch: [
    '**/__tests__/**/*.{ts,tsx}',
    '**/*.{spec,test}.{ts,tsx}',
  ],
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!src/**/*.d.ts',
    '!src/main.{ts,tsx}',
  ],
  coverageReporters: ['text', 'html', 'lcov'],
};

module.exports = config;

// Setup

Run npm install --save-dev jest ts-jest @types/jest jest-environment-jsdom. Create src/setupTests.ts and import @testing-library/jest-dom if you are using React Testing Library. Create __mocks__/fileMock.js exporting an empty string for CSS/image imports.

// How to use

npx jest runs all tests; npx jest --coverage adds a coverage report. Use npx jest --watch during development for interactive re-runs. Pass --testPathPattern=MyComponent to run a single file.

vitest.config.ts — Vite + Vitest

Jest / VitestTS

Vitest 1+ config with TypeScript, jsdom environment, global test helpers, setup files, v8 coverage provider with text and HTML reporters, and path alias.

// Config file

import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    environment: 'jsdom',
    globals: true,
    setupFiles: ['./src/setupTests.ts'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html'],
      exclude: [
        'node_modules/',
        'src/setupTests.ts',
        '**/*.d.ts',
        '**/*.config.{ts,js}',
      ],
    },
  },
  resolve: {
    alias: {
      '@': '/src',
    },
  },
});

// Setup

Run npm install --save-dev vitest @vitejs/plugin-react jsdom @vitest/coverage-v8. Create src/setupTests.ts and import any test helpers (e.g. @testing-library/jest-dom/vitest). No separate babel or ts-jest config is needed — Vitest uses Vite's transform pipeline.

// How to use

npx vitest for watch mode; npx vitest run for a single CI pass; npx vitest run --coverage for the coverage report. Because globals: true is set, you can use describe, it, and expect without importing them.

// 🔧 CI/CD · 5 CONFIGS

.github/workflows/cypress.yml — parallel Cypress

CI/CDYAML

GitHub Actions workflow that runs Cypress in parallel across four containers using cypress-io/github-action@v6 — records to Cypress Cloud and uploads artefacts on failure.

// Config file

name: Cypress E2E

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  cypress-run:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        containers: [1, 2, 3, 4]
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Run Cypress
        uses: cypress-io/github-action@v6
        with:
          build: npm run build
          start: npm start
          wait-on: 'http://localhost:3000'
          record: true
          parallel: true
          ci-build-id: ${{ github.run_id }}-${{ github.run_attempt }}
        env:
          CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload screenshots
        uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: cypress-screenshots-${{ matrix.containers }}
          path: cypress/screenshots
          if-no-files-found: ignore

      - name: Upload videos
        uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: cypress-videos-${{ matrix.containers }}
          path: cypress/videos
          if-no-files-found: ignore

// Setup

Add CYPRESS_RECORD_KEY as a repository secret in GitHub (Settings → Secrets). The cypress-io/github-action handles npm ci, browser installation, and server startup automatically — you only need the build and start fields if a build step is required.

// How to use

The matrix strategy runs four containers simultaneously — tests are distributed automatically via Cypress Cloud. Remove record: true and parallel: true if you do not have a Cypress Cloud account; the tests will still run sequentially on a single container.

.github/workflows/playwright.yml — browser matrix

CI/CDYAML

GitHub Actions workflow running Playwright tests across Chromium, Firefox, and WebKit in a matrix — installs the specific browser per job and uploads the HTML report on failure.

// Config file

name: Playwright Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  playwright:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        project: [chromium, firefox, webkit]
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright browsers
        run: npx playwright install --with-deps ${{ matrix.project }}

      - name: Run Playwright tests
        run: npx playwright test --project=${{ matrix.project }}
        env:
          BASE_URL: ${{ vars.BASE_URL || 'http://localhost:3000' }}

      - name: Upload Playwright report
        uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-report-${{ matrix.project }}
          path: playwright-report/
          retention-days: 7

// Setup

No secrets required for this workflow. Set BASE_URL as a repository variable (Settings → Variables) if your app is deployed to a staging environment. The --with-deps flag installs system dependencies (fonts, libs) for each browser on the Ubuntu runner.

// How to use

fail-fast: false ensures all three browsers complete even if one fails — important for catching browser-specific regressions. To run only Chromium locally, use npx playwright test --project=chromium.

.gitlab-ci.yml — Cypress on GitLab CI

CI/CDYAML

GitLab CI pipeline for Cypress using the official cypress/included image — caches node_modules and the Cypress binary, runs headless Chrome, and saves screenshots/videos as job artefacts.

// Config file

image: cypress/included:13

stages:
  - test

variables:
  npm_config_cache: "$CI_PROJECT_DIR/.npm"
  CYPRESS_CACHE_FOLDER: "$CI_PROJECT_DIR/.cypress-cache"

cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - .npm/
    - .cypress-cache/
    - node_modules/

cypress-e2e:
  stage: test
  script:
    - npm ci
    - npx cypress run --headless --browser chrome
  artifacts:
    when: on_failure
    paths:
      - cypress/screenshots/
      - cypress/videos/
    expire_in: 7 days
  only:
    - main
    - merge_requests

// Setup

No additional setup required — cypress/included:13 ships with Chrome, Firefox, Electron, and all system dependencies pre-installed. Set your CYPRESS_baseUrl or CYPRESS_RECORD_KEY as CI/CD variables in GitLab (Settings → CI/CD → Variables).

// How to use

The cache key is scoped to the branch name, so cache is shared across pipeline runs for the same branch. Switch --browser chrome to --browser firefox for Firefox coverage. Add parallel: 4 and a Cypress Cloud key to enable parallelisation.

Jenkinsfile — Playwright declarative pipeline

CI/CDGroovy

Jenkins declarative pipeline that runs Playwright inside the official Microsoft Playwright Docker image — stages for install, lint, and test, with HTML report archiving.

// Config file

pipeline {
  agent {
    docker {
      image 'mcr.microsoft.com/playwright:v1.44.0-jammy'
      args '-u root'
    }
  }

  environment {
    BASE_URL = "${env.BASE_URL ?: 'http://localhost:3000'}"
  }

  stages {
    stage('Install') {
      steps {
        sh 'npm ci'
      }
    }

    stage('Lint') {
      steps {
        sh 'npm run lint'
      }
    }

    stage('Test') {
      steps {
        sh 'npx playwright test --reporter=line,html'
      }
    }
  }

  post {
    always {
      archiveArtifacts(
        artifacts: 'playwright-report/**',
        allowEmptyArchive: true
      )
    }
    failure {
      archiveArtifacts(
        artifacts: 'test-results/**',
        allowEmptyArchive: true
      )
    }
  }
}

// Setup

Requires the Docker and Pipeline plugins in Jenkins. The mcr.microsoft.com/playwright image includes all Chromium, Firefox, and WebKit binaries. Set BASE_URL as a Jenkins environment variable or credential if you are testing against a deployed environment.

// How to use

The args '-u root' flag is required for the Playwright image to write artefact files. Remove the Lint stage if your project does not have a lint script. Add a withCredentials block around the test stage to inject TEST_EMAIL and TEST_PASSWORD from Jenkins credentials.

.circleci/config.yml — Playwright on CircleCI

CI/CDYAML

CircleCI pipeline running Playwright with parallelism 4 — splits tests by timing data, caches node_modules, and stores the HTML report as a build artefact.

// Config file

version: 2.1

jobs:
  playwright:
    parallelism: 4
    docker:
      - image: mcr.microsoft.com/playwright:v1.44.0-jammy
    steps:
      - checkout

      - restore_cache:
          keys:
            - node-modules-{{ checksum "package-lock.json" }}
            - node-modules-

      - run:
          name: Install dependencies
          command: npm ci

      - save_cache:
          key: node-modules-{{ checksum "package-lock.json" }}
          paths:
            - node_modules

      - run:
          name: Run Playwright tests
          command: |
            TESTS=$(circleci tests glob "tests/**/*.spec.ts" \
              | circleci tests split --split-by=timings)
            npx playwright test $TESTS --reporter=line,html

      - store_artifacts:
          path: playwright-report
          destination: playwright-report

workflows:
  test:
    jobs:
      - playwright

// Setup

No additional config required — the Playwright Docker image includes all browsers and system dependencies. Enable test timing data in CircleCI (it is on by default for paid plans) to improve the quality of the --split-by=timings distribution across parallel containers.

// How to use

The circleci tests glob + split --split-by=timings pattern distributes spec files evenly across the four parallel containers based on historical run times. On first run (no timing data), files are split alphabetically. Reduce parallelism to 1 for smaller suites.

// 📐 LINTING · 3 CONFIGS

.eslintrc.json — TypeScript + React + Prettier

LintingJSON

ESLint 8 config (legacy eslintrc format) integrating @typescript-eslint, react, react-hooks, and prettier — with sensible defaults for a TypeScript React project.

// Config file

{
  "root": true,
  "env": {
    "browser": true,
    "es2022": true,
    "node": true
  },
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module",
    "ecmaFeatures": { "jsx": true },
    "project": "./tsconfig.json"
  },
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "prettier"
  ],
  "plugins": ["@typescript-eslint", "react", "react-hooks"],
  "settings": {
    "react": { "version": "detect" }
  },
  "rules": {
    "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
    "@typescript-eslint/no-explicit-any": "warn",
    "react/react-in-jsx-scope": "off",
    "react/prop-types": "off"
  }
}

// Setup

Run npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react eslint-plugin-react-hooks eslint-config-prettier. Place at the root of your project as .eslintrc.json.

// How to use

npx eslint src/ lints all source files; npx eslint src/ --fix auto-fixes safe issues. For ESLint 9 flat config, migrate to eslint.config.mjs using @eslint/migrate-config — the flat config API removes the need for "root": true and "plugins".

.prettierrc — Prettier config

LintingJSON

Standard Prettier config for TypeScript projects — single quotes, trailing commas everywhere, 100-char print width, 2-space tabs, and always-on arrow function parens.

// Config file

{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all",
  "printWidth": 100,
  "tabWidth": 2,
  "arrowParens": "always",
  "endOfLine": "lf"
}

// Setup

Run npm install --save-dev prettier. Place at the root of your project as .prettierrc. Add a .prettierignore for generated files (e.g. dist/, coverage/, .next/). Integrate with your editor using the Prettier VS Code extension.

// How to use

npx prettier --write . formats all files in place; npx prettier --check . is the CI-safe version that exits non-zero if any file is unformatted. Add "format": "prettier --write ." to your package.json scripts for convenience.

tsconfig.json — strict TypeScript starter

LintingJSON

Strict TypeScript config for test automation and React projects — enables strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes, ESNext module, ES2022 target, and a path alias for @/.

// Config file

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "exactOptionalPropertyTypes": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "outDir": "dist",
    "declaration": true,
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

// Setup

Place at the root of your project as tsconfig.json. For test files with different settings (e.g. CommonJS modules for Jest), create a tsconfig.test.json that extends this file and overrides module and moduleResolution. Change moduleResolution to node if you are not using a bundler.

// How to use

noUncheckedIndexedAccess adds | undefined to every array index and object key access — this catches many real bugs but requires null-checks. exactOptionalPropertyTypes prevents assigning undefined to an optional property; remove it if your codebase has many prop?: string fields set explicitly to undefined.

// 🐳 DOCKER · 2 CONFIGS

Dockerfile — Cypress in Docker

DockerDockerfile

Lean Dockerfile for running Cypress E2E tests in CI — based on cypress/included:13.6.0 which includes Chrome, Firefox, and all system dependencies. Copies and installs the project then runs specs headlessly.

// Config file

FROM cypress/included:13.6.0

WORKDIR /e2e

# Install dependencies separately to leverage Docker layer caching
COPY package*.json ./
RUN npm ci --prefer-offline

# Copy project files
COPY . .

# Give Cypress extra time to verify in slow CI environments
ENV CYPRESS_VERIFY_TIMEOUT=100000

ENTRYPOINT ["npx", "cypress", "run"]

// Setup

Build with docker build -t cypress-tests . from your project root. Add a .dockerignore file excluding node_modules/, .git/, and cypress/screenshots/ to keep the image small. Ensure cypress.config.ts is present at the root.

// How to use

docker run --rm cypress-tests runs all specs; docker run --rm cypress-tests --browser firefox targets Firefox. Mount screenshot/video directories with -v $(pwd)/cypress/screenshots:/e2e/cypress/screenshots to retrieve artefacts after the container exits.

docker-compose.yml — app + Postgres + Cypress

DockerYAML

Docker Compose stack for integration testing — your app, a Postgres database with a health check, and a Cypress runner — all on a shared network with volumes for test artefacts.

// Config file

version: '3.9'

services:
  app:
    build: .
    ports:
      - '3000:3000'
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/testdb
    depends_on:
      db:
        condition: service_healthy
    networks:
      - test-network

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: testdb
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U postgres']
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - test-network

  cypress:
    image: cypress/included:13.6.0
    depends_on:
      - app
    environment:
      - CYPRESS_baseUrl=http://app:3000
    volumes:
      - ./cypress:/e2e/cypress
      - ./cypress.config.ts:/e2e/cypress.config.ts
      - cypress-screenshots:/e2e/cypress/screenshots
      - cypress-videos:/e2e/cypress/videos
    working_dir: /e2e
    networks:
      - test-network

volumes:
  cypress-screenshots:
  cypress-videos:

networks:
  test-network:
    driver: bridge

// Setup

Run docker compose up --build --abort-on-container-exit --exit-code-from cypress — Docker will build the app, wait for the database health check, then start the Cypress runner. The --abort-on-container-exit flag stops all services once Cypress finishes.

// How to use

The depends_on with service_healthy ensures Postgres is ready before the app starts — preventing connection errors on cold starts. Add a command: ['npx', 'cypress', 'run', '--browser', 'firefox'] override to the cypress service to target a specific browser.

// 🔌 API TESTING · 3 CONFIGS

pom.xml — RestAssured + TestNG base

API TestingXML

Maven POM for Java API testing with RestAssured 5 and TestNG — includes the JSON schema validator, Jackson for POJO mapping, and Maven Surefire wired to a testng.xml suite file.

// Config file

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>api-tests</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <rest-assured.version>5.4.0</rest-assured.version>
    <testng.version>7.9.0</testng.version>
    <jackson.version>2.17.0</jackson.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>rest-assured</artifactId>
      <version>${rest-assured.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>json-schema-validator</artifactId>
      <version>${rest-assured.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>${testng.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.2.5</version>
        <configuration>
          <suiteXmlFiles>
            <suiteXmlFile>testng.xml</suiteXmlFile>
          </suiteXmlFiles>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

// Setup

Run mvn install from the project root to download all dependencies. Requires Java 17+ and Maven 3.9+. Create a testng.xml suite file at the root — see the selenium-java-testng-suite config for a ready-to-use starter.

// How to use

mvn test runs the TestNG suite; mvn test -Dgroups=smoke limits execution to smoke-tagged tests. Override the base URL with -DBASE_URL=https://staging.example.com. Increase parallelism by adding <parallel>methods</parallel><threadCount>8</threadCount> inside the Surefire <configuration> block.

karate-config.js — Karate BDD starter

API TestingJS

Karate 1.x global config that selects the base URL by environment, injects an Authorization header from an env var, and sets connect/read timeouts — loaded automatically before every feature.

// Config file

function fn() {
  var env = karate.env;
  karate.log('karate.env system property was:', env);

  if (!env) {
    env = 'dev';
  }

  var config = {
    env: env,
    baseUrl: 'http://localhost:8080',
    connectTimeout: 10000,
    readTimeout: 30000,
  };

  if (env === 'staging') {
    config.baseUrl = 'https://staging-api.example.com';
  } else if (env === 'prod') {
    config.baseUrl = 'https://api.example.com';
  }

  var token = java.lang.System.getenv('API_TOKEN');
  if (token) {
    karate.configure('headers', { Authorization: 'Bearer ' + token });
  }

  karate.configure('connectTimeout', config.connectTimeout);
  karate.configure('readTimeout', config.readTimeout);

  return config;
}

// Setup

Place at src/test/java/karate-config.js — Karate's default lookup path for Maven projects. Add karate-core and karate-junit5 to your pom.xml dependencies. Set karate.env via -Dkarate.env=staging at runtime or as a CI/CD environment variable.

// How to use

Karate loads this file before every feature run. The returned config object is available inside .feature files as karate.get('baseUrl'). To add a global Authorization header, set the API_TOKEN environment variable — the config injects it automatically without touching individual feature files.

.github/workflows/postman-newman.yml — Newman CI

API TestingYAML

GitHub Actions workflow that runs a Postman collection with Newman on push, pull request, and a weekday cron schedule — generates an HTML report and uploads it as a build artefact.

// Config file

name: Postman / Newman API Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1-5'

jobs:
  newman:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install Newman
        run: npm install -g newman newman-reporter-htmlextra

      - name: Run Postman collection
        run: |
          newman run collections/api-tests.json \
            --environment environments/staging.json \
            --env-var "baseUrl=${{ vars.BASE_URL }}" \
            --env-var "apiKey=${{ secrets.API_KEY }}" \
            --reporters cli,htmlextra \
            --reporter-htmlextra-export reports/newman-report.html \
            --bail

      - name: Upload report
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: newman-report
          path: reports/newman-report.html
          retention-days: 7

// Setup

Export your Postman collection to collections/api-tests.json and your environment to environments/staging.json and commit both. Add API_KEY as a GitHub secret and BASE_URL as a repository variable under Settings → Secrets and Variables.

// How to use

The --bail flag stops Newman on the first failing request — remove it to run all requests regardless of failures. The schedule trigger fires on weekday mornings; delete it if you only need push/PR triggers. Swap htmlextra for junit to emit JUnit XML for GitHub's native test summary view.

// 🌐 SELENIUM · 3 CONFIGS

testng.xml — Selenium Java test suite

SeleniumXML

TestNG 7 XML suite for a Selenium Java project — parallel test execution across four threads, browser and base-URL parameters, and hooks for a retry listener and Extent Reports.

// Config file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Selenium E2E Suite" verbose="1" parallel="tests" thread-count="4">

  <parameter name="browser" value="chrome"/>
  <parameter name="headless" value="true"/>
  <parameter name="baseUrl" value="http://localhost:3000"/>

  <listeners>
    <listener class-name="com.example.listeners.RetryListener"/>
    <listener class-name="com.example.listeners.ExtentReportListener"/>
  </listeners>

  <test name="Smoke Tests">
    <classes>
      <class name="com.example.tests.LoginTest"/>
      <class name="com.example.tests.HomePageTest"/>
    </classes>
  </test>

  <test name="Regression Tests">
    <packages>
      <package name="com.example.tests"/>
    </packages>
  </test>

</suite>

// Setup

Place at the project root alongside pom.xml. Wire it to Maven Surefire with <suiteXmlFiles><suiteXmlFile>testng.xml</suiteXmlFile></suiteXmlFiles> in your plugin configuration. Create your listener classes under src/main/java/com/example/listeners/, or remove the <listeners> block if you do not need retry logic or custom reporting.

// How to use

parallel="tests" with thread-count="4" runs the Smoke and Regression test tags concurrently. Change to parallel="methods" to parallelise individual test methods. Override parameters from the command line with -Dbrowser=firefox -Dheadless=false — TestNG reads them as system properties.

conftest.py — Selenium + pytest fixtures

SeleniumPY

pytest conftest with session-scoped and function-scoped Selenium fixtures — launches one Chrome instance for the suite, then resets cookies and navigates to baseUrl before each test.

// Config file

import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
import os

BASE_URL = os.getenv('BASE_URL', 'http://localhost:3000')
HEADLESS = os.getenv('HEADLESS', 'true').lower() == 'true'


@pytest.fixture(scope='session')
def browser():
    options = Options()
    if HEADLESS:
        options.add_argument('--headless=new')
    options.add_argument('--no-sandbox')
    options.add_argument('--disable-dev-shm-usage')
    options.add_argument('--window-size=1280,720')
    driver = webdriver.Chrome(
        service=Service(),
        options=options,
    )
    driver.implicitly_wait(8)
    yield driver
    driver.quit()


@pytest.fixture(scope='function')
def driver(browser):
    browser.delete_all_cookies()
    browser.get(BASE_URL)
    yield browser


def pytest_configure(config):
    config.addinivalue_line('markers', 'smoke: smoke test suite')
    config.addinivalue_line('markers', 'regression: full regression suite')

// Setup

Install pip install selenium pytest pytest-html. Selenium Manager (built into Selenium 4.10+) handles browser driver download automatically — no separate driver installation needed. Set HEADLESS=false locally when debugging failing tests to see the browser.

// How to use

Inject the driver fixture into any test function — pytest resolves the browser → driver chain automatically. Run pytest -m smoke to execute smoke-marked tests only, or pytest --tb=short -q for compact output. Add pytest-html and --html=report.html --self-contained-html for a portable HTML report.

docker-compose.yml — Selenium Grid 4

SeleniumYAML

Docker Compose stack for a local Selenium Grid 4 — a hub with two Chrome node replicas (3 sessions each) and one Firefox node on a shared bridge network.

// Config file

version: '3.9'

services:
  selenium-hub:
    image: selenium/hub:4.20
    ports:
      - '4442:4442'
      - '4443:4443'
      - '4444:4444'
    environment:
      - SE_SESSION_REQUEST_TIMEOUT=300
    networks:
      - selenium-grid

  chrome:
    image: selenium/node-chrome:4.20
    shm_size: '2g'
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=3
    networks:
      - selenium-grid

  firefox:
    image: selenium/node-firefox:4.20
    shm_size: '2g'
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=2
    networks:
      - selenium-grid

networks:
  selenium-grid:
    driver: bridge

// Setup

Run docker compose up -d to start the hub and nodes in the background. The Grid UI is available at http://localhost:4444/ui. Point your RemoteWebDriver at http://localhost:4444 as the command executor. Scale Chrome nodes with docker compose up --scale chrome=4 for more parallelism.

// How to use

Connect tests with webdriver.Remote(command_executor='http://localhost:4444', options=options) in Python or new RemoteWebDriver(new URL('http://localhost:4444'), caps) in Java. The shm_size: '2g' prevents Chrome from crashing on pages with heavy media. Tear down with docker compose down.

// 📈 PERFORMANCE · 3 CONFIGS

load-test.js — k6 with thresholds

PerformanceJS

k6 load test that ramps to 50 virtual users, holds for 3 minutes, then ramps down — with pass/fail thresholds on p95 latency, error rate, and check pass rate.

// Config file

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '1m', target: 50 },
    { duration: '3m', target: 50 },
    { duration: '1m', target: 0 },
  ],
  thresholds: {
    http_req_duration: ['p(95)<500', 'p(99)<1500'],
    http_req_failed: ['rate<0.01'],
    checks: ['rate>0.99'],
  },
};

const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';

export default function () {
  const res = http.get(`${BASE_URL}/api/health`);

  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });

  sleep(1);
}

// Setup

Install k6 from k6.io/docs/get-started/installation — Homebrew: brew install k6; Docker: docker pull grafana/k6. No npm packages needed; k6 has a built-in HTTP client. Set BASE_URL as an environment variable or pass it with -e BASE_URL=https://staging.example.com at runtime.

// How to use

k6 run load-test.js runs locally; k6 run -e BASE_URL=https://api.example.com load-test.js targets a remote URL. The thresholds block causes k6 to exit non-zero if p95 > 500 ms, error rate > 1%, or check pass rate < 99%. Stream live metrics to Grafana with --out influxdb=http://localhost:8086/k6.

test-plan.jmx — JMeter load test plan

PerformanceXML

JMeter 5.6 test plan with 50 virtual users ramping over 60 s, a 3-minute sustained load phase, a GET sampler, and a status-200 response assertion — ready to run headlessly in CI.

// Config file

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="API Load Test">
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments">
        <collectionProp name="Arguments.arguments">
          <elementProp name="BASE_URL" elementType="Argument">
            <stringProp name="Argument.name">BASE_URL</stringProp>
            <stringProp name="Argument.value">http://localhost:3000</stringProp>
          </elementProp>
        </collectionProp>
      </elementProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Users">
        <intProp name="ThreadGroup.num_threads">50</intProp>
        <intProp name="ThreadGroup.ramp_time">60</intProp>
        <longProp name="ThreadGroup.duration">180</longProp>
        <boolProp name="ThreadGroup.scheduler">true</boolProp>
      </ThreadGroup>
      <hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testname="GET /api/health">
          <stringProp name="HTTPSampler.domain">${BASE_URL}</stringProp>
          <stringProp name="HTTPSampler.path">/api/health</stringProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <ResponseAssertion guiclass="AssertionGui" testname="Status 200">
            <collectionProp name="Asserion.test_strings">
              <stringProp>200</stringProp>
            </collectionProp>
            <intProp name="Assertion.test_type">8</intProp>
          </ResponseAssertion>
          <hashTree/>
        </hashTree>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

// Setup

Download JMeter 5.6+ from jmeter.apache.org. Open this file in the GUI via File → Open to inspect and extend the plan visually. Update BASE_URL in the User Defined Variables section to point at your target. For CI, skip the GUI entirely.

// How to use

jmeter -n -t test-plan.jmx -l results.jtl -e -o report/ runs headlessly and generates an HTML dashboard in report/. The ThreadGroup runs 50 users for 180 s after a 60 s ramp — increase num_threads and ramp_time proportionally for higher load. Add a JMeter plugin such as the Throughput Shaping Timer for more complex load shapes.

artillery.yml — Artillery load test

PerformanceYAML

Artillery 2.x load test with a ramp-up phase, sustained load, response assertions via the expect plugin, and CI-gate thresholds on p95, p99, and error rate.

// Config file

config:
  target: "http://localhost:3000"
  phases:
    - duration: 60
      arrivalRate: 5
      rampTo: 50
      name: "Ramp up"
    - duration: 180
      arrivalRate: 50
      name: "Sustained load"
  defaults:
    headers:
      Content-Type: "application/json"
  plugins:
    expect: {}
  ensure:
    p95: 500
    p99: 1500
    maxErrorRate: 1

scenarios:
  - name: "Health check"
    weight: 30
    flow:
      - get:
          url: "/api/health"
          expect:
            - statusCode: 200
  - name: "API smoke"
    weight: 70
    flow:
      - get:
          url: "/api/items"
          expect:
            - statusCode: 200
            - contentType: json
      - think: 1

// Setup

Run npm install -g artillery. The expect plugin ships with Artillery 2.x — if you have an older version, install it separately with npm install -g @artillery/plugin-expect. Set target to your app's base URL or override at runtime with --target.

// How to use

artillery run artillery.yml executes locally and prints a summary table. artillery run --output report.json artillery.yml && artillery report report.json produces an HTML report. The ensure block fails the run (non-zero exit) if p95 > 500 ms, p99 > 1500 ms, or error rate exceeds 1% — wire this into CI to gate deployments on performance.