From triqual-plugin
Unified Playwright test automation. Default runs full autonomous loop. Use --explore for interactive, --ticket ENG-123 for Linear tickets, --describe for text descriptions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/triqual-plugin:testThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate production-ready Playwright tests with multiple input modes. Default mode runs the full autonomous loop with pattern learning.
Generate production-ready Playwright tests with multiple input modes. Default mode runs the full autonomous loop with pattern learning.
/test login # Full autonomous (default) - explore → plan → generate → heal → learn
/test --explore login # Interactive exploration only - opens visible browser
/test --ticket ENG-123 # From Linear ticket - fetches AC, generates tests
/test --describe "..." # From user description - skips exploration
| Mode | Explore | Plan | Generate | Heal | Learn |
|---|---|---|---|---|---|
/test login (default) | ✅ Auto | ✅ | ✅ | ✅ 5x | ✅ |
/test --explore login | ✅ Interactive | ❌ | ❌ | ❌ | ❌ |
/test --ticket ENG-123 | ❌ (from AC) | ✅ | ✅ | ✅ | ✅ |
/test --describe "..." | ❌ (from text) | ✅ | ✅ | ✅ | ✅ |
┌─────────────────────────────────────────────────────────────────────────────┐
│ /test {feature} │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ INPUT SOURCES: │
│ ├── Default → EXPLORE with Playwright MCP │
│ ├── --ticket → Linear ticket acceptance criteria │
│ ├── --describe → User text description │
│ └── --explore → Interactive exploration only (STOPS after explore) │
│ │
│ PHASES: │
│ 0. SETUP → Auto-config, load patterns, discover credentials │
│ 0.6 LOAD CONTEXT → triqual_load_context tool builds context files (MANDATORY, BLOCKING)│
│ 1. EXPLORE → Playwright MCP (skip with --ticket/--describe) │
│ 2. PLAN → Quoth context output + input source │
│ 3. GENERATE → .spec.ts in .draft/tests/ (NEVER directly to tests/) │
│ 4. HEAL LOOP → Run → Fix → Re-run (max 5 iterations) │
│ 5. PROMOTE → User approves → Move to production test directory │
│ 6. LEARN → Save patterns + anti-patterns │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
This phase loads configuration and handles authentication before any exploration.
Read triqual.config.ts if it exists:
# Check for TypeScript config
ls triqual.config.ts 2>/dev/null
If TypeScript config exists, read it to extract configuration values. The config uses defineConfig from triqual for type safety:
// triqual.config.ts
import { defineConfig } from 'triqual';
export default defineConfig({
project_id: 'my-app',
testDir: './tests',
baseUrl: 'http://localhost:3000',
auth: { strategy: 'storageState', ... },
// ...
});
If missing, prompt user to run /init first or auto-detect basic config.
Read from ${PLUGIN_ROOT}/context/:
patterns-learned.json - Successful patterns from previous runsanti-patterns-learned.json - Known failure→fix mappingsBased on auth.strategy from config, authenticate before exploration:
storageState (Fastest)If .auth/user.json exists and is configured:
// Load saved auth state via browser_run_code
mcp__plugin_triqual-plugin_playwright__browser_run_code({
code: `async (page) => {
const fs = require('fs');
const state = JSON.parse(fs.readFileSync('.auth/user.json', 'utf8'));
// Add cookies
if (state.cookies?.length) {
await page.context().addCookies(state.cookies);
}
// Navigate to trigger cookie application
await page.goto('${baseUrl}');
// Restore localStorage if present
if (state.origins?.[0]?.localStorage?.length) {
await page.evaluate((items) => {
items.forEach(({ name, value }) => localStorage.setItem(name, value));
}, state.origins[0].localStorage);
}
return 'Auth state loaded';
}`
})
uiLogin (When no saved state)If credentials are configured but no storageState:
// Navigate to login page
mcp__plugin_triqual-plugin_playwright__browser_navigate({
url: config.auth.uiLogin.loginUrl // e.g., "/login"
})
// Get page snapshot to find form elements
mcp__plugin_triqual-plugin_playwright__browser_snapshot({})
// Fill login form using configured selectors
mcp__plugin_triqual-plugin_playwright__browser_fill_form({
fields: [
{
name: 'Email',
type: 'textbox',
ref: '{email-field-ref}', // From snapshot
value: credentials.email
},
{
name: 'Password',
type: 'textbox',
ref: '{password-field-ref}', // From snapshot
value: credentials.password
}
]
})
// Click submit
mcp__plugin_triqual-plugin_playwright__browser_click({
ref: '{submit-button-ref}',
element: 'Login submit button'
})
// Wait for navigation to success URL
mcp__plugin_triqual-plugin_playwright__browser_wait_for({
text: 'Dashboard' // Or wait for URL change
})
setupProject (Playwright native)If project uses Playwright's setup project pattern:
# Run setup project first
npx playwright test --project=setup
Then proceed with tests that have dependencies: ['setup'].
none (No auth needed)Skip authentication, proceed directly to exploration.
After auth, verify we're logged in:
// Take snapshot to confirm auth state
mcp__plugin_triqual-plugin_playwright__browser_snapshot({})
// Check for logged-in indicators:
// - User avatar/menu visible
// - Dashboard or protected content visible
// - No login form visible
After successful UI login, save state for future runs:
// Save state for future runs (optional)
mcp__plugin_triqual-plugin_playwright__browser_run_code({
code: `async (page) => {
const state = await page.context().storageState();
require('fs').writeFileSync('.auth/user.json', JSON.stringify(state, null, 2));
return 'Auth state saved to .auth/user.json';
}`
})
This is a blocking gate. The hooks will BLOCK test writing and test-planner dispatch until context files exist.
Call the context loading tool with just the feature name - the system automatically determines the optimal depth:
triqual_load_context({ feature: "{feature}" })
If you have a Linear ticket:
triqual_load_context({ feature: "{feature}", ticket: "ENG-123" })
If you have a description:
triqual_load_context({ feature: "{feature}", description: "..." })
Wait for the tool to complete before proceeding to Phase 1.
The tool intelligently analyzes your request and optimizes context depth:
This optimization saves ~70% tokens on average without any manual configuration.
Context files are written to .triqual/context/{feature}/:
patterns.md — Proven patterns from Quothcodebase.md — Relevant source files, selectors, routesexisting-tests.md — Reusable tests and page objectssummary.md — Index of all contextAdditional files are added automatically when needed:
anti-patterns.md — Known failures to avoidfailures.md — Exolar failure historyrequirements.md — Ticket detailsIf tool fails (MCP unavailable), retry with force: true or check MCP connectivity with /mcp.
After authentication, explore the feature:
// Navigate to feature (already authenticated)
mcp__plugin_triqual-plugin_playwright__browser_navigate({ url: featureUrl })
// Capture state
mcp__plugin_triqual-plugin_playwright__browser_snapshot({})
For exploration-only mode:
headless: falseOutput exploration notes but do NOT generate test files.
Fetch ticket from Linear:
mcp__linear__get_issue({ issueId: "ENG-123" })
Parse acceptance criteria as test requirements. Skip exploration phase.
Use the provided description as test requirements. Skip exploration phase.
test-planner reads the context files at .triqual/context/{feature}/ and uses them for:
Based on input source (exploration notes, ticket AC, or description):
Output: specs/{feature}-plan.md
# Test Plan: {feature}
## Scope
- Feature: {feature}
- URL: /feature-path
- Auth required: Yes/No
## Test Cases
### TC-001: Should display feature page
- Navigate to /feature-path
- Assert page title is visible
- Assert main elements present
### TC-002: Should submit form successfully
- Fill form with valid data
- Click submit
- Assert success message visible
### TC-003: Should show validation errors
- Submit empty form
- Assert error messages visible
## Page Object
- Use existing: FeaturePage (from Quoth)
- Or create new with identified selectors
Before creating ANY new code, check what already exists:
.triqual/context/{feature}/existing-tests.md for available Page Objects, helpers, fixturesCopy from ${PLUGIN_ROOT}/context/seed.template.ts and customize:
// .draft/tests/seed.spec.ts
import { test as base } from '@playwright/test';
import { testUsers } from '../../shared/test-data/users';
type AuthFixtures = {
authenticatedPage: Page;
};
export const test = base.extend<AuthFixtures>({
authenticatedPage: async ({ page }, use) => {
await page.goto('/login');
await page.fill('[data-testid="email"]', testUsers.standard.email);
await page.fill('[data-testid="password"]', testUsers.standard.password);
await page.click('[type="submit"]');
await page.waitForURL('**/dashboard');
await use(page);
},
});
Output: .draft/tests/{feature}.spec.ts
import { test, expect } from './seed.spec';
import { FeaturePage } from '../pages/feature.page';
test.describe('{Feature}', () => {
let featurePage: FeaturePage;
test.beforeEach(async ({ authenticatedPage }) => {
featurePage = new FeaturePage(authenticatedPage);
await featurePage.goto();
});
test('should display feature page', async () => {
await expect(featurePage.title).toBeVisible();
await expect(featurePage.mainContainer).toBeVisible();
});
test('should submit form successfully', async () => {
await featurePage.fillForm({ field1: 'value1' });
await featurePage.submit();
await expect(featurePage.successMessage).toBeVisible();
});
test('should show validation errors', async () => {
await featurePage.submit(); // Empty form
await expect(featurePage.errorMessage).toBeVisible();
});
});
Only if Quoth didn't find an existing one:
// tests/pages/{feature}.page.ts
import { Page, Locator } from '@playwright/test';
export class FeaturePage {
readonly page: Page;
readonly title: Locator;
readonly mainContainer: Locator;
readonly submitButton: Locator;
readonly successMessage: Locator;
readonly errorMessage: Locator;
constructor(page: Page) {
this.page = page;
this.title = page.locator('h1');
this.mainContainer = page.locator('[data-testid="feature-container"]');
this.submitButton = page.locator('[data-testid="submit-btn"]');
this.successMessage = page.locator('.success-message');
this.errorMessage = page.locator('.error-message');
}
async goto() {
await this.page.goto('/feature-path');
}
async fillForm(data: Record<string, string>) {
// Fill form fields
}
async submit() {
await this.submitButton.click();
}
}
npx playwright test .draft/tests/{feature}.spec.ts --reporter=list
mcp__exolar-qa__query_exolar_data({
dataset: "failures",
filters: { error_pattern: "{error_message}" }
})
If found, use previous fix as guidance.
Common fixes:
| Error | Fix |
|---|---|
strict mode violation | Add :visible or more specific selector |
Timeout exceeded | Add explicit wait or increase timeout |
Element not found | Verify selector with Playwright MCP snapshot |
Multiple elements | Use .first() or nth() with :visible |
If unsure about element existence:
mcp__plugin_triqual-plugin_playwright__browser_navigate({ url: failingPageUrl })
mcp__plugin_triqual-plugin_playwright__browser_snapshot({})
// Inspect snapshot to find correct selector
Edit the test file with the fix.
{
"error": "strict mode violation: getByRole('button') resolved to 3 elements",
"fix": "Use getByRole('button', { name: 'Submit' })",
"feature": "{feature}",
"iteration": 1
}
npx playwright test .draft/tests/{feature}.spec.ts --reporter=list
Repeat until passing or max iterations (5) reached.
⚠️ Promotion MUST NOT happen automatically. Ask the user first.
Present the passing test results and ask:
✅ Tests PASSING in .draft/tests/{feature}.spec.ts
Files ready for promotion:
- .draft/tests/{feature}.spec.ts → tests/{category}/{feature}.spec.ts
- .draft/pages/{Page}.ts → pages/{Page}.ts (if applicable)
**Promote these files to production?** (say "yes" to confirm)
# Determine target directory based on feature
# auth/ for login, signup
# dashboard/ for dashboard, home
# settings/ for settings, profile
mkdir -p tests/{category}/
mv .draft/tests/{feature}.spec.ts tests/{category}/{feature}.spec.ts
mv .draft/pages/*.ts pages/ 2>/dev/null || true # Page Objects if created
npx playwright test tests/{category}/{feature}.spec.ts --reporter=list
Append to ${PLUGIN_ROOT}/context/patterns-learned.json:
{
"id": "{feature}-{date}",
"feature": "{feature}",
"date": "2025-01-27",
"selectors": {
"submitButton": "[data-testid='submit-btn']",
"successMessage": ".success-message"
},
"waits": ["waitForURL('**/dashboard')"],
"assertions": ["expect(element).toBeVisible()"]
}
Append to ${PLUGIN_ROOT}/context/anti-patterns-learned.json:
{
"id": "{error-type}-{date}",
"error": "strict mode violation...",
"fix": "Use more specific selector with name",
"feature": "{feature}",
"date": "2025-01-27"
}
If a pattern is reusable across features:
"I discovered that for this project, modals always use [data-testid='modal-overlay'].
Should I propose adding this to Quoth documentation?"
## Test Complete: {feature}
**Generated Files:**
- `tests/{category}/{feature}.spec.ts` - 3 test cases
- `tests/pages/{feature}.page.ts` - Page Object
**Test Results:**
- 3/3 tests passing
- 2 heal iterations required
**Patterns Learned:**
- Selector: [data-testid="submit-btn"] for submit buttons
- Wait: Always waitForURL after form submission
**Anti-Patterns Recorded:**
- Don't use getByRole('button') without name in this project
**Next Steps:**
1. Review generated tests
2. Add to version control
3. Run full test suite to ensure no conflicts
/test --explore login
Behavior:
Use when you want to:
/test --ticket ENG-123
Behavior:
/test --describe "user can reset password via email link"
Behavior:
Run /mcp to check connection status
Check these locations:
automation/shared/test-data/users.tstests/fixtures/users.ts.env.test for environment variablesIf 5 iterations fail:
Run /init first, or provide --ticket or --describe with explicit requirements.
/check)/rules)/init, though auto-config works)npx claudepluginhub montinou/triqual --plugin triqual-pluginGenerates test plans, manual test cases, automated Playwright tests, regression suites, and bug reports using markdown templates for QA automation.
Generates Playwright tests from user stories, URLs, components, or features. Explores codebase, uses templates for auth, CRUD, checkout, and follows best practices for locators and assertions.
Creates QA test procedure markdown files for features, including metadata, test cases with step tables, and qa-tests directories. Optionally launches Playwright qa-tester agent for exploration.