Skip to content
Open
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
12 changes: 12 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"mcpServers": {
"github": {
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": ""
}
}
}
}
2 changes: 1 addition & 1 deletion dev-dist/sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ define(['./workbox-21a80088'], (function (workbox) { 'use strict';
*/
workbox.precacheAndRoute([{
"url": "index.html",
"revision": "0.v4bl8mgtcdg"
"revision": "0.tf31j3pna0c"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
Expand Down
64 changes: 32 additions & 32 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
import { defineConfig, devices } from "@playwright/test";
import { defineConfig, devices } from '@playwright/test';

/**
* Playwright configuration for screenshot generation
* See https://playwright.dev/docs/test-configuration
*/
export default defineConfig({
testDir: "./tests",
// Only run screenshot tests
testMatch: "**/screenshots.spec.ts",
// Run tests in parallel
fullyParallel: true,
// Fail the build on CI if you accidentally left test.only in the source code
forbidOnly: !!process.env.CI,
// Retry on CI only
retries: process.env.CI ? 2 : 0,
// Opt out of parallel tests on CI
workers: process.env.CI ? 1 : undefined,
// Reporter to use
reporter: "list",
// Shared settings for all the projects below
use: {
// Base URL to use in actions like `await page.goto('/')`
baseURL: "http://localhost:8080",
// Collect trace when retrying the failed test
trace: "on-first-retry",
// Screenshot on failure
screenshot: "only-on-failure",
},
testDir: './tests',
// Only run screenshot tests
testMatch: '**/screenshots.spec.ts',
// Run tests in parallel
fullyParallel: true,
// Fail the build on CI if you accidentally left test.only in the source code
forbidOnly: !!process.env.CI,
// Retry on CI only
retries: process.env.CI ? 2 : 0,
// Opt out of parallel tests on CI
workers: process.env.CI ? 1 : undefined,
// Reporter to use
reporter: 'list',
// Shared settings for all the projects below
use: {
// Base URL to use in actions like `await page.goto('/')`
baseURL: 'http://localhost:8080',
// Collect trace when retrying the failed test
trace: 'on-first-retry',
// Screenshot on failure
screenshot: 'only-on-failure'
},

// Configure projects for major browsers (only chromium needed for screenshots)
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
// Configure projects for major browsers (only chromium needed for screenshots)
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
}
]

// Don't start the dev server automatically
// Run `npm run dev` manually before taking screenshots
// Don't start the dev server automatically
// Run `npm run dev` manually before taking screenshots
});
Binary file removed public/screenshots/Categories-1920x1080.png
Binary file not shown.
Binary file removed public/screenshots/Categories-750x1334.png
Binary file not shown.
Binary file removed public/screenshots/EnteredTime-1920x1080.png
Binary file not shown.
Binary file removed public/screenshots/EnteredTime-750x1334.png
Binary file not shown.
Binary file removed public/screenshots/Settings-1920x1080.png
Binary file not shown.
Binary file removed public/screenshots/Settings-750x1334.png
Binary file not shown.
Binary file removed public/screenshots/TimeTrackerPro-1920x1080.png
Binary file not shown.
Binary file removed public/screenshots/TimeTrackerPro-750x1334.png
Binary file not shown.
Binary file added public/screenshots/desktop-1-archive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/screenshots/desktop-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/screenshots/desktop-2-active.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/screenshots/mobile-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/screenshots/mobile-2-nav.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/screenshots/mobile-settings-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
108 changes: 63 additions & 45 deletions tests/SCREENSHOTS_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Automated screenshot generation for TimeTracker Pro PWA using Playwright.
## 🎯 What This Does

This script automatically captures professional screenshots for your PWA in the correct sizes:

- **Desktop:** 1920×1080 (wide format for desktop/laptop displays)
- **Mobile:** 750×1334 (narrow format for mobile devices)

Expand Down Expand Up @@ -35,18 +36,19 @@ npm run screenshots
```

Screenshots will be saved to:

- `public/screenshots/desktop-1.png` (1920×1080)
- `public/screenshots/mobile-1.png` (750×1334)
- `public/screenshots/desktop-2-active.png` (optional - with active day)
- `public/screenshots/mobile-2-nav.png` (optional - with nav visible)

## 📋 Available Commands

| Command | Description |
|---------|-------------|
| `npm run screenshots` | Capture all screenshots (headless) |
| `npm run screenshots:headed` | Capture with visible browser (debugging) |
| `npm run screenshots:install` | Install Playwright browsers |
| Command | Description |
| ----------------------------- | ---------------------------------------- |
| `npm run screenshots` | Capture all screenshots (headless) |
| `npm run screenshots:headed` | Capture with visible browser (debugging) |
| `npm run screenshots:install` | Install Playwright browsers |

## 🎨 Customization

Expand All @@ -56,35 +58,35 @@ Edit `tests/screenshots.spec.ts` to customize what's captured:

```typescript
// Example: Navigate to specific page before screenshot
await page.goto("http://localhost:8080/archive");
await page.goto('http://localhost:8080/archive');

// Example: Click a button to show specific content
await page.click("button:has-text('Start Day')");

// Example: Fill a form
await page.fill("input[name='title']", "Example Task");
await page.fill("input[name='title']", 'Example Task');

// Example: Wait for element to appear
await page.waitForSelector(".task-item");
await page.waitForSelector('.task-item');
```

### Add More Screenshots

Add new test cases in `screenshots.spec.ts`:

```typescript
test("capture archive page", async ({ browser }) => {
const context = await browser.newContext(DESKTOP_CONFIG);
const page = await context.newPage();
test('capture archive page', async ({ browser }) => {
const context = await browser.newContext(DESKTOP_CONFIG);
const page = await context.newPage();

await page.goto("http://localhost:8080/archive");
await page.waitForSelector("#root");
await page.goto('http://localhost:8080/archive');
await page.waitForSelector('#root');

await page.screenshot({
path: path.join(SCREENSHOTS_DIR, "desktop-archive.png"),
});
await page.screenshot({
path: path.join(SCREENSHOTS_DIR, 'desktop-archive.png')
});

await context.close();
await context.close();
});
```

Expand All @@ -95,55 +97,64 @@ Update the configuration in `screenshots.spec.ts`:
```typescript
// Custom desktop size
const DESKTOP_CONFIG = {
viewport: { width: 2560, height: 1440 }, // 4K
deviceScaleFactor: 1,
viewport: { width: 2560, height: 1440 }, // 4K
deviceScaleFactor: 1
};

// Custom mobile size
const MOBILE_CONFIG = {
viewport: { width: 1080, height: 1920 }, // Full HD mobile
deviceScaleFactor: 2,
viewport: { width: 1080, height: 1920 }, // Full HD mobile
deviceScaleFactor: 2
};
```

## 🔧 Troubleshooting

### "Connection refused" or "net::ERR_CONNECTION_REFUSED"

**Problem:** Dev server is not running.
**Solution:** Run `npm run dev` in a separate terminal first.

### Screenshots are blank or show loading state

**Problem:** App taking too long to load.
**Solution:** Increase timeout in `screenshots.spec.ts`:

```typescript
await page.waitForSelector("#root", { timeout: 20000 }); // 20 seconds
await page.waitForSelector('#root', { timeout: 20000 }); // 20 seconds
```

### Want to see what's happening

**Problem:** Headless mode makes it hard to debug.
**Solution:** Use headed mode:

```bash
npm run screenshots:headed
```

### Screenshots include unwanted elements

**Problem:** Test data or UI elements you don't want visible.
**Solution:** Hide elements before screenshot:

```typescript
// Hide specific element
await page.evaluate(() => {
document.querySelector(".unwanted-element")?.remove();
document.querySelector('.unwanted-element')?.remove();
});

// Or add CSS to hide it
await page.addStyleTag({
content: ".unwanted-element { display: none !important; }"
content: '.unwanted-element { display: none !important; }'
});
```

### File size too large

**Problem:** Screenshots are > 1MB.
**Solution:** Use image optimization tools after capture:

```bash
# Install optimizer
npm install -g sharp-cli
Expand All @@ -157,13 +168,15 @@ npx sharp -i public/screenshots/desktop-1.png -o public/screenshots/desktop-1.pn
Your screenshots meet PWA standards:

### Desktop Screenshots (Wide)

- ✅ **Size:** 1920×1080
- ✅ **Aspect Ratio:** 16:9
- ✅ **Form Factor:** wide
- ✅ **Format:** PNG
- ✅ **Max File Size:** < 1MB recommended

### Mobile Screenshots (Narrow)

- ✅ **Size:** 750×1334
- ✅ **Aspect Ratio:** 9:16
- ✅ **Form Factor:** narrow
Expand All @@ -173,13 +186,15 @@ Your screenshots meet PWA standards:
## 🎯 Best Practices

### ✅ DO:

- Show real data (realistic tasks, projects)
- Capture app in use (active timer, tasks visible)
- Use light mode for better visibility
- Show key features (navigation, main dashboard)
- Keep UI clean (no debug tools visible)

### ❌ DON'T:

- Show empty states or placeholders
- Include sensitive client data
- Capture error messages or warnings
Expand All @@ -200,26 +215,26 @@ When you update your app's design:
### Capture Specific User Flow

```typescript
test("capture onboarding flow", async ({ browser }) => {
const context = await browser.newContext(DESKTOP_CONFIG);
const page = await context.newPage();
test('capture onboarding flow', async ({ browser }) => {
const context = await browser.newContext(DESKTOP_CONFIG);
const page = await context.newPage();

await page.goto("http://localhost:8080");
await page.goto('http://localhost:8080');

// Step 1: Homepage
await page.screenshot({ path: "public/screenshots/step-1.png" });
// Step 1: Homepage
await page.screenshot({ path: 'public/screenshots/step-1.png' });

// Step 2: Start day
await page.click("button:has-text('Start Day')");
await page.waitForTimeout(500);
await page.screenshot({ path: "public/screenshots/step-2.png" });
// Step 2: Start day
await page.click("button:has-text('Start Day')");
await page.waitForTimeout(500);
await page.screenshot({ path: 'public/screenshots/step-2.png' });

// Step 3: Create task
await page.click("button:has-text('New Task')");
await page.waitForTimeout(500);
await page.screenshot({ path: "public/screenshots/step-3.png" });
// Step 3: Create task
await page.click("button:has-text('New Task')");
await page.waitForTimeout(500);
await page.screenshot({ path: 'public/screenshots/step-3.png' });

await context.close();
await context.close();
});
```

Expand All @@ -228,12 +243,15 @@ test("capture onboarding flow", async ({ browser }) => {
```typescript
// Inject mock data before screenshot
await page.evaluate(() => {
localStorage.setItem("mock_data", JSON.stringify({
tasks: [
{ id: 1, title: "Design Homepage", duration: 7200 },
{ id: 2, title: "Code Feature", duration: 5400 }
]
}));
localStorage.setItem(
'mock_data',
JSON.stringify({
tasks: [
{ id: 1, title: 'Design Homepage', duration: 7200 },
{ id: 2, title: 'Code Feature', duration: 5400 }
]
})
);
});

await page.reload();
Expand Down
Loading
Loading