Add analytics plugin package for tracking and metrics. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
334 lines
11 KiB
TypeScript
334 lines
11 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { RevenuePage } from '../page-objects/RevenuePage'
|
|
import { loginAsAdmin } from '../helpers/auth.helper'
|
|
|
|
/**
|
|
* E2E Tests: Revenue Analytics Page
|
|
*
|
|
* Tests revenue page functionality:
|
|
* - KPI cards render with data-testid attributes
|
|
* - Date range filters work correctly
|
|
* - Charts and tables display data
|
|
* - Export functionality
|
|
*
|
|
* These tests use mock data from the analytics hooks,
|
|
* so values are consistent across test runs.
|
|
*/
|
|
test.describe('Revenue Analytics Page', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await loginAsAdmin(page)
|
|
})
|
|
|
|
test('should load revenue page and display key metrics', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Verify page loaded
|
|
await expect(page).toHaveURL('/analytics/revenue')
|
|
await expect(revenuePage.pageTitle).toBeVisible()
|
|
await expect(revenuePage.pageTitle).toHaveText('Revenue Analytics')
|
|
|
|
// Verify KPI section visible
|
|
await expect(revenuePage.kpiSection).toBeVisible()
|
|
})
|
|
|
|
test('should display all 6 KPI cards', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Verify all 6 KPI cards are present
|
|
const allCardsPresent = await revenuePage.verifyAllKpiCardsPresent()
|
|
expect(allCardsPresent).toBe(true)
|
|
|
|
// Verify each card individually
|
|
await expect(revenuePage.totalRevenueCard).toBeVisible()
|
|
await expect(revenuePage.mrrCard).toBeVisible()
|
|
await expect(revenuePage.oneTimeRevenueCard).toBeVisible()
|
|
await expect(revenuePage.cryptoRevenueCard).toBeVisible()
|
|
await expect(revenuePage.growthRateCard).toBeVisible()
|
|
await expect(revenuePage.arpuCard).toBeVisible()
|
|
})
|
|
|
|
test('should display total revenue with currency format', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Get total revenue value
|
|
const totalRevenue = await revenuePage.getTotalRevenue()
|
|
expect(totalRevenue).toBeTruthy()
|
|
|
|
// Verify has currency format ($ prefix)
|
|
expect(totalRevenue).toContain('$')
|
|
})
|
|
|
|
test('should display monthly recurring revenue', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Get MRR value
|
|
const mrr = await revenuePage.getMonthlyRecurring()
|
|
expect(mrr).toBeTruthy()
|
|
|
|
// Verify has currency format
|
|
expect(mrr).toContain('$')
|
|
})
|
|
|
|
test('should display growth rate with percentage', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Get growth rate value
|
|
const growthRate = await revenuePage.getKpiCardValue('growth-rate-card')
|
|
expect(growthRate).toBeTruthy()
|
|
|
|
// Verify has percentage format (% suffix)
|
|
expect(growthRate).toContain('%')
|
|
})
|
|
|
|
test('should render revenue trend chart', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Verify chart section visible
|
|
await expect(revenuePage.revenueTrendChart).toBeVisible()
|
|
|
|
// Verify chart has title
|
|
const chartTitle = revenuePage.revenueTrendChart.locator('h2')
|
|
await expect(chartTitle).toHaveText('Revenue Trend')
|
|
})
|
|
|
|
test('should render revenue breakdown by source', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Verify breakdown table renders
|
|
const tableRendered = await revenuePage.verifyRevenueBreakdownTableRendered()
|
|
expect(tableRendered).toBe(true)
|
|
|
|
// Verify table headers
|
|
const table = page.locator('[data-testid="breakdown-table"]')
|
|
await expect(table.locator('th:has-text("Source")')).toBeVisible()
|
|
await expect(table.locator('th:has-text("Amount")')).toBeVisible()
|
|
await expect(table.locator('th:has-text("Percentage")')).toBeVisible()
|
|
})
|
|
|
|
test('should render revenue breakdown by provider', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Verify provider breakdown table renders
|
|
const tableRendered = await revenuePage.verifyProviderBreakdownTableRendered()
|
|
expect(tableRendered).toBe(true)
|
|
|
|
// Verify table headers
|
|
const table = page.locator('[data-testid="provider-table"]')
|
|
await expect(table.locator('th:has-text("Provider")')).toBeVisible()
|
|
await expect(table.locator('th:has-text("Amount")')).toBeVisible()
|
|
await expect(table.locator('th:has-text("Percentage")')).toBeVisible()
|
|
})
|
|
|
|
test.describe('Date Range Filters', () => {
|
|
test('should have date range filter section', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Verify filter section visible
|
|
await expect(revenuePage.dateFilter).toBeVisible()
|
|
|
|
// Verify filter buttons present
|
|
await expect(page.locator('[data-testid="filter-7d"]')).toBeVisible()
|
|
await expect(page.locator('[data-testid="filter-30d"]')).toBeVisible()
|
|
await expect(page.locator('[data-testid="filter-this-month"]')).toBeVisible()
|
|
await expect(page.locator('[data-testid="filter-last-month"]')).toBeVisible()
|
|
await expect(page.locator('[data-testid="filter-custom"]')).toBeVisible()
|
|
})
|
|
|
|
test('should select 7 days filter', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Click 7 days filter
|
|
await revenuePage.selectDateRange('7d')
|
|
|
|
// Verify button is active
|
|
const button = page.locator('[data-testid="filter-7d"]')
|
|
await expect(button).toHaveClass(/active/)
|
|
})
|
|
|
|
test('should select 30 days filter', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Click 30 days filter
|
|
await revenuePage.selectDateRange('30d')
|
|
|
|
// Verify button is active (default state)
|
|
const button = page.locator('[data-testid="filter-30d"]')
|
|
await expect(button).toHaveClass(/active/)
|
|
})
|
|
|
|
test('should select this month filter', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Click this month filter
|
|
await revenuePage.selectDateRange('this-month')
|
|
|
|
// Verify button is active
|
|
const button = page.locator('[data-testid="filter-this-month"]')
|
|
await expect(button).toHaveClass(/active/)
|
|
})
|
|
|
|
test('should switch between date ranges', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Select 7 days
|
|
await revenuePage.selectDateRange('7d')
|
|
let activeButton = page.locator('[data-testid="filter-7d"]')
|
|
await expect(activeButton).toHaveClass(/active/)
|
|
|
|
// Switch to this month
|
|
await revenuePage.selectDateRange('this-month')
|
|
activeButton = page.locator('[data-testid="filter-this-month"]')
|
|
await expect(activeButton).toHaveClass(/active/)
|
|
|
|
// Switch back to 30 days
|
|
await revenuePage.selectDateRange('30d')
|
|
activeButton = page.locator('[data-testid="filter-30d"]')
|
|
await expect(activeButton).toHaveClass(/active/)
|
|
})
|
|
})
|
|
|
|
test.describe('Export Functionality', () => {
|
|
test('should have export section', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Verify export section visible
|
|
await expect(revenuePage.exportSection).toBeVisible()
|
|
})
|
|
|
|
test('should open export menu', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Open export menu
|
|
await revenuePage.openExportMenu()
|
|
|
|
// Verify export options visible
|
|
await expect(revenuePage.exportCsvButton).toBeVisible()
|
|
await expect(revenuePage.exportExcelButton).toBeVisible()
|
|
await expect(revenuePage.exportPdfButton).toBeVisible()
|
|
})
|
|
|
|
test('should have CSV export option', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Open export menu
|
|
await revenuePage.openExportMenu()
|
|
|
|
// Verify CSV button
|
|
await expect(revenuePage.exportCsvButton).toBeVisible()
|
|
await expect(revenuePage.exportCsvButton).toHaveText('CSV')
|
|
})
|
|
|
|
test('should have Excel export option', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Open export menu
|
|
await revenuePage.openExportMenu()
|
|
|
|
// Verify Excel button
|
|
await expect(revenuePage.exportExcelButton).toBeVisible()
|
|
await expect(revenuePage.exportExcelButton).toHaveText('Excel')
|
|
})
|
|
|
|
test('should have PDF export option', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Open export menu
|
|
await revenuePage.openExportMenu()
|
|
|
|
// Verify PDF button
|
|
await expect(revenuePage.exportPdfButton).toBeVisible()
|
|
await expect(revenuePage.exportPdfButton).toHaveText('PDF')
|
|
})
|
|
})
|
|
|
|
test('should handle page refresh', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Get initial title
|
|
const initialTitle = await revenuePage.pageTitle.textContent()
|
|
|
|
// Refresh page
|
|
await page.reload()
|
|
await page.waitForLoadState('networkidle')
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Verify page still loads with same title
|
|
const updatedTitle = await revenuePage.pageTitle.textContent()
|
|
expect(updatedTitle).toBe(initialTitle)
|
|
|
|
// Verify KPI cards still visible
|
|
const allCardsPresent = await revenuePage.verifyAllKpiCardsPresent()
|
|
expect(allCardsPresent).toBe(true)
|
|
})
|
|
|
|
test('should handle graceful degradation when data is missing', async ({ page }) => {
|
|
const revenuePage = new RevenuePage(page)
|
|
|
|
await revenuePage.goto()
|
|
await revenuePage.waitForLoad()
|
|
|
|
// Page should always show KPI section (even with mock data or errors)
|
|
await expect(revenuePage.kpiSection).toBeVisible()
|
|
|
|
// Should have at least one KPI card visible
|
|
const cardCount = await revenuePage.verifyKpiCardsRendered()
|
|
expect(cardCount).toBeGreaterThan(0)
|
|
})
|
|
})
|