Scale your test suite with reusable code patterns and dynamic data to reduce duplication and maintain consistency across thousands of tests.
Code Snippets for Reusability
Scale your test suite with reusable code snippets to reduce duplication and maintain consistency:
Helper Functions
Page Objects
Custom Commands
// Reusable authentication helpers
export async function login ( testdriver , { email , password }) {
await testdriver . find ( 'email input' ). type ( email );
await testdriver . find ( 'password input' ). type ( password );
await testdriver . find ( 'login button' ). click ();
await testdriver . assert ( 'logged in successfully' );
}
export async function logout ( testdriver ) {
await testdriver . find ( 'user menu' ). click ();
await testdriver . find ( 'logout button' ). click ();
}
import { login } from './helpers/auth.js' ;
test ( 'checkout flow' , async ( context ) => {
const { testdriver } = await chrome ( context , { url });
await login ( testdriver , {
email: '[email protected] ' ,
password: 'password123'
});
// Continue with checkout test
});
export class LoginPage {
constructor ( testdriver ) {
this . testdriver = testdriver ;
}
async login ( email , password ) {
await this . testdriver . find ( 'email input' ). type ( email );
await this . testdriver . find ( 'password input' ). type ( password );
await this . testdriver . find ( 'submit button' ). click ();
}
async assertError ( message ) {
await this . testdriver . assert ( `error message shows " ${ message } "` );
}
}
import { LoginPage } from './pages/LoginPage.js' ;
test ( 'invalid login shows error' , async ( context ) => {
const { testdriver } = await chrome ( context , { url });
const loginPage = new LoginPage ( testdriver );
await loginPage . login ( '[email protected] ' , 'wrong' );
await loginPage . assertError ( 'Invalid credentials' );
});
test/commands/navigation.js
export function addNavigationCommands ( testdriver ) {
testdriver . navigateTo = async ( section ) => {
await testdriver . find ( ` ${ section } nav link` ). click ();
await testdriver . assert ( ` ${ section } page is loaded` );
};
testdriver . searchFor = async ( query ) => {
await testdriver . find ( 'search input' ). type ( query );
await testdriver . find ( 'search button' ). click ();
await testdriver . assert ( 'search results are displayed' );
};
return testdriver ;
}
import { addNavigationCommands } from './commands/navigation.js' ;
test ( 'search functionality' , async ( context ) => {
let { testdriver } = await chrome ( context , { url });
testdriver = addNavigationCommands ( testdriver );
await testdriver . searchFor ( 'laptop' );
// Custom command used
});
Reusable snippets reduce test maintenance time by up to 70% in large test suites.
Dynamic Variables for Data-Driven Tests
Scale your testing with dynamic data to cover more scenarios:
Environment Variables
Test Fixtures
Dynamic Data Generation
import { test } from 'vitest' ;
import { chrome } from 'testdriverai/presets' ;
test ( 'multi-environment testing' , async ( context ) => {
const env = process . env . TEST_ENV || 'staging' ;
const urls = {
dev: 'https://dev.myapp.com' ,
staging: 'https://staging.myapp.com' ,
production: 'https://myapp.com'
};
const { testdriver } = await chrome ( context , {
url: urls [ env ]
});
await testdriver . assert ( 'app is running' );
});
# Run against different environments
TEST_ENV = dev npx vitest run
TEST_ENV = staging npx vitest run
TEST_ENV = production npx vitest run
export const testUsers = [
{ email: '[email protected] ' , role: 'admin' },
{ email: '[email protected] ' , role: 'user' },
{ email: '[email protected] ' , role: 'guest' }
];
export const products = [
{ name: 'Laptop' , price: 999 },
{ name: 'Mouse' , price: 29 },
{ name: 'Keyboard' , price: 89 }
];
import { test } from 'vitest' ;
import { chrome } from 'testdriverai/presets' ;
import { testUsers } from './fixtures/users.js' ;
test . each ( testUsers )( '$role can access dashboard' , async ({ email , role }, context ) => {
const { testdriver } = await chrome ( context , { url });
await testdriver . find ( 'email input' ). type ( email );
await testdriver . find ( 'password input' ). type ( 'password123' );
await testdriver . find ( 'login button' ). click ();
if ( role === 'admin' ) {
await testdriver . assert ( 'admin panel is visible' );
} else {
await testdriver . assert ( 'user dashboard is visible' );
}
});
import { test } from 'vitest' ;
import { chrome } from 'testdriverai/presets' ;
import { faker } from '@faker-js/faker' ;
test ( 'user registration with dynamic data' , async ( context ) => {
const { testdriver } = await chrome ( context , { url });
// Generate unique test data for each run
const userData = {
firstName: faker . person . firstName (),
lastName: faker . person . lastName (),
email: faker . internet . email (),
password: faker . internet . password ({ length: 12 })
};
await testdriver . find ( 'first name input' ). type ( userData . firstName );
await testdriver . find ( 'last name input' ). type ( userData . lastName );
await testdriver . find ( 'email input' ). type ( userData . email );
await testdriver . find ( 'password input' ). type ( userData . password );
await testdriver . find ( 'register button' ). click ();
await testdriver . assert ( 'registration successful' );
console . log ( 'Registered user:' , userData . email );
});
npm install --save-dev @faker-js/faker
Dynamic Variables Best Practices
Environment configs: Store URLs, credentials, and settings in env vars
Test fixtures: Maintain reusable test data in separate files
Data generators: Use libraries like Faker for unique test data
Parameterization: Test multiple scenarios with test.each()
CI/CD integration: Pass dynamic values via environment variables
Secure Secrets Management
Protect sensitive data in your tests with built-in secrets handling:
Secret Type Option
Environment Variables
CI/CD Secrets
Helper Functions
import { test } from 'vitest' ;
import { chrome } from 'testdriverai/presets' ;
test ( 'login with secret password' , async ( context ) => {
const { testdriver } = await chrome ( context , { url });
await testdriver . find ( 'email input' ). type ( '[email protected] ' );
// Password is never logged or cached
await testdriver . find ( 'password input' ). type ( process . env . TEST_PASSWORD , {
secret: true
});
await testdriver . find ( 'login button' ). click ();
await testdriver . assert ( 'logged in successfully' );
});
The secret: true option prevents sensitive data from appearing in logs, dashcam replays, or cache entries.
// .env.test
TEST_PASSWORD = my - secret - password
TEST_API_KEY = sk - 1234567890 abcdef
TEST_CREDIT_CARD = 4111111111111111
// test/auth.test.js
import { test } from 'vitest' ;
import { chrome } from 'testdriverai/presets' ;
test ( 'secure authentication' , async ( context ) => {
const { testdriver } = await chrome ( context , { url });
// Load secrets from environment
await testdriver . find ( 'password input' ). type (
process . env . TEST_PASSWORD ,
{ secret: true }
);
await testdriver . find ( 'api key input' ). type (
process . env . TEST_API_KEY ,
{ secret: true }
);
});
# Install dotenv for local development
npm install --save-dev dotenv
# Load in test setup
import 'dotenv/config' ;
.github/workflows/test.yml
name : E2E Tests
on : [ push , pull_request ]
jobs :
test :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v3
- uses : actions/setup-node@v3
- run : npm install
- run : npx vitest run
env :
TD_API_KEY : ${{ secrets.TD_API_KEY }}
TEST_PASSWORD : ${{ secrets.TEST_PASSWORD }}
TEST_API_KEY : ${{ secrets.TEST_API_KEY }}
Configure secrets in your CI/CD platform:
GitHub: Settings → Secrets and variables → Actions
GitLab: Settings → CI/CD → Variables
CircleCI: Project Settings → Environment Variables
// Centralized secret handling
export function getSecret ( key ) {
const value = process . env [ key ];
if ( ! value ) {
throw new Error ( `Secret ${ key } not found in environment` );
}
return value ;
}
export async function typeSecret ( testdriver , selector , secretKey ) {
const value = getSecret ( secretKey );
await testdriver . find ( selector ). type ( value , { secret: true });
}
import { typeSecret } from './helpers/secrets.js' ;
test ( 'payment form' , async ( context ) => {
const { testdriver } = await chrome ( context , { url });
await typeSecret ( testdriver , 'credit card input' , 'TEST_CREDIT_CARD' );
await typeSecret ( testdriver , 'cvv input' , 'TEST_CVV' );
await testdriver . find ( 'submit payment' ). click ();
});
Never hardcode secrets in test files or commit them to version control. Always use environment variables or secure secret management systems.
Familiar Test Syntax
TestDriver works with the test frameworks you already know:
import { test , describe , beforeAll , afterAll } from 'vitest' ;
import { chrome } from 'testdriverai/presets' ;
describe ( 'My Feature' , () => {
test ( 'should work' , async ( context ) => {
const { testdriver } = await chrome ( context , { url });
await testdriver . find ( 'button' ). click ();
});
});
import { chrome } from 'testdriverai/presets' ;
describe ( 'My Feature' , () => {
test ( 'should work' , async () => {
const { testdriver } = await chrome ({ url });
await testdriver . find ( 'button' ). click ();
});
});
import { chrome } from 'testdriverai/presets' ;
describe ( 'My Feature' , function () {
it ( 'should work' , async function () {
const { testdriver } = await chrome ( this , { url });
await testdriver . find ( 'button' ). click ();
});
});
Existing Systems Integration
Drop TestDriver into your current workflow without disruption:
import { test , expect } from 'vitest' ;
import { chrome } from 'testdriverai/presets' ;
test ( 'integrates with existing assertions' , async ( context ) => {
const { testdriver } = await chrome ( context , { url });
// Use TestDriver's AI assertions
await testdriver . assert ( 'welcome message is visible' );
// Or use traditional assertions
const button = await testdriver . find ( 'submit button' );
expect ( button . coordinates ). toBeDefined ();
expect ( button . text ). toContain ( 'Submit' );
// Mix and match as needed
const element = await testdriver . exec ( 'js' , 'document.title' );
expect ( element ). toBe ( 'My App' );
});
Learn More