diff --git a/docs/superpowers/specs/2026-05-16-s6-ci-qa-checklist-design.md b/docs/superpowers/specs/2026-05-16-s6-ci-qa-checklist-design.md new file mode 100644 index 0000000..9bac017 --- /dev/null +++ b/docs/superpowers/specs/2026-05-16-s6-ci-qa-checklist-design.md @@ -0,0 +1,239 @@ +# S6 — CI workflow + Manual QA Checklist + +- Beads: `cp-la7` (parent epic `cp-cw4`) +- Depends on: `cp-i17` (S5 — URL filters, закрыт) +- Blocks: `cp-7cf` (S7 — README + Gitea release + PUC) +- Дата: 2026-05-16 + +## Цель + +Завершить тестовый этап MVP. Текущий 98-тестовый Brain Monkey suite уже покрывает 5 функциональных областей из изначального cp-la7 (Taxonomy, CPT, Shortcode, Router, Filters) на уровне call-signature через мок-фреймворк. Реальные WP integration tests с DB и WP-core checkout overkill для MVP и существенно overlap с unit-тестами. + +S6 добавляет два artifact'а, которые закроют epic acceptance: +- **Gitea Actions workflow** — автоматический прогон phpunit на push/PR, готов к активации при mirror'е в Gitea (S7). +- **Manual QA checklist** — пошаговая ручная проверка против реального WP с ACF Pro, привязанная к acceptance criteria из cp-cw4. + +И обновление README с инструкциями по тестированию. + +Никаких новых PHP-классов, никаких новых phpunit-тестов. S6 — infrastructure-only. + +## Артефакты + +``` +wp-multi-city/ +├── .gitea/workflows/test.yml # CREATE — CI workflow +├── docs/QA-CHECKLIST.md # CREATE — manual smoke checklist +└── README.md # MODIFY — Testing section + roadmap status +``` + +## CI workflow — `.gitea/workflows/test.yml` + +Gitea Actions syntax — subset of GitHub Actions, поддерживается с Gitea 1.19. Workflow срабатывает на push и pull_request: + +```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 +``` + +Файл лежит готовым; станет активным когда Gitea remote добавится в S7. До этого момента — placeholder без active CI. + +PHP matrix (8.0/8.1/8.2/8.3), coverage report, lint job — отложено до S7+. + +## Manual QA checklist — `docs/QA-CHECKLIST.md` + +Структура: 13 секций, каждая с шагами и expected outcome. Каждый чекпойнт — `- [ ]` для отметки. Привязка к acceptance criteria из cp-cw4 явная. + +### Шаблон секций + +```markdown +## N. <Имя секции> + +**Cp-cw4 acceptance: <привязка>** + +Preconditions: <состояние сайта> + +Steps: +1. <шаг> +2. <шаг> + +Expected: +- <критерий> +- <критерий> + +- [ ] Section pass +``` + +### Полные 13 секций + +**1. Plugin activation** +- Preconditions: чистый WP 6.0+ с активированным ACF Pro. +- Steps: загрузить плагин в `wp-content/plugins/wp-multi-city/`; активировать через admin → Plugins. +- Expected: нет PHP-ошибок; нет admin notice "WP Multi City requires ACF Pro"; в admin menu появляются «Шорткоды», «WP Multi City». + +**2. ACF dependency notice** +- Preconditions: WP с деактивированным ACF Pro. +- Steps: активировать плагин. +- Expected: admin notice "WP Multi City: требуется активный плагин Advanced Custom Fields Pro" виден; плагин не вешает хуки. + +**3. Admin UI — taxonomy town** +- Preconditions: плагин активен, ACF Pro установлен. +- Steps: открыть admin → Шорткоды → Города. +- Expected: пустой список term'ов; кнопка «Добавить» работает; создать term `tyumen` и `spb`. + +**4. Admin UI — CPT shortcode-town** +- Preconditions: §3 завершён. +- Steps: открыть admin → Шорткоды → Добавить. +- Expected: edit-screen показывает поле title + ACF repeater «Подстановки по городам» (`list_shortcode`) с двумя sub-fields «Город» и «Значение»; town sub-field — dropdown с термами spb/tyumen. + +**5. Admin UI — Options page** +- Preconditions: §3 завершён. +- Steps: открыть admin → WP Multi City. +- Expected: ACF options page «WPMultiCity / Routing» с 4 полями: main_town_slug (dropdown town), index_town_alt (page picker), page_unic (repeater 3 sub-fields), page_no_rep (repeater 1 sub-field link). + +**6. Helper functions smoke** +- Preconditions: §3 завершён, создана страница «About» (ID=10). +- Steps: в functions.php темы (или mu-plugin) вставить: + ```php + add_action('wp_footer', function() { + error_log('current_town: ' . var_export(wpmc_current_town(), true)); + error_log('get_term: ' . var_export(wpmc_get_term_by_slug('tyumen'), true)); + error_log('remove_domain_link: ' . wpmc_remove_domain_link('https://example.com/foo/')); + }); + ``` + Открыть `/tyumen/about/` (или с любым term-slug в URL). +- Expected: error log показывает `current_town: 'tyumen'`, `get_term: WP_Term { term_id: ..., slug: 'tyumen' }`, `remove_domain_link: '/foo/'`. Открытие `/`: `current_town` = `null` (без main_town_slug в options). + +**7. [shortcode_dmr] resolves by town** +- Preconditions: §3-4 завершено. Создан shortcode-town пост ID=42 с одной row: town=tyumen, text=«Тюмень-текст». +- Steps: создать страницу с content `[shortcode_dmr id=42]`. Открыть на `/tyumen//`. Открыть на `//`. +- Expected: на /tyumen/ — «Тюмень-текст»; на / — пусто (без main_town_slug). + +**8. Router patches $wp_query** +- Preconditions: §3, §5 завершено. В Options Page выставлен main_town_slug=spb. Создана page «About» ID=10. +- Steps: открыть `/tyumen/about/`. +- Expected: контентом загружается страница About (через `$wp_query->page_id=10`); `get_query_var('town')` в шаблоне = `'tyumen'`. Открыть `/spb/about/` — стандартный WP fallback (main town не патчится). + +**9. /{slug}/ root with main_page_alt** +- Preconditions: §8 завершено. Создана страница «Главная Тюмень» ID=20. На term'е tyumen выставлен ACF field `main_page_alt` = 20. +- Steps: открыть `/tyumen/`. +- Expected: загружается page 20; `is_front_page()` = true; `get_queried_object()->ID` = 20. + +**10. page_unic clone swap** +- Preconditions: §8 завершено. Создана клон-страница «About-Tyumen» ID=21. В Options Page → page_unic добавлена row: page_old=10, page_new=21, town=tyumen. +- Steps: открыть `/tyumen/about/`. +- Expected: контентом загружается page 21 (клон), не 10 (оригинал); URL остаётся `/tyumen/about/`. + +**11. URL filters — town prefix injection** +- Preconditions: §8 завершено. Тема выводит menu (nav-menu) со ссылкой на `/uslugi/`. +- Steps: открыть `/tyumen/about/`; inspect menu link; inspect ACF link field value (если есть). +- Expected: menu link href = `/tyumen/uslugi/` или `https://host/tyumen/uslugi/`; ссылка на main town (`/spb/uslugi/`) не префиксится; внешние ссылки (`https://other.com/`) не префиксятся; mailto:/tel: не меняются; ссылки уже с городским префиксом (`/tyumen/contact/`) не получают double-prefix. + +**12. page_no_rep exclusions** +- Preconditions: §8 завершено. В Options Page → page_no_rep добавлен `/privacy/`. Создана страница Privacy ID=30. +- Steps: открыть `/privacy/`. +- Expected: стандартный WP load page Privacy; `$wp_query` не патчится; ссылки на этой странице (по template) не префиксятся (`is_routable_path` возвращает false). + +**13. rest_url + REST API** +- Preconditions: WP REST API доступен. +- Steps: в шаблоне `echo rest_url('wp/v2/posts')`; обратиться к этому URL через curl или браузер. +- Expected: URL возвращает `https://{HTTP_HOST}/wp-json/wp/v2/posts` (не site_url); ответ — JSON список постов. + +### Финал чеклиста + +В конце документа — раздел "Release sign-off": + +```markdown +## Release sign-off + +- [ ] All 13 sections passed +- [ ] PHPUnit suite green (`vendor/bin/phpunit` → OK 98 tests, 175 assertions) +- [ ] PHP lint clean (`composer test-lint` или `find includes -name '*.php' -exec php -l {} \;`) +- [ ] Tested on WP 6.0+ with PHP 8.0+ +- [ ] No PHP notices/warnings in error_log + +Tester: ______________ Date: ______________ Plugin version: ______________ +``` + +## README — обновление + +Добавить новую секцию «Testing» после раздела «Установка»: + +```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 WP required; runs in <1s). + +### Manual QA + +For end-to-end verification against a real WordPress installation, see `docs/QA-CHECKLIST.md`. Run through the checklist before any release. + +### CI + +`.gitea/workflows/test.yml` runs PHPUnit on every push and pull request. Active once the repo is mirrored to Gitea (S7). +``` + +Обновить roadmap-таблицу: пометить S6 как `done`: + +``` +| S6 | PHPUnit-тесты (бутстрап + integration) | cp-la7 | done | +``` + +## Acceptance verification + +После всех 3 артефактов: + +1. `.gitea/workflows/test.yml` — YAML валидный, проходит через `yamllint` (опционально). +2. `docs/QA-CHECKLIST.md` существует, 13 секций + sign-off, плюс прохождение хотя бы 1 секции в реальной среде для smoke (опционально). +3. README обновлён, roadmap-таблица показывает S6=done. +4. `vendor/bin/phpunit` всё ещё зелёный (98/175) — ни один из 3 артефактов не ломает существующий suite. + +## Что НЕ входит в S6 + +- Реальные WP integration tests с DB и WP-core checkout (overlap с unit-тестами, overkill для MVP). +- Docker/wp-env runtime для тестов. +- PHP matrix в CI (8.0/8.1/8.2/8.3) — отложено до S7+. +- Code coverage reports (phpunit coverage) — отложено. +- `bin/smoke.php` wp-cli helper — отложено; manual checklist покрывает MVP. +- Lint job в CI (phpcs/phpstan) — отложено. + +## Open questions + +Нет. Все решения зафиксированы.