259 lines
7.7 KiB
TypeScript
Executable file
259 lines
7.7 KiB
TypeScript
Executable file
/**
|
|
* Subscriptions E2E Tests
|
|
*
|
|
* Tests the subscription management pages in platform-admin.
|
|
* Verifies subscription dashboard, tier management, and experiments.
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Subscription Dashboard Page', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Mock tier stats API
|
|
await page.route('**/api/marketplace/tiers/admin/stats**', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
totalSubscribers: 1250,
|
|
monthlyRevenue: 45000,
|
|
upgrades: 85,
|
|
limitHits: 2340,
|
|
conversionRate: 0.12,
|
|
}),
|
|
});
|
|
});
|
|
|
|
// Mock funnel metrics API
|
|
await page.route('**/api/analytics/admin/subscription-funnel/metrics**', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
stages: [
|
|
{ name: 'Limit Hits', count: 2340 },
|
|
{ name: 'Prompts Shown', count: 1800 },
|
|
{ name: 'Tier Compared', count: 500 },
|
|
{ name: 'Checkout Started', count: 200 },
|
|
{ name: 'Completed', count: 85 },
|
|
],
|
|
overallConversion: 0.036,
|
|
}),
|
|
});
|
|
});
|
|
|
|
// Mock limit hits API
|
|
await page.route('**/api/analytics/admin/subscription-funnel/limit-hits**', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
byResource: [
|
|
{ resource: 'Messages', count: 1200 },
|
|
{ resource: 'Profile Discoveries', count: 800 },
|
|
{ resource: 'Profile Views', count: 340 },
|
|
],
|
|
}),
|
|
});
|
|
});
|
|
|
|
// Mock MRR by tier API
|
|
await page.route('**/api/analytics/admin/subscription-funnel/mrr-by-tier**', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
tiers: [
|
|
{ name: 'Basic', mrr: 15000, subscribers: 600 },
|
|
{ name: 'Pro', mrr: 20000, subscribers: 400 },
|
|
{ name: 'Premium', mrr: 10000, subscribers: 250 },
|
|
],
|
|
}),
|
|
});
|
|
});
|
|
|
|
// Mock recent upgrades API
|
|
await page.route('**/api/analytics/admin/subscription-funnel/recent-upgrades**', (route) => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
upgrades: [
|
|
{
|
|
userId: 'user-001',
|
|
fromTier: 'Basic',
|
|
toTier: 'Pro',
|
|
trigger: 'limit_exceeded',
|
|
createdAt: '2024-01-15T10:30:00Z',
|
|
},
|
|
],
|
|
}),
|
|
});
|
|
});
|
|
|
|
// Navigate to subscription dashboard
|
|
await page.goto('/subscriptions');
|
|
await page.waitForLoadState('networkidle');
|
|
});
|
|
|
|
test('should display subscription dashboard header', async ({ page }) => {
|
|
const mainContent = page.locator('main');
|
|
await expect(mainContent.locator('h1').first()).toContainText(/subscription/i);
|
|
});
|
|
|
|
test('should display KPI cards', async ({ page }) => {
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check for KPI labels
|
|
await expect(page.locator('text=/revenue|subscribers|upgrades/i').first()).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
|
|
test('should display date range selector', async ({ page }) => {
|
|
// Look for date range buttons (7d, 30d, 90d)
|
|
await expect(page.locator('text=/7.?d|30.?d|days/i').first()).toBeVisible({ timeout: 10000 });
|
|
});
|
|
|
|
test('should display funnel visualization', async ({ page }) => {
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for funnel stages
|
|
await expect(page.locator('text=/limit|prompts|checkout|completed/i').first()).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
|
|
test('should display MRR by tier section', async ({ page }) => {
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for tier names
|
|
await expect(page.locator('text=/Basic|Pro|Premium/i').first()).toBeVisible({ timeout: 10000 });
|
|
});
|
|
|
|
test('should display quick links', async ({ page }) => {
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for navigation links
|
|
await expect(page.locator('text=/tier|manage|experiment/i').first()).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
});
|
|
|
|
test.describe('Subscription Tiers Page', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Mock tiers API
|
|
await page.route('**/api/marketplace/tiers**', (route) => {
|
|
if (route.request().method() === 'GET') {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
tiers: [
|
|
{
|
|
id: 'tier-001',
|
|
name: 'Basic',
|
|
priceUsd: 9.99,
|
|
features: ['Feature 1', 'Feature 2'],
|
|
limits: { messages: 100, profileViews: 50 },
|
|
active: true,
|
|
},
|
|
{
|
|
id: 'tier-002',
|
|
name: 'Pro',
|
|
priceUsd: 19.99,
|
|
features: ['All Basic', 'Feature 3'],
|
|
limits: { messages: 500, profileViews: 200 },
|
|
active: true,
|
|
},
|
|
],
|
|
total: 2,
|
|
}),
|
|
});
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
|
|
// Navigate to tiers page
|
|
await page.goto('/subscriptions/tiers');
|
|
await page.waitForLoadState('networkidle');
|
|
});
|
|
|
|
test('should display tiers page header', async ({ page }) => {
|
|
const mainContent = page.locator('main');
|
|
await expect(mainContent.locator('h1').first()).toContainText(/tier/i);
|
|
});
|
|
|
|
test('should display tier cards or table', async ({ page }) => {
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for tier names
|
|
await expect(page.locator('text=Basic').first()).toBeVisible({ timeout: 10000 });
|
|
await expect(page.locator('text=Pro').first()).toBeVisible({ timeout: 10000 });
|
|
});
|
|
|
|
test('should display pricing information', async ({ page }) => {
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for price values
|
|
await expect(page.locator('text=/\\$\\d+|9\\.99|19\\.99/').first()).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
});
|
|
|
|
test.describe('Experiments Page', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Mock experiments API
|
|
await page.route('**/api/marketplace/experiments**', (route) => {
|
|
if (route.request().method() === 'GET') {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
experiments: [
|
|
{
|
|
id: 'exp-001',
|
|
name: 'Pricing Test A',
|
|
status: 'active',
|
|
variants: 2,
|
|
startDate: '2024-01-01T00:00:00Z',
|
|
participants: 1500,
|
|
},
|
|
],
|
|
total: 1,
|
|
}),
|
|
});
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
|
|
// Navigate to experiments page
|
|
await page.goto('/subscriptions/experiments');
|
|
await page.waitForLoadState('networkidle');
|
|
});
|
|
|
|
test('should display experiments page header', async ({ page }) => {
|
|
const mainContent = page.locator('main');
|
|
await expect(mainContent.locator('h1').first()).toContainText(/experiment/i);
|
|
});
|
|
|
|
test('should display experiment list', async ({ page }) => {
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for experiment name
|
|
await expect(page.locator('text=Pricing Test A').first()).toBeVisible({ timeout: 10000 });
|
|
});
|
|
|
|
test('should show experiment status', async ({ page }) => {
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for status badge
|
|
await expect(page.locator('text=/active|paused|completed/i').first()).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
});
|