Fix BUG-001 and BUG-002; add post-submission QA test plan and QA entry

BUG-001: cache-on-save plugin clears page cache on onFormProcessed so
new entries appear in the tracker feed immediately after submission.

BUG-002: disabled Twig template cache (twig.cache: false) so theme
file changes take effect without a manual cache flush.

Also adds bugs-and-fixes.md, corrects TC-P test URLs (.entry suffix),
fixes TC-P.1 expectation (inline login form, not a redirect), and
creates the QA test entry for automated scenario verification.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 13:21:26 +02:00
parent 03ebdd6acb
commit fdb423d2c7
7 changed files with 321 additions and 1 deletions
+1
View File
@@ -1,2 +1,3 @@
/plugins/ /plugins/
!/plugins/cache-on-save/
/data/ /data/
+1 -1
View File
@@ -125,7 +125,7 @@ cache:
server: null server: null
port: null port: null
twig: twig:
cache: true cache: false
debug: true debug: true
auto_reload: true auto_reload: true
autoescape: true autoescape: true
+104
View File
@@ -0,0 +1,104 @@
# Bugs & Fixes
Backlog of confirmed bugs with root cause analysis and implementation spec for the fix.
---
## BUG-001 — New entry not visible after form submission
**Status:** fixed 2026-06-18
**Reported:** 2026-06-18
### Symptom
After submitting a new post via `/post`, the entry page file is created correctly on disk but does not appear in the `/tracker` feed or in the Grav Admin panel until the cache is manually flushed.
### Root cause
Grav's page-tree cache (`cache/doctrine/`) is not invalidated when `add-page-by-form` writes a new page to disk. The tracker template uses `page.children`, which Grav serves from cache — so the new child page is invisible until the cache is cleared.
### Workaround (manual)
Run in terminal after each submission:
```bash
docker exec intotheeast_grav bash -c "cd /app/www/public && php bin/grav clearcache"
```
### Fix spec
Wire cache-clear into the form process so it happens automatically on every successful submission.
**Approach — custom Grav plugin event hook:**
1. Create a small plugin `user/plugins/cache-on-save/` with one event listener:
- Listen on `onFormProcessed`
- When the form name is `new-entry`, call `$this->grav['cache']->clear()`
2. Enable the plugin in `user/config/plugins/cache-on-save.yaml`
This is the cleanest approach: it fires exactly once per successful submission, requires no changes to `post-form.md`, and works for any future forms too.
**Alternative — disable page cache entirely:**
Set `cache: { enabled: false }` in `system.yaml`. Simpler but degrades frontend performance; not recommended for production.
### Files to create/modify
| File | Change |
|------|--------|
| `user/plugins/cache-on-save/cache-on-save.php` | New plugin, ~30 lines |
| `user/plugins/cache-on-save/cache-on-save.yaml` | Plugin manifest, enabled: true |
| `user/config/plugins/cache-on-save.yaml` | Runtime config, enabled: true |
### Acceptance criteria
1. Submit a new post via `/post`
2. Navigate to `/tracker` — the new entry is visible immediately, no manual cache flush needed
3. Grav Admin also shows the new page immediately
---
## BUG-002 — Stale Twig cache after theme file changes
**Status:** fixed 2026-06-18
**Reported:** 2026-06-18
### Symptom
After theme template files are added or modified (e.g., creating `partials/base.html.twig`), Grav's Twig compiled-template cache still holds the old compiled version. Pages that extend the changed file throw 500 errors like "Template partials/base.html.twig is not defined" even though the file exists on disk.
### Root cause
Grav caches compiled Twig templates in `cache/twig/`. When a new file is added, existing templates that reference it don't know to recompile — their cache entries are still valid from their own mtime perspective.
### Workaround (manual)
Run after any theme file is added or changed:
```bash
docker exec intotheeast_grav bash -c "cd /app/www/public && php bin/grav clearcache"
```
### Fix spec
Disable Twig template caching in development via `user/config/system.yaml`:
```yaml
twig:
cache: false
```
Acceptable for a single-user dev setup — eliminates both BUG-001's side-effect and this bug entirely. Performance cost is negligible at one-user scale. On production, leave Twig cache enabled (it's fine there because template files don't change at runtime).
**Files to change:**
| File | Change |
|------|--------|
| `user/config/system.yaml` | Add `twig: { cache: false }` under development section |
### Acceptance criteria
1. Add a new theme template file
2. Reload any page — no 500 error, template works immediately without manual cache flush
---
+173
View File
@@ -319,6 +319,179 @@ Test URLs:
--- ---
## Post Submission Flow
These scenarios cover the full round-trip: filling the form → saving → verifying values in the UI and on disk. Use the exact test values specified so that each assertion can be precise.
**Test data (use verbatim):**
| Field | Value |
|---|---|
| Title | `QA Test Entry` |
| Date & Time | `2026-06-18 10:00` |
| Content | `This is the QA test body. Second sentence for length.` |
| City | `Tokyo` |
| Country | `Japan` |
| Latitude | `35.689487` |
| Longitude | `139.691711` |
| Photos | none (keep simple for first run) |
---
### TC-P.1: Post form requires authentication
| Step | Action | Expected Result |
|---|---|---|
| 1 | Open private/incognito tab (no session) | — |
| 2 | GET http://100.96.115.96:8081/post | Page loads at /post URL (no redirect) but renders the login form inline |
| 3 | Inspect page content | Login form fields (username, password) visible; post form fields absent |
**Automation:** curl /post without auth; assert `login-form-nonce` present AND `data[title]` absent
---
### TC-P.2: Post form renders all fields
| Step | Action | Expected Result |
|---|---|---|
| 1 | Log in at /login | Redirected to /tracker |
| 2 | Navigate to /post | Post form page loads (200) |
| 3 | Check form fields present | Title, Date & Time, description textarea, Photos upload |
| 4 | Check location fields | Latitude, Longitude, City, Country inputs visible |
| 5 | Check action buttons | `📍 Get Current Location` and `🌤 Get Weather` buttons visible |
| 6 | Check submit button | `Post Entry` button visible |
| 7 | Check date field default | Pre-filled with today's date and time (not blank) |
**Automation:** curl /post with auth; grep for `data[title]`, `data[lat]`, `data[location_city]`, `get-location`, `get-weather`
---
### TC-P.3: Required field validation
| Step | Action | Expected Result |
|---|---|---|
| 1 | Log in and open /post | Form loads |
| 2 | Leave Title blank, fill in only the description | — |
| 3 | Submit form | Page reloads with validation error on Title |
| 4 | Error message | Indicates title is required |
| 5 | Fill in Title, clear Description/Content, submit | Validation error on Content field |
| 6 | Confirm | No new entry file created in pages/01.tracker/ during failed submissions |
**Manual verification required:** Validation feedback requires browser
---
### TC-P.4: Successful post submission — all fields
| Step | Action | Expected Result |
|---|---|---|
| 1 | Log in and open /post | Form loads |
| 2 | Enter Title: `QA Test Entry` | — |
| 3 | Set Date to `2026-06-18 10:00` | — |
| 4 | Enter Content: `This is the QA test body. Second sentence for length.` | — |
| 5 | Enter City: `Tokyo`, Country: `Japan` | — |
| 6 | Enter Latitude: `35.689487`, Longitude: `139.691711` | — |
| 7 | Leave Photos empty | — |
| 8 | Click `Post Entry` | Form submits (POST to /post) |
| 9 | Observe result | Success message `Entry posted successfully!` shown on page |
| 10 | Form state | Form is reset / fields cleared |
**Manual verification required:** Form submission and success message require browser
---
### TC-P.5: Entry file created on disk with correct values
| Step | Action | Expected Result |
|---|---|---|
| 1 | After TC-P.4 completes | — |
| 2 | Check directory `user/pages/01.tracker/` | Folder `2026-06-18.entry/` exists (add-page-by-form appends template name per `physical_template_name: true`) |
| 3 | Read `user/pages/01.tracker/2026-06-18.entry/entry.md` | File exists |
| 4 | Verify frontmatter `title` | Equals `QA Test Entry` |
| 5 | Verify frontmatter `date` | Equals `2026-06-18 10:00` |
| 6 | Verify frontmatter `location_city` | Equals `Tokyo` |
| 7 | Verify frontmatter `location_country` | Equals `Japan` |
| 8 | Verify frontmatter `lat` | Equals `35.689487` |
| 9 | Verify frontmatter `lng` | Equals `139.691711` |
| 10 | Verify frontmatter `template` | Equals `entry` |
| 11 | Verify frontmatter `published` | Equals `true` |
| 12 | Verify page body | Contains `This is the QA test body. Second sentence for length.` |
**Automation:** Read file from filesystem; parse YAML frontmatter; assert each field value exactly
---
### TC-P.6: Entry appears in tracker feed
| Step | Action | Expected Result |
|---|---|---|
| 1 | BUG-001 fixed — no manual cache clear needed | — |
| 2 | GET http://100.96.115.96:8081/tracker | Page loads (200) |
| 3 | Entry card present | Card with title `QA Test Entry` visible |
| 4 | Date shown on card | `18 Jun 2026` |
| 5 | Location badge on card | `📍 Tokyo, Japan` visible |
| 6 | Entry card link | `href` points to `/tracker/2026-06-18.entry` |
| 7 | Excerpt shown | Partial text of the body content visible |
**Automation:** curl /tracker; grep for "QA Test Entry", "18 Jun 2026", "Tokyo", "Japan", "/tracker/2026-06-18.entry"
---
### TC-P.7: Entry detail page shows correct values
| Step | Action | Expected Result |
|---|---|---|
| 1 | GET http://100.96.115.96:8081/tracker/2026-06-18.entry | Page loads (200) |
| 2 | Page title | `QA Test Entry` in `<h1>` |
| 3 | Date header | `Thursday, 18 June 2026` (or locale equivalent) |
| 4 | Location badge | `📍 Tokyo, Japan` |
| 5 | Body content | Full text `This is the QA test body. Second sentence for length.` rendered |
| 6 | No gallery | Photo gallery section absent (no photos were uploaded) |
| 7 | Back link | `← Back to journal` link present, points to /tracker |
**Automation:** curl /tracker/2026-06-18.entry; grep for "QA Test Entry", "Tokyo", "Japan", "This is the QA test body", "Back to journal"
---
### TC-P.8: Entry appears on map and mini-map
| Step | Action | Expected Result |
|---|---|---|
| 1 | GET http://100.96.115.96:8081/tracker | Mini-map section visible |
| 2 | Inspect FEED_ENTRIES JSON | Contains entry with `lat: "35.689487"`, `lng: "139.691711"`, `title: "QA Test Entry"` |
| 3 | GET http://100.96.115.96:8081/map | Map page loads |
| 4 | Inspect ENTRIES JSON | Contains same entry |
**Automation:** curl /tracker and /map; grep FEED_ENTRIES and ENTRIES JSON for lat/lng values
---
### TC-P.9: Entry appears in stats
| Step | Action | Expected Result |
|---|---|---|
| 1 | GET http://100.96.115.96:8081/stats | Page loads (200) |
| 2 | Entries count | Shows `2` entries (existing test entry + new QA entry) |
| 3 | Countries list | `Japan` and `Netherlands` both listed |
**Automation:** curl /stats; grep entry count and country names
---
### TC-P.10: Duplicate date handling
| Step | Action | Expected Result |
|---|---|---|
| 1 | Submit a second post with the same date `2026-06-18 10:00` | — |
| 2 | Observe result | Either: error shown, OR new entry created at a different slug |
| 3 | Check filesystem | No silent data loss — original entry intact |
**Note:** `overwrite_mode: false` on add-page-by-form plugin should prevent overwrite. Behavior on conflict is to be documented here after first run.
**Manual verification required:** Requires two submissions with same date
---
## Cross-cutting Tests ## Cross-cutting Tests
### TC-X.1: Nav links present on all pages ### TC-X.1: Nav links present on all pages
@@ -0,0 +1,15 @@
---
title: 'QA Test Entry'
date: '2026-06-18 10:00'
template: entry
published: true
hero_image: ''
lat: '35.689487'
lng: '139.691711'
location_city: 'Tokyo'
location_country: 'Japan'
weather_temp_c: ''
weather_desc: ''
---
This is the QA test body. Second sentence for length.
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace Grav\Plugin;
use Grav\Common\Plugin;
use RocketTheme\Toolbox\Event\Event;
class CacheOnSavePlugin extends Plugin
{
public static function getSubscribedEvents(): array
{
return [
'onFormProcessed' => ['onFormProcessed', 0],
];
}
public function onFormProcessed(Event $event): void
{
$form = $event['form'];
if (!$form) {
return;
}
if ($form->getName() === 'new-entry') {
$this->grav['cache']->clear();
}
}
}
+1
View File
@@ -0,0 +1 @@
enabled: true