Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
74 changes: 74 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Tests

on:
pull_request:
branches:
- main
push:
branches:
- main

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Install dependencies
run: uv sync --extra dev

- name: Run all tests with coverage
run: uv run pytest -v --cov=app --cov-report=xml --cov-report=term

docker-integration:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Create test environment file
run: |
cat > .env.test <<EOF
POSTGRES_USER=test
POSTGRES_PASSWORD=test
POSTGRES_DB=myapp_test
DEBUG=true
EOF

- name: Build and start services
run: docker compose -f docker-compose.dev.yml --env-file .env.test up -d --build

- name: Wait for services to be ready
run: |
echo "Waiting for services to start..."
timeout 60 bash -c 'until curl -f http://localhost:8000/api/health > /dev/null 2>&1; do sleep 2; done'
echo "Services are ready!"

- name: Sanity check API endpoints
run: |
echo "Testing health endpoint..."
curl -f http://localhost:8000/api/health

echo "Testing items endpoint..."
curl -f http://localhost:8000/api/items

- name: Show logs on failure
if: failure()
run: docker compose -f docker-compose.dev.yml logs

- name: Tear down services
if: always()
run: docker compose -f docker-compose.dev.yml down -v
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,12 @@ Thumbs.db # Windows
# Miscellaneous
# Logs and database files (if local)
*.log
*.sqlite3
*.sqlite3
/.playwright-mcp/
/.idea/
.coverage
htmlcov/

# Environment files
.env.test
.env.local
67 changes: 66 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,69 @@ docker compose -f docker-compose.dev.yml exec app uv run alembic upgrade head
docker compose -f docker-compose.dev.yml exec db psql -U dev -d myapp_dev

#### Tear down
docker compose -f docker-compose.dev.yml down
docker compose -f docker-compose.dev.yml down

## Testing

### Setup
Install development dependencies:
```bash
uv sync --extra dev
```

### Running Tests

Run all tests:
```bash
uv run pytest
```

Run only unit tests:
```bash
uv run pytest -m unit
```

Run only integration tests:
```bash
uv run pytest -m integration
```

Run tests with verbose output:
```bash
uv run pytest -v
```

Run tests without coverage:
```bash
uv run pytest --no-cov
```

### Test Structure

- `tests/unit/` - Fast, isolated unit tests
- `tests/integration/` - Full application flow tests
- `tests/conftest.py` - Shared fixtures and configuration

### Coverage

Tests automatically generate coverage reports. View the HTML report:
```bash
open htmlcov/index.html # macOS
xdg-open htmlcov/index.html # Linux
```

### Docker Sanity Checks

Test the full Docker stack (app + database):
```bash
./test-docker.sh
```

This script:
- Builds the Docker containers
- Starts the application and PostgreSQL
- Waits for services to be ready
- Sanity checks API endpoints with curl
- Cleans up automatically

**Note:** Requires `jq` for JSON formatting. Install with `apt install jq` (Linux) or `brew install jq` (macOS).
36 changes: 21 additions & 15 deletions frontend/css/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@ body {
overflow-x: hidden;
transition: background-color var(--transition-slow);
min-height: 100vh;
position: relative;
}

