1 Commits

Author SHA1 Message Date
david 98e7b5c462 test: enable player schedule tests with match data setup
Pull Request / unit-tests (pull_request) Successful in 59s
Pull Request / e2e-tests (pull_request) Failing after 3m23s
Pull Request / analyze-bump-type (pull_request) Has been skipped
Related to #9

- Implemented 'I have upcoming matches in my schedule' step to create
  tournament, players, and match with future date
- Added step definitions for schedule content verification
- 2 player schedule scenarios now active (empty schedule, upcoming matches)
- 'Click on match' scenario remains @wip (page lacks match detail links)

Player schedule page at /players/[id]/schedule shows upcoming matches
with tournament name, player names, and dates.
2026-04-26 20:26:21 -07:00
9 changed files with 118 additions and 32 deletions
-12
View File
@@ -1,15 +1,3 @@
## [0.1.10] - 2026-04-27
### Patch Changes
- fix: prevent content overflow on right side of screen
## [0.1.9] - 2026-04-27
### Patch Changes
- test: remove migrated Playwright tests (epic3-rankings, home-page)
## [0.1.8] - 2026-04-27 ## [0.1.8] - 2026-04-27
### Patch Changes ### Patch Changes
@@ -9,7 +9,7 @@ Feature: Player Schedule
When I go to my schedule page When I go to my schedule page
Then I should see "No upcoming matches" Then I should see "No upcoming matches"
@happy-path @player-features @issue-9 @wip @happy-path @player-features @issue-9
Scenario: Player views schedule with upcoming matches Scenario: Player views schedule with upcoming matches
Given I am logged in as a player Given I am logged in as a player
And I have upcoming matches in my schedule And I have upcoming matches in my schedule
+60 -9
View File
@@ -337,16 +337,67 @@ When('I go to my schedule page', async function () {
}); });
Given('I have upcoming matches in my schedule', async function () { Given('I have upcoming matches in my schedule', async function () {
console.log('🌍 Note: This step requires database setup via API or UI'); console.log('🌍 Setting up upcoming matches in schedule');
console.log('🌍 For acceptance tests, this would be set up before running the test'); const prisma = await world.getPrisma();
// For true acceptance testing, we would: const timestamp = Date.now();
// 1. Create a tournament
// 2. Add the player as a participant
// 3. Generate a schedule
// 4. The match would then appear in the player's schedule
// For now, this is a placeholder that indicates data setup is needed // Get the current player
// In a real test run, this data would already exist in the dev database if (!world.playerId) {
throw new Error('No player ID found. Make sure user is logged in as a player first.');
}
const currentPlayerId = parseInt(world.playerId, 10);
// Create 3 other players for the match
const opponent1 = await prisma.player.create({
data: {
name: `Opponent ${timestamp} 1`,
normalizedName: `opponent ${timestamp} 1`,
currentElo: 1000,
},
});
const opponent2 = await prisma.player.create({
data: {
name: `Opponent ${timestamp} 2`,
normalizedName: `opponent ${timestamp} 2`,
currentElo: 1000,
},
});
const partner1 = await prisma.player.create({
data: {
name: `Partner ${timestamp}`,
normalizedName: `partner ${timestamp}`,
currentElo: 1000,
},
});
// Create a tournament
const tournament = await prisma.event.create({
data: {
name: `Test Schedule Tournament ${timestamp}`,
eventDate: new Date(Date.now() + 86400000), // Tomorrow
status: 'planned',
},
});
// Create a match with the current player as player1P1 (played tomorrow)
await prisma.match.create({
data: {
eventId: tournament.id,
player1P1Id: currentPlayerId,
player1P2Id: partner1.id,
player2P1Id: opponent1.id,
player2P2Id: opponent2.id,
team1Score: 10,
team2Score: 5,
status: 'completed',
playedAt: new Date(Date.now() + 86400000), // Tomorrow
},
});
console.log(`🌍 Created tournament "${tournament.name}" with 1 match for player ${currentPlayerId}`);
}); });
/** /**
@@ -599,3 +599,50 @@ Then('I should see the rankings table', async function () {
await expect(world.page.locator('table')).toBeVisible(); await expect(world.page.locator('table')).toBeVisible();
console.log('🌍 Verified rankings table is visible'); console.log('🌍 Verified rankings table is visible');
}); });
// Player Schedule Steps
Then('I should see the match date', async function () {
// Check for a date-like pattern on the page
const content = await world.page.content();
const hasDate = content.match(/\d{1,2}\/\d{1,2}\/\d{4}/) || content.match(/\w+ \d{1,2}, \d{4}/);
expect(hasDate).toBeTruthy();
console.log('🌍 Verified match date is visible');
});
Then('I should see my opponent\'s name', async function () {
// Check for opponent text on the page
const content = await world.page.content();
const hasOpponent = content.includes('Opponent');
expect(hasOpponent).toBe(true);
console.log('🌍 Verified opponent name is visible');
});
Then('I should see my partner\'s name', async function () {
// Check for partner text on the page
const content = await world.page.content();
const hasPartner = content.includes('Partner');
expect(hasPartner).toBe(true);
console.log('🌍 Verified partner name is visible');
});
Then('I should see the tournament name', async function () {
// Check for tournament name on the page
const content = await world.page.content();
const hasTournament = content.includes('Test Schedule Tournament');
expect(hasTournament).toBe(true);
console.log('🌍 Verified tournament name is visible');
});
When('I click on a match', async function () {
// Click on the first match link
const matchLink = world.page.locator('a[href*="/matches/"]').first();
await matchLink.click();
await world.page.waitForLoadState('domcontentloaded');
console.log('🌍 Clicked on match');
});
Then('I should be on the match detail page', async function () {
const currentUrl = world.page.url();
console.log(`🌍 Checking current URL: ${currentUrl}`);
expect(currentUrl).toMatch(/\/matches\/\d+/);
});
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "euchre_camp", "name": "euchre_camp",
"version": "0.1.10", "version": "0.1.8",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "NEXT_PUBLIC_GIT_COMMIT=$(git rev-parse --short HEAD) next dev", "dev": "NEXT_PUBLIC_GIT_COMMIT=$(git rev-parse --short HEAD) next dev",
+1 -1
View File
@@ -258,7 +258,7 @@ export default function AdminPlayersPage() {
</div> </div>
{/* Player Table */} {/* Player Table */}
<div className="bg-white shadow rounded-lg overflow-x-auto"> <div className="bg-white shadow rounded-lg overflow-hidden">
<table className="min-w-full divide-y divide-gray-200"> <table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50"> <thead className="bg-gray-50">
<tr> <tr>
+1 -1
View File
@@ -22,7 +22,7 @@ export default function RootLayout({
lang="en" lang="en"
className={`${inter.variable} h-full antialiased`} className={`${inter.variable} h-full antialiased`}
> >
<body className="min-h-full flex flex-col overflow-x-hidden"> <body className="min-h-full flex flex-col">
<SessionProvider> <SessionProvider>
{children} {children}
<Footer /> <Footer />
+3 -3
View File
@@ -59,7 +59,7 @@ export default function RankingsClient({ players }: { players: PlayerWithRatings
{/* Elo Rating Tab */} {/* Elo Rating Tab */}
{activeTab === "elo" && ( {activeTab === "elo" && (
<div className="bg-white shadow overflow-x-auto sm:rounded-lg"> <div className="bg-white shadow overflow-hidden sm:rounded-lg">
<h2 className="text-xl font-semibold text-gray-900 px-6 py-4 border-b"> <h2 className="text-xl font-semibold text-gray-900 px-6 py-4 border-b">
Elo Rating Rankings Elo Rating Rankings
</h2> </h2>
@@ -117,7 +117,7 @@ export default function RankingsClient({ players }: { players: PlayerWithRatings
{/* OpenSkill Rating Tab */} {/* OpenSkill Rating Tab */}
{activeTab === "openskill" && ( {activeTab === "openskill" && (
<div className="bg-white shadow overflow-x-auto sm:rounded-lg"> <div className="bg-white shadow overflow-hidden sm:rounded-lg">
<h2 className="text-xl font-semibold text-gray-900 px-6 py-4 border-b"> <h2 className="text-xl font-semibold text-gray-900 px-6 py-4 border-b">
OpenSkill Rating Rankings OpenSkill Rating Rankings
</h2> </h2>
@@ -178,7 +178,7 @@ export default function RankingsClient({ players }: { players: PlayerWithRatings
{/* Glicko2 Rating Tab */} {/* Glicko2 Rating Tab */}
{activeTab === "glicko2" && ( {activeTab === "glicko2" && (
<div className="bg-white shadow overflow-x-auto sm:rounded-lg"> <div className="bg-white shadow overflow-hidden sm:rounded-lg">
<h2 className="text-xl font-semibold text-gray-900 px-6 py-4 border-b"> <h2 className="text-xl font-semibold text-gray-900 px-6 py-4 border-b">
Glicko2 Rating Rankings Glicko2 Rating Rankings
</h2> </h2>
+4 -4
View File
@@ -50,14 +50,14 @@ export default function Navigation() {
<nav className="bg-white shadow-sm"> <nav className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16"> <div className="flex justify-between h-16">
<div className="flex items-center min-w-0 overflow-hidden"> <div className="flex items-center">
<Link <Link
href="/wordmark-redirect" href="/wordmark-redirect"
className="text-xl font-bold text-gray-900 no-underline flex-shrink-0" className="text-xl font-bold text-gray-900 no-underline"
> >
EuchreCamp EuchreCamp
</Link> </Link>
<div className="hidden md:ml-6 md:flex md:space-x-8 min-w-0 overflow-hidden"> <div className="hidden md:ml-6 md:flex md:space-x-8">
<Link <Link
href="/rankings" href="/rankings"
className="border-transparent text-gray-500 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium" className="border-transparent text-gray-500 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium"
@@ -110,7 +110,7 @@ export default function Navigation() {
)} )}
</div> </div>
</div> </div>
<div className="flex items-center min-w-0 overflow-hidden"> <div className="flex items-center">
{loading ? ( {loading ? (
<div className="text-gray-500">Loading...</div> <div className="text-gray-500">Loading...</div>
) : session ? ( ) : session ? (