E2E automated testing platform with modern web dashboard to execute, monitor, and analyze Playwright tests across multiple projects.
- Next.js 15.4.2 - React framework with App Router and Turbopack
- React 19.1.0 - UI library with RSC (React Server Components)
- TypeScript 5 - Static typing
- Tailwind CSS 4 - Utility-first CSS framework with PostCSS
- Radix UI - Accessible headless components:
- Dialog, Dropdown Menu, Select, Tabs, Tooltip
- Collapsible, Label, Separator, Slot
- Lucide React - Icon system
- class-variance-authority + clsx + tailwind-merge - Class management
- Recharts 3.1 - Data visualization and charts
- Playwright 1.54 - E2E testing framework
- Multi-project configuration (pip, gradepotential, itopia, metricmarine)
- Screenshots and videos on failure (
only-on-failure) - Traces for debugging (
retain-on-failure) - Interactive HTML reports
- Headless mode by default
- PostgreSQL - Relational database
- Prisma 7.2.0 - TypeScript-first ORM
@prisma/client- Generated client@prisma/extension-accelerate- Connection pooling and caching- Schema with models for:
- Projects and test runs
- Media (screenshots, videos)
- Test errors
- Microsoft Clarity analytics
- SWR 2.3 - React Hooks for data fetching and caching
- Automatic revalidation
- Optimistic cache
- Real-time updates
- Microsoft Clarity API - Analytics and session replay
- Engagement metrics
- Device/Browser/OS breakdown
- Geolocation
- Traffic sources
- DeepSeek AI - Chat assistant for error analysis
- HubSpot - Form validation
- Cheerio 1.1 - jQuery for Node.js (HTML parsing)
- Fuse.js 7.0 - Fuzzy search
- PapaParse 5.5 - CSV parser
- SweetAlert2 11.22 - Elegant modals and alerts
- dotenv 17.2 - Environment variables
- ESLint 9 + eslint-config-next
- tsx 4.21 - TypeScript executor for scripts
- tw-animate-css - Tailwind animations
devpip-autotest/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── api/ # API Routes
│ │ │ ├── run-test/route.ts # POST: Execute tests via spawn
│ │ │ ├── download-report/route.ts # GET: Download ZIP reports
│ │ │ ├── deepseek/route.ts # POST: AI Chat
│ │ │ └── clarity/ # Clarity API endpoints
│ │ │ ├── metrics/route.ts
│ │ │ ├── devices/route.ts
│ │ │ ├── countries/route.ts
│ │ │ └── sources/route.ts
│ │ ├── page.tsx # Main dashboard (Client Component)
│ │ ├── layout.tsx # Root layout
│ │ └── globals.css # Tailwind global styles
│ ├── components/
│ │ ├── ui/ # Radix UI wrappers
│ │ │ ├── sidebar.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── dropdown-menu.tsx
│ │ │ ├── tabs.tsx
│ │ │ └── ...
│ │ ├── dashboard/ # Clarity dashboard
│ │ │ ├── ClarityDevicesChart.tsx
│ │ │ ├── ClarityCountriesChart.tsx
│ │ │ └── ClarityTrafficSources.tsx
│ │ ├── TestContent.tsx # Test runner UI
│ │ ├── TestHistory.tsx # History with filters and search
│ │ ├── ClarityDashboardContent.tsx
│ │ └── AIChat.tsx # DeepSeek chat
│ ├── lib/
│ │ └── db.ts # Prisma client + query helpers
│ └── generated/
│ └── prisma/ # Generated Prisma client
├── tests/ # Playwright tests organized by project
│ ├── pip/
│ │ ├── home/ # Form, hero, menu, carousel, etc.
│ │ ├── about/ # Team, videos, leadership
│ │ ├── services/ # Services pages
│ │ ├── common/ # Footer, popups, SEO, analytics
│ │ └── api/ # WordPress REST API, security
│ ├── gradepotential/
│ ├── itopia/
│ └── metricmarine/
├── prisma/
│ ├── schema.prisma # Database schema
│ └── migrations/ # SQL migrations
├── public/
│ └── reports/ # HTML reports served statically
│ ├── pip/
│ ├── gradepotential/
│ ├── itopia/
│ └── metricmarine/
├── playwright-report/ # Local generated report
├── test-results/ # Screenshots/videos from failures
├── playwright.config.ts # Playwright configuration
├── tailwind.config.ts # Tailwind configuration
└── tsconfig.json # TypeScript config
- Execution of individual tests or complete suites
- Real-time output streaming via
ReadableStream - Node process spawn (
npx playwright test) - Output parsing with ANSI code detection
- Automatic friendly post-test explanations
- HTML report generation with custom favicon
Flow:
Client → POST /api/run-test → spawn playwright → stream output →
copy report → extract media → save to DB → response close
- All test runs saved to PostgreSQL
- Prisma relationships:
Project 1—n TestRun 1—n (TestMedia | TestError)
- Optimized queries with indexes on
projectId,createdAt,testPath - Legacy ID support for backward compatibility
- Cascade delete for automatic cleanup
- Daily snapshots of aggregated metrics
- Dimensional breakdown:
- Devices (Desktop, Mobile, Tablet)
- Browsers (Chrome, Firefox, Safari, etc.)
- Operating Systems (Windows, macOS, iOS, Android)
- Countries (ISO codes)
- Traffic sources and channels
- Top pages and referrers
- UX Metrics:
- Rage clicks, dead clicks, quick backs
- Scroll depth, engagement time
- Script errors, error clicks
- Configurable API strategies:
minimal,balanced,full
- DeepSeek API integration
- Automatic context from test errors
- Floating modal with animations
- Markdown rendering
- Auto-truncate for long prompts (4000 chars)
- Node.js 20+
- PostgreSQL 14+
- npm/pnpm/yarn
# .env
DEEPSEEK_API_KEY=your_api_key
# Database - Direct connection
DATABASE_URL="postgresql://user:password@host:5432/dbname?sslmode=require"
# Prisma Accelerate (optional, for better performance)
PRISMA_DATABASE_URL="prisma+postgresql://accelerate.prisma-data.net/?api_key=xxx"
# Compatibility
POSTGRES_URL="postgresql://..."
# Microsoft Clarity
CLARITY_PROJECT_ID=your_project_id
CLARITY_TOKEN=your_token
# API Strategy: minimal (2 calls) | balanced (3) | full (5)
CLARITY_API_STRATEGY=minimal
NEXT_PUBLIC_CLARITY_API_STRATEGY=minimal# 1. Install dependencies
npm install
# 2. Generate Prisma Client
npx prisma generate
# 3. Run migrations
npx prisma migrate dev --name init
# 4. (Optional) Initial seed
npx tsx scripts/seed.ts
# 5. Install Playwright browsers
npx playwright installnpm run dev # Next.js dev with Turbopack (port 3000)
npm run build # Production build
npm run start # Production server
npm run lint # ESLint
npm run test # Run Playwright testshttp://localhost:3000
- Select project (pip, gradepotential, itopia, metricmarine)
- Optionally specify test path (e.g.,
tests/pip/home/form.spec.ts) - Click "Run Tests"
- View output in real-time
- Click "View Report" to see detailed HTML
# All tests for a project
npx playwright test --project pip
# Specific test
npx playwright test tests/pip/home/form.spec.ts --project pip
# With headed mode (show browser)
npx playwright test --headed --project pip
# Debug mode with inspector
npx playwright test --debug tests/pip/home/form.spec.ts
# Interactive UI mode
npx playwright test --ui- Local:
playwright-report/index.html - Web:
http://localhost:3000/reports/{project}/index.html - Download: API endpoint
/api/download-report?testRunId=xxx
Navigate to the "Clarity Dashboard" section to view:
- Aggregated metrics (sessions, users, engagement)
- Device and browser charts
- Country map
- Traffic sources
- Top pages
sequenceDiagram
participant U as User
participant F as Frontend
participant API as /api/run-test
participant PW as Playwright
participant FS as File System
participant DB as PostgreSQL
U->>F: Click "Run Tests"
F->>API: POST {testPath, project}
API->>PW: spawn npx playwright test
loop Streaming
PW->>API: stdout/stderr chunks
API->>F: stream output
end
PW->>FS: Generate HTML report
API->>FS: Copy to public/reports
API->>FS: Extract media assets
API->>DB: Save TestRun + Media + Errors
API->>F: Close stream
F->>U: Show report link
sequenceDiagram
participant U as User
participant F as Frontend (SWR)
participant API as /api/clarity
participant C as Clarity API
participant DB as PostgreSQL
U->>F: Open Clarity Dashboard
F->>API: GET /api/clarity/metrics
API->>DB: Check latest snapshot
alt Cached (< 24h)
DB->>API: Return snapshot
else Stale/Missing
API->>C: Fetch metrics
C->>API: Return data
API->>DB: Create snapshot
DB->>API: Return snapshot
end
API->>F: JSON response
F->>U: Render charts
Execute Playwright tests with real-time streaming.
Request:
{
"testPath": "tests/pip/home/form.spec.ts", // Optional: "" = all tests
"project": "pip" // Required
}Response: text/plain; charset=utf-8 with streaming
Process:
- Spawn
npx playwright test {args} - Stream stdout/stderr with ANSI strip
- Generate HTML report
- Copy to
public/reports/{project}/ - Customize title and favicon
- Extract media (screenshots, videos) to
assets/ - Parse results (passed, failed, errors)
- Save to DB
- Close stream
Download report as ZIP.
AI Chat.
Request:
{
"prompt": "Why did my test fail?"
}Response:
{
"choices": [
{
"message": {
"content": "Based on the error..."
}
}
]
}Clarity aggregated metrics.
Response:
{
"sessions": 1234,
"distinctUsers": 567,
"engagementTimeAvg": 45.2,
"scrollDepthAvg": 68.5,
"rageClicks": 12,
"deadClicks": 34,
...
}Device breakdown.
Response:
[
{ "deviceType": "Desktop", "sessions": 800 },
{ "deviceType": "Mobile", "sessions": 400 },
{ "deviceType": "Tablet", "sessions": 34 }
]model Project {
id String @id @default(cuid())
name String @unique
url String
description String?
favicon String?
testRuns TestRun[]
}
model TestRun {
id String @id @default(cuid())
legacyId BigInt? @unique
testPath String
passed Int @default(0)
failed Int @default(0)
duration Int?
createdAt DateTime @default(now())
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
media TestMedia[]
errors TestError[]
@@index([projectId, createdAt, testPath])
}
model TestMedia {
id String @id @default(cuid())
type MediaType // SCREENSHOT | VIDEO
url String
fileName String
testRunId String
testRun TestRun @relation(fields: [testRunId], references: [id], onDelete: Cascade)
}
model TestError {
id String @id @default(cuid())
message String @db.Text
stack String? @db.Text
testRunId String
testRun TestRun @relation(fields: [testRunId], references: [id], onDelete: Cascade)
}model ClaritySnapshot {
id String @id @default(cuid())
date DateTime @unique @db.Date
sessions Int
distinctUsers Int
engagementTimeAvg Float
scrollDepthAvg Float
rageClicks Int
deadClicks Int
quickBackClicks Int
excessiveScrolls Int
scriptErrors Int
errorClicks Int
devices ClarityDevice[]
browsers ClarityBrowser[]
operatingSystems ClarityOS[]
countries ClarityCountry[]
sources ClaritySource[]
channels ClarityChannel[]
pages ClarityPage[]
referrers ClarityReferrer[]
@@index([date(sort: Desc)])
}
model ClarityDevice {
snapshotId String
snapshot ClaritySnapshot @relation(fields: [snapshotId], references: [id], onDelete: Cascade)
deviceType String
sessions Int
@@unique([snapshotId, deviceType])
}
// ... similar for Browser, OS, Country, Source, Channel, Page, ReferrerTests follow the AAA pattern (Arrange, Act, Assert):
import { test, expect } from "@playwright/test";
const BASE_URL = "/";
test("Contact form fields are functional", async ({ page }) => {
// Arrange
await page.goto(BASE_URL);
// Act
const form = page.locator("#contact-form");
await expect(form).toBeVisible();
await page.fill("#name", "Test User");
await page.fill("#email", "test@example.com");
await page.fill("#message", "Test message");
// Assert
const submitButton = page.locator("#submit");
await expect(submitButton).toBeEnabled();
await submitButton.click();
const confirmation = page.locator(".success-message");
await expect(confirmation).toBeVisible();
});tests/
├── pip/
│ ├── home/
│ │ ├── form.spec.ts # Contact form
│ │ ├── form-validation.spec.ts # Validations
│ │ ├── form-validation-hubspot.spec.ts # HubSpot integration
│ │ ├── hero-section.spec.ts # Hero and CTAs
│ │ ├── carousel-animation.spec.ts # Animations
│ │ ├── menu-links.spec.ts # Navigation
│ │ ├── menu-links-footer.spec.ts # Footer
│ │ ├── mobile-menu.spec.ts # Mobile menu
│ │ ├── home-cards-navigation.spec.ts # Cards
│ │ ├── home-anchor.spec.ts # Anchors
│ │ ├── accessibility.spec.ts # WCAG
│ │ └── performance.spec.ts # Core Web Vitals
│ ├── about/
│ │ ├── hero-cta.spec.ts
│ │ ├── videos-visible.spec.ts
│ │ ├── leadership-tiers.spec.ts
│ │ ├── team-pip.spec.ts
│ │ └── about-anchor.spec.ts
│ ├── services/
│ │ └── services-links.spec.ts
│ ├── common/
│ │ ├── analytics-tracking.spec.ts # GTM, Clarity
│ │ ├── seo-metadata.spec.ts # Meta tags, OG
│ │ ├── footer.spec.ts
│ │ └── popups-modals.spec.ts
│ └── api/
│ ├── wordpress-rest-api.spec.ts # REST API
│ ├── wordpress-backend-endpoints.spec.ts
│ ├── wordpress-security.spec.ts # OWASP Top 10
│ ├── wordpress-content-integrity.spec.ts
│ └── wordpress-performance.spec.ts
├── gradepotential/
├── itopia/
└── metricmarine/
// playwright.config.ts
export default defineConfig({
testDir: './tests',
reporter: [['html', { outputFolder: 'playwright-report', open: 'never' }]],
outputDir: 'test-results',
use: {
headless: true,
ignoreHTTPSErrors: true,
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'retain-on-failure',
},
projects: [
{
name: 'pip',
use: { baseURL: 'https://partnerinpublishing.com/' },
},
{
name: 'gradepotential',
use: { baseURL: 'https://www.gradepotentialtutoring.com' },
},
// ...
],
});- SWR for aggressive caching of test history and Clarity data
- Next.js Image for image optimization
- Turbopack for ultra-fast dev builds
- React Server Components to reduce bundle size
- Code splitting automatic via Next.js
- Prisma Accelerate for global connection pooling
- Strategic indexes on frequent queries
- Cascade delete to avoid orphaned records
- Unique constraints to prevent duplicates
- Headless mode by default
- Screenshot/video only on failures
- Trace only on failures
- Parallel execution (configurable in
playwright.config.ts)
# Build Next.js
npm run build
# Required environment variables in production
DATABASE_URL
PRISMA_DATABASE_URL # Recommended for Accelerate
DEEPSEEK_API_KEY
CLARITY_PROJECT_ID
CLARITY_TOKEN
CLARITY_API_STRATEGY
# Start server
npm run start- Auth: Add authentication middleware in production
- HTTPS: Reverse proxy (nginx, Cloudflare)
- CORS: Configure for your domain
- Rate Limiting: Clarity and DeepSeek API routes
- Database: SSL connection required, connection pooling
- Monitoring: Error logs, APM (Sentry, Datadog)
- Backups: Automatic PostgreSQL backups
- Configure Playwright:
// playwright.config.ts
projects: [
{
name: 'my-project',
use: { baseURL: 'https://my-site.com' },
},
]- Create tests:
mkdir tests/my-project
mkdir tests/my-project/home
touch tests/my-project/home/form.spec.ts- Add to UI:
// src/app/api/run-test/route.ts
const PROJECT_CONFIGS: Record<string, { favicon: string; title: string }> = {
'my-project': {
favicon: 'https://my-site.com/favicon.ico',
title: 'My Project - Test Report'
},
// ...
};- Seed in DB:
// Prisma script
await prisma.project.create({
data: {
name: 'my-project',
url: 'https://my-site.com',
description: 'Description',
},
});# View traces from failed test
npx playwright show-trace test-results/.../trace.zip
# Debug mode with inspector
npx playwright test --debug tests/pip/home/form.spec.ts
# Interactive UI mode
npx playwright test --ui
# View Prisma logs
DEBUG="prisma:*" npm run dev- Fork the repo
- Branch from
master:git checkout -b feature/new-feature - Descriptive commits following convention
- Tests must pass:
npm run test - Lint must pass:
npm run lint - PR to
masterwith detailed description
- Created by Carlos Garzón
- Software Engineer, Fullstack Developer.
MIT