Skip to content
Open
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
3f9f219
Initial plan
Copilot Jul 30, 2025
9d3c6a9
Add Playwright e2e tests for core website functionality
Copilot Jul 30, 2025
67ed72d
Clean up gitignore and remove test artifacts from repo
Copilot Jul 30, 2025
2e318d0
Update Playwright configuration to use only WebKit browser
Copilot Jul 30, 2025
5f32c3f
Update Playwright configuration to use only Chromium browser
Copilot Jul 30, 2025
fb56683
Small markdown updates
GeekTrainer Aug 26, 2025
75abcdf
Fix numbering in testing documentation for clarity
GeekTrainer Aug 26, 2025
aefe4b1
Add playwright report directory to .gitignore
GeekTrainer Aug 26, 2025
0433ce7
Updated folder structure to remove 1-hour version
GeekTrainer Aug 26, 2025
4dc7f9b
README cleanup
GeekTrainer Aug 26, 2025
f73bcf8
Updated setup instructions
GeekTrainer Aug 26, 2025
33644f1
Update 2-issues.md to clarify project updates and improve issue creat…
GeekTrainer Aug 26, 2025
76f2b0a
Removed PowerShell script
GeekTrainer Aug 26, 2025
5356ff1
Removed code vestige
GeekTrainer Aug 26, 2025
5744d20
Refactor content in 3-codespaces.md for improved readability and stru…
GeekTrainer Aug 26, 2025
ce5b532
Add test for 404 error when dog is not found by ID
GeekTrainer Aug 26, 2025
be28164
Centralized tests into tests folder
GeekTrainer Aug 26, 2025
baa1c33
Refactor instructions in 4-testing.md for clarity and conciseness
GeekTrainer Aug 26, 2025
501ec96
Enhance setup scripts: add environment setup checks and improve error…
GeekTrainer Aug 26, 2025
7bacaa1
Add run-tests.sh script to automate Python unit testing
GeekTrainer Aug 26, 2025
aae808f
Add copilot instructions for Tailspin Shelter project
GeekTrainer Aug 27, 2025
2b432e4
Renamed file
GeekTrainer Aug 28, 2025
215818e
Added Playwright
GeekTrainer Aug 28, 2025
1449a19
Restructure and other updates
GeekTrainer Aug 28, 2025
85b12cb
Merge branch 'copilot/fix-114' into update-workshop
GeekTrainer Aug 28, 2025
68f6aef
Updated versions
GeekTrainer Aug 28, 2025
a1f2327
Playwright config updates
GeekTrainer Aug 28, 2025
e948956
Instructions updates
GeekTrainer Aug 28, 2025
6b72688
Updated instructions files
GeekTrainer Aug 28, 2025
752cdce
Add instructions for using GitHub Copilot to implement dog filtering …
GeekTrainer Aug 28, 2025
07858ab
Enhance E2E Testing and Documentation
GeekTrainer Aug 28, 2025
989a6aa
Update testing guidelines to specify allowed test types
GeekTrainer Aug 28, 2025
4aff354
Refactor testing section in Copilot instructions for clarity and orga…
GeekTrainer Aug 28, 2025
23581ef
Add guideline to utilize tests for validating app behavior
GeekTrainer Aug 28, 2025
7e044f0
Changed feature request
GeekTrainer Aug 28, 2025
20bce48
Merge branch 'main' into update-workshop
GeekTrainer Sep 2, 2025
2eddd0f
Refine workshop content to clarify GitHub Copilot's role and enhance …
GeekTrainer Sep 2, 2025
ba6aa68
Update GitHub flow documentation to correct step numbering and clarif…
GeekTrainer Sep 2, 2025
6e5b4a0
Updated links
GeekTrainer Sep 2, 2025
c233b85
Removed language specific url tags
GeekTrainer Sep 3, 2025
ed6c255
Remove debug logging from API request middleware
GeekTrainer Sep 3, 2025
ed01e06
Removed answer
GeekTrainer Sep 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/.copilotignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- content/**/*
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# For more information, see [docs](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax)
# For more information, see [docs](https://docs.github.com/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax)