/* Background Decal Pattern */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
z-index: 0;
opacity: var(--decal-opacity);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120' viewBox='0 0 120 120'%3E%3Cg fill='rgba(128,128,128,0.5)'%3E%3C!-- Star 1 --%3E%3Cpath d='M15 8l1.5 3 3.5.5-2.5 2.5.5 3.5-3-1.5-3 1.5.5-3.5-2.5-2.5 3.5-.5z'/%3E%3C!-- Checkmark --%3E%3Cpath d='M70 15l-8 8-4-4-2 2 6 6 10-10z'/%3E%3C!-- Small diamond --%3E%3Cpath d='M105 12l4 4-4 4-4-4z'/%3E%3C!-- Calendar icon --%3E%3Crect x='8' y='55' width='14' height='12' rx='2' fill='none' stroke='rgba(128,128,128,0.5)' stroke-width='1.5'/%3E%3Cline x1='8' y1='59' x2='22' y2='59' stroke='rgba(128,128,128,0.5)' stroke-width='1.5'/%3E%3Ccircle cx='12' cy='63' r='1'/%3E%3Ccircle cx='18' cy='63' r='1'/%3E%3C!-- Star 2 --%3E%3Cpath d='M65 55l1 2 2.5.3-1.8 1.8.4 2.4-2.1-1.1-2.1 1.1.4-2.4-1.8-1.8 2.5-.3z'/%3E%3C!-- Plus sign --%3E%3Cpath d='M100 52v8M96 56h8' stroke='rgba(128,128,128,0.5)' stroke-width='1.5' stroke-linecap='round'/%3E%3C!-- Circle dot --%3E%3Ccircle cx='35' cy='35' r='3' fill='none' stroke='rgba(128,128,128,0.5)' stroke-width='1.5'/%3E%3Ccircle cx='35' cy='35' r='1'/%3E%3C!-- Lightning bolt --%3E%3Cpath d='M90 90l-3 5h4l-3 5' fill='none' stroke='rgba(128,128,128,0.5)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C!-- Small star --%3E%3Cpath d='M40 95l1 2 2 .3-1.4 1.4.3 2-1.9-1-1.9 1 .3-2-1.4-1.4 2-.3z'/%3E%3C!-- Clock outline --%3E%3Ccircle cx='15' cy='105' r='5' fill='none' stroke='rgba(128,128,128,0.5)' stroke-width='1.5'/%3E%3Cpath d='M15 102v3.5l2 1.5' fill='none' stroke='rgba(128,128,128,0.5)' stroke-width='1.2' stroke-linecap='round'/%3E%3C!-- Sparkle --%3E%3Cpath d='M75 85c0-2 0-2 2 0 0 2 0 2-2 0-2 0-2 0 0-2 0 2 0 2 2 0z'/%3E%3C/g%3E%3C/svg%3E");
background-size: 120px 120px;
background-repeat: repeat;
}

/* ============================================
Expand All @@ -35,6 +52,8 @@ body {
flex-direction: column;
min-height: 100vh;
width: 100%;
position: relative;
z-index: 1;
}

.header {
Expand Down Expand Up @@ -831,10 +850,6 @@ body {
.input-hint {
display: block;
}

.stats-grid {
grid-template-columns: repeat(3, 1fr);
}
}

/* Desktop (1024px and up) */
Expand All @@ -861,17 +876,6 @@ body {
}
}

/* ============================================
BOTTOM SECTION
============================================ */
.stats-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
padding-top: 1rem;
border-top: 1px solid var(--color-border);
}

/* ============================================
ACTION BUTTONS ROW
============================================ */
Expand Down Expand Up @@ -1621,6 +1625,8 @@ body {
justify-content: center;
padding: 2rem 1rem;
background: var(--color-bg-dark);
position: relative;
z-index: 1;
}

.auth-container {
Expand Down
83 changes: 83 additions & 0 deletions frontend/css/themes.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
/* Utility Colors */
--color-border: var(--color-surface-highlight);
--color-divider: rgba(255, 255, 255, 0.05);

/* Background Decals */
--decal-opacity: 0.03;
--decal-color: 255, 255, 255;
}

/* Alternative Theme 1: Ocean Blue */
Expand Down Expand Up @@ -122,6 +126,85 @@
--color-text-placeholder: rgba(240, 168, 186, 0.5);
}

/* ============================================
LIGHT THEMES
============================================ */

