← 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);
    });
  });
});