veza/tests/e2e/audit/design-tokens.ts
senke a3f4ac6b70 fix: sync E2E tests with seed data + i18n fix
- Update E2E test credentials to match actual seed users
  (user@veza.music, artist@veza.music, admin@veza.music, mod@veza.music)
- Fix hardcoded "Suggested Accounts" in SuggestionsWidget with i18n key
- Replace hardcoded amelie_dubois references with CONFIG.users.creator
- Refactor auth, player, upload E2E tests for reliability
- Add tmt test plans and scripts for CI integration
- Simplify CI workflow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 19:42:03 +02:00

769 lines
24 KiB
TypeScript

// =============================================================================
// SUMI DESIGN SYSTEM v2.0 — Source de vérité pour les tests audit
// Extrait de : apps/web/src/index.css + tailwind.config.ts + composants
// =============================================================================
// =============================================================================
// COULEURS
// =============================================================================
export const COLORS = {
dark: {
bg: {
void: '#0c0c0f',
base: '#121215',
raised: '#1a1a1f',
overlay: '#222228',
hover: '#2a2a31',
active: '#32323a',
wash: '#18181d',
},
surface: {
inset: '#101013',
subtle: '#1e1e24',
card: '#1a1a1f',
elevated: '#242430',
},
border: {
faint: 'rgba(255,255,255, 0.06)',
default: 'rgba(255,255,255, 0.10)',
strong: 'rgba(255,255,255, 0.16)',
focus: 'rgba(139,170,220, 0.50)',
accent: 'rgba(139,170,220, 0.30)',
},
text: {
primary: '#f0ede8',
secondary: '#a8a4a0',
tertiary: '#706c68',
disabled: '#4a4844',
inverse: '#121215',
link: '#8baade',
},
accent: {
DEFAULT: '#7c9dd6',
hover: '#93afe0',
active: '#6b8dc6',
muted: 'rgba(124,157,214, 0.20)',
subtle: 'rgba(124,157,214, 0.12)',
emphasis: '#5a7fba',
},
vermillion: {
DEFAULT: '#d4634a',
hover: '#de7a64',
subtle: 'rgba(212,99,74, 0.12)',
},
sage: {
DEFAULT: '#7a9e6c',
hover: '#8eb280',
subtle: 'rgba(122,158,108, 0.12)',
},
gold: {
DEFAULT: '#c9a84c',
hover: '#d6b860',
subtle: 'rgba(201,168,76, 0.12)',
},
live: '#e05a5a',
shadows: {
xs: '0 1px 2px rgba(0,0,0,0.30)',
sm: '0 2px 4px rgba(0,0,0,0.25), 0 1px 2px rgba(0,0,0,0.20)',
md: '0 4px 12px rgba(0,0,0,0.30), 0 2px 4px rgba(0,0,0,0.15)',
lg: '0 8px 24px rgba(0,0,0,0.35), 0 4px 8px rgba(0,0,0,0.20)',
xl: '0 16px 48px rgba(0,0,0,0.40), 0 8px 16px rgba(0,0,0,0.20)',
'2xl': '0 24px 64px rgba(0,0,0,0.50)',
glow: '0 0 0 3px rgba(124,157,214,0.25)',
glowLg: '0 0 20px rgba(124,157,214,0.15)',
},
glass: {
bg: 'rgba(18,18,21, 0.80)',
border: 'rgba(255,255,255, 0.08)',
blur: '12px',
},
scrollbar: {
track: 'transparent',
thumb: 'rgba(255,255,255, 0.10)',
hover: 'rgba(255,255,255, 0.18)',
},
},
light: {
bg: {
void: '#f0ece4',
base: '#f6f3ed',
raised: '#ffffff',
overlay: '#ffffff',
hover: '#ede9e1',
active: '#e4e0d8',
wash: '#f8f6f1',
},
surface: {
inset: '#ebe7df',
subtle: '#f2eee6',
card: '#ffffff',
elevated: '#ffffff',
},
border: {
faint: 'rgba(0,0,0, 0.05)',
default: 'rgba(0,0,0, 0.10)',
strong: 'rgba(0,0,0, 0.18)',
focus: 'rgba(80,110,170, 0.45)',
accent: 'rgba(80,110,170, 0.25)',
},
text: {
primary: '#1a1816',
secondary: '#5c5854',
tertiary: '#8a8580',
disabled: '#b5b0aa',
inverse: '#f0ede8',
link: '#4a6fa5',
},
accent: {
DEFAULT: '#4a6fa5',
hover: '#3a5f95',
active: '#5a7fb5',
subtle: 'rgba(74,111,165, 0.12)',
muted: 'rgba(74,111,165, 0.20)',
emphasis: '#3d5f90',
},
vermillion: {
DEFAULT: '#b84a35',
hover: '#a03e2e',
subtle: 'rgba(184,74,53, 0.12)',
},
sage: {
DEFAULT: '#5a7e4e',
hover: '#4d6e42',
subtle: 'rgba(90,126,78, 0.12)',
},
gold: {
DEFAULT: '#9a7d2e',
hover: '#8a6d20',
subtle: 'rgba(154,125,46, 0.12)',
},
live: '#c84040',
shadows: {
xs: '0 1px 2px rgba(0,0,0,0.05)',
sm: '0 2px 4px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)',
md: '0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.04)',
lg: '0 8px 24px rgba(0,0,0,0.10), 0 4px 8px rgba(0,0,0,0.05)',
xl: '0 16px 48px rgba(0,0,0,0.12), 0 8px 16px rgba(0,0,0,0.06)',
'2xl': '0 24px 64px rgba(0,0,0,0.15)',
glow: '0 0 0 3px rgba(74,111,165,0.25)',
},
glass: {
bg: 'rgba(255,255,255, 0.85)',
border: 'rgba(0,0,0, 0.06)',
},
scrollbar: {
thumb: 'rgba(0,0,0, 0.12)',
hover: 'rgba(0,0,0, 0.22)',
},
},
contextual: {
graffitiMagenta: '#c840a0',
gamingGold: '#d4b040',
terminalGreen: '#3eaa5e',
sakura: '#e0a0b8',
},
charts: {
1: '#7c9dd6', // accent
2: '#d4634a', // vermillion
3: '#7a9e6c', // sage
4: '#c9a84c', // gold
5: '#8b7ec8', // purple
},
semantic: {
destructiveForeground: '#ffffff',
successForeground: '#ffffff',
primaryForeground: '#121215', // dark theme text-inverse
},
} as const;
// =============================================================================
// TYPOGRAPHIE
// =============================================================================
export const FONTS = {
heading: {
family: 'Space Grotesk',
fallback: "'Space Grotesk', 'Inter', sans-serif",
weights: [400, 500, 600, 700] as const,
},
body: {
family: 'Inter',
fallback: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
weights: [300, 400, 500, 600, 700] as const,
},
mono: {
family: 'JetBrains Mono',
fallback: "'JetBrains Mono', 'SF Mono', 'Consolas', monospace",
weights: [400, 500, 600] as const,
},
serif: {
family: 'Noto Serif JP',
fallback: "'Noto Serif JP', Georgia, serif",
weights: [400, 600] as const,
},
sizes: {
'4xl': '2.25rem', // 36px
'3xl': '1.875rem', // 30px
'2xl': '1.5rem', // 24px
xl: '1.25rem', // 20px
lg: '1.125rem', // 18px
md: '1rem', // 16px
base: '0.875rem', // 14px
sm: '0.8125rem', // 13px
xs: '0.75rem', // 12px
},
lineHeights: {
none: '1',
tight: '1.25',
snug: '1.375',
normal: '1.5',
relaxed: '1.625',
loose: '1.75',
},
letterSpacing: {
tighter: '-0.03em',
tight: '-0.015em',
normal: '0',
wide: '0.025em',
wider: '0.05em',
widest: '0.1em',
},
weights: {
light: 300,
regular: 400,
medium: 500,
semibold: 600,
bold: 700,
},
// SUMI typography presets — class → expected styles
presets: {
'sumi-display': { family: 'Space Grotesk', size: '2.25rem', weight: 700, leading: 1.25, tracking: '-0.03em' },
'sumi-h1': { family: 'Space Grotesk', size: '1.875rem', weight: 600, leading: 1.25, tracking: '-0.015em' },
'sumi-h2': { family: 'Space Grotesk', size: '1.5rem', weight: 600, leading: 1.375, tracking: '-0.015em' },
'sumi-h3': { family: 'Space Grotesk', size: '1.25rem', weight: 500, leading: 1.375, tracking: '0' },
'sumi-h4': { family: 'Space Grotesk', size: '1.125rem', weight: 500, leading: 1.375, tracking: '0' },
'sumi-body-lg': { family: 'Inter', size: '1rem', weight: 400, leading: 1.625, tracking: '0' },
'sumi-body': { family: 'Inter', size: '0.875rem', weight: 400, leading: 1.5, tracking: '0' },
'sumi-body-sm': { family: 'Inter', size: '0.8125rem', weight: 400, leading: 1.5, tracking: '0' },
'sumi-caption': { family: 'Inter', size: '0.75rem', weight: 400, leading: 1.5, tracking: '0' },
'sumi-label': { family: 'Inter', size: '0.75rem', weight: 500, leading: 1.5, tracking: '0.05em' },
'sumi-mono': { family: 'JetBrains Mono', size: '0.8125rem', weight: 400, leading: 1.5, tracking: '0' },
},
} as const;
// =============================================================================
// ESPACEMENT
// =============================================================================
export const SPACING = {
normal: {
'0.5': '2px',
'1': '4px',
'1.5': '6px',
'2': '8px',
'2.5': '10px',
'3': '12px',
'4': '16px',
'5': '20px',
'6': '24px',
'8': '32px',
'10': '40px',
'12': '48px',
'16': '64px',
'20': '80px',
},
compact: {
'0.5': '1.5px',
'1': '3px',
'1.5': '4.5px',
'2': '6px',
'2.5': '7.5px',
'3': '9px',
'4': '12px',
'5': '15px',
'6': '18px',
'8': '24px',
'10': '30px',
'12': '36px',
'16': '48px',
'20': '60px',
},
} as const;
// =============================================================================
// RAYON DE BORDURE
// =============================================================================
export const BORDER_RADIUS = {
xs: '2px',
sm: '4px',
md: '6px', // --radius default
lg: '12px',
xl: '16px',
'2xl': '20px',
full: '9999px',
} as const;
// =============================================================================
// TRANSITIONS / MOTION
// =============================================================================
export const MOTION = {
duration: {
instant: '75ms',
fast: '150ms',
normal: '200ms',
slow: '300ms',
slower: '500ms',
},
easing: {
default: 'cubic-bezier(0.25, 0.1, 0.25, 1)',
out: 'cubic-bezier(0.33, 1, 0.68, 1)',
in: 'cubic-bezier(0.32, 0, 0.67, 0)',
inOut: 'cubic-bezier(0.65, 0, 0.35, 1)',
bounce: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
spring: 'cubic-bezier(0.175, 0.885, 0.32, 1.1)',
},
} as const;
// =============================================================================
// Z-INDEX
// =============================================================================
export const Z_INDEX = {
base: 0,
raised: 10,
dropdown: 100,
sticky: 200,
overlay: 300,
modal: 400,
popover: 500,
toast: 600,
tooltip: 700,
max: 999,
// Application-specific
sidebarOverlay: 90,
sidebar: 95,
player: 200, // var(--sumi-z-sticky)
} as const;
// =============================================================================
// LAYOUT
// =============================================================================
export const LAYOUT = {
maxWidth: {
DEFAULT: '1400px',
content: '1200px',
narrow: '800px',
prose: '65ch',
layoutContent: '100rem',
},
header: {
height: '4rem', // 64px
},
sidebar: {
widthExpanded: '15rem', // 240px
widthCollapsed: '5rem', // 80px
offsetLeft: '1.5rem', // 24px
offsetTop: '5rem', // 80px
offsetBottom: '1.5rem', // 24px
},
player: {
height: '80px',
},
main: {
offsetTop: '5rem', // 80px
offsetBottom: '9rem', // 144px
marginLeftExpanded: '18rem', // 288px
marginLeftCollapsed: '7rem', // 112px
minHeight: 'calc(100vh - 4rem)',
},
headerLeft: {
expanded: '18rem', // 288px
collapsed: '5rem', // 80px
},
gaps: {
DEFAULT: '1rem', // 16px
sm: '0.75rem', // 12px
lg: '1.5rem', // 24px
},
} as const;
// =============================================================================
// BREAKPOINTS
// =============================================================================
export const BREAKPOINTS = {
sm: 640,
md: 768,
lg: 1024, // Principale — sidebar responsive
xl: 1280,
'2xl': 1536,
} as const;
// =============================================================================
// VIEWPORTS DE TEST
// =============================================================================
export const VIEWPORTS = {
mobileSE: { width: 375, height: 667 },
mobile14: { width: 390, height: 844 },
mobilePro: { width: 430, height: 932 },
tablet: { width: 768, height: 1024 },
tabletLandscape: { width: 1024, height: 768 },
laptop: { width: 1280, height: 720 },
desktop: { width: 1440, height: 900 },
wide: { width: 1920, height: 1080 },
} as const;
// =============================================================================
// ROUTES
// =============================================================================
interface RouteInfo {
path: string;
name: string;
component: string;
}
export const ROUTES = {
/** Routes publiques — pas d'auth requise, redirige vers /dashboard si connecté */
public: [
{ path: '/login', name: 'Login', component: 'LoginPage' },
{ path: '/register', name: 'Register', component: 'RegisterPage' },
{ path: '/forgot-password', name: 'Forgot Password', component: 'ForgotPasswordPage' },
{ path: '/verify-email', name: 'Verify Email', component: 'VerifyEmailPage' },
{ path: '/reset-password', name: 'Reset Password', component: 'ResetPasswordPage' },
] satisfies RouteInfo[],
/** Routes publiques standalone — pas de layout dashboard */
publicStandalone: [
{ path: '/design-system', name: 'Design System', component: 'DesignSystemDemo' },
// /u/:username et /playlists/shared/:token nécessitent des params dynamiques — testés séparément
] satisfies RouteInfo[],
/** Routes protégées — auth requise, DashboardLayout */
listener: [
{ path: '/dashboard', name: 'Dashboard', component: 'DashboardPage' },
{ path: '/feed', name: 'Feed', component: 'FeedPage' },
{ path: '/discover', name: 'Discover', component: 'DiscoverPage' },
{ path: '/library', name: 'Library', component: 'LibraryPage' },
{ path: '/queue', name: 'Queue', component: 'QueuePage' },
{ path: '/search', name: 'Search', component: 'SearchPage' },
{ path: '/profile', name: 'Profile', component: 'UserProfilePage' },
{ path: '/settings', name: 'Settings', component: 'SettingsPage' },
{ path: '/settings/sessions', name: 'Sessions', component: 'SessionsPage' },
{ path: '/notifications', name: 'Notifications', component: 'NotificationsPage' },
{ path: '/playlists', name: 'Playlists', component: 'PlaylistListPage' },
{ path: '/social', name: 'Social', component: 'SocialPage' },
{ path: '/chat', name: 'Chat', component: 'ChatPage' },
{ path: '/marketplace', name: 'Marketplace', component: 'MarketplacePage' },
{ path: '/wishlist', name: 'Wishlist', component: 'WishlistPage' },
{ path: '/purchases', name: 'Purchases', component: 'PurchasesPage' },
{ path: '/subscription', name: 'Subscription', component: 'SubscriptionPage' },
{ path: '/live', name: 'Live', component: 'LivePage' },
{ path: '/cloud', name: 'Cloud', component: 'CloudPage' },
{ path: '/education', name: 'Education', component: 'EducationPage' },
{ path: '/support', name: 'Support', component: 'SupportPage' },
] satisfies RouteInfo[],
/** Routes créateur — auth requise, DashboardLayout */
creator: [
{ path: '/analytics', name: 'Analytics', component: 'AnalyticsPage' },
{ path: '/sell', name: 'Seller Dashboard', component: 'SellerDashboardPage' },
{ path: '/distribution', name: 'Distribution', component: 'DistributionPage' },
{ path: '/gear', name: 'Gear', component: 'GearPage' },
{ path: '/live/go-live', name: 'Go Live', component: 'GoLivePage' },
{ path: '/developer', name: 'Developer', component: 'DeveloperDashboardPage' },
{ path: '/webhooks', name: 'Webhooks', component: 'WebhooksPage' },
] satisfies RouteInfo[],
/** Routes admin — auth requise, DashboardLayout (autorisations backend) */
admin: [
{ path: '/admin', name: 'Admin Dashboard', component: 'AdminDashboardPage' },
{ path: '/admin/moderation', name: 'Moderation', component: 'AdminModerationPage' },
{ path: '/admin/platform', name: 'Platform', component: 'AdminPlatformPage' },
{ path: '/admin/transfers', name: 'Transfers', component: 'AdminTransfersPage' },
{ path: '/admin/roles', name: 'Roles', component: 'RolesPage' },
] satisfies RouteInfo[],
/** Routes d'erreur */
error: [
{ path: '/404', name: 'Not Found', component: 'NotFoundPage' },
{ path: '/500', name: 'Server Error', component: 'ServerErrorPage' },
] satisfies RouteInfo[],
} as const;
/** Toutes les routes protégées (listener + creator + admin) */
export const ALL_PROTECTED_ROUTES = [
...ROUTES.listener,
...ROUTES.creator,
...ROUTES.admin,
] as const;
/** Toutes les routes (public + protégées + erreur) */
export const ALL_ROUTES = [
...ROUTES.public,
...ROUTES.publicStandalone,
...ROUTES.listener,
...ROUTES.creator,
...ROUTES.admin,
...ROUTES.error,
] as const;
// =============================================================================
// COMPOSANTS INTERACTIFS — sélecteurs et états attendus
// =============================================================================
export const INTERACTIVE_COMPONENTS = {
buttons: {
default: {
selector: 'button:not([disabled])',
description: 'Primary CTA — bg-primary text-primary-foreground',
expectedHover: { cursor: 'pointer' },
expectedFocus: { outline: '2px solid', outlineOffset: '2px' },
expectedDisabled: { opacity: '0.5', pointerEvents: 'none' },
activeScale: '0.95',
},
variants: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
primary: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-border bg-transparent hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
glass: 'bg-white/10 backdrop-blur-xl border border-white/10 text-white hover:bg-white/15',
},
sizes: {
default: { height: '40px', paddingX: '16px', paddingY: '8px' },
sm: { height: '36px', paddingX: '16px', borderRadius: '9999px', fontSize: '12px' },
lg: { height: '48px', paddingX: '32px', borderRadius: '9999px', fontSize: '16px' },
icon: { height: '40px', width: '40px', borderRadius: '9999px' },
},
},
inputs: {
text: {
selector: 'input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="number"], input[type="tel"], input[type="url"]',
height: '44px', // h-11
borderRadius: '12px', // rounded-xl
expectedFocus: { ring: '2px', ringColor: 'var(--ring)' },
expectedError: { borderColor: 'var(--destructive)' },
expectedDisabled: { opacity: '0.5', cursor: 'not-allowed' },
},
textarea: {
selector: 'textarea',
minHeight: '96px', // min-h-24
borderRadius: '8px', // rounded-lg
},
checkbox: {
selector: 'input[type="checkbox"]',
size: '20px', // w-5 h-5
},
select: {
selector: 'select, [role="listbox"], [role="combobox"]',
},
},
modals: {
backdrop: {
selector: '[data-dialog-backdrop], .fixed.inset-0',
expectedBg: 'rgba(0, 0, 0, 0.6)',
expectedBlur: 'blur(4px)',
},
content: {
selector: '[role="dialog"]',
expectedZIndex: Z_INDEX.modal,
sizes: {
sm: 'max-w-sm',
md: 'max-w-md',
lg: 'max-w-2xl',
xl: 'max-w-4xl',
full: 'max-w-full',
},
},
},
toasts: {
selector: '[data-testid="toast-alert"]',
types: {
success: { icon: 'CheckCircle', colorClass: 'lime' },
error: { icon: 'XCircle', colorClass: 'vermillion' },
info: { icon: 'Info', colorClass: 'cyan' },
},
animation: 'animate-slide-in-right',
autoDismiss: 4000,
minWidth: '288px', // min-w-72
maxWidth: '448px', // max-w-md
},
sidebar: {
selector: '[data-testid="app-sidebar"]',
widthExpanded: '240px',
widthCollapsed: '80px',
zIndex: 95,
overlayZIndex: 90,
responsiveBreakpoint: 1024, // lg
},
playerBar: {
selector: '[data-testid="global-player"]',
height: '80px',
zIndex: 200,
controls: {
playPause: 'button[aria-label*="Play"], button[aria-label*="Pause"], button[aria-label*="Lire"]',
previous: 'button[aria-label*="Previous"], button[aria-label*="Précédent"]',
next: 'button[aria-label*="Next"], button[aria-label*="Suivant"]',
shuffle: 'button[aria-label*="Shuffle"], button[aria-label*="Aléatoire"]',
repeat: 'button[aria-label*="Repeat"], button[aria-label*="Répéter"]',
progress: '[role="slider"][aria-label="Progression"]',
volume: '[data-testid="volume-control"] [role="slider"]',
},
},
cards: {
track: {
selector: '[role="article"]',
description: 'TrackCard — used in TrackGrid on /feed, /discover',
},
generic: {
selector: '[class*="card"], [data-variant="card"]',
defaultBorderRadius: '12px', // rounded-lg
},
},
dropdowns: {
container: {
selector: '[role="menu"], [role="listbox"]',
expectedZIndex: Z_INDEX.dropdown,
borderRadius: '12px', // rounded-xl
},
},
slider: {
selector: '[role="slider"]',
trackHeight: '4px', // h-1
trackHoverHeight: '6px', // group-hover:h-1.5
thumbSize: '20px', // h-5 w-5
thumbHiddenUntilHover: true,
},
avatar: {
selector: '[class*="avatar"], img[class*="rounded-full"]',
sizes: {
xs: '24px', // w-6 h-6
sm: '32px', // w-8 h-8
md: '40px', // w-10 h-10
lg: '48px', // w-12 h-12
xl: '64px', // w-16 h-16
'2xl': '96px', // w-24 h-24
'3xl': '128px', // w-32 h-32
},
},
badge: {
selector: '[class*="badge"]',
variants: ['cyan', 'magenta', 'lime', 'gold'],
sizes: {
sm: { paddingX: '8px', paddingY: '2px', fontSize: '12px' },
md: { paddingX: '10px', paddingY: '2px', fontSize: '12px' },
lg: { paddingX: '16px', paddingY: '4px', fontSize: '12px' },
},
},
switch: {
selector: '[role="switch"]',
width: '44px', // w-11
height: '24px', // h-6
thumbSize: '20px', // h-5 w-5
},
} as const;
// =============================================================================
// ACCESSIBILITÉ — critères WCAG
// =============================================================================
export const WCAG = {
/** Ratio de contraste minimum (WCAG AA) */
contrastMinNormal: 4.5,
/** Ratio de contraste minimum pour grand texte (>= 18px ou >= 14px bold) */
contrastMinLarge: 3.0,
/** Taille minimale de cible tactile (WCAG 2.5.8) */
minTouchTarget: 44,
/** Focus doit être visible */
focusVisible: {
outlineWidth: '2px',
outlineStyle: 'solid',
outlineOffset: '2px',
},
} as const;
// =============================================================================
// SÉLECTEURS DE TEST RÉUTILISABLES
// =============================================================================
export const SELECTORS = {
// Layout
sidebar: '[data-testid="app-sidebar"]',
header: 'header, [data-testid="app-header"], [role="banner"]',
playerBar: '[data-testid="global-player"]',
mainContent: 'main, [role="main"]',
// Auth
loginForm: '[data-testid="login-form"]',
registerForm: '[data-testid="register-form"]',
loginSubmit: '[data-testid="login-submit"]',
// Player
audioElement: '[data-testid="audio-element"]',
progressBar: '[role="slider"][aria-label="Progression"]',
volumeSlider: '[data-testid="volume-control"] [role="slider"]',
// Content
trackCard: '[role="article"]',
searchInput: '[data-testid="search-input"], [role="search"] input, input[type="search"], input[role="searchbox"]',
// Feedback
toast: '[data-testid="toast-alert"]',
dialog: '[role="dialog"]',
alert: '[role="alert"]',
// Interactive elements (pour overlap/hover/focus tests)
allInteractive: 'button, a, input, select, textarea, [role="button"], [role="link"], [role="tab"], [role="switch"], [role="slider"], [tabindex]:not([tabindex="-1"])',
allButtons: 'button:visible, [role="button"]:visible',
allLinks: 'a[href]:visible',
allInputs: 'input:visible, textarea:visible, select:visible',
} as const;
// =============================================================================
// COMPTES DE TEST (seed)
// =============================================================================
export const TEST_USERS = {
listener: {
email: 'user@veza.music',
password: 'User123!',
username: 'music_fan',
},
creator: {
email: 'artist@veza.music',
password: 'Artist123!',
username: 'top_artist',
},
admin: {
email: 'admin@veza.music',
password: 'Admin123!',
username: 'admin_veza',
},
moderator: {
email: 'mod@veza.music',
password: 'Mod123!',
username: 'mod_veza',
},
} as const;
// =============================================================================
// TIMEOUTS POUR LES TESTS
// =============================================================================
export const TIMEOUTS = {
navigation: 15_000,
action: 5_000,
animation: 1_000,
networkIdle: 10_000,
hoverTransition: 250,
focusTransition: 150,
toastDismiss: 5_000,
} as const;