/* Light Theme 1: Daylight (Warm light theme) */
[data-theme="daylight"] {
--color-primary: #d97706;
--color-primary-hover: #b45309;
--color-primary-light: rgba(217, 119, 6, 0.1);
--color-primary-shadow: rgba(217, 119, 6, 0.15);

--color-bg-dark: #fffbf5;
--color-surface-dark: #fff7ed;
--color-surface-highlight: #fef3e2;
--color-surface-highlight-hover: #fde9cc;
--color-surface-highlight-alpha-30: rgba(254, 243, 226, 0.6);

--color-text-primary: #1c1917;
--color-text-secondary: #78716c;
--color-text-placeholder: rgba(120, 113, 108, 0.6);
--color-text-dark: #1c1917;

--color-border: #e7e5e4;
--color-divider: rgba(0, 0, 0, 0.06);

--decal-opacity: 0.04;
--decal-color: 0, 0, 0;
}

/* Light Theme 2: Cloud (Cool neutral light theme) */
[data-theme="cloud"] {
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
--color-primary-light: rgba(37, 99, 235, 0.1);
--color-primary-shadow: rgba(37, 99, 235, 0.15);

--color-bg-dark: #f8fafc;
--color-surface-dark: #f1f5f9;
--color-surface-highlight: #e2e8f0;
--color-surface-highlight-hover: #cbd5e1;
--color-surface-highlight-alpha-30: rgba(226, 232, 240, 0.6);

--color-text-primary: #0f172a;
--color-text-secondary: #64748b;
--color-text-placeholder: rgba(100, 116, 139, 0.6);
--color-text-dark: #0f172a;

--color-border: #e2e8f0;
--color-divider: rgba(0, 0, 0, 0.06);

--decal-opacity: 0.335;
--decal-color: 0, 0, 0;
}

/* Light Theme 3: Meadow (Fresh green light theme) */
[data-theme="meadow"] {
--color-primary: #059669;
--color-primary-hover: #047857;
--color-primary-light: rgba(5, 150, 105, 0.1);
--color-primary-shadow: rgba(5, 150, 105, 0.15);

--color-bg-dark: #f7fdf9;
--color-surface-dark: #ecfdf5;
--color-surface-highlight: #d1fae5;
--color-surface-highlight-hover: #a7f3d0;
--color-surface-highlight-alpha-30: rgba(209, 250, 229, 0.6);

--color-text-primary: #14532d;
--color-text-secondary: #4d7c5f;
--color-text-placeholder: rgba(77, 124, 95, 0.6);
--color-text-dark: #14532d;

--color-border: #bbf7d0;
--color-divider: rgba(0, 0, 0, 0.06);

--decal-opacity: 0.345;
--decal-color: 0, 0, 0;
}

/* ============================================
DESIGN TOKENS (Non-color variables)
============================================ */
Expand Down
5 changes: 4 additions & 1 deletion frontend/js/utils/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ class ThemeManager {
this.currentTheme = DEFAULT_THEME;
this.listeners = [];

// Available themes
// Available themes (dark themes first, then light themes)
this.themes = {
default: 'Golden Hour',
ocean: 'Ocean Blue',
forest: 'Forest Green',
midnight: 'Midnight Purple',
sunset: 'Sunset Orange',
rose: 'Rose Pink',
daylight: 'Daylight',
cloud: 'Cloud',
meadow: 'Meadow',
};

// Load saved theme
Expand Down
4 changes: 0 additions & 4 deletions frontend/pages/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,3 @@ <h2 class="section-heading">Last Processed</h2>
</div>
</div>
</div>
<!-- Bottom Section: Quick Stats or Tips -->
<div class="stats-grid">

</div>
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ requires-python = ">=3.10"
dependencies = [
"fastapi[standard]>=0.128.0",
]

[project.optional-dependencies]
dev = [
"pytest>=8.0.0",
"pytest-asyncio>=0.23.0",
"pytest-cov>=4.1.0",
"httpx>=0.27.0",
]
20 changes: 20 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
asyncio_mode = auto
asyncio_default_fixture_loop_scope = function

# Coverage settings
addopts =
--cov=app
--cov-report=term-missing
--cov-report=html
-v

# Markers for different test types
markers =
unit: Unit tests
integration: Integration tests
slow: Slow running tests
Empty file added tests/__init__.py
Empty file.
Loading