Files
wp-multi-city/docs/superpowers/plans/2026-05-17-s6-ci-qa-checklist.md
T
2026-05-17 00:07:57 +05:00

601 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# S6 — CI workflow + Manual QA Checklist Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add Gitea Actions CI workflow that runs PHPUnit on push/PR, write a manual QA checklist covering all S2-S5 acceptance criteria from cp-cw4, and update README with testing instructions.
**Architecture:** Three documentation/config artifacts. No PHP code. No new tests. The existing 98-test Brain Monkey suite covers all functional areas at call-signature level; S6 adds infrastructure (CI) and human verification (manual checklist) to close out the testing story.
**Tech Stack:** Gitea Actions (GitHub-Actions-compatible YAML), Markdown, existing PHPUnit suite untouched.
**Spec:** `docs/superpowers/specs/2026-05-16-s6-ci-qa-checklist-design.md`
**Beads:** `cp-la7`
---
## Working Directory
```
/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city
```
---
## File Structure
```
wp-multi-city/
├── .gitea/workflows/test.yml # CREATE — Gitea Actions CI workflow
├── docs/QA-CHECKLIST.md # CREATE — 13-section manual QA checklist
└── README.md # MODIFY — Testing section + roadmap S6=done
```
No PHP changes. No phpunit changes. Existing 98 tests must remain green throughout.
---
## Task 1: Gitea Actions CI workflow
**Files:**
- Create: `.gitea/workflows/test.yml`
- [ ] **Step 1.1: Create the workflow file**
Create directory + file:
```bash
mkdir -p "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city/.gitea/workflows"
```
Write `.gitea/workflows/test.yml`:
```yaml
name: tests
on:
push:
branches: [main]
pull_request:
jobs:
phpunit:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup PHP 8.0
uses: shivammathur/setup-php@v2
with:
php-version: '8.0'
coverage: none
tools: composer:v2
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: vendor
key: ${{ runner.os }}-php8.0-${{ hashFiles('composer.json') }}
restore-keys: |
${{ runner.os }}-php8.0-
- name: Install dependencies
run: composer install --no-progress --prefer-dist --no-interaction
- name: Run PHPUnit
run: vendor/bin/phpunit
```
- [ ] **Step 1.2: Verify YAML syntax**
Check the file parses as valid YAML using PHP:
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
php -r 'var_dump(yaml_parse_file(".gitea/workflows/test.yml"));' 2>&1 | head -20
```
If the `yaml` PHP extension is not installed, fall back to a Python check:
```bash
python3 -c "import yaml; yaml.safe_load(open('.gitea/workflows/test.yml'))" && echo OK
```
Expected: parsed structure dumped or `OK` printed. If neither tool is available, eyeball-check the indentation against the source above (Gitea Actions tolerates indentation errors poorly).
- [ ] **Step 1.3: Verify existing tests still green**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
vendor/bin/phpunit
```
Expected: OK (98 tests, 175 assertions). The CI file shouldn't affect runtime tests — this is a sanity check that no accidental file move broke the suite.
- [ ] **Step 1.4: Commit**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
git add .gitea/workflows/test.yml
git commit -m "ci(wpmc S6): Gitea Actions workflow for PHPUnit on push/PR"
```
---
## Task 2: Manual QA checklist
**Files:**
- Create: `docs/QA-CHECKLIST.md`
- [ ] **Step 2.1: Write the full checklist**
Create `docs/QA-CHECKLIST.md` with this exact content:
````markdown
# WP Multi City — Manual QA Checklist
End-to-end verification against a real WordPress installation. Run through every section before any release. Each section maps to a specific acceptance criterion from the MVP epic (`cp-cw4`).
**Test environment:**
- WordPress 6.0+ (clean install, no other plugins beyond ACF Pro)
- PHP 8.0+
- Advanced Custom Fields Pro 6.0+ activated
**Tester:** ______________ **Date:** ______________ **Plugin version:** ______________
---
## 1. Plugin activation
**cp-cw4 acceptance:** "Плагин активируется на чистом WP с ACF Pro"
Preconditions: clean WP 6.0+ with activated ACF Pro.
Steps:
1. Copy plugin to `wp-content/plugins/wp-multi-city/`.
2. Go to admin → Plugins → activate "WP Multi City".
Expected:
- No PHP errors in `debug.log` (with `WP_DEBUG=true`).
- No admin notice about missing ACF Pro.
- Admin menu shows: "Шорткоды" (top-level), "WP Multi City" (top-level).
- [ ] Section pass
---
## 2. ACF dependency notice
Preconditions: WP with ACF Pro **deactivated**.
Steps:
1. Deactivate ACF Pro.
2. Refresh admin.
Expected:
- Admin notice visible: "WP Multi City: требуется активный плагин Advanced Custom Fields Pro".
- No "Шорткоды" or "WP Multi City" menu items (plugin self-disabled).
After verification, reactivate ACF Pro for the rest of the checklist.
- [ ] Section pass
---
## 3. Taxonomy `town` admin UI
**cp-cw4 acceptance:** "В админке создаётся taxonomy town"
Preconditions: §1 passed.
Steps:
1. Open admin → Шорткоды → Города.
2. Create term with slug `spb`, name "Санкт-Петербург".
3. Create term with slug `tyumen`, name "Тюмень".
Expected:
- Both terms appear in the list.
- No "Hierarchical" parent-picker (taxonomy is flat).
- No public archive page accessible at `/town/spb/` (404 expected — taxonomy is non-public).
- [ ] Section pass
---
## 4. CPT `shortcode-town` admin UI
**cp-cw4 acceptance:** "В админке создаётся CPT shortcode-town"
Preconditions: §3 passed.
Steps:
1. Open admin → Шорткоды → Добавить.
2. Observe the edit screen.
3. Set title "Shortcode #1", save as Published (or Draft).
4. Note the post ID (e.g., 42) from the URL.
Expected:
- Edit screen shows: title field + ACF repeater "Подстановки по городам" (`list_shortcode`) with two sub-fields "Город" (taxonomy dropdown of town terms) + "Значение" (textarea).
- No editor / content / featured-image fields (CPT `supports=['title']` only).
- Post saves successfully.
- [ ] Section pass
---
## 5. Options page UI
Preconditions: §3 passed.
Steps:
1. Open admin → WP Multi City.
Expected:
- ACF options page renders with 4 fields:
- `main_town_slug` — taxonomy dropdown of town terms.
- `index_town_alt` — page picker.
- `page_unic` — repeater with 3 sub-fields (page_old, page_new, town).
- `page_no_rep` — repeater with 1 sub-field (link).
- Set `main_town_slug = spb` and save.
- [ ] Section pass
---
## 6. Helper functions smoke
**cp-cw4 acceptance:** "Шаблоны темы могут вызвать current_town(), _alt_get_field()"
Preconditions: §5 passed. Create page "About" via admin → Pages → Add New, slug `about`, note ID (e.g., 10).
Steps:
1. Add this to `wp-content/themes/<active-theme>/functions.php` (or as a mu-plugin):
```php
add_action('wp_footer', function () {
error_log('wpmc_current_town: ' . var_export(wpmc_current_town(), true));
error_log('wpmc_remove_domain_link: ' . wpmc_remove_domain_link('https://example.com/foo/'));
$term = wpmc_get_term_by_slug('tyumen');
error_log('wpmc_get_term_by_slug: ' . ($term ? $term->slug : 'null'));
});
```
2. Open `/tyumen/about/` (front-end). Check `wp-content/debug.log`.
3. Open `/` (front-end). Check log again.
Expected:
- `/tyumen/about/` log: `wpmc_current_town: 'tyumen'`, `wpmc_remove_domain_link: '/foo/'`, `wpmc_get_term_by_slug: 'tyumen'`.
- `/` log: `wpmc_current_town: 'spb'` (from main_town_slug option fallback).
Clean up: remove the debug snippet from functions.php.
- [ ] Section pass
---
## 7. `[shortcode_dmr]` resolves by current town
**cp-cw4 acceptance:** "[shortcode_dmr id=N] подменяется на значение для текущего города"
Preconditions: §6 passed. Open the shortcode-town post created in §4 (ID=42). Add two rows to `list_shortcode`:
- Row 1: Город = spb, Значение = `СПб-текст`
- Row 2: Город = tyumen, Значение = `Тюмень-текст`
Save the shortcode-town post.
Steps:
1. Create page "Test", slug `test`, content: `[shortcode_dmr id=42]`. Publish.
2. Open `/tyumen/test/`.
3. Open `/test/` (no city prefix).
4. Open `/nonexistent-city/test/` (no matching term).
Expected:
- `/tyumen/test/` displays `Тюмень-текст`.
- `/test/` displays `СПб-текст` (current_town falls back to main_town_slug=spb).
- `/nonexistent-city/test/` displays `СПб-текст` (first segment not a town slug → main town fallback) or empty depending on permalink resolution.
- [ ] Section pass
---
## 8. URL router patches `$wp_query`
**cp-cw4 acceptance:** "На /{city-slug}/{page-slug}/ открывается соответствующая страница"
Preconditions: §6 passed (page About ID=10 exists). §5 with main_town_slug=spb.
Steps:
1. Open `/tyumen/about/`.
2. View page source (or template debug output) — check `get_queried_object_id()` value.
3. Open `/spb/about/`.
Expected:
- `/tyumen/about/` loads page About (id=10) content; `get_queried_object_id()` = 10.
- In a template, `get_query_var('town')` returns `'tyumen'`.
- `/spb/about/` returns 404 OR loads About — main town routing is a no-op (Router skips), so default WP rewrite rules govern.
- [ ] Section pass
---
## 9. `/{slug}/` root with `main_page_alt`
Preconditions: §8 passed. Create page "Главная Тюмень", slug `glavnaya-tyumen`, note ID (e.g., 20). On the `tyumen` term (admin → Шорткоды → Города → tyumen → Edit), set ACF field `main_page_alt` = 20. Save term.
Steps:
1. Open `/tyumen/`.
Expected:
- Page Главная Тюмень (id=20) loads.
- `is_front_page()` returns true in template.
- `get_queried_object_id()` returns 20.
- [ ] Section pass
---
## 10. `page_unic` clone swap
**cp-cw4 acceptance:** "На /{city-slug}/{page-slug}/ открывается соответствующая страница" (with clone)
Preconditions: §8 passed (page About ID=10 exists). Create clone page "About-Тюмень", slug `about-tyumen`, ID=21, content "Тюменский About-content".
In admin → WP Multi City → page_unic, add one row: page_old=10 (About), page_new=21 (About-Тюмень), town=tyumen. Save options.
Steps:
1. Open `/tyumen/about/`.
2. Inspect page content.
Expected:
- URL bar stays `/tyumen/about/`.
- Content rendered is "Тюменский About-content" (from page 21, the clone).
- `get_queried_object_id()` returns 21 (not 10).
- [ ] Section pass
---
## 11. URL filters — town prefix injection
**cp-cw4 acceptance:** (implied) Links in templates auto-prefix with city slug.
Preconditions: §8 passed. The active theme renders a primary navigation menu. Add menu items: link to `/uslugi/` (custom link), link to page About.
Steps:
1. Open `/tyumen/about/`.
2. Inspect HTML of menu items (view source). Inspect any ACF link field used by the theme. Add a template snippet that outputs `home_url('/contact/')` and `home_url('/tyumen/contact/')` separately.
3. Open `/` (main town context).
Expected on `/tyumen/about/`:
- Custom menu link `/uslugi/` rendered as `https://<host>/tyumen/uslugi/` (or `/tyumen/uslugi/`).
- About menu link (originally `/about/`) rendered with `/tyumen/` prefix.
- `home_url('/contact/')` returns `https://<host>/tyumen/contact/`.
- `home_url('/tyumen/contact/')` returns `https://<host>/tyumen/contact/` (no double-prefix).
- ACF link field `/internal/` value rendered as `/tyumen/internal/`.
- mailto: and tel: links unchanged.
- External `https://other-domain.com/` links unchanged.
Expected on `/`:
- All URLs unchanged (current_town == main_town_slug → no prefix).
- [ ] Section pass
---
## 12. `page_no_rep` exclusions
Preconditions: §11 passed. In admin → WP Multi City → page_no_rep, add row: link = `/privacy/`. Save. Create page "Privacy", slug `privacy`, ID=30.
Steps:
1. Open `/privacy/`.
2. Open `/tyumen/privacy/`.
3. Check menu/template link to `/privacy/` from a `/tyumen/...` context.
Expected:
- `/privacy/` loads page Privacy normally, no city routing.
- `/tyumen/privacy/` — Router does not patch `$wp_query` (path excluded); WP either 404s or falls back depending on rewrite rules.
- A link to `/privacy/` rendered from `/tyumen/about/` stays `/privacy/` (NOT prefixed to `/tyumen/privacy/`).
- [ ] Section pass
---
## 13. REST API URL
**cp-cw4 acceptance:** (implied) REST API URL correct under host alias / proxy.
Preconditions: REST API enabled (default).
Steps:
1. In a template, add: `<?php echo esc_html(rest_url('wp/v2/posts')); ?>`. Render any front-end page.
2. Open the printed URL in browser or via curl.
Expected:
- Rendered URL: `https://<HTTP_HOST>/wp-json/wp/v2/posts` (HTTPS forced, HTTP_HOST used).
- HTTP request returns JSON array of posts (200 OK).
- If `HTTP_HOST` differs from `site_url()`'s host, the rendered URL prefers `HTTP_HOST`.
- [ ] Section pass
---
## Release sign-off
- [ ] All 13 sections passed
- [ ] PHPUnit suite green (`vendor/bin/phpunit` → OK 98 tests, 175 assertions)
- [ ] PHP lint clean (`for f in includes/*.php; do php -l "$f"; done` → "No syntax errors detected" for every file)
- [ ] Tested on WP 6.0+ with PHP 8.0+
- [ ] No PHP notices/warnings in `wp-content/debug.log`
**Tester signature:** ______________ **Date:** ______________
````
- [ ] **Step 2.2: Verify Markdown renders**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
wc -l docs/QA-CHECKLIST.md
head -30 docs/QA-CHECKLIST.md
```
Expected: file >150 lines, header section renders, first checkboxes visible. Markdown syntax is conventional Github/Gitea-flavored, no validation needed beyond visual review.
- [ ] **Step 2.3: Commit**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
git add docs/QA-CHECKLIST.md
git commit -m "docs(wpmc S6): manual QA checklist covering 13 cp-cw4 acceptance areas"
```
---
## Task 3: README — Testing section + roadmap status
**Files:**
- Modify: `README.md`
- [ ] **Step 3.1: Read current README**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
cat README.md
```
Expected: see existing structure — heading, status line, требования, installation, roadmap table, "Источник" section.
- [ ] **Step 3.2: Add Testing section before the Roadmap section**
Open `README.md`. Locate the line `## Roadmap MVP` (or equivalent header). Immediately before it, insert this new section:
```markdown
## Testing
### Automated (PHPUnit)
```bash
composer install
vendor/bin/phpunit
```
Expected: 98 tests, 175 assertions green. Unit-level coverage via Brain Monkey + Mockery + Patchwork — no real WordPress required; the full suite runs in under one second.
### Manual QA
For end-to-end verification against a real WordPress installation, see `docs/QA-CHECKLIST.md` — 13 sections covering activation, admin UI, helpers, shortcode, router, page clones, URL filters, REST URL.
### CI
`.gitea/workflows/test.yml` runs PHPUnit on every push to `main` and every pull request. The workflow becomes active once the repo is mirrored to Gitea (S7).
```
(Note: the trailing blank line is intentional.)
- [ ] **Step 3.3: Mark S6 done in roadmap table**
In the same `README.md`, find the S6 row in the roadmap table:
```
| S6 | PHPUnit-тесты (бутстрап + integration) | cp-la7 | open |
```
Change to:
```
| S6 | PHPUnit-тесты (бутстрап + integration) | cp-la7 | done |
```
- [ ] **Step 3.4: Verify suite + lint still green**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
vendor/bin/phpunit
```
Expected: OK (98 tests, 175 assertions). No PHP files changed in this task, so this is a no-op sanity check.
- [ ] **Step 3.5: Commit**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
git add README.md
git commit -m "docs(wpmc S6): README Testing section + mark S6 done in roadmap"
```
---
## Task 4: Final verification + close beads
**Files:** none
- [ ] **Step 4.1: Full PHPUnit run**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
vendor/bin/phpunit
```
Expected: OK (98 tests, 175 assertions), exit 0, no risky/warnings.
- [ ] **Step 4.2: Working tree clean**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
git status
```
Expected: `nothing to commit, working tree clean`.
- [ ] **Step 4.3: Verify artifacts exist**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
ls -la .gitea/workflows/test.yml docs/QA-CHECKLIST.md
grep -c "^## " docs/QA-CHECKLIST.md
grep -n "^## Testing" README.md
grep -n "cp-la7" README.md
```
Expected:
- Both files exist with non-zero size.
- `grep -c "^## "` on QA-CHECKLIST shows at least 14 (13 sections + Release sign-off).
- README shows `## Testing` heading line number.
- README S6 row contains `done`.
- [ ] **Step 4.4: Git log review**
```bash
cd "/Users/vladimirbryzgalov/Yandex.Disk.localized/Работа/Сайты/+Разработка сайтов+/Плагины Wordpress/wp-multi-city"
git log --oneline | head -10
```
Expected: top of log shows the S6 commits in order:
1. `docs(wpmc S6): README Testing section + mark S6 done in roadmap`
2. `docs(wpmc S6): manual QA checklist covering 13 cp-cw4 acceptance areas`
3. `ci(wpmc S6): Gitea Actions workflow for PHPUnit on push/PR`
4. `spec(wpmc S6): CI workflow + manual QA checklist (minimal scope)`
- [ ] **Step 4.5: Close beads task**
```bash
bd close cp-la7 --reason "S6 done (minimal scope per spec): Gitea Actions CI workflow at .gitea/workflows/test.yml runs PHPUnit on push/PR. 13-section manual QA checklist at docs/QA-CHECKLIST.md covers all cp-cw4 acceptance criteria. README updated with Testing section + roadmap marked done. Existing 98-test Brain Monkey suite remains green and covers what original cp-la7 envisioned as 5 integration test files (Taxonomy, CPT, Shortcode, Router, Filters) at call-signature level."
```
Expected: cp-la7 CLOSED. `bd ready` now lists cp-7cf (S7) as the next ready front under cp-cw4.
---
## Acceptance map (spec → tasks)
| Spec requirement | Task |
|---|---|
| `.gitea/workflows/test.yml` valid Gitea Actions YAML | 1 |
| Workflow runs phpunit on push + pull_request | 1 |
| PHP 8.0 setup + composer cache + dependency install | 1 |
| `docs/QA-CHECKLIST.md` 13 sections | 2 |
| Each section linked to cp-cw4 acceptance criterion | 2 |
| Release sign-off block at end of checklist | 2 |
| README Testing section (automated + manual + CI) | 3 |
| README roadmap S6 → done | 3 |
| PHPUnit suite remains 98/175 green | 1, 3, 4 |
| beads cp-la7 closed | 4 |