import { createClient } from '@supabase/supabase-js'; import * as fs from 'fs'; import * as path from 'path'; /** * Global teardown: delete all test-created data from Supabase. * Runs after all Playwright tests complete. * * Matches documents/folders/kanbans by name prefixes used in tests: * "Test Folder", "Test Doc", "Test Board", "Nav Folder", "Rename Me", "Renamed" * Matches kanban boards by name prefix: "PW Board", "Board A", "Board B" * Matches org_invites by email pattern: "playwright-test-*@example.com" * Matches org_roles by name prefix: "Tester" */ // Load .env manually since we're outside Vite function loadEnv() { try { const envPath = path.resolve(process.cwd(), '.env'); const content = fs.readFileSync(envPath, 'utf-8'); for (const line of content.split('\n')) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const eqIdx = trimmed.indexOf('='); if (eqIdx === -1) continue; const key = trimmed.slice(0, eqIdx); const value = trimmed.slice(eqIdx + 1); if (!process.env[key]) process.env[key] = value; } } catch { /* .env not found - rely on process.env */ } } loadEnv(); const SUPABASE_URL = process.env.PUBLIC_SUPABASE_URL || ''; const SUPABASE_KEY = process.env.PUBLIC_SUPABASE_ANON_KEY || ''; // Name prefixes used by tests when creating data const DOC_PREFIXES = ['Test Folder', 'Test Doc', 'Test Board', 'Nav Folder', 'Rename Me', 'Renamed']; const BOARD_PREFIXES = ['PW Board', 'PW Card Board', 'PW Detail Board', 'Board A', 'Board B']; const ROLE_PREFIX = 'Tester'; const TAG_PREFIX = 'PW Tag'; const EVENT_PREFIXES = ['PW Event', 'PW Detail', 'PW Delete', 'PW Test Event', 'PW Finance']; const SPONSOR_PREFIXES = ['PW Sponsor']; const CONTACT_PREFIXES = ['PW Contact', 'PW Vendor']; const INVITE_EMAIL_PATTERN = 'playwright-test-%@example.com'; export default async function globalTeardown() { if (!SUPABASE_KEY) { console.log('[cleanup] No SUPABASE_ANON_KEY - skipping cleanup'); return; } // Authenticate using the test user credentials directly const supabase = createClient(SUPABASE_URL, SUPABASE_KEY); const { error: authError } = await supabase.auth.signInWithPassword({ email: 'tipilan@ituk.ee', password: 'gu&u6QTMbJK7nT', }); if (authError) { console.log('[cleanup] Auth failed - skipping cleanup:', authError.message); return; } // Get the org ID for root-test const { data: org } = await supabase .from('organizations') .select('id') .eq('slug', 'root-test') .single(); if (!org) { console.log('[cleanup] root-test org not found - skipping cleanup'); return; } const orgId = org.id; let totalDeleted = 0; // 1. Delete test documents (folders, docs, kanbans) for (const prefix of DOC_PREFIXES) { const { data: docs } = await supabase .from('documents') .select('id') .eq('org_id', orgId) .ilike('name', `${prefix}%`); if (docs && docs.length > 0) { const ids = docs.map(d => d.id); const { error } = await supabase .from('documents') .delete() .in('id', ids); if (!error) { totalDeleted += docs.length; } else { console.log(`[cleanup] Failed to delete docs with prefix "${prefix}":`, error.message); } } } // 2. Delete test kanban boards for (const prefix of BOARD_PREFIXES) { const { data: boards } = await supabase .from('kanban_boards') .select('id') .eq('org_id', orgId) .ilike('name', `${prefix}%`); if (boards && boards.length > 0) { const ids = boards.map(b => b.id); const { error } = await supabase .from('kanban_boards') .delete() .in('id', ids); if (!error) { totalDeleted += boards.length; } else { console.log(`[cleanup] Failed to delete boards with prefix "${prefix}":`, error.message); } } } // 3. Delete test invites (playwright-test-*@example.com) const { data: invites } = await supabase .from('org_invites') .select('id') .eq('org_id', orgId) .ilike('email', INVITE_EMAIL_PATTERN); if (invites && invites.length > 0) { const ids = invites.map(i => i.id); await supabase.from('org_invites').delete().in('id', ids); totalDeleted += invites.length; } // 4. Delete test roles const { data: roles } = await supabase .from('org_roles') .select('id') .eq('org_id', orgId) .ilike('name', `${ROLE_PREFIX}%`); if (roles && roles.length > 0) { const ids = roles.map(r => r.id); await supabase.from('org_roles').delete().in('id', ids); totalDeleted += roles.length; } // 5. Delete test tags const { data: tags } = await supabase .from('tags') .select('id') .eq('org_id', orgId) .ilike('name', `${TAG_PREFIX}%`); if (tags && tags.length > 0) { const ids = tags.map(t => t.id); await supabase.from('tags').delete().in('id', ids); totalDeleted += tags.length; } // 6. Delete test calendar events for (const prefix of EVENT_PREFIXES) { const { data: events } = await supabase .from('calendar_events') .select('id') .eq('org_id', orgId) .ilike('title', `${prefix}%`); if (events && events.length > 0) { const ids = events.map(e => e.id); const { error } = await supabase .from('calendar_events') .delete() .in('id', ids); if (!error) { totalDeleted += events.length; } else { console.log(`[cleanup] Failed to delete events with prefix "${prefix}":`, error.message); } } } // 7. Delete test sponsors (via name prefix) for (const prefix of SPONSOR_PREFIXES) { const { data: sponsors } = await (supabase as any) .from('sponsors') .select('id') .ilike('name', `${prefix}%`); if (sponsors && sponsors.length > 0) { const ids = sponsors.map((s: any) => s.id); const { error } = await (supabase as any) .from('sponsors') .delete() .in('id', ids); if (!error) { totalDeleted += sponsors.length; } else { console.log(`[cleanup] Failed to delete sponsors with prefix "${prefix}":`, error.message); } } } // 8. Delete test org contacts (via name prefix) for (const prefix of CONTACT_PREFIXES) { const { data: contacts } = await (supabase as any) .from('org_contacts') .select('id') .eq('org_id', orgId) .ilike('name', `${prefix}%`); if (contacts && contacts.length > 0) { const ids = contacts.map((c: any) => c.id); const { error } = await (supabase as any) .from('org_contacts') .delete() .in('id', ids); if (!error) { totalDeleted += contacts.length; } else { console.log(`[cleanup] Failed to delete contacts with prefix "${prefix}":`, error.message); } } } if (totalDeleted > 0) { console.log(`[cleanup] Deleted ${totalDeleted} test-created items from root-test org`); } else { console.log('[cleanup] No test data to clean up'); } }