Test-framework agnostic JavaScript testing runner that supports TDD workflows and CI integration across multiple browsers and environments.
—
Testem provides a JavaScript API for integrating into build tools, custom workflows, and automated systems.
Main programmatic interface for controlling testem execution modes.
/**
* Main API class for programmatic testem control
*/
class Api {
/**
* Start development mode with file watching and interactive UI
* @param options - Configuration options for development mode
* @param finalizer - Optional callback when development session ends
*/
startDev(options: TestemOptions, finalizer?: (exitCode: number, error?: Error) => void): void;
/**
* Start CI mode for automated test execution
* @param options - Configuration options for CI mode
* @param finalizer - Callback when tests complete with exit code and error
*/
startCI(options: TestemOptions, finalizer?: (exitCode: number, error?: Error) => void): void;
/**
* Start server-only mode without launching browsers
* @param options - Configuration options for server mode
*/
startServer(options: TestemOptions): void;
/**
* Restart the current test run (development mode only)
*/
restart(): void;
/**
* Set default options that will be merged with runtime options
* @param defaultOptions - Default configuration options
*/
setDefaultOptions(defaultOptions: TestemOptions): void;
}Usage Examples:
const Api = require('testem/lib/api');
// Development mode
const api = new Api();
api.startDev({
port: 7357,
launch: ['Chrome', 'Firefox'],
src_files: ['src/**/*.js', 'test/**/*.js']
});
// CI mode with callback
api.startCI({
reporter: 'tap',
parallel: 3,
launch_in_ci: ['Chrome', 'Firefox']
}, (exitCode, error) => {
if (exitCode === 0) {
console.log('All tests passed!');
} else {
console.error('Tests failed:', error);
}
process.exit(exitCode);
});
// Server-only mode
api.startServer({
port: 8080,
serve_files: ['dist/bundle.js']
});Configuration management with file parsing and option merging.
/**
* Configuration management class
*/
class Config {
/**
* Create a new configuration instance
* @param appMode - Application mode (dev, ci, server)
* @param progOptions - Programmatic options
* @param config - Direct configuration object
*/
constructor(appMode: 'dev' | 'ci' | 'server', progOptions?: TestemOptions, config?: ConfigOptions);
/**
* Read configuration from file system
* @param callback - Called when configuration is loaded
*/
read(callback: () => void): void;
/**
* Get a configuration value
* @param key - Configuration key
* @returns Configuration value
*/
get(key: string): any;
/**
* Set a configuration value
* @param key - Configuration key
* @param value - Value to set
*/
set(key: string, value: any): void;
/**
* Resolve a file path relative to working directory
* @param filepath - File path to resolve
* @returns Absolute file path
*/
resolvePath(filepath: string): string;
/**
* Get client-side configuration
* @returns Configuration object for browser clients
*/
client(): object;
/**
* Print information about available launchers
*/
printLauncherInfo(): void;
/**
* Set default configuration options
* @param defaultOptions - Default options to merge
*/
setDefaultOptions(defaultOptions: TestemOptions): void;
}Usage Examples:
const Config = require('testem/lib/config');
// Create configuration
const config = new Config('ci', {
port: 7357,
reporter: 'tap'
});
// Read from file
config.read(() => {
console.log('Framework:', config.get('framework'));
console.log('Source files:', config.get('src_files'));
// Modify configuration
config.set('parallel', 5);
// Resolve paths
const testFile = config.resolvePath('test/runner.js');
});Express-based server for serving test files and managing client connections.
/**
* Web server for serving tests and managing browser connections
*/
class Server extends EventEmitter {
/**
* Create a new server instance
* @param config - Configuration instance
*/
constructor(config: Config);
/**
* Start the server
* @param callback - Optional callback when server starts
* @returns Promise that resolves when server is listening
*/
start(callback?: () => void): Promise<void>;
/**
* Stop the server and close all connections
* @param callback - Optional callback when server stops
* @returns Promise that resolves when server is closed
*/
stop(callback?: () => void): Promise<void>;
// Events emitted:
// 'server-start' - Server has started listening
// 'server-error' - Server encountered an error
}Usage Examples:
const Server = require('testem/lib/server');
const Config = require('testem/lib/config');
// Create and start server
const config = new Config('server', { port: 8080 });
config.read(() => {
const server = new Server(config);
server.on('server-start', () => {
console.log('Server started on port', config.get('port'));
});
server.on('server-error', (error) => {
console.error('Server error:', error);
});
server.start().then(() => {
console.log('Server is ready');
});
});Main application orchestrator that coordinates all testem components.
/**
* Main application class that orchestrates test execution
*/
class App extends EventEmitter {
/**
* Create a new application instance
* @param config - Configuration instance
* @param finalizer - Function called when app exits
*/
constructor(config: Config, finalizer?: (exitCode: number, error?: Error) => void);
/**
* Start the application
* @param callback - Optional callback when app starts
* @returns Promise that resolves when app is running
*/
start(callback?: () => void): Promise<void>;
/**
* Exit the application
* @param error - Optional error that caused exit
*/
exit(error?: Error): void;
/**
* Trigger a new test run (development mode)
* @param reason - Reason for triggering run
*/
triggerRun(reason: string): void;
// Events emitted:
// 'tests-finish' - All tests completed successfully
// 'tests-error' - Tests completed with errors
}Usage Examples:
const App = require('testem/lib/app');
const Config = require('testem/lib/config');
// Create application
const config = new Config('dev', { port: 7357 });
config.read(() => {
const app = new App(config, (exitCode, error) => {
console.log('App exited with code:', exitCode);
if (error) console.error('Error:', error);
});
app.on('tests-finish', () => {
console.log('Tests completed successfully');
});
app.on('tests-error', () => {
console.log('Tests completed with errors');
});
app.start();
});// Example: Gulp integration
const gulp = require('gulp');
const Api = require('testem/lib/api');
gulp.task('test', (done) => {
const api = new Api();
api.startCI({
launch_in_ci: ['Chrome', 'Firefox'],
reporter: 'tap'
}, (exitCode, error) => {
if (exitCode !== 0) {
done(new Error('Tests failed'));
} else {
done();
}
});
});
// Example: Webpack plugin
class TestemPlugin {
apply(compiler) {
compiler.hooks.afterEmit.tapAsync('TestemPlugin', (compilation, callback) => {
const api = new Api();
api.startCI({
src_files: ['dist/bundle.js', 'test/**/*.js']
}, (exitCode) => {
callback();
});
});
}
}// Custom test runner with programmatic control
const Api = require('testem/lib/api');
const Config = require('testem/lib/config');
class CustomTestRunner {
constructor(options) {
this.api = new Api();
this.config = new Config('ci', options);
}
async run() {
return new Promise((resolve, reject) => {
this.config.read(() => {
this.api.startCI(this.config, (exitCode, error) => {
if (exitCode === 0) {
resolve({ success: true, exitCode });
} else {
reject({ success: false, exitCode, error });
}
});
});
});
}
}
// Usage
const runner = new CustomTestRunner({
framework: 'jasmine',
src_files: ['src/**/*.js', 'test/**/*.js'],
launch_in_ci: ['Chrome', 'Firefox']
});
runner.run()
.then(result => console.log('Tests passed'))
.catch(result => console.error('Tests failed:', result.error));// Example: Express middleware integration
const express = require('express');
const Server = require('testem/lib/server');
const Config = require('testem/lib/config');
const app = express();
// Regular routes
app.get('/api/*', handleApiRequests);
// Testem integration for testing
const config = new Config('server', {
port: 0, // Use random port for testem
src_files: ['public/js/**/*.js', 'test/**/*.js']
});
config.read(() => {
const testemServer = new Server(config);
testemServer.start().then(() => {
const testemPort = config.get('port');
// Proxy test requests to testem
app.use('/testem', proxy(`http://localhost:${testemPort}`));
app.listen(3000, () => {
console.log('Development server with testem integration ready');
});
});
});Install with Tessl CLI
npx tessl i tessl/npm-testem