293 lines
10 KiB
TypeScript
293 lines
10 KiB
TypeScript
/**
|
|
* Issue #22: Team Configuration Options
|
|
* Acceptance Test: Tournament Creation with Team Configuration
|
|
*
|
|
* User Story: As a tournament admin, I want to configure team creation options for round robin tournaments
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import { prisma } from '@/lib/prisma';
|
|
const BASE_URL = process.env.CI ? 'https://euchre-ci.notsosm.art' : 'http://localhost:3000';
|
|
|
|
function getTestCredentials() {
|
|
const timestamp = Date.now();
|
|
return {
|
|
email: `config-admin-${timestamp}@example.com`,
|
|
password: 'AdminPassword123!',
|
|
name: `Config Admin ${timestamp}`,
|
|
};
|
|
}
|
|
|
|
test.describe.serial('Issue #22: Team Configuration', () => {
|
|
let testEmail: string;
|
|
let testPassword: string;
|
|
let tournamentId: number;
|
|
|
|
test.beforeAll(async () => {
|
|
const credentials = getTestCredentials();
|
|
testEmail = credentials.email;
|
|
testPassword = credentials.password;
|
|
|
|
// Create admin user via API
|
|
const response = await fetch(`${BASE_URL}/api/auth/sign-up/email`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Origin: BASE_URL,
|
|
},
|
|
body: JSON.stringify({
|
|
email: testEmail,
|
|
password: testPassword,
|
|
name: credentials.name,
|
|
}),
|
|
});
|
|
|
|
console.log('Config test user creation response:', response.status);
|
|
|
|
// Update user to club_admin role
|
|
const user = await prisma.user.findUnique({
|
|
where: { email: testEmail },
|
|
});
|
|
if (user) {
|
|
await prisma.user.update({
|
|
where: { id: user.id },
|
|
data: { role: 'club_admin' },
|
|
});
|
|
}
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
try {
|
|
// Clean up tournament if created
|
|
if (tournamentId) {
|
|
await prisma.bracketMatchup.deleteMany({ where: { eventId: tournamentId } });
|
|
await prisma.tournamentRound.deleteMany({ where: { eventId: tournamentId } });
|
|
await prisma.eventParticipant.deleteMany({ where: { eventId: tournamentId } });
|
|
await prisma.event.delete({ where: { id: tournamentId } }).catch(() => {});
|
|
}
|
|
|
|
// Clean up user
|
|
const user = await prisma.user.findUnique({ where: { email: testEmail } });
|
|
if (user) {
|
|
await prisma.user.delete({ where: { id: user.id } });
|
|
}
|
|
} catch (error) {
|
|
console.error('Cleanup error:', error);
|
|
}
|
|
});
|
|
|
|
test('Tournament creation form shows team configuration options', async ({ page }) => {
|
|
// Login
|
|
await page.goto('/auth/login');
|
|
await page.fill('input[name="email"]', testEmail);
|
|
await page.fill('input[name="password"]', testPassword);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL(/\/(admin|players)/, { timeout: 10000 });
|
|
|
|
// Navigate to tournament creation
|
|
await page.goto('/admin/tournaments/new');
|
|
|
|
// Select Round Robin format
|
|
await page.selectOption('select[name="format"]', 'round_robin');
|
|
|
|
// Check that team configuration section is visible
|
|
await expect(page.locator('text=Team Configuration')).toBeVisible();
|
|
|
|
// Check team durability options
|
|
await expect(page.locator('input[name="teamDurability"][value="permanent"]')).toBeVisible();
|
|
await expect(page.locator('input[name="teamDurability"][value="variable"]')).toBeVisible();
|
|
await expect(page.locator('input[name="teamDurability"][value="per_round"]')).toBeVisible();
|
|
});
|
|
|
|
test('Create tournament with permanent teams', async ({ page }) => {
|
|
// Login
|
|
await page.goto('/auth/login');
|
|
await page.fill('input[name="email"]', testEmail);
|
|
await page.fill('input[name="password"]', testPassword);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL(/\/(admin|players)/, { timeout: 10000 });
|
|
|
|
// Navigate to tournament creation
|
|
await page.goto('/admin/tournaments/new');
|
|
|
|
// Fill in tournament details
|
|
await page.fill('input[name="name"]', `Test Tournament ${Date.now()}`);
|
|
await page.selectOption('select[name="format"]', 'round_robin');
|
|
|
|
// Select permanent teams
|
|
await page.click('input[name="teamDurability"][value="permanent"]');
|
|
|
|
// Move to step 2 (Participants)
|
|
await page.click('button:has-text("Next")');
|
|
|
|
// Add players using the player creation feature
|
|
const playerName1 = `Player ${Date.now()}`;
|
|
const playerName2 = `Player ${Date.now() + 1}`;
|
|
const playerName3 = `Player ${Date.now() + 2}`;
|
|
const playerName4 = `Player ${Date.now() + 3}`;
|
|
|
|
// Create first player
|
|
await page.fill('input[placeholder*="Search"]', playerName1);
|
|
await page.waitForTimeout(500);
|
|
await page.click(`text=+ Create "${playerName1}" as new player`);
|
|
await page.fill('input[placeholder*="Enter player name"]', playerName1);
|
|
await page.click('button:has-text("Add")');
|
|
|
|
// Create second player
|
|
await page.fill('input[placeholder*="Search"]', playerName2);
|
|
await page.waitForTimeout(500);
|
|
await page.click(`text=+ Create "${playerName2}" as new player`);
|
|
await page.fill('input[placeholder*="Enter player name"]', playerName2);
|
|
await page.click('button:has-text("Add")');
|
|
|
|
// Create third player
|
|
await page.fill('input[placeholder*="Search"]', playerName3);
|
|
await page.waitForTimeout(500);
|
|
await page.click(`text=+ Create "${playerName3}" as new player`);
|
|
await page.fill('input[placeholder*="Enter player name"]', playerName3);
|
|
await page.click('button:has-text("Add")');
|
|
|
|
// Create fourth player
|
|
await page.fill('input[placeholder*="Search"]', playerName4);
|
|
await page.waitForTimeout(500);
|
|
await page.click(`text=+ Create "${playerName4}" as new player`);
|
|
await page.fill('input[placeholder*="Enter player name"]', playerName4);
|
|
await page.click('button:has-text("Add")');
|
|
|
|
// Submit the form
|
|
await page.click('button:has-text("Create Tournament")');
|
|
|
|
// Wait for redirect to schedule page
|
|
await page.waitForURL(/\/schedule$/, { timeout: 10000 });
|
|
|
|
// Verify tournament was created
|
|
const url = page.url();
|
|
const match = url.match(/\/admin\/tournaments\/(\d+)\/schedule/);
|
|
expect(match).toBeTruthy();
|
|
tournamentId = parseInt(match![1]);
|
|
|
|
// Verify team configuration was saved
|
|
const tournament = await prisma.event.findUnique({
|
|
where: { id: tournamentId },
|
|
});
|
|
expect(tournament).toBeTruthy();
|
|
expect(tournament?.teamDurability).toBe('permanent');
|
|
});
|
|
|
|
test('Create tournament with variable teams and partner rotation', async ({ page }) => {
|
|
// Login
|
|
await page.goto('/auth/login');
|
|
await page.fill('input[name="email"]', testEmail);
|
|
await page.fill('input[name="password"]', testPassword);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL(/\/(admin|players)/, { timeout: 10000 });
|
|
|
|
// Navigate to tournament creation
|
|
await page.goto('/admin/tournaments/new');
|
|
|
|
// Fill in tournament details
|
|
await page.fill('input[name="name"]', `Variable Teams Tournament ${Date.now()}`);
|
|
await page.selectOption('select[name="format"]', 'round_robin');
|
|
|
|
// Select variable teams
|
|
await page.click('input[name="teamDurability"][value="variable"]');
|
|
|
|
// Check that partner rotation options appear
|
|
await expect(page.locator('text=Partner Rotation Strategy')).toBeVisible();
|
|
|
|
// Select minimize repeat partners
|
|
await page.click('input[name="partnerRotation"][value="minimize_repeat"]');
|
|
|
|
// Move to step 2 (Participants)
|
|
await page.click('button:has-text("Next")');
|
|
|
|
// Create players
|
|
const playerName1 = `VarPlayer ${Date.now()}`;
|
|
const playerName2 = `VarPlayer ${Date.now() + 1}`;
|
|
const playerName3 = `VarPlayer ${Date.now() + 2}`;
|
|
const playerName4 = `VarPlayer ${Date.now() + 3}`;
|
|
|
|
for (const name of [playerName1, playerName2, playerName3, playerName4]) {
|
|
await page.fill('input[placeholder*="Search"]', name);
|
|
await page.waitForTimeout(500);
|
|
await page.click(`text=+ Create "${name}" as new player`);
|
|
await page.fill('input[placeholder*="Enter player name"]', name);
|
|
await page.click('button:has-text("Add")');
|
|
}
|
|
|
|
// Submit the form
|
|
await page.click('button:has-text("Create Tournament")');
|
|
|
|
// Wait for redirect to schedule page
|
|
await page.waitForURL(/\/schedule$/, { timeout: 10000 });
|
|
|
|
// Verify tournament was created
|
|
const url = page.url();
|
|
const match = url.match(/\/admin\/tournaments\/(\d+)\/schedule/);
|
|
expect(match).toBeTruthy();
|
|
tournamentId = parseInt(match![1]);
|
|
|
|
// Verify team configuration was saved
|
|
const tournament = await prisma.event.findUnique({
|
|
where: { id: tournamentId },
|
|
});
|
|
expect(tournament).toBeTruthy();
|
|
expect(tournament?.teamDurability).toBe('variable');
|
|
expect(tournament?.partnerRotation).toBe('minimize_repeat');
|
|
});
|
|
|
|
test('Edit tournament team configuration', async ({ page }) => {
|
|
// First create a tournament with default settings
|
|
const createResponse = await fetch(`${BASE_URL}/api/tournaments`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Origin: BASE_URL,
|
|
},
|
|
body: JSON.stringify({
|
|
name: `Edit Test Tournament ${Date.now()}`,
|
|
format: 'round_robin',
|
|
}),
|
|
});
|
|
|
|
const createData = await createResponse.json();
|
|
tournamentId = createData.tournament.id;
|
|
|
|
// Login
|
|
await page.goto('/auth/login');
|
|
await page.fill('input[name="email"]', testEmail);
|
|
await page.fill('input[name="password"]', testPassword);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL(/\/(admin|players)/, { timeout: 10000 });
|
|
|
|
// Navigate to edit tournament page
|
|
await page.goto(`/admin/tournaments/${tournamentId}/edit`);
|
|
|
|
// Check that team configuration section is visible
|
|
await expect(page.locator('text=Team Configuration')).toBeVisible();
|
|
|
|
// Change team durability to variable
|
|
await page.click('input[name="teamDurability"][value="variable"]');
|
|
|
|
// Check that partner rotation options appear
|
|
await expect(page.locator('text=Partner Rotation Strategy')).toBeVisible();
|
|
|
|
// Select maximize even partners
|
|
await page.click('input[name="partnerRotation"][value="maximize_even"]');
|
|
|
|
// Save changes
|
|
await page.click('button:has-text("Save Changes")');
|
|
|
|
// Wait for success message
|
|
await expect(page.locator('text=Tournament updated successfully!')).toBeVisible();
|
|
|
|
// Verify changes were saved
|
|
const tournament = await prisma.event.findUnique({
|
|
where: { id: tournamentId },
|
|
});
|
|
expect(tournament).toBeTruthy();
|
|
expect(tournament?.teamDurability).toBe('variable');
|
|
expect(tournament?.partnerRotation).toBe('maximize_even');
|
|
});
|
|
});
|