Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build maintainable test suites with reusable code patterns
.claude/skills/testdriverai-testdriver-reusable-code/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 100% | 7 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-11 | ✗→✓ | ▲ Improved | 68% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 54% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 49% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 8% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 31% | 0% |
<!-- Generated from reusable-code.mdx. DO NOT EDIT. -->
As your test suite grows, you'll want to extract common patterns into reusable code. This keeps tests DRY, readable, and easy to maintain.
The simplest approach is extracting common actions into helper functions. Create a helpers/ directory for shared utilities:
javascript test/helpers/auth.jsexport 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> Avoid hardcoding dynamic values in element descriptions. Element selectors should describe the type of element, not specific content that might 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.mjsimport { 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:
javascript test/pages/LoginPage.jsexport 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.mjsimport { 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:
javascript test/fixtures/index.jsexport 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.mjsimport { 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(); }); });
<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>
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-11 | fail→pass | 10,436 | 6,568 | -37% | 1 | 1 | 0% | 1,959 | 3,287 | +68% | 0 | 0 | — |
case-01 | fail→pass | 13,226 | 13,288 | +0% | 1 | 1 | 0% | 2,578 | 3,977 | +54% | 0 | 0 | — |
case-02 | fail→pass | 15,687 | 12,183 | -22% | 1 | 1 | 0% | 2,998 | 4,479 | +49% | 0 | 0 | — |
case-03 | fail→pass | 15,351 | 5,808 | -62% | 1 | 1 | 0% | 2,936 | 3,162 | +8% | 0 | 0 | — |
case-04 | pass→pass | 11,941 | 9,130 | -24% | 1 | 1 | 0% | 2,204 | 3,719 | +69% | 0 | 0 | — |
case-05 | pass→pass | 15,710 | 10,992 | -30% | 1 | 1 | 0% | 2,961 | 4,187 | +41% | 0 | 0 | — |
case-06 | pass→pass | 7,522 | 6,041 | -20% | 1 | 1 | 0% | 1,376 | 3,063 | +123% | 0 | 0 | — |
case-07 | pass→pass | 10,077 | 3,371 | -67% | 1 | 1 | 0% | 1,643 | 2,581 | +57% | 0 | 0 | — |
case-08 | pass→pass | 9,818 | 6,768 | -31% | 1 | 1 | 0% | 1,654 | 3,224 | +95% | 0 | 0 | — |
case-09 | pass→pass | 11,805 | 3,256 | -72% | 1 | 1 | 0% | 1,980 | 2,588 | +31% | 0 | 0 | — |
case-10 | fail→pass | 15,717 | 5,266 | -66% | 1 | 1 | 0% | 2,286 | 2,990 | +31% | 0 | 0 | — |
case-12 | pass→pass | 9,010 | 4,387 | -51% | 1 | 1 | 0% | 1,610 | 2,775 | +72% | 0 | 0 | — |
case-13 | fail→pass | 10,006 | 3,894 | -61% | 1 | 1 | 0% | 1,869 | 2,764 | +48% | 0 | 0 | — |
case-14 | pass→pass | 13,369 | 5,701 | -57% | 1 | 1 | 0% | 1,879 | 2,299 | +22% | 0 | 0 | — |
case-15 | pass→pass | 13,404 | 4,902 | -63% | 1 | 1 | 0% | 1,842 | 2,660 | +44% | 0 | 0 | — |
case-16 | pass→pass | 9,463 | 1,519 | -84% | 1 | 1 | 0% | 1,297 | 2,174 | +68% | 0 | 0 | — |
case-17 | pass→pass | 13,288 | 4,977 | -63% | 1 | 1 | 0% | 2,434 | 2,942 | +21% | 0 | 0 | — |
case-18 | fail→pass | 8,386 | 3,475 | -59% | 1 | 1 | 0% | 1,509 | 2,460 | +63% | 0 | 0 | — |
case-19 | pass→pass | 9,773 | 4,434 | -55% | 1 | 1 | 0% | 1,889 | 2,823 | +49% | 0 | 0 | — |
case-20 | fail→pass | 15,641 | 4,798 | -69% | 1 | 1 | 0% | 2,266 | 2,859 | +26% | 0 | 0 | — |
case-21 | pass→pass | 13,670 | 5,566 | -59% | 1 | 1 | 0% | 2,018 | 2,877 | +43% | 0 | 0 | — |
case-22 | fail→pass | 7,105 | 3,794 | -47% | 1 | 1 | 0% | 1,140 | 2,421 | +112% | 0 | 0 | — |
case-23 | fail→pass | 3,174 | 3,794 | +20% | 1 | 1 | 0% | 523 | 2,566 | +391% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +43 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.