This commit updates all documentation files throughout the project: - Updated IMPLEMENTATION_PLAN.md with new implementation details - Updated PROJECT_GUIDELINES.md with coding standards and practices - Updated README.md with current project information - Updated SCREENSHOT_AUTOMATION.md with new automation details - Added TEST_DATA.md with test fixtures data - Updated cover_image_serving_plan.md with static URL patterns Documentation API updates: - Updated API reference documentation for all endpoints including: - Authentication (login, logout, register, refresh_token) - Book matching (auto_link, bulk_link, link_book, search) - Collections (CRUD operations, shelf mappings, auto-assign rules) - Conflicts (bulk operations, resolve/dismiss) - Devices (registration, approval, shelf management) - Highlights (create, update, delete, get) - Kobo sync (bookmark, markup, initialization, sync) - KOReader sync (library, metadata, bookmarks, progress) - Libraries (CRUD, folders, media items, stats) - Media items (bulk operations, CRUD) - Notes (CRUD operations) - OPDS (acquisition, feeds, publication) - Progress (reading progress tracking) - Queue (device queue management) - Ratings (star ratings) - Scanner (watch mode, scan operations) - Sync protocols (Kobo, KOReader) - Users (profile, password, admin operations) - WebSocket protocols - Updated user guides (admin, dashboard, settings, sync) - Updated device setup guides (Kobo, KOReader) - Updated developer guides (testing, contributing, operations) - Updated scripts/README.md
853 lines
25 KiB
Markdown
853 lines
25 KiB
Markdown
# Screenshot Automation Plan for Bookhoard Documentation
|
|
|
|
> **Status**: Ready to implement when frontend is complete
|
|
> **Last Updated**: 2026-02-03
|
|
|
|
This document outlines the complete plan for automatically generating screenshots for Bookhoard documentation using Playwright.
|
|
|
|
## Overview
|
|
|
|
Playwright will be used to:
|
|
|
|
1. Navigate the running Bookhoard server
|
|
2. Perform key user/admin workflows
|
|
3. Capture screenshots at each step
|
|
4. Save to `docs/images/` organized by documentation section
|
|
5. Generate/update markdown files with proper image references
|
|
|
|
## Prerequisites (To Verify When Ready)
|
|
|
|
### Frontend Pages Complete
|
|
|
|
Verify these pages are fully functional before starting:
|
|
|
|
- [ ] `/` - Login page
|
|
- [ ] `/register` - Registration page
|
|
- [ ] `/dashboard` - Main dashboard with library browsing
|
|
- [ ] `/collections` - Collections management
|
|
- [ ] `/devices` - Device management and registration
|
|
- [ ] `/conflicts` - Sync conflict resolution
|
|
- [ ] `/progress` - Reading progress tracking
|
|
- [ ] `/analytics` - Usage analytics
|
|
- [ ] `/queue` - Sync queue monitoring
|
|
- [ ] `/admin` - Admin dashboard
|
|
- [ ] `/admin/profile` - Profile settings
|
|
- [ ] `/admin/library` - Library management
|
|
- [ ] `/api-explorer` - API testing interface
|
|
|
|
### Test Environment Ready
|
|
|
|
- [ ] Bookhoard server running on `http://localhost:8765`
|
|
- [ ] Test database seeded with sample data (books, collections, devices)
|
|
- [ ] Test admin account ready (username, password, role=admin)
|
|
- [ ] Test regular user account ready (username, password, role=user)
|
|
|
|
## Installation & Setup
|
|
|
|
```bash
|
|
# Install Playwright (run in project root)
|
|
npm init -y
|
|
npm install -D @playwright/test
|
|
npx playwright install chromium
|
|
```
|
|
|
|
Project structure after setup:
|
|
|
|
```
|
|
bookhoard/
|
|
├── docs/
|
|
│ ├── images/ # Screenshots will be stored here
|
|
│ │ ├── user/
|
|
│ │ ├── admin/
|
|
│ │ ├── devices/
|
|
│ │ └── sync/
|
|
│ └── *.md # Existing markdown files
|
|
├── screenshots/ # Playwright test files
|
|
│ ├── auth.spec.ts
|
|
│ ├── user-workflows.spec.ts
|
|
│ ├── admin-workflows.spec.ts
|
|
│ ├── device-workflows.spec.ts
|
|
│ ├── sync-workflows.spec.ts
|
|
│ └── config.ts
|
|
├── .env.screenshots # Environment configuration
|
|
└── ...
|
|
```
|
|
|
|
## Environment Setup
|
|
|
|
Create `.env.screenshots` in the project root:
|
|
|
|
```env
|
|
# Bookhoard Server
|
|
BASE_URL=http://localhost:8765
|
|
|
|
# Admin Account (for admin guide screenshots)
|
|
ADMIN_EMAIL=admin@example.com
|
|
ADMIN_USERNAME=admin
|
|
ADMIN_PASSWORD=SecureAdminPass123!
|
|
|
|
# Regular User Account (for user guide screenshots)
|
|
USER_EMAIL=user@example.com
|
|
USER_USERNAME=user
|
|
USER_PASSWORD=SecureUserPass456!
|
|
```
|
|
|
|
## Questions to Ask When Ready
|
|
|
|
Before running the screenshot automation, ask the user:
|
|
|
|
### 1. Server Access
|
|
|
|
**Q**: Where is the Bookhoard server running?
|
|
|
|
- [ ] `localhost:8765` (default)
|
|
- [ ] Custom port: `________`
|
|
- [ ] Remote URL: `________`
|
|
|
|
### 2. Test Credentials
|
|
|
|
**Q**: What credentials should Playwright use?
|
|
|
|
**Admin Account** (for admin guide screenshots):
|
|
|
|
- Username: `________`
|
|
- Password: `________`
|
|
|
|
**Regular User Account** (for user guide screenshots):
|
|
|
|
- Username: `________`
|
|
- Password: `________`
|
|
|
|
### 3. Screenshot Format
|
|
|
|
**Q**: What format for screenshots?
|
|
|
|
- [ ] **WebP** (recommended - modern, good compression)
|
|
- [ ] PNG (highest quality, larger files)
|
|
- [ ] JPEG (smaller files, compression artifacts)
|
|
|
|
### 4. Screenshot Dimensions
|
|
|
|
**Q**: What viewport sizes for screenshots?
|
|
|
|
- [ ] **Desktop**: 1920x1080 (full-width screenshots)
|
|
- [ ] **Tablet**: 768x1024 (responsive documentation)
|
|
- [ ] **Mobile**: 375x667 (mobile documentation)
|
|
- [ ] All three sizes (comprehensive coverage)
|
|
|
|
### 5. Theme
|
|
|
|
**Q**: What theme should screenshots use?
|
|
|
|
- [ ] **Default** (Tokyo Night theme as seen in templates)
|
|
- [ ] Light theme (if implemented)
|
|
- [ ] Multiple themes (document theme switching)
|
|
|
|
### 6. Language
|
|
|
|
**Q**: What language/region for the UI?
|
|
|
|
- [ ] **English** (default)
|
|
- [ ] Other: `________`
|
|
|
|
## Screenshot Plan by Documentation Section
|
|
|
|
### 1. User Guide Screenshots
|
|
|
|
**File**: `docs/user/user-guide.md`
|
|
|
|
**Screenshots Needed**:
|
|
|
|
| Screenshot Name | Description | Page/Action |
|
|
| ------------------------- | ---------------------------------- | --------------------------------------- |
|
|
| `login-page.webp` | Login form with filled credentials | `/` - Login page |
|
|
| `dashboard-overview.webp` | Main dashboard showing libraries | `/dashboard` |
|
|
| `library-grid.webp` | Media items grid view | `/dashboard` → Click library |
|
|
| `book-detail.webp` | Book detail view with metadata | `/dashboard` → Click book |
|
|
| `search-results.webp` | Search in action | `/dashboard` → Type in search |
|
|
| `filter-panel.webp` | Filter options expanded | `/dashboard` → Open filters |
|
|
| `collections-list.webp` | Collections grid view | `/collections` |
|
|
| `create-collection.webp` | New collection modal | `/collections` → Click "New Collection" |
|
|
| `reading-progress.webp` | Progress tracking view | `/progress` |
|
|
| `analytics-view.webp` | User analytics dashboard | `/analytics` |
|
|
|
|
**Estimated Screenshots**: ~10
|
|
|
|
### 2. Admin Guide Screenshots
|
|
|
|
**File**: `docs/user/admin-guide.md`
|
|
|
|
**Screenshots Needed**:
|
|
|
|
| Screenshot Name | Description | Page/Action |
|
|
| ------------------------ | ------------------------- | -------------------------------------- |
|
|
| `admin-dashboard.webp` | Admin overview panel | `/admin` |
|
|
| `user-management.webp` | User list with actions | `/admin` → Users section |
|
|
| `add-user.webp` | Add new user form | `/admin` → Click "Add User" |
|
|
| `library-settings.webp` | Library configuration | `/admin/library` |
|
|
| `add-library.webp` | Create new library form | `/admin/library` → Click "Add Library" |
|
|
| `analytics-admin.webp` | Admin analytics view | `/analytics` (admin view) |
|
|
| `sync-queue.webp` | Sync queue monitoring | `/queue` |
|
|
| `theme-settings.webp` | Theme selection interface | `/admin/profile` → Theme section |
|
|
| `user-profile-edit.webp` | Edit user profile | `/admin/profile` |
|
|
|
|
**Estimated Screenshots**: ~9
|
|
|
|
### 3. Device Setup Screenshots
|
|
|
|
**File**: `docs/user/devices/kobo-setup.md` and `koreader-setup.md`
|
|
|
|
**Screenshots Needed**:
|
|
|
|
| Screenshot Name | Description | Page/Action |
|
|
| --------------------------- | --------------------------------- | ----------------------------------- |
|
|
| `device-list.webp` | Device management page | `/devices` |
|
|
| `add-device-modal.webp` | Add new device modal | `/devices` → Click "Add New Device" |
|
|
| `device-form-kobo.webp` | Kobo device registration form | `/devices` → Select Kobo type |
|
|
| `device-form-koreader.webp` | KOReader device registration form | `/devices` → Select KOReader type |
|
|
| `device-qr-code.webp` | QR code for device approval | After device registration |
|
|
| `device-approved.webp` | Device approved confirmation | After approving device |
|
|
| `device-sync-settings.webp` | Sync configuration for device | `/devices` → Click device settings |
|
|
| `shelf-mapping.webp` | Collection to shelf mapping | `/devices` → Click shelf mapping |
|
|
| `sync-queue-item.webp` | Device sync in queue | `/queue` (device specific) |
|
|
|
|
**Estimated Screenshots**: ~9
|
|
|
|
### 4. Sync Guide Screenshots
|
|
|
|
**File**: `docs/user/sync-guide.md`
|
|
|
|
**Screenshots Needed**:
|
|
|
|
| Screenshot Name | Description | Page/Action |
|
|
| -------------------------- | ------------------------- | ------------------------------ |
|
|
| `sync-conflicts.webp` | Conflicts list view | `/conflicts` |
|
|
| `conflict-resolution.webp` | Resolve conflict dialog | `/conflicts` → Click resolve |
|
|
| `unlinked-books.webp` | Unlinked books list | `/unlinked-books` |
|
|
| `book-linking.webp` | Link book to metadata | `/unlinked-books` → Click link |
|
|
| `sync-success.webp` | Successful sync indicator | Any page after sync |
|
|
|
|
**Estimated Screenshots**: ~5
|
|
|
|
### 5. Settings Guide Screenshots
|
|
|
|
**File**: `docs/user/settings-guide.md`
|
|
|
|
**Screenshots Needed**:
|
|
|
|
| Screenshot Name | Description | Page/Action |
|
|
| ----------------------- | ------------------------ | --------------------------------- |
|
|
| `profile-overview.webp` | Profile settings page | `/admin/profile` |
|
|
| `update-username.webp` | Username change form | `/admin/profile` |
|
|
| `update-email.webp` | Email change form | `/admin/profile` |
|
|
| `change-password.webp` | Password change form | `/admin/profile` |
|
|
| `theme-selector.webp` | Theme selection dropdown | `/admin/profile` (if implemented) |
|
|
|
|
**Estimated Screenshots**: ~5
|
|
|
|
## Playwright Test Files
|
|
|
|
### `screenshots/config.ts` - Playwright Configuration
|
|
|
|
```typescript
|
|
import { defineConfig, devices } from "@playwright/test";
|
|
|
|
export default defineConfig({
|
|
testDir: "./",
|
|
fullyParallel: false,
|
|
retries: 1,
|
|
reporter: "list",
|
|
use: {
|
|
baseURL: process.env.BASE_URL || "http://localhost:8765",
|
|
trace: "on-first-retry",
|
|
screenshot: "only-on-failure",
|
|
},
|
|
projects: [
|
|
{
|
|
name: "chromium-desktop",
|
|
use: {
|
|
...devices["Desktop Chrome"],
|
|
viewport: { width: 1920, height: 1080 },
|
|
},
|
|
},
|
|
],
|
|
});
|
|
```
|
|
|
|
### `screenshots/auth.spec.ts` - Authentication Screenshots
|
|
|
|
```typescript
|
|
import { test, expect } from "@playwright/test";
|
|
|
|
test.describe("Authentication Screenshots", () => {
|
|
test("Login page", async ({ page }) => {
|
|
await page.goto("/");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/login-page.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("User login flow", async ({ page }) => {
|
|
await page.goto("/");
|
|
|
|
await page.fill('input[name="login"]', process.env.USER_USERNAME || "user");
|
|
await page.fill(
|
|
'input[name="password"]',
|
|
process.env.USER_PASSWORD || "password",
|
|
);
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/login-form-filled.webp",
|
|
fullPage: true,
|
|
});
|
|
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL("/dashboard", { timeout: 5000 });
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/dashboard-after-login.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Admin login flow", async ({ page }) => {
|
|
await page.goto("/");
|
|
|
|
await page.fill(
|
|
'input[name="login"]',
|
|
process.env.ADMIN_USERNAME || "admin",
|
|
);
|
|
await page.fill(
|
|
'input[name="password"]',
|
|
process.env.ADMIN_PASSWORD || "password",
|
|
);
|
|
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL("/dashboard", { timeout: 5000 });
|
|
|
|
await page.goto("/admin");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/admin/admin-dashboard.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Registration page", async ({ page }) => {
|
|
await page.goto("/register");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/registration-page.webp",
|
|
fullPage: true,
|
|
});
|
|
|
|
await page.fill('input[name="email"]', "newuser@example.com");
|
|
await page.fill('input[name="username"]', "newuser");
|
|
await page.fill('input[name="password"]', "SecurePass123!");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/registration-form-filled.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
});
|
|
```
|
|
|
|
### `screenshots/user-workflows.spec.ts` - User Guide Screenshots
|
|
|
|
```typescript
|
|
import { test, expect } from "@playwright/test";
|
|
|
|
test.describe("User Guide Screenshots", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto("/");
|
|
await page.fill('input[name="login"]', process.env.USER_USERNAME || "user");
|
|
await page.fill(
|
|
'input[name="password"]',
|
|
process.env.USER_PASSWORD || "password",
|
|
);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL("/dashboard", { timeout: 5000 });
|
|
});
|
|
|
|
test("Dashboard overview", async ({ page }) => {
|
|
await page.goto("/dashboard");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/dashboard-overview.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Library grid view", async ({ page }) => {
|
|
await page.goto("/dashboard");
|
|
|
|
await page.waitForSelector("#libraries-container", { timeout: 5000 });
|
|
|
|
const firstLibrary = page.locator("[data-library]").first();
|
|
if (await firstLibrary.isVisible()) {
|
|
await firstLibrary.click();
|
|
await page.waitForURL(/\/dashboard/, { timeout: 5000 });
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/library-grid.webp",
|
|
fullPage: true,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("Search functionality", async ({ page }) => {
|
|
await page.goto("/dashboard");
|
|
|
|
await page.waitForSelector("#search-input", { timeout: 5000 });
|
|
|
|
await page.fill("#search-input", "science");
|
|
await page.waitForTimeout(1000);
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/search-results.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Filter panel", async ({ page }) => {
|
|
await page.goto("/dashboard");
|
|
|
|
const filterPanel = page.locator(".filter-panel details");
|
|
if (await filterPanel.isVisible()) {
|
|
await filterPanel.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/filter-panel-open.webp",
|
|
fullPage: true,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("Collections list", async ({ page }) => {
|
|
await page.goto("/collections");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/collections-list.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Create collection modal", async ({ page }) => {
|
|
await page.goto("/collections");
|
|
|
|
await page.click(
|
|
'button:has-text("New Collection"), button:has-text("Create Your First Collection")',
|
|
);
|
|
|
|
await page.waitForSelector("#create-modal", {
|
|
state: "visible",
|
|
timeout: 5000,
|
|
});
|
|
|
|
await page.fill("#collection-name", "My Reading List");
|
|
await page.fill("#collection-description", "Books I want to read");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/create-collection-modal.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Reading progress view", async ({ page }) => {
|
|
await page.goto("/progress");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/reading-progress.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Analytics view", async ({ page }) => {
|
|
await page.goto("/analytics");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/user/analytics-view.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
});
|
|
```
|
|
|
|
### `screenshots/admin-workflows.spec.ts` - Admin Guide Screenshots
|
|
|
|
```typescript
|
|
import { test, expect } from "@playwright/test";
|
|
|
|
test.describe("Admin Guide Screenshots", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto("/");
|
|
await page.fill(
|
|
'input[name="login"]',
|
|
process.env.ADMIN_USERNAME || "admin",
|
|
);
|
|
await page.fill(
|
|
'input[name="password"]',
|
|
process.env.ADMIN_PASSWORD || "password",
|
|
);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL("/dashboard", { timeout: 5000 });
|
|
});
|
|
|
|
test("Admin dashboard", async ({ page }) => {
|
|
await page.goto("/admin");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/admin/admin-dashboard.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("User management", async ({ page }) => {
|
|
await page.goto("/admin");
|
|
|
|
const userSection = page.locator('[data-section="users"], text="Users"');
|
|
if (await userSection.isVisible()) {
|
|
await page.screenshot({
|
|
path: "docs/images/admin/user-management.webp",
|
|
fullPage: true,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("Library management", async ({ page }) => {
|
|
await page.goto("/admin/library");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/admin/library-management.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Add library form", async ({ page }) => {
|
|
await page.goto("/admin/library");
|
|
|
|
const addLibraryBtn = page.locator(
|
|
'button:has-text("Add Library"), button:has-text("Create Library")',
|
|
);
|
|
if (await addLibraryBtn.isVisible()) {
|
|
await addLibraryBtn.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/admin/add-library-form.webp",
|
|
fullPage: true,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("Profile settings", async ({ page }) => {
|
|
await page.goto("/admin/profile");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/admin/profile-settings.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Theme selector", async ({ page }) => {
|
|
await page.goto("/admin/profile");
|
|
|
|
const themeSelect = page.locator(
|
|
'select[name="theme"], [data-theme-selector]',
|
|
);
|
|
if (await themeSelect.isVisible()) {
|
|
await themeSelect.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/admin/theme-selector.webp",
|
|
fullPage: true,
|
|
});
|
|
}
|
|
});
|
|
});
|
|
```
|
|
|
|
### `screenshots/device-workflows.spec.ts` - Device Setup Screenshots
|
|
|
|
```typescript
|
|
import { test, expect } from "@playwright/test";
|
|
|
|
test.describe("Device Setup Screenshots", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto("/");
|
|
await page.fill('input[name="login"]', process.env.USER_USERNAME || "user");
|
|
await page.fill(
|
|
'input[name="password"]',
|
|
process.env.USER_PASSWORD || "password",
|
|
);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL("/dashboard", { timeout: 5000 });
|
|
});
|
|
|
|
test("Device list page", async ({ page }) => {
|
|
await page.goto("/devices");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/devices/device-list.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Add device modal", async ({ page }) => {
|
|
await page.goto("/devices");
|
|
|
|
await page.click(
|
|
'button:has-text("Add New Device"), button:has-text("Add Your First Device")',
|
|
);
|
|
|
|
await page.waitForSelector('[data-modal="add-device"], #add-device-modal', {
|
|
state: "visible",
|
|
timeout: 5000,
|
|
});
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/devices/add-device-modal.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Kobo device form", async ({ page }) => {
|
|
await page.goto("/devices");
|
|
|
|
await page.click('button:has-text("Add New Device")');
|
|
|
|
await page.waitForSelector('[data-modal="add-device"]', {
|
|
state: "visible",
|
|
timeout: 5000,
|
|
});
|
|
|
|
const deviceTypeSelect = page.locator('select[name="device_type"]');
|
|
if (await deviceTypeSelect.isVisible()) {
|
|
await deviceTypeSelect.selectOption("kobo");
|
|
await page.waitForTimeout(500);
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/devices/device-form-kobo.webp",
|
|
fullPage: true,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("KOReader device form", async ({ page }) => {
|
|
await page.goto("/devices");
|
|
|
|
await page.click('button:has-text("Add New Device")');
|
|
|
|
await page.waitForSelector('[data-modal="add-device"]', {
|
|
state: "visible",
|
|
timeout: 5000,
|
|
});
|
|
|
|
const deviceTypeSelect = page.locator('select[name="device_type"]');
|
|
if (await deviceTypeSelect.isVisible()) {
|
|
await deviceTypeSelect.selectOption("koreader");
|
|
await page.waitForTimeout(500);
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/devices/device-form-koreader.webp",
|
|
fullPage: true,
|
|
});
|
|
}
|
|
});
|
|
|
|
test("Sync queue", async ({ page }) => {
|
|
await page.goto("/queue");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/devices/sync-queue.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
});
|
|
```
|
|
|
|
### `screenshots/sync-workflows.spec.ts` - Sync Guide Screenshots
|
|
|
|
```typescript
|
|
import { test, expect } from "@playwright/test";
|
|
|
|
test.describe("Sync Guide Screenshots", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto("/");
|
|
await page.fill('input[name="login"]', process.env.USER_USERNAME || "user");
|
|
await page.fill(
|
|
'input[name="password"]',
|
|
process.env.USER_PASSWORD || "password",
|
|
);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL("/dashboard", { timeout: 5000 });
|
|
});
|
|
|
|
test("Sync conflicts page", async ({ page }) => {
|
|
await page.goto("/conflicts");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/sync/sync-conflicts.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
|
|
test("Unlinked books page", async ({ page }) => {
|
|
await page.goto("/unlinked-books");
|
|
|
|
await page.screenshot({
|
|
path: "docs/images/sync/unlinked-books.webp",
|
|
fullPage: true,
|
|
});
|
|
});
|
|
});
|
|
```
|
|
|
|
## Running the Tests
|
|
|
|
### Run all tests
|
|
|
|
```bash
|
|
npx playwright test
|
|
```
|
|
|
|
### Run specific test file
|
|
|
|
```bash
|
|
npx playwright test auth.spec.ts
|
|
```
|
|
|
|
### Run in headed mode (see browser)
|
|
|
|
```bash
|
|
npx playwright test --headed
|
|
```
|
|
|
|
### Run with debug mode
|
|
|
|
```bash
|
|
npx playwright test --debug
|
|
```
|
|
|
|
## Markdown Update Strategy
|
|
|
|
### Option 1: Create New Markdown
|
|
|
|
Generate fresh markdown files with embedded screenshots.
|
|
|
|
### Option 2: Update Existing Markdown
|
|
|
|
Update existing markdown files by:
|
|
|
|
1. Finding section headers
|
|
2. Inserting screenshot references after relevant steps
|
|
3. Using alt text to describe what's shown
|
|
|
|
**Example Markdown Update**:
|
|
|
|
```markdown
|
|
### Step 1: Log In to Bookhoard
|
|
|
|
1. Open your browser and navigate to `http://localhost:8765`
|
|
2. Enter your username and password
|
|
3. Click the **Login** button
|
|
|
|

