veza/tests/e2e/30-marketplace-checkout.spec.ts
senke 6fad0ad68d fix: stabilize frontend — 98 TS errors to 0, align API endpoints, optimize bundle
- Fix 98 TypeScript errors across 37 files:
  - Service layer double-unwrapping (subscriptionService, distributionService, gearService)
  - Self-referencing variables in SearchPageResults
  - FeedView/ExploreView .posts→.items alignment
  - useQueueSync Zustand subscribe API
  - AdminAuditLogsView missing interface fields
  - Toast proxy type, interceptor type narrowing
  - 22 unused imports/variables removed
  - 5 storybook mock data fixes

- Align frontend API calls with backend endpoints:
  - Analytics: useAnalyticsView now calls /creator/analytics/dashboard (was /analytics)
  - Chat: chatService uses /conversations (was mock data), WS URL from backend token
  - Dashboard StatsSection: uses real /dashboard API data (was hardcoded zeros)
  - Settings: suppress 2FA toast error when endpoint unavailable

- Fix marketplace products: seed uses 'active' status (was 'published')
- Enrich seed: admin follows all creators (feed has content)

- Optimize bundle: vendor catch-all 793KB→318KB gzip (-60%)
  Split into vendor-charts, vendor-emoji, vendor-swagger, vendor-media, etc.

- Clean repo: remove ~100 orphaned screenshots, audit reports, logs from root

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 21:18:49 +01:00

213 lines
8.6 KiB
TypeScript

