Skip to main content

Overview

Proton WebClients uses Jest as the primary testing framework for applications and packages. Some packages also use Vitest, Karma, or Playwright for specific testing needs.

Test Frameworks

Jest

Primary testing framework for most applications and packages

Vitest

Used by select packages (e.g., @proton/wallet, @proton/meet)

Karma

Legacy test runner used by @proton/shared

Running Tests

Single Application Tests

yarn workspace proton-mail test
With coverage:
yarn workspace proton-mail test:coverage
Watch mode:
yarn workspace proton-mail test:watch

Package Tests

yarn workspace @proton/components test

Run All Tests

Run tests across all workspaces:
yarn workspaces foreach run test
This may take significant time and resources. Consider using CI mode with parallel execution.

Test Scripts Reference

Standard Jest Scripts

Most applications follow these script conventions:
ScriptDescriptionCommand
testRun all testsjest
test:ciCI test run with coveragejest --ci --coverage --runInBand
test:watchWatch modejest --watch --coverage=false
test:coverageGenerate coverage reportjest --collectCoverage

Application-Specific Test Scripts

package.json
{
  "scripts": {
    "test": "jest --logHeapUsage --forceExit",
    "test:ci": "jest --coverage --runInBand --ci --forceExit",
    "test:coverage": "jest --collectCoverage",
    "test:watch": "jest --watch --coverage=false"
  }
}
Mail tests use --forceExit to ensure process termination and --logHeapUsage for memory monitoring.
package.json
{
  "scripts": {
    "test": "jest",
    "test:ci": "jest --coverage=false --runInBand --ci",
    "test:watch": "jest --watch --coverage=false"
  }
}
package.json
{
  "scripts": {
    "test": "cross-env TZ=UTC jest",
    "test:ci": "yarn run test --ci --coverage --runInBand",
    "test:watch": "yarn run test --coverage=false --watch"
  }
}
Calendar tests require UTC timezone to ensure consistent date/time behavior.
package.json
{
  "scripts": {
    "test": "jest",
    "test:ci": "jest --coverage --runInBand --ci --logHeapUsage",
    "test:watch": "jest --watch"
  }
}
package.json
{
  "scripts": {
    "test": "NODE_ENV=test karma start test/karma.conf.js",
    "test:ci": "yarn test",
    "test:watch": "npm test -- --auto-watch --no-single-run"
  }
}
The @proton/shared package uses Karma for historical reasons. New tests should prefer Jest.

Jest Configuration

Standard Jest Configuration

Example from applications/mail/jest.config.js:
module.exports = {
  setupFilesAfterEnv: ['./jest.setup.js'],
  moduleDirectories: ['<rootDir>/node_modules', 'node_modules'],
  collectCoverage: true,
  collectCoverageFrom: [
    'src/**/*.{js,jsx,ts,tsx}',
    '!<rootDir>/src/app/locales.ts',
    '!<rootDir>/src/app/*.{js,jsx,ts,tsx}',
  ],
  testEnvironment: '@proton/jest-env',
  transformIgnorePatterns: [
    'node_modules/(?!(@proton/shared|@proton/components|pmcrypto|openpgp)/)',
  ],
  transform: {
    '^.+\\.(m?js|tsx?)$': '<rootDir>/jest.transform.js',
  },
  moduleNameMapper: {
    '\\.(css|scss|less)$': '@proton/components/__mocks__/styleMock.js',
    '\\.(jpg|jpeg|png|gif|svg|ttf|woff|woff2)$': '@proton/components/__mocks__/fileMock.js',
  },
  coverageReporters: ['text-summary', 'json'],
  reporters: ['default', ['jest-junit', { 
    suiteNameTemplate: '{filepath}', 
    outputName: 'test-report.xml' 
  }]],
};

Key Configuration Elements

testEnvironment
string
default:"@proton/jest-env"
Custom Jest environment package providing browser-like globals
transformIgnorePatterns
array
Excludes node_modules except specified packages that need transformation
moduleNameMapper
object
Maps static assets to mocks for testing
coverageReporters
array
Specifies coverage report formats (text-summary, json, html, etc.)

Test Environment

@proton/jest-env

Custom Jest environment package used across applications:
testEnvironment: '@proton/jest-env'
Provides:
  • Browser-like globals (window, document, etc.)
  • DOM manipulation capabilities
  • JSDOM environment setup

Testing Libraries

Common testing dependencies:

@testing-library/react

React component testing utilities

@testing-library/jest-dom

Custom Jest matchers for DOM assertions

@testing-library/user-event

User interaction simulation

@proton/testing

Proton-specific testing utilities

Coverage Reports

Generating Coverage

yarn workspace proton-mail test:coverage
Coverage output:
  • Console summary
  • coverage/ directory with detailed reports
  • coverage/lcov-report/index.html for browser viewing

Coverage Configuration

Configure coverage collection:
jest.config.js
{
  collectCoverage: true,
  collectCoverageFrom: [
    'src/**/*.{js,jsx,ts,tsx}',
    '!**/*.test.{js,jsx,ts,tsx}',
    '!**/node_modules/**',
  ],
  coverageThresholds: {
    global: {
      branches: 50,
      functions: 50,
      lines: 50,
      statements: 50,
    },
  },
}

CI Testing

Turbo Configuration

Tests run through Turbo with dependency orchestration:
turbo.json
{
  "tasks": {
    "test:ci": {
      "dependsOn": ["transit"]
    },
    "test:coverage": {
      "outputs": ["coverage/**"]
    }
  }
}

Running Tests in CI

# Run CI tests for all workspaces
yarn workspaces foreach run test:ci

# Run with Turbo for caching and orchestration
turbo run test:ci
Turbo caches test results. Only changed workspaces re-run tests.

Special Test Configurations

Playwright (Storybook)

yarn workspace @proton/storybook test
Configuration: applications/storybook/playwright.config.ts

Vitest Examples

yarn workspace @proton/wallet test
Configuration files: vitest.config.ts in respective packages

Debugging Tests

Run Specific Test File

yarn workspace proton-mail test path/to/test-file.test.ts

Debug in VS Code

Add to .vscode/launch.json:
{
  "type": "node",
  "request": "launch",
  "name": "Jest Debug",
  "program": "${workspaceFolder}/node_modules/.bin/jest",
  "args": ["--runInBand", "${file}"],
  "console": "integratedTerminal",
  "internalConsoleOptions": "neverOpen"
}

Verbose Output

yarn workspace proton-mail test --verbose

Best Practices

1

Run Tests Locally

Always run tests before committing:
yarn workspace <app-name> test
2

Watch Mode During Development

Use watch mode for faster feedback:
yarn workspace <app-name> test:watch
3

Check Coverage

Maintain reasonable coverage levels:
yarn workspace <app-name> test:coverage
4

Fix Failing Tests

Never commit with failing tests. Debug and fix before pushing.

Troubleshooting

Increase Jest timeout:
jest.setTimeout(10000); // 10 seconds
Or for specific test:
test('long running test', async () => {
  // test code
}, 10000);
Use --runInBand to run tests serially:
yarn workspace proton-mail test --runInBand
Or increase Node memory:
export NODE_OPTIONS="--max-old-space-size=4096"
Check transformIgnorePatterns in Jest config. Add problematic packages:
transformIgnorePatterns: [
  'node_modules/(?!(problematic-package|@proton/shared)/)',
]
Verify moduleNameMapper includes necessary mappings:
moduleNameMapper: {
  '^@/(.*)$': '<rootDir>/src/$1',
}
Some tests may depend on specific environment variables or external services. Check test setup files for requirements.

Build docs developers (and LLMs) love