← back to Wine Finder Next
e2e/membership-flows.spec.ts
377 lines
// E2E Tests for Wine Membership Platform
import { test, expect } from '@playwright/test';
test.describe('Wine Membership Platform E2E Tests', () => {
test.beforeEach(async ({ page }) => {
// Navigate to the application
await page.goto('/');
});
test.describe('Bottle Purchase Flow', () => {
test('should display available bottles', async ({ page }) => {
await page.goto('/membership');
// Wait for bottles to load
await page.waitForSelector('[data-testid="bottle-card"]', { timeout: 10000 });
// Verify bottles are displayed
const bottles = await page.locator('[data-testid="bottle-card"]').count();
expect(bottles).toBeGreaterThan(0);
});
test('should show bottle details when clicked', async ({ page }) => {
await page.goto('/membership');
await page.waitForSelector('[data-testid="bottle-card"]');
// Click first bottle
await page.locator('[data-testid="bottle-card"]').first().click();
// Verify details are shown
await expect(page.locator('[data-testid="bottle-name"]')).toBeVisible();
await expect(page.locator('[data-testid="bottle-vintage"]')).toBeVisible();
await expect(page.locator('[data-testid="bottle-price"]')).toBeVisible();
});
test('should add bottle shares to cart', async ({ page }) => {
await page.goto('/membership');
await page.waitForSelector('[data-testid="bottle-card"]');
// Click first bottle
await page.locator('[data-testid="bottle-card"]').first().click();
// Enter quantity
await page.fill('[data-testid="quantity-input"]', '5');
// Add to cart
await page.click('[data-testid="add-to-cart-btn"]');
// Verify cart badge updated
const cartBadge = await page.locator('[data-testid="cart-badge"]').textContent();
expect(parseInt(cartBadge || '0')).toBeGreaterThan(0);
});
test('should complete purchase flow', async ({ page }) => {
await page.goto('/membership');
await page.waitForSelector('[data-testid="bottle-card"]');
// Add items to cart
await page.locator('[data-testid="bottle-card"]').first().click();
await page.fill('[data-testid="quantity-input"]', '3');
await page.click('[data-testid="add-to-cart-btn"]');
// Go to checkout
await page.click('[data-testid="cart-icon"]');
await page.click('[data-testid="checkout-btn"]');
// Fill payment info (test mode)
await page.fill('[data-testid="email-input"]', 'test@example.com');
await page.fill('[data-testid="card-number"]', '4242424242424242');
await page.fill('[data-testid="card-expiry"]', '12/25');
await page.fill('[data-testid="card-cvc"]', '123');
// Complete purchase
await page.click('[data-testid="complete-purchase-btn"]');
// Verify success message
await expect(page.locator('[data-testid="purchase-success"]')).toBeVisible({ timeout: 10000 });
});
});
test.describe('Governance Voting Flow', () => {
test('should display active governance votes', async ({ page }) => {
await page.goto('/governance');
// Wait for votes to load
await page.waitForSelector('[data-testid="vote-card"]', { timeout: 10000 });
// Verify votes are displayed
const votes = await page.locator('[data-testid="vote-card"]').count();
expect(votes).toBeGreaterThan(0);
});
test('should show vote details', async ({ page }) => {
await page.goto('/governance');
await page.waitForSelector('[data-testid="vote-card"]');
// Click first vote
await page.locator('[data-testid="vote-card"]').first().click();
// Verify vote details
await expect(page.locator('[data-testid="vote-proposal"]')).toBeVisible();
await expect(page.locator('[data-testid="vote-description"]')).toBeVisible();
await expect(page.locator('[data-testid="vote-progress"]')).toBeVisible();
});
test('should cast a vote successfully', async ({ page }) => {
await page.goto('/governance');
await page.waitForSelector('[data-testid="vote-card"]');
// Click first vote
await page.locator('[data-testid="vote-card"]').first().click();
// Select voting choice
await page.click('[data-testid="vote-for-btn"]');
// Confirm vote
await page.click('[data-testid="confirm-vote-btn"]');
// Verify success message
await expect(page.locator('[data-testid="vote-success"]')).toBeVisible({ timeout: 5000 });
});
test('should display vote results', async ({ page }) => {
await page.goto('/governance');
await page.waitForSelector('[data-testid="vote-card"]');
// Click first vote
await page.locator('[data-testid="vote-card"]').first().click();
// Verify results are displayed
await expect(page.locator('[data-testid="votes-for"]')).toBeVisible();
await expect(page.locator('[data-testid="votes-against"]')).toBeVisible();
await expect(page.locator('[data-testid="votes-abstain"]')).toBeVisible();
});
test('should prevent double voting', async ({ page }) => {
await page.goto('/governance');
await page.waitForSelector('[data-testid="vote-card"]');
// Click first vote
await page.locator('[data-testid="vote-card"]').first().click();
// Try to vote
await page.click('[data-testid="vote-for-btn"]');
await page.click('[data-testid="confirm-vote-btn"]');
// Wait for vote to complete
await page.waitForTimeout(1000);
// Try to vote again
const voteBtn = await page.locator('[data-testid="vote-for-btn"]');
// Button should be disabled or show already voted message
const isDisabled = await voteBtn.isDisabled().catch(() => true);
const alreadyVoted = await page.locator('[data-testid="already-voted"]').isVisible().catch(() => false);
expect(isDisabled || alreadyVoted).toBe(true);
});
});
test.describe('Marketplace Trading Flow', () => {
test('should display marketplace listings', async ({ page }) => {
await page.goto('/marketplace');
// Wait for listings to load
await page.waitForSelector('[data-testid="listing-card"]', { timeout: 10000 });
// Verify listings are displayed
const listings = await page.locator('[data-testid="listing-card"]').count();
expect(listings).toBeGreaterThan(0);
});
test('should create a new listing', async ({ page }) => {
await page.goto('/marketplace');
// Click create listing button
await page.click('[data-testid="create-listing-btn"]');
// Fill listing details
await page.selectOption('[data-testid="select-item"]', { index: 1 });
await page.fill('[data-testid="listing-price"]', '12.50');
// Create listing
await page.click('[data-testid="submit-listing-btn"]');
// Verify success message
await expect(page.locator('[data-testid="listing-success"]')).toBeVisible({ timeout: 5000 });
});
test('should purchase a listing', async ({ page }) => {
await page.goto('/marketplace');
await page.waitForSelector('[data-testid="listing-card"]');
// Click first listing
await page.locator('[data-testid="listing-card"]').first().click();
// Click purchase button
await page.click('[data-testid="purchase-listing-btn"]');
// Confirm purchase
await page.click('[data-testid="confirm-purchase-btn"]');
// Verify success message
await expect(page.locator('[data-testid="purchase-success"]')).toBeVisible({ timeout: 5000 });
});
test('should filter listings by bottle', async ({ page }) => {
await page.goto('/marketplace');
await page.waitForSelector('[data-testid="listing-card"]');
// Open filter
await page.click('[data-testid="filter-btn"]');
// Select bottle filter
await page.selectOption('[data-testid="bottle-filter"]', { index: 1 });
// Apply filter
await page.click('[data-testid="apply-filter-btn"]');
// Verify filtered results
await page.waitForTimeout(1000);
const listings = await page.locator('[data-testid="listing-card"]').count();
expect(listings).toBeGreaterThanOrEqual(0);
});
test('should cancel a listing', async ({ page }) => {
await page.goto('/marketplace/my-listings');
await page.waitForSelector('[data-testid="my-listing-card"]', { timeout: 10000 });
// Click cancel button on first listing
await page.locator('[data-testid="cancel-listing-btn"]').first().click();
// Confirm cancellation
await page.click('[data-testid="confirm-cancel-btn"]');
// Verify success message
await expect(page.locator('[data-testid="cancel-success"]')).toBeVisible({ timeout: 5000 });
});
});
test.describe('Mobile Responsiveness', () => {
test('should display properly on mobile', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 }); // iPhone SE
await page.goto('/membership');
// Verify mobile layout
await expect(page.locator('[data-testid="mobile-menu"]')).toBeVisible();
// Check bottle cards stack vertically
const bottleCards = await page.locator('[data-testid="bottle-card"]').all();
if (bottleCards.length >= 2) {
const firstCardBox = await bottleCards[0].boundingBox();
const secondCardBox = await bottleCards[1].boundingBox();
expect(firstCardBox).not.toBeNull();
expect(secondCardBox).not.toBeNull();
// Cards should be stacked (second card below first)
if (firstCardBox && secondCardBox) {
expect(secondCardBox.y).toBeGreaterThan(firstCardBox.y);
}
}
});
test('should handle mobile navigation', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/');
// Open mobile menu
await page.click('[data-testid="mobile-menu-btn"]');
// Navigate to governance
await page.click('[data-testid="nav-governance"]');
// Verify navigation
await expect(page).toHaveURL(/.*governance/);
});
test('should handle touch interactions', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/marketplace');
await page.waitForSelector('[data-testid="listing-card"]');
// Tap on listing
await page.locator('[data-testid="listing-card"]').first().tap();
// Verify details opened
await expect(page.locator('[data-testid="listing-details"]')).toBeVisible();
});
});
test.describe('Error Handling', () => {
test('should display error message for invalid purchase', async ({ page }) => {
await page.goto('/membership');
// Try to purchase without selecting quantity
await page.waitForSelector('[data-testid="bottle-card"]');
await page.locator('[data-testid="bottle-card"]').first().click();
await page.click('[data-testid="add-to-cart-btn"]');
// Verify error message
await expect(page.locator('[data-testid="error-message"]')).toBeVisible();
});
test('should handle network errors gracefully', async ({ page }) => {
// Simulate offline
await page.route('**/api/**', route => route.abort());
await page.goto('/membership');
// Verify error state
await expect(page.locator('[data-testid="error-state"]')).toBeVisible({ timeout: 10000 });
});
test('should handle rate limiting', async ({ page }) => {
// Make many rapid requests
for (let i = 0; i < 150; i++) {
await page.evaluate(() => {
fetch('/api/membership/bottles').catch(() => {});
});
}
await page.waitForTimeout(1000);
// Next request should show rate limit message
await page.goto('/membership');
// May show rate limit notice
const hasRateLimitNotice = await page.locator('[data-testid="rate-limit-notice"]').isVisible().catch(() => false);
// Test passes either way, just checking it handles it
expect(typeof hasRateLimitNotice).toBe('boolean');
});
});
test.describe('Accessibility', () => {
test('should have proper ARIA labels', async ({ page }) => {
await page.goto('/membership');
// Check main navigation
const nav = await page.locator('nav[aria-label]');
await expect(nav).toBeVisible();
// Check buttons have aria labels
const buttons = await page.locator('button[aria-label]').all();
expect(buttons.length).toBeGreaterThan(0);
});
test('should be keyboard navigable', async ({ page }) => {
await page.goto('/membership');
// Tab through elements
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
// Verify focus is visible
const focusedElement = await page.evaluate(() => document.activeElement?.tagName);
expect(focusedElement).toBeDefined();
});
test('should have proper heading hierarchy', async ({ page }) => {
await page.goto('/membership');
// Check for h1
const h1 = await page.locator('h1').count();
expect(h1).toBeGreaterThan(0);
// Check for proper heading levels
const headings = await page.locator('h1, h2, h3, h4, h5, h6').all();
expect(headings.length).toBeGreaterThan(0);
});
});
});