Back to skills
SKILL.md
Testdriver Reusable Code
ASecurityBuild maintainable test suites with reusable code patterns
- 243 stars
- 0 votes
- 0 copies
- 2 views
- Added September 6, 2026
Works with
Security analysis
100/100npx -y skills add testdriverai/testdriverai --skill testdriver-reusable-code --agent claude-codeAre you the author of Testdriver Reusable Code?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/testdriverai-testdriver-reusable-code)---
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.
## Helper Functions
The most simple method is to put the common actions into helper functions. Make a `helpers/` directory for the shared utilities:
```javascript test/helpers/auth.js
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:
```javascript test/checkout.test.mjs
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();
});
});
```
## Page Objects
For larger test suites, the Page Object pattern encapsulates all interactions with a specific page or component:
```javascript test/pages/LoginPage.js
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:
```javascript test/auth.test.mjs
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();
});
});
```
## Shared Test Fixtures
Create reusable fixtures for common test setup scenarios:
```javascript test/fixtures/index.js
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');
}
```
```javascript test/admin.test.mjs
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({
url: `${testUrls.staging}/login`,
});
await setupAuthenticatedSession(testdriver, testUsers.admin);
const settingsLink = await testdriver.find('admin settings link');
await settingsLink.click();
const result = await testdriver.assert('admin settings panel is visible');
expect(result).toBeTruthy();
});
});
```
## Suggested Project Structure
<FileTree>
<Folder name="test" defaultOpen>
<Folder name="fixtures" defaultOpen>
<File name="index.js" />
</Folder>
<Folder name="helpers" defaultOpen>
<File name="auth.js" />
<File name="navigation.js" />
<File name="forms.js" />
</Folder>
<Folder name="pages" defaultOpen>
<File name="LoginPage.js" />
<File name="DashboardPage.js" />
<File name="CheckoutPage.js" />
</Folder>
<Folder name="specs" defaultOpen>
<File name="auth.test.mjs" />
<File name="checkout.test.mjs" />
<File name="search.test.mjs" />
</Folder>
</Folder>
</FileTree>
| Folder | Purpose |
|--------|---------|
| `fixtures/` | Test data and setup utilities |
| `helpers/` | Reusable helper functions |
| `pages/` | Page object classes |
| `specs/` | Test files |
<Tip>
Start simple with helper functions. Only introduce page objects when you find yourself duplicating the same element interactions across multiple tests.
</Tip>
Attribution
Comments
Loading comments…