|
|
```
|
|
|
|
## Workflow When Ready
|
|
|
|
### Step 1: Verify Frontend Complete
|
|
|
|
- Check all pages listed in "Prerequisites" are working
|
|
- Confirm no TODO placeholders in templates
|
|
|
|
### Step 2: Seed Test Data
|
|
|
|
```bash
|
|
# Add sample books, collections, devices
|
|
# Create test admin and user accounts
|
|
```
|
|
|
|
### Step 3: Set Up Playwright
|
|
|
|
```bash
|
|
npm install -D @playwright/test
|
|
npx playwright install chromium
|
|
```
|
|
|
|
### Step 4: Create Environment File
|
|
|
|
Create `.env.screenshots` with the test credentials
|
|
|
|
### Step 5: Run Screenshot Scripts
|
|
|
|
```bash
|
|
# Run all screenshot workflows
|
|
npx playwright test
|
|
|
|
# Or run specific suites
|
|
npx playwright test auth.spec.ts
|
|
npx playwright test user-workflows.spec.ts
|
|
```
|
|
|
|
### Step 6: Review and Adjust
|
|
|
|
- Manually review screenshots
|
|
- Retake any that need adjustment
|
|
- Update markdown files if needed
|
|
|
|
## Customizing for Your Implementation
|
|
|
|
When you're ready to run these, you may need to update:
|
|
|
|
1. **Selectors**: Update CSS selectors to match your actual HTML structure
|
|
2. **Routes**: Ensure all routes match your router configuration
|
|
3. **Wait conditions**: Adjust timeouts based on your app's performance
|
|
4. **Modal IDs**: Update modal selectors to match your implementation
|
|
|
|
## Tips for Better Screenshots
|
|
|
|
1. **Wait for animations**: Add `page.waitForTimeout(500)` after clicking to let animations complete
|
|
2. **Full page vs viewport**: Use `fullPage: true` for complete page screenshots
|
|
3. **Hide scrollbars**: Use `page.evaluate(() => document.body.style.overflow = 'hidden')` for cleaner screenshots
|
|
4. **Dark theme**: Most screenshots will use the default Tokyo Night theme
|
|
5. **Clean test data**: Use consistent test data for reproducible screenshots
|
|
|
|
## Estimated Time Investment
|
|
|
|
| Task | Time |
|
|
| ------------------------------ | ------------- |
|
|
| Install & configure Playwright | 15 min |
|
|
| Seed test database | 30 min |
|
|
| Write Playwright scripts | 2-3 hours |
|
|
| Run screenshot automation | 10 min |
|
|
| Review & retake screenshots | 30-60 min |
|
|
| Update markdown files | 30-60 min |
|
|
| **Total** | **4-6 hours** |
|
|
|
|
## Future Enhancements
|
|
|
|
- **Visual Regression Testing**: Use Playwright to detect UI changes
|
|
- **Multi-theme Screenshots**: Automatically capture all theme variations
|
|
- **Multi-language Screenshots**: Capture screenshots in different locales
|
|
- **Auto-update on Deploy**: Run screenshots as part of CI/CD on documentation updates
|
|
|
|
## References
|
|
|
|
- [Playwright Documentation](https://playwright.dev/)
|
|
- [Playwright Screenshots API](https://playwright.dev/docs/screenshots)
|
|
- [Bookhoard Frontend Templates](templates/)
|
|
- [Bookhoard API Documentation](docs/developer/api/)
|
|
|
|
---
|
|
|
|
**Created by**: GLM (OpenCode)
|
|
**Version**: 1.0
|
|
**Ready for Implementation**: When frontend development is complete
|