import { test, expect } from '@chromatic-com/playwright';
import { loginViaAPI, CONFIG, navigateTo } from './helpers';
/**
* MARKETPLACE & CHECKOUT — Tests du flux d'achat
* Sélecteurs basés sur MarketplacePage.tsx, ProductCard.tsx, Cart.tsx, CartStore.ts
*/
test.describe('MARKETPLACE & CHECKOUT @critical', () => {
test.beforeEach(async ({ page }) => {
await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password);
// Clear cart before each test
await page.evaluate(() => {
localStorage.removeItem('veza-cart-storage');
});
});
test('Marketplace — produits affichés avec prix et boutons @critical', async ({ page }) => {
await navigateTo(page, '/marketplace');
// Wait for products grid to load
const productCard = page.locator('[aria-label^="Product:"]').first()
.or(page.locator('[class*="CardFooter"]').first());
const hasProducts = await productCard.isVisible({ timeout: 10_000 }).catch(() => false);
if (hasProducts) {
// Verify price is visible (soft check)
const price = page.locator('text=/\\$|€|USD/').first();
const hasPrice = await price.isVisible({ timeout: 3000 }).catch(() => false);
console.log(` Products found, price visible: ${hasPrice}`);
// Verify Buy button exists (soft check)
const buyBtn = page.getByRole('button', { name: /buy|acheter/i }).first();
const hasBuy = await buyBtn.isVisible({ timeout: 3000 }).catch(() => false);
console.log(` Buy button visible: ${hasBuy}`);
} else {
// Empty marketplace is valid — just check the page loaded without crash
const body = await page.textContent('body') || '';
expect(body).not.toMatch(/500|Internal Server Error/);
expect(body.length).toBeGreaterThan(50);
console.log(' No products found — marketplace may be empty (valid state)');
}
});
test('Recherche marketplace — filtrer les produits', async ({ page }) => {
await navigateTo(page, '/marketplace');
const searchInput = page.locator('input[placeholder*="Search" i]').first()
.or(page.locator('input[placeholder*="Recherch" i]').first());
if (await searchInput.isVisible({ timeout: 5000 }).catch(() => false)) {
await searchInput.fill('beat');
await page.waitForTimeout(1000);
// Results should update (either products or empty state)
const body = await page.textContent('body');
expect(body!.length).toBeGreaterThan(50);
}
});
test('Ajout au panier → badge panier incrémente @critical', async ({ page }) => {
await navigateTo(page, '/marketplace');
// Find a product card
const productCard = page.locator('[aria-label^="Product:"]').first();
if (await productCard.isVisible({ timeout: 10_000 }).catch(() => false)) {
// Hover to reveal Add to Cart
await productCard.hover();
await page.waitForTimeout(300);
const addToCartBtn = productCard.getByRole('button', { name: /add to cart|ajouter/i }).first()
.or(productCard.locator('button[class*="outline"]').first());
if (await addToCartBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await addToCartBtn.click();
await page.waitForTimeout(500);
// Check cart badge updated
const cartBadge = page.locator('text=/^1$|^[1-9]$/').first();
const hasBadge = await cartBadge.isVisible({ timeout: 3000 }).catch(() => false);
if (hasBadge) {
console.log('✅ Cart badge shows item count');
}
}
}
});
test('Ouvrir le panier — affiche les produits ajoutés @critical', async ({ page }) => {
await navigateTo(page, '/marketplace');
// Add a product to cart first
const productCard = page.locator('[aria-label^="Product:"]').first();
if (await productCard.isVisible({ timeout: 10_000 }).catch(() => false)) {
await productCard.hover();
await page.waitForTimeout(300);
const addBtn = productCard.getByRole('button', { name: /add to cart|ajouter/i }).first()
.or(productCard.locator('button[class*="outline"]').first());
if (await addBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await addBtn.click();
await page.waitForTimeout(500);
}
}
// Open cart
const cartBtn = page.getByRole('button', { name: /cart|panier/i }).first()
.or(page.locator('button').filter({ has: page.locator('[class*="ShoppingCart"]') }).first());
if (await cartBtn.isVisible({ timeout: 5000 }).catch(() => false)) {
await cartBtn.click();
await page.waitForTimeout(500);
// Cart dialog should open
const cartDialog = page.locator('[role="dialog"]').first();
if (await cartDialog.isVisible({ timeout: 3000 }).catch(() => false)) {
// Should show cart title
const cartTitle = cartDialog.locator('text=/shopping cart|panier/i').first();
await expect(cartTitle).toBeVisible({ timeout: 3000 });
}
}
});
test('Panier — supprimer un produit @critical', async ({ page }) => {
await navigateTo(page, '/marketplace');
// Add product then open cart
const productCard = page.locator('[aria-label^="Product:"]').first();
if (await productCard.isVisible({ timeout: 10_000 }).catch(() => false)) {
await productCard.hover();
const addBtn = productCard.getByRole('button', { name: /add to cart|ajouter/i }).first()
.or(productCard.locator('button[class*="outline"]').first());
if (await addBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await addBtn.click();
await page.waitForTimeout(500);
}
}
const cartBtn = page.getByRole('button', { name: /cart|panier/i }).first();
if (await cartBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await cartBtn.click();
await page.waitForTimeout(500);
const removeBtn = page.locator('[aria-label="Remove item"]').first();
if (await removeBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await removeBtn.click();
await page.waitForTimeout(500);
// Cart should now show empty
const emptyCart = page.locator('text=/cart is empty|panier est vide/i').first();
const isEmpty = await emptyCart.isVisible({ timeout: 3000 }).catch(() => false);
if (isEmpty) {
console.log('✅ Cart emptied after removing item');
}
}
}
});
test('Panier vide — message et CTA vers marketplace', async ({ page }) => {
await navigateTo(page, '/marketplace');
// Open cart without adding anything
const cartBtn = page.getByRole('button', { name: /cart|panier/i }).first();
if (await cartBtn.isVisible({ timeout: 5000 }).catch(() => false)) {
await cartBtn.click();
await page.waitForTimeout(500);
const emptyMsg = page.locator('text=/cart is empty|panier est vide/i').first();
await expect(emptyMsg).toBeVisible({ timeout: 3000 });
}
});
test('Checkout — le formulaire de paiement se charge @critical', async ({ page }) => {
await navigateTo(page, '/marketplace');
// Add product and go to checkout
const productCard = page.locator('[aria-label^="Product:"]').first();
if (await productCard.isVisible({ timeout: 10_000 }).catch(() => false)) {
await productCard.hover();
const addBtn = productCard.getByRole('button', { name: /add to cart|ajouter/i }).first()
.or(productCard.locator('button[class*="outline"]').first());
if (await addBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await addBtn.click();
await page.waitForTimeout(500);
}
}
const cartBtn = page.getByRole('button', { name: /cart|panier/i }).first();
if (await cartBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await cartBtn.click();
await page.waitForTimeout(500);
// Look for checkout/pay button
const checkoutBtn = page.getByRole('button', { name: /checkout|payer|pay/i }).first();
if (await checkoutBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await checkoutBtn.click();
await page.waitForTimeout(2000);
// Verify payment form loads (Hyperswitch iframe or payment form)
const paymentForm = page.locator('iframe').first()
.or(page.locator('text=/complete payment|paiement/i').first());
const hasPayment = await paymentForm.isVisible({ timeout: 5000 }).catch(() => false);
if (hasPayment) {
console.log('✅ Payment form loaded');
} else {
// Payment might need server-side setup
console.warn('⚠ Payment form not loaded (Hyperswitch may not be configured)');
}
}
}
});
});