# This repository is maintained by:
* @geektrainer @peckjon @chrisreddington
115 changes: 115 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Tailspin Shelter

The Tailspin Shelter is a full-stack web application that showcases a fictional dog shelter.

## Technology Stack

### Frontend (Client)

- **Framework**: [Astro](https://astro.build/) v5.4+ - Static site generator with server-side rendering
- **Component Library**: [Svelte](https://svelte.dev/) v5.23+ - For dynamic interactive components
- **Styling**: [Tailwind CSS](https://tailwindcss.com/) v4.0+ - Utility-first CSS framework
- **Language**: TypeScript - Strongly typed JavaScript
- **Adapter**: Node.js adapter for server-side rendering

### Backend (Server)

- **Framework**: [Flask](https://flask.palletsprojects.com/) - Python web framework
- **Database**: SQLite with [SQLAlchemy](https://www.sqlalchemy.org/) ORM
- **Language**: Python 3.13+ with type hints
- **CORS**: Flask-CORS for cross-origin requests

### Testing

- **Python/Server/Flask**: Python unittest framework
- **E2E testing**: [Playwright](https://playwright.dev/) v1.49+ - End-to-end testing framework

## Project Structure

```
pets-workshop/
├── client/ # Astro frontend application
│ ├── src/components/ # Svelte components (DogList, DogDetails)
│ ├── src/layouts/ # Astro layout templates
│ ├── src/pages/ # Astro pages (routing)
│ ├── src/styles/ # Global CSS and Tailwind imports
│ └── e2e-tests/ # Playwright end-to-end tests
├── server/ # Flask backend API
│ ├── models/ # SQLAlchemy models (Dog, Breed)
│ ├── tests/ # Python unit tests
│ └── app.py # Main Flask application
└── scripts/ # Automation scripts (run-tests.sh, setup-environment.sh, start-app.sh)
```

## Design Philosophy & Theme

**CRITICAL**: Maintain the dark, modern aesthetic throughout:
- **HTML Class**: Always include `class="dark"` on the html element
- **Background**: Use `bg-slate-900` for main backgrounds
- **Text**: Default to `text-white` for primary content
- **Typography**: Inter font family with clean, readable text
- **Responsive**: Mobile-first approach using Tailwind's responsive prefixes
- **Transitions**: Include `transition-colors duration-300` for smooth interactions

## Development Guidelines

### Use Scripts, Not Direct Commands
**IMPORTANT**: Always prefer using the provided scripts in the `scripts/` directory rather than running commands directly:
- **Testing**: Use `./scripts/run-server-tests.sh` instead of `python -m unittest`
- **E2E Testing**: Use `./scripts/run-e2e-tests` instead of `npm run tests:e2e`
- **Environment Setup**: Use `./scripts/setup-environment.sh` for initial setup
- **Application Start**: Use `./scripts/start-app.sh` to launch the application

### API Patterns
- **Endpoints**: RESTful API design with `/api/` prefix
- **Response Format**: Always return JSON with proper HTTP status codes
- **Type Hints**: Use Python type hints for all function parameters and returns

### Frontend Patterns
- **Component Structure**: Use Svelte for interactive components, Astro for static layouts
- **Data Fetching**: Fetch data on the server side when possible
- **Styling**: Use Tailwind utility classes, avoid custom CSS unless necessary
- **Routing**: File-based routing through Astro's pages directory
- **Test Identifiers**: Always include `data-testid` attributes for E2E testing resilience (see [`test-identifiers.md`](./instructions/test-identifiers.md))

### Database Patterns
- **Models**: Use SQLAlchemy declarative base with proper relationships
- **Queries**: Prefer SQLAlchemy query syntax over raw SQL
- **Data Seeding**: Use the utilities in `utils/seed_database.py`

### Testing Patterns

Below are the only types of tests we use in this project. Do not add additional test types unless instructed otherwise.

- **E2E Tests**: Playwright tests in `client/e2e-tests/` cover full user workflows
- **Unit tests**: Unit tests for Flask endpoints and utilities, stored in `server/tests`

## Coding Standards

### Python (Backend)
- Follow PEP 8 style guidelines
- Use type hints for all function signatures
- Use meaningful variable names with snake_case
- Handle exceptions gracefully with proper error messages

### TypeScript/JavaScript (Frontend)
- Use TypeScript for type safety
- Follow Astro's component conventions
- Use camelCase for variables and functions
- Include proper prop types for Svelte components

### CSS/Styling
- Use Tailwind utility classes primarily
- Maintain dark theme consistency
- Ensure responsive design across all breakpoints
- Use semantic HTML elements when possible

## AI Assistant Guidelines

When working with this codebase:
1. Always maintain the dark theme aesthetic
2. Use the provided scripts for common operations
3. Follow the established patterns for API responses and component structure
4. Utilize the tests to validate app behavior; don't launch the app or run `curl` commands to do so
4. Ensure type safety in both Python and TypeScript code
5. Test changes using the appropriate testing frameworks
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# - Checks for updates weekly
# - Groups updates based on their type (dev grouped by minor/patch or prod grouped by patch)
#
# Learn more at https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#configuration-options-for-the-dependabotyml-file
# Learn more at https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#configuration-options-for-the-dependabotyml-file
version: 2
updates:
- package-ecosystem: npm
Expand Down
215 changes: 215 additions & 0 deletions .github/instructions/flask-tests.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
---
applyTo: "**/test_*.py"
---

# Flask Testing Guidelines for Tailspin Shelter

Essential patterns for writing unit tests for Flask APIs using Python's unittest framework.

## Core Principles

1. **Mock Database Queries** - Use `unittest.mock` to isolate API logic
2. **Test HTTP Responses** - Verify status codes, JSON structure, error messages
3. **Helper Methods** - Create reusable mock objects and setup methods

## Basic Test Structure

```python
import unittest
from unittest.mock import patch, MagicMock
import json
from app import app

class TestDogAPI(unittest.TestCase):
def setUp(self):
self.app = app.test_client()
self.app.testing = True
app.config['TESTING'] = True
```

## Testing GET Endpoints

### List Endpoint
```python
@patch('app.db.session.query')
def test_get_dogs_success(self, mock_query):
# Setup mock
mock_dog = MagicMock()
mock_dog.id = 1
mock_dog.name = "Buddy"
mock_dog.breed = "Golden Retriever"

mock_query_instance = MagicMock()
mock_query.return_value = mock_query_instance
mock_query_instance.join.return_value = mock_query_instance
mock_query_instance.all.return_value = [mock_dog]

response = self.app.get('/api/dogs')

self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertEqual(len(data), 1)
self.assertEqual(data[0]['name'], "Buddy")
```

### Single Resource
```python
@patch('app.db.session.query')
def test_get_dog_by_id_success(self, mock_query):
mock_dog = MagicMock()
mock_dog.id = 1
mock_dog.name = "Buddy"
mock_dog.status.name = "AVAILABLE"

mock_query_instance = MagicMock()
mock_query.return_value = mock_query_instance
mock_query_instance.join.return_value = mock_query_instance
mock_query_instance.filter.return_value = mock_query_instance
mock_query_instance.first.return_value = mock_dog

response = self.app.get('/api/dogs/1')

self.assertEqual(response.status_code, 200)
data = json.loads(response.data)
self.assertEqual(data['name'], "Buddy")

@patch('app.db.session.query')
def test_get_dog_not_found(self, mock_query):
mock_query_instance = MagicMock()
mock_query.return_value = mock_query_instance
mock_query_instance.join.return_value = mock_query_instance
mock_query_instance.filter.return_value = mock_query_instance
mock_query_instance.first.return_value = None

response = self.app.get('/api/dogs/999')

self.assertEqual(response.status_code, 404)
data = json.loads(response.data)
self.assertEqual(data['error'], "Dog not found")
```

## Testing POST Endpoints

```python
@patch('app.db.session')
@patch('app.Dog')
def test_create_dog_success(self, mock_dog_class, mock_session):
mock_dog_instance = MagicMock()
mock_dog_instance.id = 1
mock_dog_instance.name = "New Dog"
mock_dog_class.return_value = mock_dog_instance

dog_data = {'name': 'New Dog', 'breed_id': 1, 'age': 2}

response = self.app.post('/api/dogs',
data=json.dumps(dog_data),
content_type='application/json')

self.assertEqual(response.status_code, 201)
data = json.loads(response.data)
self.assertEqual(data['name'], "New Dog")
mock_session.add.assert_called_once()
mock_session.commit.assert_called_once()

def test_create_dog_missing_fields(self):
dog_data = {'name': 'Incomplete Dog'} # Missing breed_id

response = self.app.post('/api/dogs',
data=json.dumps(dog_data),
content_type='application/json')

self.assertEqual(response.status_code, 400)
data = json.loads(response.data)
self.assertIn('Missing required fields', data['error'])
```

## Testing PUT/DELETE Endpoints

```python
@patch('app.Dog.query')
@patch('app.db.session')
def test_update_dog_success(self, mock_session, mock_query):
mock_dog = MagicMock()
mock_dog.id = 1
mock_query.get.return_value = mock_dog

update_data = {'name': 'Updated Name', 'age': 4}

response = self.app.put('/api/dogs/1',
data=json.dumps(update_data),
content_type='application/json')

self.assertEqual(response.status_code, 200)
self.assertEqual(mock_dog.name, "Updated Name")
mock_session.commit.assert_called_once()

@patch('app.Dog.query')
@patch('app.db.session')
def test_delete_dog_success(self, mock_session, mock_query):
mock_dog = MagicMock()
mock_query.get.return_value = mock_dog

response = self.app.delete('/api/dogs/1')

self.assertEqual(response.status_code, 200)
mock_session.delete.assert_called_once_with(mock_dog)
mock_session.commit.assert_called_once()
```

## Error Handling Tests

```python
@patch('app.db.session')
def test_database_error_handling(self, mock_session):
mock_session.commit.side_effect = Exception("Database error")

dog_data = {'name': 'Test Dog', 'breed_id': 1}

response = self.app.post('/api/dogs',
data=json.dumps(dog_data),
content_type='application/json')

self.assertEqual(response.status_code, 500)
data = json.loads(response.data)
self.assertIn('Internal server error', data['error'])
mock_session.rollback.assert_called_once()
```

## Helper Methods

```python
def _create_mock_dog(self, dog_id: int, name: str, breed: str):
mock_dog = MagicMock()
mock_dog.id = dog_id
mock_dog.name = name
mock_dog.breed = breed
mock_dog.status.name = "AVAILABLE"
return mock_dog

def _setup_query_mock(self, mock_query, return_data):
mock_query_instance = MagicMock()
mock_query.return_value = mock_query_instance
mock_query_instance.join.return_value = mock_query_instance
mock_query_instance.filter.return_value = mock_query_instance
mock_query_instance.all.return_value = return_data
mock_query_instance.first.return_value = return_data[0] if return_data else None
return mock_query_instance
```

## Running Tests

```bash
# Run all tests
python -m unittest discover tests/

# Run with project script
./scripts/run-server-tests.sh
```

## Key Testing Patterns

- **Mock database operations** to isolate API logic
- **Test both success and error cases** for each endpoint
- **Verify HTTP status codes** and response JSON structure
- **Use helper methods** to reduce code duplication
- **Assert database operations** like `add()`, `commit()`, `rollback()`
Loading
Loading