mirror of
https://github.com/kamask-dev/pchelpmaster.git
synced 2026-11-11 13:59:38 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edb538c44e | ||
|
|
ad493f6f52 | ||
|
|
0d2c25a8f7 |
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: review-changes
|
||||
description: Ручное ревью всех изменений после последнего коммита (безопасность, качество кода, сохранение паттернов, общие принципы построения приложения). Запускать вручную в новой сессии.
|
||||
disable-model-invocation: true
|
||||
model: opus
|
||||
effort: high
|
||||
---
|
||||
|
||||
Review all the changes made after the last commit for security, code quality, preservation of existing patterns, and general principles of application construction.
|
||||
|
||||
## Scope
|
||||
|
||||
- Take the diff of everything after the last commit: `git diff HEAD` plus untracked files from `git status --short` (read them in full).
|
||||
- Read the surrounding code, callers, tests, and configs the changes touch. Do not judge a hunk in isolation.
|
||||
- Read the project's CLAUDE.md and treat its rules as the baseline for "patterns" and "principles".
|
||||
|
||||
## Checklist
|
||||
|
||||
1. **Security:** missing validation at trust boundaries, auth and access rules (per-user isolation), injection/XSS, secrets in code or config, unsafe defaults, data loss on errors.
|
||||
2. **Code quality:** bugs, unhandled edge cases, error handling, dead code created by these changes, needless complexity, missing tests for non-trivial logic.
|
||||
3. **Pattern preservation:** does the new code match the naming, structure, idioms, and conventions of the surrounding code and the rules in CLAUDE.md? Flag any deviation or reinvented helper that already exists.
|
||||
4. **General principles:** single responsibility, feature-based boundaries, no premature abstractions, no speculative code, no changes outside the scope of the task.
|
||||
5. **Frontend / visual design (only if the changes touch the frontend or visuals):** invoke the skills `ui-ux-pro-max` and `frontend-design` (`frontend-design:frontend-design`) via the Skill tool and review the UI changes against them (layout, typography, color, responsiveness, accessibility, interaction). Verify the real rendering in the browser via the Playwright MCP (`mcp__playwright__*`: snapshot, screenshot, console messages, `browser_resize` for responsive sizes). Skip this item when there are no frontend/visual changes.
|
||||
|
||||
## Rules
|
||||
|
||||
- This is a review only: do NOT edit files, do NOT commit.
|
||||
- Report only real, verified issues; no style nitpicks and no praise padding.
|
||||
- Answer in Russian. Keep code identifiers, commands, and paths as is.
|
||||
|
||||
## Output
|
||||
|
||||
List findings by severity (critical / important / minor). For each: `file:line`, what is wrong, a concrete failure scenario, a suggested fix. If nothing is found, say so in one line and name what you checked.
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
name: review-drill
|
||||
description: Тренировка ревью. Скрытно внедряет от 1 до 3 правдоподобных дефектов разного уровня в изменения текущей задачи, считает найденные пользователем и блокирует коммит, пока тренировка не завершена. Запускать вручную после того, как агент выполнил задачу.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
The user has just received a finished task from an agent and wants to practice code review. Secretly seed 1–3 defects into the changes of this task, then act as the game master while the user reviews: count what they find and keep the defects from being committed.
|
||||
|
||||
The user asked for this. Seeding the defects is the point, not sabotage. Everything below exists so the drill stays a game: the defects stay small, can be fully reverted, and never leave the working tree.
|
||||
|
||||
## 1. Preconditions
|
||||
|
||||
- Work in the current git repository. The review scope is everything after the last commit: `git diff HEAD` plus untracked files from `git status --short`. If that is empty, stop and ask the user which changes to seed.
|
||||
- If `<git-dir>/review-drill/` already exists, a drill is already running: do not start a new one, report its progress instead (section 4).
|
||||
|
||||
## 2. Seeding
|
||||
|
||||
Seed in a subagent so the edits do not show up in this conversation: call the Agent tool with `subagent_type: "fork"` (it inherits the task context). If forks are unavailable, seed yourself. Whoever seeds follows these rules:
|
||||
|
||||
- **Count:** at most 3, scaled to the size of the diff: a diff of a few lines gets 1 defect, a larger one 2–3. With more than one defect, each is in a **different category**:
|
||||
1. logic: wrong condition, off-by-one, a missed edge case (empty string, `null`, empty list), wrong order of operations;
|
||||
2. contract and data: wrong field or key, wrong error type or mapping, a broken return type or an `await` that is no longer awaited, a swapped argument;
|
||||
3. security and isolation: a weakened access or ownership check, a missing validation at a trust boundary, data reaching a place it must not reach;
|
||||
4. tests: an assertion that no longer checks anything (wrong expected value that still passes, a check on the wrong object, a missing `await expect`);
|
||||
5. docs and config: documentation or config that contradicts the code. Use only if they are part of the diff.
|
||||
- **Where:** only in lines that already differ from `HEAD` or in untracked files.
|
||||
- **Plausible:** each defect looks like an honest mistake of the agent that wrote the task. Match the surrounding style. Change 1–3 lines per defect. No comments, `TODO`s, odd names or formatting that give it away.
|
||||
- **It matters:** each defect would cause a real bug or is technically wrong. Good kinds: wrong logic; misuse of an API against its documentation (the wrong function, method, option or keyword that still exists, e.g. `replace` instead of `replaceAll`, `some` instead of `every`, `toBe` instead of `toEqual`); a misspelled API name where nothing checks it (a config key, an untyped option). Never syntax or punctuation (commas, brackets, semicolons), no style nitpicks, no typos in prose.
|
||||
- **It survives a skim:** the project must still build and pass lint/type checks (run the project's fast check, if it has one). Prefer places the existing tests do not cover. If a defect has to break a test to exist, pick another one.
|
||||
- **Forbidden:** destructive code (deleting files or data, `rm`, dropping tables); migrations, schema files and anything that runs against persistent data (e.g. a dev database), because reverting the file will not revert the data; secrets; files outside the project; `--no-verify`; commits.
|
||||
- Edit files only with Edit/Write (formatter hooks run on them). After each edit, re-read the changed lines so the recorded text matches the file as formatted.
|
||||
|
||||
Then save the answer key to `<git-dir>/review-drill/state.json` (get `<git-dir>` with `git rev-parse --git-dir`):
|
||||
|
||||
```json
|
||||
{
|
||||
"defects": [
|
||||
{
|
||||
"id": 1,
|
||||
"file": "path/from/repo/root",
|
||||
"category": "logic",
|
||||
"summary": "что не так и к чему приводит, по-русски",
|
||||
"original": "exact original lines",
|
||||
"seeded": "exact seeded lines",
|
||||
"found": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Install the commit block: if `<git-dir>/hooks/pre-commit` does not exist and `git config core.hooksPath` is empty, copy `pre-commit` from this skill's folder to `<git-dir>/hooks/pre-commit` and make it executable. Otherwise do not touch the existing hook, and tell the user that only this skill's instructions block the commit.
|
||||
|
||||
The subagent reports back only the number of defects and whether the hook was installed.
|
||||
|
||||
Tell the user, in Russian: how many defects were seeded (not where and not of which kind), that the commit is blocked, and that they should not expand the seeding tool calls because they show the edits. Explain how the drill works: they point out a problem in any words; «сдаюсь» reveals the remaining defects; «отменить тренировку» reverts everything.
|
||||
|
||||
## 3. During the review
|
||||
|
||||
Read `state.json` whenever you need the answer key; it survives context compaction and a new session.
|
||||
|
||||
- **The user names a problem.** Compare it with the key: the same place and the same essence count as a hit, even in different words. On a hit, set `found: true` and announce: «Найден внедрённый дефект N из M: <summary>». If the place is right but the essence is not, say only that the place is worth a closer look. If it is a real problem that was not seeded, say so and handle it like an ordinary review finding.
|
||||
- **The user asks what was done and why.** Answer honestly about the real task. When the question touches a seeded line, do not invent a justification and do not reveal the defect: say that this line is part of the changes and ask what the user sees in it.
|
||||
- **The user fixes a found defect themselves.** Fine. Check that the line now does what `original` did.
|
||||
- **Commit, "done", wrapping up.** While a defect is not found, do not commit, do not call the task finished, do not suggest bypassing the hook. Remind the user how many defects remain and that «сдаюсь» ends the drill.
|
||||
|
||||
## 4. Ending the drill
|
||||
|
||||
The drill ends when all defects are found, or on «сдаюсь», or on «отменить тренировку».
|
||||
|
||||
1. List every defect: file and line, category, summary, found or not.
|
||||
2. Revert every seeded defect the user has not fixed to `original` with Edit. Leave the user's own fixes in place if they are correct.
|
||||
3. Check that no seeded text is left in the files (search for each `seeded` snippet) and run the project's fast check, if it has one.
|
||||
4. Remove `<git-dir>/review-drill/` and `<git-dir>/hooks/pre-commit` if it is the hook from this skill (it contains the `# review-drill:` marker line).
|
||||
5. Report in Russian: the score «найдено X из M», what was missed and why it was easy to miss, and that committing is open again.
|
||||
|
||||
## Rules
|
||||
|
||||
- Answer in Russian. Keep code identifiers, commands and paths as they are.
|
||||
- Never commit while the drill is running and never use `--no-verify`.
|
||||
- Seeded defects must never outlive the drill: on any doubt, revert them first.
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
# review-drill: блокирует коммит, пока идёт тренировка ревью (удаляется при её завершении).
|
||||
if [ -d "$(git rev-parse --git-dir)/review-drill" ]; then
|
||||
echo "review-drill: тренировка ревью не завершена, внедрённые дефекты ещё в коде." >&2
|
||||
echo "Найди их или скажи агенту «сдаюсь» — тогда он откатит дефекты и снимет блокировку." >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: surgical-senior
|
||||
description: >
|
||||
A hybrid skill combining extreme laziness with surgical precision.
|
||||
Use this for all coding tasks to ensure minimal, exact, and safe changes without over-engineering or drive-by refactoring.
|
||||
license: MIT
|
||||
---
|
||||
|
||||
# Surgical Senior
|
||||
|
||||
You are a cautious, lazy senior developer. The best code is the code never written. You solve problems with surgical precision, modifying only what is absolutely necessary, and avoiding all speculation.
|
||||
|
||||
## 1. Think Before You Type
|
||||
- **No assumptions:** Clarify ambiguities before coding. If multiple interpretations exist, present them to the user; do not silently guess.
|
||||
- **Define success:** Transform tasks into testable goals (e.g., "Write test -> verify it passes").
|
||||
- **Scope the impact:** Read callers, tests, and configs that your change touches before modifying anything. Extra features are out of scope.
|
||||
|
||||
## 2. The Lazy Ladder
|
||||
Take the first option that fully works:
|
||||
1. **Does it need to exist?** Reject features, options, and flexibility nobody explicitly asked for.
|
||||
2. **Reuse:** Use existing helpers, components, or patterns already in the codebase.
|
||||
3. **Native/Libs:** Use standard libraries or already installed dependencies.
|
||||
4. **One-liner:** Can it be one readable line?
|
||||
5. **Minimal code:** Write the absolute minimum required to pass the success criteria.
|
||||
|
||||
## 3. Surgical Precision
|
||||
- **No drive-by refactoring:** Do not touch adjacent code, formatting, or pre-existing dead code that you didn't create.
|
||||
- **Clean your own mess:** Remove dead code (imports/variables/functions) ONLY if your current changes made them unused.
|
||||
- **No speculative abstractions:** No "for later" code or configuring for impossible scenarios.
|
||||
- **Self-Check:** Every changed line must directly map to the user's request.
|
||||
|
||||
## 4. Quality & Safety
|
||||
- **Never cut corners on:** Validation at trust boundaries, error handling that prevents data loss, and security.
|
||||
- **Testing:** New non-trivial logic (branches, loops, security) requires a small test.
|
||||
- **Shortcuts:** If you take a known shortcut, comment it: `shortcut: <the limit>, <when to upgrade>`.
|
||||
|
||||
## 5. Required Output Format
|
||||
End your reply with 1-2 concise lines stating:
|
||||
1. What you skipped or did not check.
|
||||
2. Any risks the user must know.
|
||||
@@ -54,4 +54,9 @@ __screenshots__/
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.Zone.Identifier
|
||||
skills-lock.json
|
||||
|
||||
# GigaCode
|
||||
AGENTS.md
|
||||
.gigacode_vsc/
|
||||
|
||||
@@ -54,7 +54,7 @@ pnpm start # приложение на http://localhost:4200/
|
||||
| `pnpm lint` | ESLint |
|
||||
| `pnpm test` | unit-тесты |
|
||||
| `pnpm test:api` | API-тесты PocketBase: схема, правила доступа, изоляция пользователей |
|
||||
| `pnpm test:integration` | репозитории приложения против тестового PocketBase |
|
||||
| `pnpm test:integration` | репозитории приложения на тестовом PocketBase |
|
||||
| `pnpm test:lint` | тесты конфигов ESLint, dev-прокси и Playwright; первый шаг `test`, `test:api`, `test:integration`, `e2e` |
|
||||
| `pnpm e2e` | сценарии в браузере и проверка доступности (axe) |
|
||||
| `pnpm e2e:prod` | сценарии на production-сборке |
|
||||
@@ -88,7 +88,7 @@ docs/ документация разработчика
|
||||
|
||||
- [architecture.md](docs/architecture.md) — общая схема, слои, DI, SSR и изоляция данных, nginx, принципы UI;
|
||||
- [data-model.md](docs/data-model.md) — коллекции PocketBase, поля и правила доступа;
|
||||
- [testing.md](docs/testing.md) — уровни тестов и тестовый PocketBase;
|
||||
- [testing.md](docs/testing.md) — уровни тестов и тестовый PocketBase; устройство стендов — в [docs/guide/tests/](docs/guide/tests/);
|
||||
- `docs/guide/` — руководство по каждой части приложения: где лежит код, как им пользоваться и как его расширить. Пополняется вместе с кодом.
|
||||
|
||||
## Production
|
||||
|
||||
+13
-3
@@ -5,20 +5,24 @@
|
||||
## Основы
|
||||
|
||||
| Документ | О чём |
|
||||
| --- | --- |
|
||||
| ---------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| [architecture.md](architecture.md) | общая схема, слои внутри фичи, DI, SSR и изоляция данных, nginx, UI-принципы |
|
||||
| [data-model.md](data-model.md) | коллекции PocketBase, поля, правила доступа, публичный маршрут |
|
||||
| [testing.md](testing.md) | уровни тестов, тестовый PocketBase, контрактные тесты |
|
||||
| [testing.md](testing.md) | уровни тестов, тестовый PocketBase, контрактные тесты, правила (стенды — в `guide/tests/`) |
|
||||
|
||||
## Руководство (`guide/`)
|
||||
|
||||
Страницы появляются вместе с соответствующим кодом.
|
||||
|
||||
| Страница | О чём |
|
||||
| --- | --- |
|
||||
| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
|
||||
| `guide/project-structure.md` | карта репозитория: `src/app`, `pocketbase/`, `tests/`, `scripts/`; где что искать |
|
||||
| `guide/adding-a-feature.md` | пошаговый пример добавления новой сущности от миграции до экрана (на примере меток) |
|
||||
| [guide/shared.md](guide/shared.md) | общая инфраструктура: клиент PocketBase, доменные ошибки, общие UI-компоненты |
|
||||
| [guide/tests/api-harness.md](guide/tests/api-harness.md) | стенд API-тестов: Vitest в Node, `global-setup`, проверка типов |
|
||||
| [guide/tests/integration-harness.md](guide/tests/integration-harness.md) | стенд интеграционных тестов: цель `test-integration` builder'а Angular |
|
||||
| [guide/tests/e2e-harness.md](guide/tests/e2e-harness.md) | стенд e2e: Playwright, два `webServer`, фикстуры, axe |
|
||||
| [guide/tests/config-tests.md](guide/tests/config-tests.md) | тесты конфигов: ESLint-границы, dev-прокси, Playwright |
|
||||
| `guide/auth.md` | вход, регистрация, сессия, cookie, guard'ы, работа с текущим пользователем |
|
||||
| `guide/tags.md` | метки: репозиторий, диалог управления, компонент выбора |
|
||||
| `guide/clients.md` | клиенты: телефоны, поиск, форма, точки расширения карточки |
|
||||
@@ -37,21 +41,27 @@
|
||||
Одно-два предложения: что делает фича и для чего.
|
||||
|
||||
## Где лежит
|
||||
|
||||
Дерево папок фичи с пояснением каждой важной папки и файла.
|
||||
|
||||
## Публичный API
|
||||
|
||||
Что экспортирует `index.ts`: абстрактные классы, типы, компоненты (селектор, входы, выходы), функции `provideXxx()`, токены расширения. По каждому — одна строка назначения.
|
||||
|
||||
## Как пользоваться
|
||||
|
||||
Короткие примеры кода для типовых задач: получить данные, встроить компонент, обработать ошибку.
|
||||
|
||||
## Как расширить или заменить
|
||||
|
||||
Как подменить реализацию через DI, как подключиться к точке расширения, что поменять при добавлении поля.
|
||||
|
||||
## Тесты
|
||||
|
||||
Где лежат тесты фичи, какой контракт у репозитория, какие фейки доступны.
|
||||
|
||||
## Подводные камни
|
||||
|
||||
Неочевидное: ограничения SSR, правила доступа, особенности PocketBase.
|
||||
```
|
||||
|
||||
|
||||
@@ -46,8 +46,8 @@ async list(): Promise<Tag[]> {
|
||||
## Тесты
|
||||
|
||||
- `to-domain-error.spec.ts`, `pocketbase-client.spec.ts` — unit.
|
||||
- `pocketbase-client.integration.spec.ts` — клиент из DI против тестового PocketBase, изоляция двух пользователей и гостя.
|
||||
- `tests/lint/eslint-rules.spec.ts` — границы ESLint, см. [testing.md](../testing.md).
|
||||
- `pocketbase-client.integration.spec.ts` — клиент из DI на тестовом PocketBase, изоляция двух пользователей и гостя.
|
||||
- `tests/lint/eslint-rules.spec.ts` — границы ESLint, см. [config-tests.md](tests/config-tests.md).
|
||||
|
||||
## Подводные камни
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Стенд API-тестов
|
||||
|
||||
Тесты бэкенда (схема, миграции, правила доступа, изоляция пользователей) на чистом Vitest в Node, с запросами к временному PocketBase. Общие правила и устройство тестового PocketBase — в [testing.md](../../testing.md).
|
||||
|
||||
`pnpm test:api` = `tsc -p tsconfig.tests.json && vitest run --config vitest.api.config.mts`: чистый Vitest в Node, без Angular.
|
||||
|
||||
- Сначала проверяются типы тестового кода (`tsconfig.tests.json`: `tests/**`, `vitest.*.config.mts` и `proxy.conf.mjs`; `allowJs` нужен, чтобы `tests/lint/` импортировали `.mjs`-конфиг): сам Vitest типы не проверяет.
|
||||
- `vitest.api.config.mts`: `include: tests/api/**/*.api.spec.ts`, `globalSetup: tests/support/global-setup.ts`, таймауты 20 с (запас на старт PocketBase внутри теста).
|
||||
- `global-setup.ts` поднимает один PocketBase на прогон и передаёт тестам `pbUrl`, `pbDataDir`, `pbSuperuser` через `project.provide`; в тестах — `inject('pbUrl')` и т. д. из `vitest`. Типы этих значений — `tests/support/provided-context.d.ts`.
|
||||
- Тесты самого стенда — `tests/api/harness.api.spec.ts`. Проверка уборки по `SIGINT`/`SIGTERM` запускает фикстуру `tests/support/fixtures/hold-pocketbase.ts` отдельным процессом `node`: она поднимает PocketBase, печатает `{ url, dataDir }` и ждёт сигнала.
|
||||
- Поэтому `pocketbase-server.ts` выполняется чистым Node без сборки: в нём только импорты `node:*` и стираемый TypeScript (без `enum`, `namespace`, параметров-свойств).
|
||||
@@ -0,0 +1,10 @@
|
||||
# Тесты конфигов (ESLint, dev-прокси, Playwright)
|
||||
|
||||
Проверки конфигурации, поломка которой иначе остаётся незамеченной. Общие правила — в [testing.md](../../testing.md).
|
||||
|
||||
`pnpm test:lint` = `tsc -p tsconfig.tests.json && vitest run --config vitest.lint.config.mts`: чистый Vitest в Node, файлы `tests/lint/**/*.spec.ts`.
|
||||
|
||||
- Проверяют, что ESLint держит границы, от которых зависит изоляция данных при SSR: `new PocketBase(` и импорт класса `PocketBase` (в том числе под другим именем и через `pocketbase/*`) только в фабрике `shared/pocketbase/pocketbase-client.ts` (и в тестовых хелперах `tests/`), импорт `pocketbase` и `pocketbase/*` запрещён в `domain/` и `ui/`.
|
||||
- `tests/lint/pb-dev-proxy.spec.ts` проверяет `proxy.conf.mjs` (dev-прокси `ng serve`): правило `/pb/`, `target` из `PB_URL` с тем же значением по умолчанию, что у серверного провайдера (в том числе при пустой переменной), и срезание префикса `/pb`.
|
||||
- Способ ESLint-тестов: `ESLint#lintText(code, { filePath })` на коротких фрагментах кода, проверяется `ruleId` сообщений. Конфиг ESLint ломается молча (опечатка в glob, слишком широкое исключение), и `pnpm lint` при этом остаётся зелёным; тест это ловит.
|
||||
- Запускаются первым шагом в `pnpm test`, `pnpm test:api`, `pnpm test:integration` и `pnpm e2e`, чтобы поломка правила обнаружилась при любом прогоне. ESLint — Node-библиотека, в бандл builder'а Angular её не подключить, поэтому это отдельный набор.
|
||||
@@ -0,0 +1,12 @@
|
||||
# Стенд e2e
|
||||
|
||||
Сценарии в браузере (Playwright) и проверка доступности (axe) на `ng serve` с тестовым PocketBase. Общие правила и устройство тестового PocketBase — в [testing.md](../../testing.md).
|
||||
|
||||
`pnpm e2e` = `pnpm test:lint && playwright test`. Перед первым запуском: `pnpm exec playwright install chromium`.
|
||||
|
||||
- `playwright.config.ts`: тесты `tests/e2e/**/*.e2e.ts`, один проект `chromium`, при падении — скриншот и трейс в `test-results/` (в `.gitignore`).
|
||||
- Два `webServer`: тестовый PocketBase (`node tests/support/serve-test-pocketbase.ts 8190` — тот же `startPocketBase()`, только с фиксированным портом) и `ng serve --port 4300` с `PB_URL` тестового PocketBase: на него смотрят и SSR, и dev-прокси `/pb`. Адреса и порты — `tests/e2e/urls.ts`; они не пересекаются с `pnpm pb:serve` (8090) и `pnpm start` (4200), а `pb_data` разработчика не используется.
|
||||
- Серверы останавливаются по `SIGTERM` (`gracefulShutdown`): по умолчанию Playwright шлёт `SIGKILL`, и стенд не успел бы удалить временную папку PocketBase.
|
||||
- Без `CI` уже запущенные серверы переиспользуются, повторов нет; при `CI=true` серверы всегда свои, повтор упавшего теста один. Это проверяет `tests/lint/playwright-config.spec.ts` (см. [config-tests.md](config-tests.md)).
|
||||
- Фикстуры (`tests/e2e/fixtures.ts`) не зависят от UI входа и суперпользователя (при переиспользовании PocketBase его пароль неизвестен): `user` регистрируется через открытую регистрацию `users`, `api` — SDK, вошедший как `user`, `signedInPage` — страница в **отдельном** контексте браузера с cookie `pb_auth` в формате `exportToCookie`, поэтому обычная `page` остаётся гостевой.
|
||||
- `expectNoA11yViolations(page)` (`tests/e2e/a11y.ts`): axe со всеми правилами (WCAG и best-practice, как требует CLAUDE.md), падает со списком `правило: описание (элементов: N)`.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Стенд интеграционных тестов
|
||||
|
||||
PocketBase-реализации репозиториев и сервисов на настоящем PocketBase, внутри Angular-окружения (`TestBed`). Общие правила и устройство тестового PocketBase — в [testing.md](../../testing.md).
|
||||
|
||||
`pnpm test:integration` = `ng run pchelpcrm:test-integration`: отдельная цель builder'а `@angular/build:unit-test` в `angular.json`.
|
||||
|
||||
- `include: ["**/*.integration.spec.ts"]` в цели `test-integration`; у цели `test` (`pnpm test`) — `exclude` тех же файлов, поэтому unit-тесты не требуют PocketBase.
|
||||
- `watch: false` в цели: прогон завершается сам; слежение за файлами — разово `pnpm test:integration --watch`.
|
||||
- `runnerConfig: "vitest.integration.config.mts"` добавляет `globalSetup: tests/support/global-setup.ts` — тот же файл, что у API-тестов, он вызывает `startPocketBase()`. Список файлов в этом конфиге не задаётся: builder его игнорирует.
|
||||
- В тестах работают `TestBed` и `inject()` Angular, URL — `inject('pbUrl')` из `vitest`, хелперы импортируются из `tests/support/clients.ts`.
|
||||
|
||||
Почему цель builder'а, а не отдельный `vitest.integration.config.ts` с плагином: спеки собираются тем же компилятором Angular и с той же инициализацией TestBed, что и в `pnpm test`, без своего плагина компиляции и его настройки.
|
||||
|
||||
`tests/support/clients.ts` компилируется в двух программах — `tsconfig.tests.json` и `tsconfig.spec.json`. Типы Node подключены в обеих (спеки выполняются Vitest в Node, а код в `src/` вроде серверного провайдера `PB_URL` читает `process.env`), а `tests/support/provided-context.d.ts` с типами `inject('pbUrl')` перечислен в `include` обоих tsconfig: `references` в `tsconfig.json` программы не объединяют.
|
||||
+7
-45
@@ -5,57 +5,19 @@
|
||||
## Уровни тестов
|
||||
|
||||
| Уровень | Что проверяет | Где лежит | Команда |
|
||||
| --------------------- | -------------------------------------------------------------------------- | --------------------------------------------- | ----------------------- |
|
||||
| --------------------- | ------------------------------------------------------------------------- | --------------------------------------------- | ----------------------- |
|
||||
| **API** (e2e бэкенда) | схема, миграции, правила доступа, изоляция пользователей, хуки PocketBase | `tests/api/**/*.api.spec.ts` | `pnpm test:api` |
|
||||
| **Unit** | доменные функции, компоненты и сервисы с фейками вместо PocketBase | рядом с кодом, `src/**/*.spec.ts` | `pnpm test` |
|
||||
| **Integration** | PocketBase-реализации репозиториев и сервисов против настоящего PocketBase | рядом с кодом, `src/**/*.integration.spec.ts` | `pnpm test:integration` |
|
||||
| **Integration** | PocketBase-реализации репозиториев и сервисов на настоящем PocketBase | рядом с кодом, `src/**/*.integration.spec.ts` | `pnpm test:integration` |
|
||||
| **E2E** | пользовательские сценарии в браузере, SSR, доступность (axe) | `tests/e2e/**/*.e2e.ts` | `pnpm e2e` |
|
||||
| **E2E prod** | сценарии на production-сборке, включая изоляцию данных при SSR | `tests/e2e/` (проект `ssr-prod`) | `pnpm e2e:prod` |
|
||||
|
||||
Команды появляются по мере того, как настраиваются соответствующие стенды.
|
||||
Команды появляются по мере того, как настраиваются соответствующие стенды. Устройство каждого стенда описано в `guide/tests/`:
|
||||
|
||||
## Стенд API-тестов
|
||||
|
||||
`pnpm test:api` = `tsc -p tsconfig.tests.json && vitest run --config vitest.api.config.mts`: чистый Vitest в Node, без Angular.
|
||||
|
||||
- Сначала проверяются типы тестового кода (`tsconfig.tests.json`: `tests/**`, `vitest.*.config.mts` и `proxy.conf.mjs`; `allowJs` нужен, чтобы `tests/lint/` импортировали `.mjs`-конфиг): сам Vitest типы не проверяет.
|
||||
- `vitest.api.config.mts`: `include: tests/api/**/*.api.spec.ts`, `globalSetup: tests/support/global-setup.ts`, таймауты 20 с (запас на старт PocketBase внутри теста).
|
||||
- `global-setup.ts` поднимает один PocketBase на прогон и передаёт тестам `pbUrl`, `pbDataDir`, `pbSuperuser` через `project.provide`; в тестах — `inject('pbUrl')` и т. д. из `vitest`. Типы этих значений — `tests/support/provided-context.d.ts`.
|
||||
- Тесты самого стенда — `tests/api/harness.api.spec.ts`. Проверка уборки по `SIGINT`/`SIGTERM` запускает фикстуру `tests/support/fixtures/hold-pocketbase.ts` отдельным процессом `node`: она поднимает PocketBase, печатает `{ url, dataDir }` и ждёт сигнала.
|
||||
- Поэтому `pocketbase-server.ts` выполняется чистым Node без сборки: в нём только импорты `node:*` и стираемый TypeScript (без `enum`, `namespace`, параметров-свойств).
|
||||
|
||||
## Стенд интеграционных тестов
|
||||
|
||||
`pnpm test:integration` = `ng run pchelpcrm:test-integration`: отдельная цель builder'а `@angular/build:unit-test` в `angular.json`.
|
||||
|
||||
- `include: ["**/*.integration.spec.ts"]` в цели `test-integration`; у цели `test` (`pnpm test`) — `exclude` тех же файлов, поэтому unit-тесты не требуют PocketBase.
|
||||
- `watch: false` в цели: прогон завершается сам; слежение за файлами — разово `pnpm test:integration --watch`.
|
||||
- `runnerConfig: "vitest.integration.config.mts"` добавляет `globalSetup: tests/support/global-setup.ts` — тот же файл, что у API-тестов, он вызывает `startPocketBase()`. Список файлов в этом конфиге не задаётся: builder его игнорирует.
|
||||
- В тестах работают `TestBed` и `inject()` Angular, URL — `inject('pbUrl')` из `vitest`, хелперы импортируются из `tests/support/clients.ts`.
|
||||
|
||||
Почему цель builder'а, а не отдельный `vitest.integration.config.ts` с плагином: спеки собираются тем же компилятором Angular и с той же инициализацией TestBed, что и в `pnpm test`, без своего плагина компиляции и его настройки.
|
||||
|
||||
`tests/support/clients.ts` компилируется в двух программах — `tsconfig.tests.json` и `tsconfig.spec.json`. Типы Node подключены в обеих (спеки выполняются Vitest в Node, а код в `src/` вроде серверного провайдера `PB_URL` читает `process.env`), а `tests/support/provided-context.d.ts` с типами `inject('pbUrl')` перечислен в `include` обоих tsconfig: `references` в `tsconfig.json` программы не объединяют.
|
||||
|
||||
## Стенд e2e
|
||||
|
||||
`pnpm e2e` = `pnpm test:lint && playwright test`. Перед первым запуском: `pnpm exec playwright install chromium`.
|
||||
|
||||
- `playwright.config.ts`: тесты `tests/e2e/**/*.e2e.ts`, один проект `chromium`, при падении — скриншот и трейс в `test-results/` (в `.gitignore`).
|
||||
- Два `webServer`: тестовый PocketBase (`node tests/support/serve-test-pocketbase.ts 8190` — тот же `startPocketBase()`, только с фиксированным портом) и `ng serve --port 4300` с `PB_URL` тестового PocketBase: на него смотрят и SSR, и dev-прокси `/pb`. Адреса и порты — `tests/e2e/urls.ts`; они не пересекаются с `pnpm pb:serve` (8090) и `pnpm start` (4200), а `pb_data` разработчика не используется.
|
||||
- Серверы останавливаются по `SIGTERM` (`gracefulShutdown`): по умолчанию Playwright шлёт `SIGKILL`, и стенд не успел бы удалить временную папку PocketBase.
|
||||
- Без `CI` уже запущенные серверы переиспользуются, повторов нет; при `CI=true` серверы всегда свои, повтор упавшего теста один. Это проверяет `tests/lint/playwright-config.spec.ts`.
|
||||
- Фикстуры (`tests/e2e/fixtures.ts`) не зависят от UI входа и суперпользователя (при переиспользовании PocketBase его пароль неизвестен): `user` регистрируется через открытую регистрацию `users`, `api` — SDK, вошедший как `user`, `signedInPage` — страница в **отдельном** контексте браузера с cookie `pb_auth` в формате `exportToCookie`, поэтому обычная `page` остаётся гостевой.
|
||||
- `expectNoA11yViolations(page)` (`tests/e2e/a11y.ts`): axe со всеми правилами (WCAG и best-practice, как требует CLAUDE.md), падает со списком `правило: описание (элементов: N)`.
|
||||
|
||||
## Тесты конфигов (ESLint, dev-прокси, Playwright)
|
||||
|
||||
`pnpm test:lint` = `tsc -p tsconfig.tests.json && vitest run --config vitest.lint.config.mts`: чистый Vitest в Node, файлы `tests/lint/**/*.spec.ts`.
|
||||
|
||||
- Проверяют, что ESLint держит границы, от которых зависит изоляция данных при SSR: `new PocketBase(` и импорт класса `PocketBase` (в том числе под другим именем и через `pocketbase/*`) только в фабрике `shared/pocketbase/pocketbase-client.ts` (и в тестовых хелперах `tests/`), импорт `pocketbase` и `pocketbase/*` запрещён в `domain/` и `ui/`.
|
||||
- `tests/lint/pb-dev-proxy.spec.ts` проверяет `proxy.conf.mjs` (dev-прокси `ng serve`): правило `/pb/`, `target` из `PB_URL` с тем же значением по умолчанию, что у серверного провайдера (в том числе при пустой переменной), и срезание префикса `/pb`.
|
||||
- Способ ESLint-тестов: `ESLint#lintText(code, { filePath })` на коротких фрагментах кода, проверяется `ruleId` сообщений. Конфиг ESLint ломается молча (опечатка в glob, слишком широкое исключение), и `pnpm lint` при этом остаётся зелёным; тест это ловит.
|
||||
- Запускаются первым шагом в `pnpm test`, `pnpm test:api`, `pnpm test:integration` и `pnpm e2e`, чтобы поломка правила обнаружилась при любом прогоне. ESLint — Node-библиотека, в бандл builder'а Angular её не подключить, поэтому это отдельный набор.
|
||||
- [Стенд API-тестов](guide/tests/api-harness.md)
|
||||
- [Стенд интеграционных тестов](guide/tests/integration-harness.md)
|
||||
- [Стенд e2e](guide/tests/e2e-harness.md)
|
||||
- [Тесты конфигов (ESLint, dev-прокси, Playwright)](guide/tests/config-tests.md)
|
||||
|
||||
## Контрактные тесты репозиториев
|
||||
|
||||
|
||||
+3
-1
@@ -1,7 +1,9 @@
|
||||
// @ts-check
|
||||
// Повторяет `location /pb/` из docs/architecture.md (раздел 7): только пути с префиксом `/pb/`, префикс срезается.
|
||||
// Формат — `server.proxy` dev-сервера Vite (@angular/build).
|
||||
/** @type {Record<string, { target: string; changeOrigin?: boolean; rewrite?: (path: string) => string }>} */
|
||||
/** @typedef {Record<string, { target: string; changeOrigin?: boolean; rewrite?: (path: string) => string }>} ProxyRules */
|
||||
|
||||
/** @type {ProxyRules} */
|
||||
export default {
|
||||
'/pb/': {
|
||||
target: process.env['PB_URL'] || 'http://127.0.0.1:8090',
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Проверяет правило `/pb/` в proxy.conf.mjs: оно должно повторять `location /pb/` из nginx и серверный `PB_URL`.
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { ProxyRules } from '../../proxy.conf.mjs';
|
||||
|
||||
type Rules = typeof import('../../proxy.conf.mjs').default;
|
||||
|
||||
async function loadRules(pbUrl: string | undefined): Promise<Rules> {
|
||||
async function loadRules(pbUrl: string | undefined): Promise<ProxyRules> {
|
||||
vi.stubEnv('PB_URL', pbUrl);
|
||||
vi.resetModules();
|
||||
return (await import('../../proxy.conf.mjs')).default;
|
||||
|
||||
Reference in New Issue
Block a user