testdriver-agent
How the TestDriver agent behaves on GitHub issues, pull requests, and @mentions
Build maintainable test suites with reusable code patterns
Claude Code
$ npx -y skills add testdriverai/testdriverai --skill testdriver-reusable-code --agent claude-code
Codex
$ npx skills add testdriverai/testdriverai --skill testdriver-reusable-code -a codex -y
Cursor
$ npx skills add testdriverai/testdriverai --skill testdriver-reusable-code -a cursor -y
Gemini CLI
$ npx skills add testdriverai/testdriverai --skill testdriver-reusable-code -a gemini-cli -y
GitHub Copilot
$ npx skills add testdriverai/testdriverai --skill testdriver-reusable-code -a github-copilot -y
OpenCode
$ npx skills add testdriverai/testdriverai --skill testdriver-reusable-code -a opencode -y
Windsurf
$ npx skills add testdriverai/testdriverai --skill testdriver-reusable-code -a windsurf -y
name: testdriver:reusable-code description: Build maintainable test suites with reusable code patterns
<!-- Generated from reusable-code.mdx. DO NOT EDIT. -->
When your test suite becomes larger, put the common patterns into reusable code. This keeps the tests short, easy to read, and easy to keep.
The most simple method is to put the common actions into helper functions. Make a `helpers/` directory for the shared utilities:
export async function login(testdriver, { email, password }) {
const emailInput = await testdriver.find('email input');
await emailInput.click();
await testdriver.type(email);
const passwordInput = await testdriver.find('password input');
await passwordInput.click();
await testdriver.type(password);
const loginButton = await testdriver.find('login button');
await loginButton.click();
const result = await testdriver.assert('user is logged in');
return result;
}
export async function logout(testdriver) {
const userMenu = await testdriver.find('user menu');
await userMenu.click();
const logoutButton = await testdriver.find('logout button');
await logoutButton.click();
}<Warning> **Do not put dynamic values in element descriptions.** An element selector must describe the *type* of the element. It must not describe specific content that can change.
**❌ Bad:** `await testdriver.find('profile name TestDriver in the top right')` **✅ Good:** `await testdriver.find('user profile name in the top right')`
Hardcoded values like usernames, product names, or prices will cause tests to fail when the data changes. Use generic descriptions that work regardless of the specific content displayed. </Warning>
Now import and use these helpers in any test:
import { describe, expect, it } from "vitest";
import { TestDriver } from "testdriverai/vitest/hooks";
import { login } from './helpers/auth.js';
describe("Checkout", () => {
it("should complete checkout as logged in user", async (context) => {
const testdriver = TestDriver(context);
await testdriver.provision.chrome({
url: 'https://shop.example.com',
});
// Use the helper
await login(testdriver, {
email: 'user@example.com',
password: 'password123'
});
// Continue with checkout steps...
const cartButton = await testdriver.find('cart button');
await cartButton.click();
});
});For larger test suites, the Page Object pattern encapsulates all interactions with a specific page or component:
export class LoginPage {
constructor(testdriver) {
this.td = testdriver;
}
async enterEmail(email) {
const input = await this.td.find('email input');
await input.click();
await this.td.type(email);
}
async enterPassword(password) {
const input = await this.td.find('password input');
await input.click();
await this.td.type(password);
}
async submit() {
const button = await this.td.find('submit button');
await button.click();
}
async login(email, password) {
await this.enterEmail(email);
await this.enterPassword(password);
await this.submit();
}
async assertError(message) {
return await this.td.assert(`error message shows "${message}"`);
}
async assertLoggedIn() {
return await this.td.assert('user dashboard is visible');
}
}Use the page object in your tests:
import { describe, expect, it } from "vitest";
import { TestDriver } from "testdriverai/vitest/hooks";
import { LoginPage } from './pages/LoginPage.js';
describe("Authentication", () => {
it("should show error for invalid credentials", async (context) => {
const testdriver = TestDriver(context);
await testdriver.provision.chrome({
url: 'https://app.example.com/login',
});
const loginPage = new LoginPage(testdriver);
await loginPage.login('invalid@test.com', 'wrongpassword');
const hasError = await loginPage.assertError('Invalid credentials');
expect(hasError).toBeTruthy();
});
it("should redirect to dashboard on success", async (context) => {
const testdriver = TestDriver(context);
await testdriver.provision.chrome({
url: 'https://app.example.com/login',
});
const loginPage = new LoginPage(testdriver);
await loginPage.login('valid@test.com', 'correctpassword');
const isLoggedIn = await loginPage.assertLoggedIn();
expect(isLoggedIn).toBeTruthy();
});
});Create reusable fixtures for common test setup scenarios:
export const testUsers = {
admin: { email: 'admin@example.com', password: 'admin123' },
regular: { email: 'user@example.com', password: 'user123' },
guest: { email: 'guest@example.com', password: 'guest123' },
};
export const testUrls = {
staging: 'https://staging.example.com',
production: 'https://example.com',
};
export async function setupAuthenticatedSession(testdriver, user = testUsers.regular) {
const emailInput = await testdriver.find('email input');
await emailInput.click();
await testdriver.type(user.email);
const passwordInput = await testdriver.find('password input');
await passwordInput.click();
await testdriver.type(user.password);
const loginButton = await testdriver.find('login button');
await loginButton.click();
await testdriver.assert('user is logged in');
}import { describe, expect, it } from "vitest";
import { TestDriver } from "testdriverai/vitest/hooks";
import { testUsers, testUrls, setupAuthenticatedSession } from './fixtures/index.js';
describe("Admin Panel", () => {
it("should access admin settings", async (context) => {
const testdriver = TestDriver(context);
await testdriver.provision.chrome({How the TestDriver agent behaves on GitHub issues, pull requests, and @mentions
Deploy TestDriver on your AWS infrastructure using CloudFormation
How TestDriver learns your app and caches what it discovers for instant, deterministic replays
Produce the "Playwright 1.xx API Review" artifact for a range of commits - the public API additions and the breaking or behavior…
Write, refine, run, and QA promptfoo evaluation suites: promptfooconfig.yaml, prompts, providers, vars, tests, assertions,…
DeepEval evaluation workflow for AI agents and LLM applications. TRIGGER when the user wants to evaluate or improve an AI agent,…