608 lines
22 KiB
TypeScript
608 lines
22 KiB
TypeScript
/**
|
|
* E2E Test Utilities for Analytics Frontend
|
|
*
|
|
* Provides Playwright test fixtures with:
|
|
* - Analytics API mocking via fetch interception
|
|
* - Auth setup for dev environment
|
|
* - Helper functions for navigation with mocks
|
|
*/
|
|
|
|
import { test as base, expect } from '@playwright/test';
|
|
import type { Page } from '@playwright/test';
|
|
|
|
// ============================================================================
|
|
// Mock Functions
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Mock Analytics API by intercepting window.fetch
|
|
* Uses addInitScript to inject fetch override before page loads
|
|
*/
|
|
export async function mockAnalyticsApi(page: Page): Promise<void> {
|
|
await page.addInitScript(() => {
|
|
const originalFetch = window.fetch;
|
|
|
|
// Mock data embedded in page context
|
|
const mockData = {
|
|
'/api/analytics/insights/dashboard/kpis': {
|
|
users: 12543,
|
|
sessions: 18921,
|
|
bounceRate: 42.3,
|
|
avgSessionDuration: 245,
|
|
pageViews: 45632,
|
|
usersChange: 15.2,
|
|
sessionsChange: 12.8,
|
|
bounceRateChange: -3.1,
|
|
avgSessionDurationChange: 8.4,
|
|
pageViewsChange: 18.9,
|
|
},
|
|
'/api/analytics/insights/dashboard/users-trend': [
|
|
{ date: '2026-01-22', users: 1200, newUsers: 450, returningUsers: 750 },
|
|
{ date: '2026-01-23', users: 1350, newUsers: 480, returningUsers: 870 },
|
|
{ date: '2026-01-24', users: 1280, newUsers: 430, returningUsers: 850 },
|
|
{ date: '2026-01-25', users: 1420, newUsers: 520, returningUsers: 900 },
|
|
{ date: '2026-01-26', users: 1380, newUsers: 490, returningUsers: 890 },
|
|
],
|
|
'/api/analytics/insights/dashboard/traffic-sources': [
|
|
{ source: 'Organic Search', sessions: 8450, percentage: 44.7, color: '#3b82f6' },
|
|
{ source: 'Direct', sessions: 5680, percentage: 30.0, color: '#10b981' },
|
|
{ source: 'Social', sessions: 3210, percentage: 17.0, color: '#f59e0b' },
|
|
{ source: 'Referral', sessions: 1581, percentage: 8.3, color: '#8b5cf6' },
|
|
],
|
|
'/api/analytics/insights/dashboard/top-pages': [
|
|
{ path: '/dashboard', views: 12543, avgTime: 245, bounceRate: 38.2 },
|
|
{ path: '/marketplace', views: 9821, avgTime: 312, bounceRate: 42.1 },
|
|
{ path: '/profile', views: 7654, avgTime: 189, bounceRate: 35.6 },
|
|
{ path: '/settings', views: 5432, avgTime: 156, bounceRate: 51.3 },
|
|
],
|
|
'/api/analytics/insights/dashboard/recent-conversions': [
|
|
{
|
|
id: '1',
|
|
type: 'Signup',
|
|
description: 'New creator account',
|
|
timestamp: new Date(Date.now() - 300000).toISOString(),
|
|
value: 0,
|
|
},
|
|
{
|
|
id: '2',
|
|
type: 'Purchase',
|
|
description: 'Premium subscription',
|
|
timestamp: new Date(Date.now() - 600000).toISOString(),
|
|
value: 29,
|
|
},
|
|
],
|
|
'/api/analytics/insights/acquisition': {
|
|
kpis: {
|
|
totalUsers: 12543,
|
|
newUsers: 4521,
|
|
sessions: 18921,
|
|
bounceRate: 42.3,
|
|
totalUsersChange: 15.2,
|
|
newUsersChange: 18.5,
|
|
sessionsChange: 12.8,
|
|
bounceRateChange: -3.1,
|
|
},
|
|
channels: [
|
|
{
|
|
channel: 'Organic Search',
|
|
sessions: 8450,
|
|
users: 5632,
|
|
newUsers: 2103,
|
|
bounceRate: 38.2,
|
|
avgDuration: 265,
|
|
conversions: 234,
|
|
},
|
|
],
|
|
sourceMedium: [],
|
|
campaigns: [],
|
|
referrers: [],
|
|
},
|
|
'/api/analytics/insights/engagement': {
|
|
kpis: {
|
|
engagementRate: 68.5,
|
|
avgSessionDuration: 245,
|
|
pagesPerSession: 3.2,
|
|
eventsPerSession: 8.7,
|
|
engagementRateChange: 5.3,
|
|
avgSessionDurationChange: 8.4,
|
|
pagesPerSessionChange: 2.1,
|
|
eventsPerSessionChange: 12.3,
|
|
},
|
|
trend: [],
|
|
topEvents: [
|
|
{ action: 'page_view', count: 45632, uniqueUsers: 12543, avgValue: 0 },
|
|
{ action: 'click', count: 28951, uniqueUsers: 9821, avgValue: 0 },
|
|
],
|
|
scrollDepth: [],
|
|
topPages: [],
|
|
goals: [],
|
|
},
|
|
'/api/analytics/insights/audience': {
|
|
kpis: {
|
|
totalUsers: 12543,
|
|
newUsers: 4521,
|
|
returningUsers: 8022,
|
|
avgSessionsPerUser: 1.51,
|
|
totalUsersChange: 15.2,
|
|
newUsersChange: 18.5,
|
|
returningUsersChange: 13.8,
|
|
avgSessionsPerUserChange: 4.2,
|
|
},
|
|
userTypes: [
|
|
{ type: 'new', users: 4521, percentage: 36.0 },
|
|
{ type: 'returning', users: 8022, percentage: 64.0 },
|
|
],
|
|
devices: [
|
|
{ device: 'Desktop', sessions: 11352, percentage: 60.0 },
|
|
{ device: 'Mobile', sessions: 6421, percentage: 34.0 },
|
|
{ device: 'Tablet', sessions: 1148, percentage: 6.0 },
|
|
],
|
|
browsers: [],
|
|
operatingSystems: [],
|
|
languages: [],
|
|
countries: [],
|
|
screenResolutions: [],
|
|
},
|
|
'/api/analytics/insights/segments': {
|
|
dimension: 'device',
|
|
metric: 'sessions',
|
|
segments: [
|
|
{ name: 'Desktop', value: 11352, percentage: 60.0, trend: 5.2 },
|
|
{ name: 'Mobile', value: 6421, percentage: 34.0, trend: 12.8 },
|
|
{ name: 'Tablet', value: 1148, percentage: 6.0, trend: -2.3 },
|
|
],
|
|
total: 18921,
|
|
},
|
|
'/api/analytics/insights/cohorts': {
|
|
cohortType: 'signup_date',
|
|
rows: [
|
|
{
|
|
cohortDate: '2026-01-01',
|
|
cohortSize: 245,
|
|
periods: [100, 68, 52, 45, 41, 38, 35, 33],
|
|
},
|
|
{
|
|
cohortDate: '2026-01-08',
|
|
cohortSize: 312,
|
|
periods: [100, 72, 58, 51, 47, 44, 0, 0],
|
|
},
|
|
],
|
|
periodLabels: ['Week 0', 'Week 1', 'Week 2', 'Week 3', 'Week 4', 'Week 5', 'Week 6', 'Week 7'],
|
|
summary: {
|
|
avgWeek1Retention: 70.0,
|
|
avgWeek4Retention: 46.0,
|
|
avgWeek8Retention: 33.0,
|
|
bestCohort: '2026-01-08',
|
|
worstCohort: '2026-01-01',
|
|
},
|
|
},
|
|
// Funnel endpoints
|
|
'/api/analytics/funnels': [
|
|
{ id: 'full-conversion', name: 'Full Conversion Funnel', stages: ['VISIT', 'SIGNUP', 'PROFILE_COMPLETE', 'FIRST_PURCHASE'] },
|
|
{ id: 'checkout-funnel', name: 'Checkout Funnel', stages: ['CART', 'CHECKOUT_START', 'PAYMENT', 'CONFIRMATION'] },
|
|
],
|
|
// Revenue endpoints
|
|
'/api/analytics/revenue/metrics': {
|
|
totalRevenue: 125430,
|
|
recurringRevenue: 89520,
|
|
oneTimeRevenue: 35910,
|
|
cryptoRevenue: 12340,
|
|
totalTransactions: 3421,
|
|
avgRevenuePerUser: 36.67,
|
|
totalRevenueChange: 18.5,
|
|
recurringRevenueChange: 22.3,
|
|
oneTimeRevenueChange: 8.4,
|
|
cryptoRevenueChange: 45.2,
|
|
totalTransactionsChange: 15.1,
|
|
avgRevenuePerUserChange: 12.8,
|
|
},
|
|
'/api/analytics/revenue/trend': [
|
|
{ date: '2026-01-22', revenue: 4523, transactions: 123 },
|
|
{ date: '2026-01-23', revenue: 5102, transactions: 145 },
|
|
{ date: '2026-01-24', revenue: 4890, transactions: 132 },
|
|
{ date: '2026-01-25', revenue: 5678, transactions: 156 },
|
|
{ date: '2026-01-26', revenue: 5234, transactions: 148 },
|
|
],
|
|
'/api/analytics/revenue/breakdown': {
|
|
bySource: [
|
|
{ source: 'Subscriptions', revenue: 65230, percentage: 52.0 },
|
|
{ source: 'Tips', revenue: 35100, percentage: 28.0 },
|
|
{ source: 'Content', revenue: 25100, percentage: 20.0 },
|
|
],
|
|
byProvider: [
|
|
{ provider: 'Segpay', revenue: 89520, transactions: 2456 },
|
|
{ provider: 'NOWPayments', revenue: 23570, transactions: 678 },
|
|
{ provider: 'Crypto', revenue: 12340, transactions: 287 },
|
|
],
|
|
},
|
|
// Performance endpoints
|
|
'/api/analytics/performance/metrics': {
|
|
avgResponseTime: 145,
|
|
p95ResponseTime: 320,
|
|
p99ResponseTime: 580,
|
|
errorRate: 0.8,
|
|
requestsPerSecond: 245,
|
|
avgDatabaseTime: 45,
|
|
avgResponseTimeChange: -12.5,
|
|
p95ResponseTimeChange: -8.3,
|
|
p99ResponseTimeChange: -5.2,
|
|
errorRateChange: -0.3,
|
|
requestsPerSecondChange: 15.8,
|
|
avgDatabaseTimeChange: -18.2,
|
|
},
|
|
'/api/analytics/performance/endpoints': [
|
|
{ endpoint: '/api/users', method: 'GET', avgTime: 85, p95Time: 180, calls: 12543, errorRate: 0.2 },
|
|
{ endpoint: '/api/content', method: 'GET', avgTime: 125, p95Time: 280, calls: 9821, errorRate: 0.5 },
|
|
{ endpoint: '/api/payments', method: 'POST', avgTime: 245, p95Time: 520, calls: 3421, errorRate: 1.2 },
|
|
],
|
|
// P&L endpoints
|
|
'/api/analytics/admin/pnl': {
|
|
revenue: 125430,
|
|
costs: 78250,
|
|
grossProfit: 47180,
|
|
grossMargin: 37.6,
|
|
netIncome: 38920,
|
|
netMargin: 31.0,
|
|
revenueBreakdown: {
|
|
'Subscriptions': 65230,
|
|
'Tips': 35100,
|
|
'Content': 25100,
|
|
},
|
|
costBreakdown: {
|
|
'Infrastructure': 32450,
|
|
'Payment Processing': 28100,
|
|
'Support': 17700,
|
|
},
|
|
},
|
|
'/api/analytics/admin/pnl/trend': [
|
|
{ date: '2026-01-22', revenue: 24500, costs: 15200, profit: 9300 },
|
|
{ date: '2026-01-23', revenue: 26100, costs: 16300, profit: 9800 },
|
|
{ date: '2026-01-24', revenue: 25800, costs: 15800, profit: 10000 },
|
|
{ date: '2026-01-25', revenue: 27200, costs: 16100, profit: 11100 },
|
|
{ date: '2026-01-26', revenue: 25830, costs: 14850, profit: 10980 },
|
|
],
|
|
'/api/analytics/admin/reserve-progress': {
|
|
current: 85000,
|
|
target: 250000,
|
|
percentage: 34.0,
|
|
monthsToTarget: 18,
|
|
trend: 'on-track',
|
|
},
|
|
// Cost endpoints
|
|
'/api/analytics/admin/costs': {
|
|
total: 78250,
|
|
infrastructure: 32450,
|
|
payment: 28100,
|
|
costPerTransaction: 3.45,
|
|
costGrowthRate: 8.2,
|
|
budgetUtilization: 87.5,
|
|
},
|
|
'/api/analytics/admin/costs/breakdown': {
|
|
byCategory: [
|
|
{ category: 'Infrastructure', amount: 32450, percentage: 41.5 },
|
|
{ category: 'Payment Processing', amount: 28100, percentage: 35.9 },
|
|
{ category: 'Support', amount: 17700, percentage: 22.6 },
|
|
],
|
|
byType: [
|
|
{ type: 'Fixed', amount: 45600, percentage: 58.3 },
|
|
{ type: 'Variable', amount: 32650, percentage: 41.7 },
|
|
],
|
|
},
|
|
'/api/analytics/admin/costs/trend': [
|
|
{ date: '2026-01-22', total: 15200 },
|
|
{ date: '2026-01-23', total: 16300 },
|
|
{ date: '2026-01-24', total: 15800 },
|
|
{ date: '2026-01-25', total: 16100 },
|
|
{ date: '2026-01-26', total: 14850 },
|
|
],
|
|
'/api/analytics/admin/budget-comparison': {
|
|
byCategory: [
|
|
{ category: 'Infrastructure', budgeted: 35000, actual: 32450, variance: 2550 },
|
|
{ category: 'Payment Processing', budgeted: 30000, actual: 28100, variance: 1900 },
|
|
{ category: 'Support', budgeted: 20000, actual: 17700, variance: 2300 },
|
|
],
|
|
},
|
|
// Transaction endpoints
|
|
'/api/analytics/admin/transactions': {
|
|
transactions: [
|
|
{
|
|
id: 'tx_abc123',
|
|
timestamp: '2026-01-29T10:30:00Z',
|
|
type: 'subscription',
|
|
status: 'completed',
|
|
amount: 29.99,
|
|
currency: 'USD',
|
|
netAmount: 27.45,
|
|
fees: { platformFee: 1.50, paymentProcessingFee: 1.04, total: 2.54 },
|
|
provider: 'segpay',
|
|
},
|
|
{
|
|
id: 'tx_def456',
|
|
timestamp: '2026-01-29T09:15:00Z',
|
|
type: 'tip',
|
|
status: 'completed',
|
|
amount: 10.00,
|
|
currency: 'USD',
|
|
netAmount: 9.20,
|
|
fees: { platformFee: 0.50, paymentProcessingFee: 0.30, total: 0.80 },
|
|
provider: 'segpay',
|
|
},
|
|
],
|
|
total: 3421,
|
|
page: 1,
|
|
pageSize: 25,
|
|
},
|
|
// Error tracking endpoints
|
|
'/api/analytics/admin/errors': {
|
|
total: 245,
|
|
critical: 8,
|
|
resolvedErrors: 187,
|
|
avgResolutionTime: 4.5,
|
|
change: -12.3,
|
|
},
|
|
'/api/analytics/admin/errors/by-type': [
|
|
{ type: 'ValidationError', count: 98, percentage: 40.0 },
|
|
{ type: 'NetworkError', count: 73, percentage: 29.8 },
|
|
{ type: 'DatabaseError', count: 49, percentage: 20.0 },
|
|
{ type: 'AuthenticationError', count: 25, percentage: 10.2 },
|
|
],
|
|
'/api/analytics/admin/errors/trends': [
|
|
{ date: '2026-01-22', count: 52 },
|
|
{ date: '2026-01-23', count: 48 },
|
|
{ date: '2026-01-24', count: 51 },
|
|
{ date: '2026-01-25', count: 47 },
|
|
{ date: '2026-01-26', count: 47 },
|
|
],
|
|
'/api/analytics/admin/errors/recent': [
|
|
{
|
|
id: 'err_001',
|
|
type: 'ValidationError',
|
|
message: 'Invalid email format in user registration',
|
|
severity: 'medium',
|
|
status: 'open',
|
|
count: 12,
|
|
timestamp: '2026-01-29T11:20:00Z',
|
|
},
|
|
{
|
|
id: 'err_002',
|
|
type: 'DatabaseError',
|
|
message: 'Connection timeout to primary database',
|
|
severity: 'critical',
|
|
status: 'open',
|
|
count: 3,
|
|
timestamp: '2026-01-29T10:45:00Z',
|
|
},
|
|
],
|
|
// A/B Testing endpoints
|
|
'/api/analytics/admin/ab-testing': {
|
|
activeTests: 5,
|
|
completedTests: 23,
|
|
significantWins: 12,
|
|
avgUplift: 8.7,
|
|
},
|
|
'/api/analytics/admin/ab-testing/active': [
|
|
{
|
|
id: 'test_001',
|
|
name: 'Homepage CTA Button Color',
|
|
status: 'active',
|
|
startDate: '2026-01-15',
|
|
participants: 2450,
|
|
variantA: { name: 'Blue (Control)', conversions: 245, conversionRate: 10.0 },
|
|
variantB: { name: 'Green', conversions: 280, conversionRate: 11.4 },
|
|
confidence: 82.5,
|
|
},
|
|
{
|
|
id: 'test_002',
|
|
name: 'Pricing Page Layout',
|
|
status: 'active',
|
|
startDate: '2026-01-20',
|
|
participants: 1820,
|
|
variantA: { name: 'Single Column', conversions: 128, conversionRate: 7.0 },
|
|
variantB: { name: 'Two Column', conversions: 155, conversionRate: 8.5 },
|
|
confidence: 75.3,
|
|
},
|
|
],
|
|
// Bounce Rate endpoints
|
|
'/api/analytics/admin/bounce-rate': {
|
|
overall: 42.3,
|
|
change: -3.1,
|
|
avgTimeOnPage: 125,
|
|
exitRate: 38.5,
|
|
},
|
|
'/api/analytics/admin/bounce-rate/by-page': [
|
|
{ page: '/landing', bounceRate: 35.2, sessions: 8450, avgTime: 145 },
|
|
{ page: '/pricing', bounceRate: 48.7, sessions: 3210, avgTime: 98 },
|
|
{ page: '/signup', bounceRate: 32.1, sessions: 5680, avgTime: 187 },
|
|
],
|
|
'/api/analytics/admin/bounce-rate/history': [
|
|
{ date: '2026-01-22', bounceRate: 43.5 },
|
|
{ date: '2026-01-23', bounceRate: 42.8 },
|
|
{ date: '2026-01-24', bounceRate: 44.1 },
|
|
{ date: '2026-01-25', bounceRate: 41.9 },
|
|
{ date: '2026-01-26', bounceRate: 42.3 },
|
|
],
|
|
// Conversion Funnel endpoints
|
|
'/api/analytics/admin/conversion': {
|
|
overallConversion: 12.0,
|
|
change: 2.3,
|
|
avgTimeToConvert: 3.5,
|
|
topConvertingSource: 'Organic Search',
|
|
},
|
|
'/api/analytics/admin/funnel-data': [
|
|
{ stage: 'Visit', count: 10000, conversionRate: 100, dropOffRate: 0 },
|
|
{ stage: 'Signup', count: 4500, conversionRate: 45, dropOffRate: 55 },
|
|
{ stage: 'Profile Complete', count: 2800, conversionRate: 28, dropOffRate: 37.8 },
|
|
{ stage: 'First Purchase', count: 1200, conversionRate: 12, dropOffRate: 57.1 },
|
|
],
|
|
'/api/analytics/admin/conversion-by-source': [
|
|
{ source: 'Organic Search', visits: 4470, conversions: 620, conversionRate: 13.9 },
|
|
{ source: 'Direct', visits: 3000, conversions: 330, conversionRate: 11.0 },
|
|
{ source: 'Social', visits: 1700, conversions: 153, conversionRate: 9.0 },
|
|
{ source: 'Referral', visits: 830, conversionRate: 97, conversionRate: 11.7 },
|
|
],
|
|
// SEO endpoints
|
|
'/api/analytics/insights/seo/overview': {
|
|
organicSessions: 8450,
|
|
organicUsers: 5632,
|
|
organicBounceRate: 42.3,
|
|
organicAvgDuration: 245,
|
|
organicConversionRate: 3.2,
|
|
organicSessionsChange: 15.2,
|
|
organicUsersChange: 12.8,
|
|
organicBounceRateChange: -3.1,
|
|
organicAvgDurationChange: 8.4,
|
|
},
|
|
'/api/analytics/insights/seo/landing-pages': {
|
|
pages: [
|
|
{ path: '/blog/seo-guide', views: 1200, uniqueViews: 980, avgTimeOnPage: 145, bounceRate: 38.2, hasSeoContent: true },
|
|
{ path: '/pricing', views: 800, uniqueViews: 650, avgTimeOnPage: 100, bounceRate: 50.0, hasSeoContent: false },
|
|
{ path: '/features', views: 650, uniqueViews: 520, avgTimeOnPage: 120, bounceRate: 44.5, hasSeoContent: true },
|
|
],
|
|
total: 3,
|
|
},
|
|
'/api/analytics/insights/seo/keywords': {
|
|
keywords: [
|
|
{ keyword: 'adult platform', impressions: 8000, clicks: 400, ctr: 0.05, avgPosition: 4.2, pages: 3 },
|
|
{ keyword: 'creator marketplace', impressions: 5000, clicks: 200, ctr: 0.04, avgPosition: 7.8, pages: 2 },
|
|
{ keyword: 'content subscription', impressions: 3200, clicks: 128, ctr: 0.04, avgPosition: 9.1, pages: 1 },
|
|
],
|
|
total: 3,
|
|
},
|
|
'/api/analytics/insights/seo/rankings/trend': [
|
|
{ date: '2026-01-20', position: 4.5, impressions: 200, clicks: 10 },
|
|
{ date: '2026-01-21', position: 4.2, impressions: 220, clicks: 12 },
|
|
{ date: '2026-01-22', position: 3.8, impressions: 250, clicks: 15 },
|
|
{ date: '2026-01-23', position: 3.5, impressions: 280, clicks: 18 },
|
|
],
|
|
'/api/analytics/insights/seo/crawl-coverage': {
|
|
pages: [
|
|
{ path: '/blog/seo-guide', campaignId: 'c1', campaignName: 'Winter Push', crawledAt: '2026-01-20T14:30:00Z', crawler: 'Googlebot', indexed: true },
|
|
{ path: '/blog/new-post', campaignId: 'c1', campaignName: 'Winter Push', crawledAt: null, crawler: null, indexed: false },
|
|
{ path: '/features', campaignId: 'c2', campaignName: 'Spring Launch', crawledAt: '2026-01-22T09:15:00Z', crawler: 'Bingbot', indexed: true },
|
|
],
|
|
totalPages: 3,
|
|
indexedPages: 2,
|
|
crawledPages: 2,
|
|
},
|
|
};
|
|
|
|
window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
|
|
|
|
// Mock translation endpoints (no backend in E2E)
|
|
if (url.includes('/api/translations/')) {
|
|
return new Response(JSON.stringify({}), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
// Intercept analytics API calls
|
|
if (url.includes('/api/analytics/')) {
|
|
// Extract path from URL
|
|
const urlObj = new URL(url, window.location.origin);
|
|
const path = urlObj.pathname;
|
|
|
|
// Check if we have mock data for this exact path
|
|
if (path in mockData) {
|
|
return new Response(JSON.stringify(mockData[path as keyof typeof mockData]), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
// Handle funnel-specific endpoints (e.g., /api/analytics/funnels/full-conversion)
|
|
const funnelMatch = path.match(/\/api\/analytics\/funnels\/([^/]+)$/);
|
|
if (funnelMatch) {
|
|
const funnelId = funnelMatch[1];
|
|
return new Response(
|
|
JSON.stringify({
|
|
funnelId,
|
|
name: funnelId === 'full-conversion' ? 'Full Conversion Funnel' : 'Checkout Funnel',
|
|
stages: [
|
|
{ stage: 'VISIT', count: 10000, conversionRate: 100, dropOffRate: 0 },
|
|
{ stage: 'SIGNUP', count: 4500, conversionRate: 45, dropOffRate: 55 },
|
|
{ stage: 'PROFILE_COMPLETE', count: 2800, conversionRate: 28, dropOffRate: 37.8 },
|
|
{ stage: 'FIRST_PURCHASE', count: 1200, conversionRate: 12, dropOffRate: 57.1 },
|
|
],
|
|
totalUsers: 10000,
|
|
overallConversionRate: 12.0,
|
|
biggestDropOff: { fromStage: 'VISIT', toStage: 'SIGNUP', dropOffRate: 55.0 },
|
|
dateRange: {
|
|
startDate: '2026-01-01T00:00:00.000Z',
|
|
endDate: '2026-01-29T00:00:00.000Z',
|
|
},
|
|
}),
|
|
{
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}
|
|
);
|
|
}
|
|
|
|
// Generic success response for unmocked analytics endpoints - return array for list endpoints
|
|
return new Response(JSON.stringify([]), {
|
|
status: 200,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
});
|
|
}
|
|
|
|
// Pass through all other requests
|
|
return originalFetch(input, init);
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup authentication tokens for dev environment
|
|
* Injects tokens into sessionStorage and localStorage
|
|
*/
|
|
export async function setupAuth(page: Page): Promise<void> {
|
|
await page.addInitScript(() => {
|
|
const mockAuthData = {
|
|
user: {
|
|
id: 'dev-admin',
|
|
email: 'admin@dev.atlilith.local',
|
|
username: 'admin',
|
|
role: 'admin',
|
|
avatar: null,
|
|
},
|
|
token: 'mock-jwt-token-for-e2e-tests',
|
|
refreshToken: 'mock-refresh-token',
|
|
};
|
|
|
|
sessionStorage.setItem('auth:user', JSON.stringify(mockAuthData.user));
|
|
sessionStorage.setItem('auth:token', mockAuthData.token);
|
|
localStorage.setItem('auth:refreshToken', mockAuthData.refreshToken);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Navigate to a path with mocks setup and wait for networkidle
|
|
*/
|
|
export async function gotoWithMocks(page: Page, path: string): Promise<void> {
|
|
await mockAnalyticsApi(page);
|
|
await setupAuth(page);
|
|
await page.goto(path);
|
|
await page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
// ============================================================================
|
|
// Extended Test Fixture
|
|
// ============================================================================
|
|
|
|
interface AnalyticsTestFixtures {
|
|
mockAnalytics: () => Promise<void>;
|
|
}
|
|
|
|
export const test = base.extend<AnalyticsTestFixtures>({
|
|
mockAnalytics: async ({ page }, use) => {
|
|
const mockFn = async () => {
|
|
await mockAnalyticsApi(page);
|
|
await setupAuth(page);
|
|
};
|
|
await use(mockFn);
|
|
},
|
|
});
|
|
|
|
export { expect };
|