Skip to content
Merged
7 changes: 6 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

This is a crowdfunding platform for games with a developer theme. The application uses a Flask backend API with SQLAlchemy ORM for database interactions, and an Astro/Svelte frontend with Tailwind CSS for styling. Please follow these guidelines when contributing:

## Agent notes

- Do not generate summary markdown files upon completion of a task
- Always use absolute paths when running scripts and BASH commands

## Code standards

### Required Before Each Commit
Expand Down Expand Up @@ -46,7 +51,7 @@ This is a crowdfunding platform for games with a developer theme. The applicatio
## Scripts

- Several scripts exist in the `scripts` folder
- Use existing scripts to perform tasks rather than performing them manually
- Always use available scripts to perform tasks rather than performing operations manually
- Existing scripts:
- `scripts/setup-env.sh`: Performs installation of all Python and Node dependencies
- `scripts/run-server-tests.sh`: Calls setup-env, then runs all Python tests
Expand Down
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
],
"github.copilot.nextEditSuggestions.enabled": true,
"editor.tabSize": 4,
"chat.agent.enabled": true
"chat.agent.enabled": true,
"chat.agent.maxRequests": 100,
}
71 changes: 33 additions & 38 deletions client/e2e-tests/games.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,35 @@ test.describe('Game Listing and Navigation', () => {
test('should display games with titles on index page', async ({ page }) => {
await page.goto('/');

// Wait for the games to load
await page.waitForSelector('[data-testid="games-grid"]', { timeout: 10000 });
// Wait for the games to load using a more specific locator
const gamesGrid = page.getByTestId('games-grid');
await expect(gamesGrid).toBeVisible();

// Check that games are displayed
const gameCards = page.locator('[data-testid="game-card"]');
const gameCards = page.getByTestId('game-card');

// Wait for at least one game card to be visible
await expect(gameCards.first()).toBeVisible();

// Check that we have at least one game
const gameCount = await gameCards.count();
expect(gameCount).toBeGreaterThan(0);
expect(await gameCards.count()).toBeGreaterThan(0);

// Check that each game card has a title
const firstGameCard = gameCards.first();
await expect(firstGameCard.locator('[data-testid="game-title"]')).toBeVisible();
await expect(gameCards.first().getByTestId('game-title')).toBeVisible();

// Verify that game titles are not empty
const gameTitle = await firstGameCard.locator('[data-testid="game-title"]').textContent();
expect(gameTitle?.trim()).toBeTruthy();
await expect(gameCards.first().getByTestId('game-title')).not.toBeEmpty();
});

test('should navigate to correct game details page when clicking on a game', async ({ page }) => {
await page.goto('/');

// Wait for games to load
await page.waitForSelector('[data-testid="games-grid"]', { timeout: 10000 });
const gamesGrid = page.getByTestId('games-grid');
await expect(gamesGrid).toBeVisible();

// Get the first game card and its data attributes
const firstGameCard = page.locator('[data-testid="game-card"]').first();
const firstGameCard = page.getByTestId('game-card').first();
const gameId = await firstGameCard.getAttribute('data-game-id');
const gameTitle = await firstGameCard.getAttribute('data-game-title');

Expand All @@ -44,58 +43,55 @@ test.describe('Game Listing and Navigation', () => {
await expect(page).toHaveURL(`/game/${gameId}`);

// Verify the game details page loads
await page.waitForSelector('[data-testid="game-details"]', { timeout: 10000 });
await expect(page.getByTestId('game-details')).toBeVisible();

// Verify the title matches what we clicked on
const detailsTitle = page.locator('[data-testid="game-details-title"]');
await expect(detailsTitle).toHaveText(gameTitle || '');
if (gameTitle) {
await expect(page.getByTestId('game-details-title')).toHaveText(gameTitle);
}
});

test('should display game details with all required information', async ({ page }) => {
// Navigate to a specific game (we'll use game ID 1 as an example)
await page.goto('/game/1');

// Wait for game details to load
await page.waitForSelector('[data-testid="game-details"]', { timeout: 10000 });
await expect(page.getByTestId('game-details')).toBeVisible();

// Check that the game title is present and not empty
const gameTitle = page.locator('[data-testid="game-details-title"]');
const gameTitle = page.getByTestId('game-details-title');
await expect(gameTitle).toBeVisible();
const titleText = await gameTitle.textContent();
expect(titleText?.trim()).toBeTruthy();
await expect(gameTitle).not.toBeEmpty();

// Check that the game description is present and not empty
const gameDescription = page.locator('[data-testid="game-details-description"]');
const gameDescription = page.getByTestId('game-details-description');
await expect(gameDescription).toBeVisible();
const descriptionText = await gameDescription.textContent();
expect(descriptionText?.trim()).toBeTruthy();
await expect(gameDescription).not.toBeEmpty();

// Check that either publisher or category (or both) are present
const publisherExists = await page.locator('[data-testid="game-details-publisher"]').isVisible();
const categoryExists = await page.locator('[data-testid="game-details-category"]').isVisible();
expect(publisherExists && categoryExists).toBeTruthy();
const publisherExists = await page.getByTestId('game-details-publisher').isVisible();
const categoryExists = await page.getByTestId('game-details-category').isVisible();
expect(publisherExists || categoryExists).toBeTruthy();
Copy link

Copilot AI Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic should check that both publisher AND category exist, not OR. The comment above states 'Check that either publisher or category (or both) are present' but the test expects both to be present based on the original logic.

See below for a potential fix:

    // Check that both publisher and category are present
    const publisherExists = await page.getByTestId('game-details-publisher').isVisible();
    const categoryExists = await page.getByTestId('game-details-category').isVisible();
    expect(publisherExists && categoryExists).toBeTruthy();

Copilot uses AI. Check for mistakes.

// If publisher exists, check it has content
if (publisherExists) {
const publisherText = await page.locator('[data-testid="game-details-publisher"]').textContent();
expect(publisherText?.trim()).toBeTruthy();
await expect(page.getByTestId('game-details-publisher')).not.toBeEmpty();
}

// If category exists, check it has content
if (categoryExists) {
const categoryText = await page.locator('[data-testid="game-details-category"]').textContent();
expect(categoryText?.trim()).toBeTruthy();
await expect(page.getByTestId('game-details-category')).not.toBeEmpty();
}
});

test('should display a button to back the game', async ({ page }) => {
await page.goto('/game/1');

// Wait for game details to load
await page.waitForSelector('[data-testid="game-details"]', { timeout: 10000 });
await expect(page.getByTestId('game-details')).toBeVisible();

// Check that the back game button is present
const backButton = page.locator('[data-testid="back-game-button"]');
const backButton = page.getByTestId('back-game-button');
await expect(backButton).toBeVisible();
await expect(backButton).toContainText('Support This Game');

Expand All @@ -107,25 +103,24 @@ test.describe('Game Listing and Navigation', () => {
await page.goto('/game/1');

// Wait for the page to load
await page.waitForSelector('[data-testid="game-details"]', { timeout: 10000 });
await expect(page.getByTestId('game-details')).toBeVisible();

// Find and click the back to all games link
const backLink = page.locator('a:has-text("Back to all games")');
// Find and click the back to all games link using a more semantic locator
const backLink = page.getByRole('link', { name: /back to all games/i });
await expect(backLink).toBeVisible();
await backLink.click();

// Verify we're back on the home page
await expect(page).toHaveURL('/');
await page.waitForSelector('[data-testid="games-grid"]', { timeout: 10000 });
await expect(page.getByTestId('games-grid')).toBeVisible();
});

test('should handle navigation to non-existent game gracefully', async ({ page }) => {
// Navigate to a game that doesn't exist
await page.goto('/game/99999');
const response = await page.goto('/game/99999');

// The page should load without crashing
// Check if there's an error message or if it handles gracefully
await page.waitForTimeout(3000);
// Check the response status or ensure the page loads without crashing
expect(response?.status()).toBeLessThan(500);

// The page should either show an error or handle it gracefully
// We expect the page to not crash and still have a valid title
Expand Down
25 changes: 13 additions & 12 deletions client/e2e-tests/home.spec.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { test, expect } from '@playwright/test';

test.describe('Home Page', () => {
test('should display the correct title', async ({ page }) => {
test.beforeEach(async ({ page }) => {
await page.goto('/');

});

test('should display the correct title', async ({ page }) => {
// Check that the page title is correct
await expect(page).toHaveTitle('Tailspin Toys - Crowdfunding your new favorite game!');
});

test('should display the main heading', async ({ page }) => {
await page.goto('/');

// Check that the main heading is present
const mainHeading = page.locator('h1').first();
await expect(mainHeading).toHaveText('Tailspin Toys');
// Check that the main page heading is present
await expect(page.getByRole('heading', { name: 'Welcome to Tailspin Toys', exact: true })).toBeVisible();
});

test('should display the site branding in header', async ({ page }) => {
// Check that the site branding is present in the header (no longer an h1)
await expect(page.getByText('Tailspin Toys').first()).toBeVisible();
});

test('should display the welcome message', async ({ page }) => {
await page.goto('/');

// Check that the welcome message is present
const welcomeMessage = page.locator('p').first();
await expect(welcomeMessage).toHaveText('Find your next game! And maybe even back one! Explore our collection!');
// Check that the welcome message is present using more specific locator
await expect(page.getByText('Find your next game! And maybe even back one! Explore our collection!')).toBeVisible();
});
});
Loading
Loading