QA Testing Plan: Blog
This document covers QA test cases for the Blog business app located at /business/apps/blog. The app supports creating and editing articles with rich content, cover images, categories, tags, publish/draft lifecycle, and URL-based status filtering.
Routes Under Test
Section titled “Routes Under Test”| Route | Purpose |
|---|---|
/business/apps/blog | Article list with status filter tabs and category display |
/business/apps/blog/new | Create article form (superforms + createArticleSchema) |
/business/apps/blog/[id] | Article detail with publish, unpublish, and delete actions |
/business/apps/blog/categories | Category CRUD (create, update, delete) |
Form Actions Reference
Section titled “Form Actions Reference”| Route | Action | Method | Key Fields |
|---|---|---|---|
/business/apps/blog/new | default | POST | title, excerpt, content, cover_image_url, category_id, is_featured, allow_comments, tags |
/business/apps/blog/[id] | publish | POST | (none) |
/business/apps/blog/[id] | unpublish | POST | (none) |
/business/apps/blog/[id] | delete | POST | (none) |
/business/apps/blog/categories | create | POST | name, description (via createCategorySchema) |
/business/apps/blog/categories | update | POST | id, name, description |
/business/apps/blog/categories | delete | POST | id |
1. Article List and Filtering
Section titled “1. Article List and Filtering”| Test ID | Description | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| BLOG-001 | Article list loads for authenticated user | User is logged in with articles | 1. Navigate to /business/apps/blog | Article list is displayed with title, cover image, category name, date, and status badge. | P0 |
| BLOG-002 | Empty state when no articles | User is logged in with no articles | 1. Navigate to /business/apps/blog | Empty state “No articles found.” with CTA “Write your first article”. | P1 |
| BLOG-003 | Unauthenticated user gets empty data | User is not logged in | 1. Navigate to /business/apps/blog | Page loads with empty articles, categories, and empty filters. No crash. | P1 |
| BLOG-004 | Status filter: All (default) | User has draft and published articles | 1. Navigate to /business/apps/blog (no query params) | All articles are shown. “All” tab is highlighted. | P0 |
| BLOG-005 | Status filter: Draft via URL | User has draft articles | 1. Navigate to /business/apps/blog?status=draft | Only draft articles are shown. “Draft” tab is highlighted. | P0 |
| BLOG-006 | Status filter: Published via URL | User has published articles | 1. Navigate to /business/apps/blog?status=published | Only published articles are shown. “Published” tab is highlighted. | P0 |
| BLOG-007 | Status filter tabs update URL | User is logged in | 1. Click “Draft” tab 2. Click “Published” tab 3. Click “All” tab | URL updates to ?status=draft, ?status=published, and clears the param respectively. Uses replaceState. | P0 |
| BLOG-008 | Category name displays from category map | Articles have category_id set | 1. View article list | Category name is resolved from the category map. Articles without category show “Uncategorized”. | P1 |
| BLOG-009 | Status badges render correctly | Draft and published articles exist | 1. View article list | draft = secondary badge, published = default badge. | P2 |
| BLOG-010 | Cover image displays when available | Article has cover_image_url | 1. View article list | Cover image thumbnail renders as 10x10 rounded image. | P2 |
| BLOG-011 | Placeholder icon when no cover image | Article has no cover_image_url | 1. View article list | ArticleIcon placeholder renders in a muted background. | P2 |
2. Create Article
Section titled “2. Create Article”| Test ID | Description | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| BLOG-020 | Create article with required fields | User is logged in | 1. Navigate to /business/apps/blog/new 2. Fill title 3. Submit | Article is created. User is redirected to /business/apps/blog/{new_id}?new=1. | P0 |
| BLOG-021 | Create article with all fields | User is logged in | 1. Fill title, excerpt, content, cover_image_url, category_id, is_featured, allow_comments, tags 2. Submit | Article is created with all fields persisted. | P0 |
| BLOG-022 | Validation: empty title rejected | User is logged in | 1. Leave title blank 2. Submit | Form returns 400 with error “Title is required”. | P0 |
| BLOG-023 | Validation: title exceeds 255 characters | User is logged in | 1. Enter title with 256+ characters 2. Submit | Form returns 400 with max length error. | P1 |
| BLOG-024 | Validation: excerpt exceeds 500 characters | User is logged in | 1. Enter excerpt with 501+ characters 2. Submit | Form returns 400 with max length error. | P1 |
| BLOG-025 | Tags array persists correctly | User is logged in | 1. Provide tags as an array of strings 2. Submit | Article is created with tags stored. | P1 |
| BLOG-026 | is_featured boolean persists | User is logged in | 1. Set is_featured: true 2. Submit | Article is created with is_featured: true. | P2 |
| BLOG-027 | allow_comments boolean persists | User is logged in | 1. Set allow_comments: false 2. Submit | Article is created with allow_comments: false. | P2 |
| BLOG-028 | Categories loaded for selection on create page | User is logged in with categories | 1. Navigate to /business/apps/blog/new | categories array is populated for the category dropdown. | P1 |
| BLOG-029 | Server error returns message to form | User is logged in, service throws | 1. Trigger server error during creation | Form returns 500 with error message via superforms. | P1 |
| BLOG-030 | Unauthenticated user redirected from create page | User is not logged in | 1. Navigate to /business/apps/blog/new | User is redirected to /login. | P0 |
3. Article Detail and Lifecycle
Section titled “3. Article Detail and Lifecycle”| Test ID | Description | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| BLOG-040 | Load article detail page | User is the author | 1. Navigate to /business/apps/blog/{id} | Article data loads with categories for editing. | P0 |
| BLOG-041 | 404 for non-existent article | User is logged in | 1. Navigate to /business/apps/blog/{invalid_id} | 404 error “Article not found” is returned. | P0 |
| BLOG-042 | 403 for article owned by another user | User is not the article author | 1. Navigate to /business/apps/blog/{other_user_article} | 403 Forbidden error is returned. | P0 |
| BLOG-043 | Publish a draft article | User is the author of a draft article | 1. Submit the publish action | Returns { published: true }. Article status updates to “published”. | P0 |
| BLOG-044 | Unpublish a published article | User is the author of a published article | 1. Submit the unpublish action | Returns { unpublished: true }. Article status reverts to “draft”. | P0 |
| BLOG-045 | Delete an article | User is the author | 1. Submit the delete action | Article is deleted. User is redirected to /business/apps/blog. | P0 |
| BLOG-046 | Publish action handles server error | User is the author, service throws | 1. Trigger server error during publish | Returns 500 with error message “Failed to publish”. | P1 |
| BLOG-047 | Unpublish action handles server error | User is the author, service throws | 1. Trigger server error during unpublish | Returns 500 with error message “Failed to unpublish”. | P1 |
| BLOG-048 | Delete action handles server error | User is the author, service throws | 1. Trigger server error during delete | Returns 500 with error message “Failed to delete”. | P1 |
4. Category Management
Section titled “4. Category Management”| Test ID | Description | Preconditions | Steps | Expected Result | Priority |
|---|---|---|---|---|---|
| BLOG-060 | Categories page loads with list and form | User is logged in | 1. Navigate to /business/apps/blog/categories | Category list and create form are displayed. | P0 |
| BLOG-061 | Create category with valid data | User is logged in | 1. Fill name and optional description 2. Submit create action | Category is created. Success message “Category created!” is returned. | P0 |
| BLOG-062 | Create category validation failure | User is logged in | 1. Submit create with invalid data per createCategorySchema | Returns 400 with form errors. | P1 |
| BLOG-063 | Create category handles server error | User is logged in, service throws | 1. Trigger server error during creation | Returns 500 with error message via superforms. | P1 |
| BLOG-064 | Update category name and description | User is logged in with categories | 1. Submit update with id, name, optional description | Category is updated. Returns { updated: true }. | P0 |
| BLOG-065 | Update category fails without id or name | User is logged in | 1. Submit update without id or name | Returns 400 “Missing fields”. | P1 |
| BLOG-066 | Update category handles server error | User is logged in, service throws | 1. Trigger server error | Returns 500 with error message. | P1 |
| BLOG-067 | Delete category by ID | User is logged in with categories | 1. Submit delete with id | Category is deleted. Returns { deleted: true }. | P0 |
| BLOG-068 | Delete category fails without id | User is logged in | 1. Submit delete without id | Returns 400 “Missing category ID”. | P1 |
| BLOG-069 | Delete category handles server error | User is logged in, service throws | 1. Trigger server error | Returns 500 with error message. | P1 |
| BLOG-070 | Create category requires authentication | User is not logged in | 1. POST to ?/create | Returns 401 fail. | P1 |