# Автоматизация тестирования · рабочая тетрадь

Редакция QA Orbit · 2026-10-28.1 · все данные синтетические.

## Договор AUTO-CORE-1

AUTO-CORE-1 — отдельная синтетическая модель в памяти. Это не HTTP-сервер, не база и не API сайта QA Orbit. status/body — обычный возвращаемый объект доменного адаптера.
A1. validateTitle(value): только строка длиной 1–80 Unicode code points включительно, без trim при сохранении. description при create — строка 0–2000 code points. Пробелы допускаются при создании.
A2. Участники из доверенных учебных fixtures: anna/red/user; boris/red/user; vera/red/editor; outsider anna/blue/editor. null — нет сессии. Параметр actor моделирует уже установленную личность; реальная проверка токена здесь отсутствует.
A3. createTask(input,actor): 401 без actor, иначе 422 при неверных title/description, иначе 201. id,authorId,teamId,state назначает модель; одноимённые поля input игнорируются. Новый state=draft. Исходный title сохраняется без обрезки.
A4. readTask: 401 без actor; 404 если id отсутствует, команда другая или draft принадлежит другому участнику; 200 иначе. Редактор тоже не читает чужой draft. Опубликованное читают участники той же команды.
A5. publishTask: проверки строго 401 (нет actor) → 404 (нет задачи/другая команда) → 403 (не автор и не editor) → 409 (уже published) → 422 (title.trim пуст) → 200. Отказы ничего не меняют; успех меняет только state draft→published существующей задачи, без копии и без trim title. Разрешение читать и публиковать различается.
A6. Ответы и snapshot — независимые копии; новая createStore() создаёт независимое состояние. publishLater возвращает Promise; после await имеет тот же эффект, что publishTask.
Начальный store содержит ровно t1 anna/red/title Ready/draft, t2 boris/red/title Other draft/draft, t3 anna/red/title из трёх пробелов/draft. description у всех пустая; следующий id t4. Каждый тест получает новую createStore().

## Основной запуск без зависимостей

Скачайте в одну новую папку `orbitdesk.mjs`, `orbitdesk.buggy.mjs`, `contract.test.mjs`, `diagnostics.test.mjs`, `run-buggy.mjs`. Нужен поддерживаемый Node.js 24 или 26. npm install не требуется.

```sh
node --version
node --test contract.test.mjs diagnostics.test.mjs
```

Для неизменённого fixed-пакета: 20 tests, 20 pass, 0 fail, exit 0. Переменная ORBITDESK_VARIANT должна отсутствовать или равняться fixed. Если она осталась от ручного опыта, очистите её перед запуском (`unset ORBITDESK_VARIANT` в sh/zsh; `Remove-Item Env:ORBITDESK_VARIANT -ErrorAction SilentlyContinue` в PowerShell).

## Намеренно красный запуск

```sh
node run-buggy.mjs
```

Launcher одинаково задаёт ORBITDESK_VARIANT=buggy на Windows/macOS/Linux. Ожидается 17 tests, 13 pass, 4 fail, exit 1. Это целенаправленно ошибочный прогон, отдельный от основного job. Три причины: неправильная Unicode-длина, чтение чужого draft, пропущенное разрешение publish. Четвёртый FAIL повторяет проблему чтения в предпосылке редакторского сценария.

`run-buggy.mjs` не называется test-файлом и не входит в обычный автопоиск. `diagnostics.test.mjs` зелёный: ожидаемая AssertionError проверяется через assert.throws. Никаких скрытых обязательных красных тестов в основном наборе нет.

## Дополнительное DOM-упражнение

Это отдельный проект. Скачайте `dom-exercise.spec.ts`; не добавляйте Playwright в приложение QA Orbit.

```sh
mkdir qa-orbit-dom-practice
cd qa-orbit-dom-practice
npm init -y
npm install --save-dev @playwright/test
npx playwright install chromium
# Сохраните dom-exercise.spec.ts в эту папку.
npx playwright test dom-exercise.spec.ts
```

Установка требует доступ к npm и загрузку браузера. setContent создаёт самостоятельный DOM; он не соединён с Node-моделью и не проверяет backend, реальную сессию или БД. При подготовке курса Playwright runner не запускался: статус Not run. Сверяйте актуальные требования Playwright по официальной документации перед установкой.

## Задания и разборы

### Автотест как программа: данные, действие, оракул

В buggy-модуле автор заменил [...value].length на value.length. ASCII-границы прошли, и он решил, что проверка корректна.

1. Составьте таблицу входов и ожидаемых true/false для семи случаев.
2. Напишите assertion для 80 emoji и объясните его отличие от 80 ASCII-букв.
3. Запустите основной набор на fixed-модуле.
4. Опишите, какое именно требование нарушит buggy-версия, не меняя оракул.

**Разбор:** Ожидания: ''→false; 'A'→true; 'A'.repeat(80)→true; repeat(81)→false; '🙂'.repeat(80)→true; null→false; три пробела→true. assert.equal(validateTitle('🙂'.repeat(80)),true) ловит подсчёт UTF-16 вместо code points. В готовом пакете fixed contract + diagnostics даёт 20 PASS, exit 0; buggy contract содержит 17 тестов и даёт 13 PASS / 4 FAIL, exit 1. Числа относятся к неизменённой версии пакета, собственный запуск фиксируется отдельно.

**Ошибки:** Проверять только обычное слово Ready.; Использовать ту же функцию validateTitle для вычисления ожидаемого значения.; Считать code points пользовательскими графемами.

### Уровни проверки и контракт состояния

Предложенный тест проверяет только truthiness возвращённого объекта. Даже 403 считается истинным значением, и тест всегда проходит.

1. Напишите точное ожидание для Boris/t1 и неизменного snapshot.
2. Для Vera/t1 отдельно проверьте чтение до публикации и разрешённую публикацию.
3. Опишите ожидаемый полный снимок после Anna/t1 и повторный статус.
4. Распределите четыре риска по уровням: длина, доменное разрешение, реальный токен, отображение статуса.

**Разбор:** Boris/t1: {status:403,body:{error:'FORBIDDEN'}} и snapshot без изменений. Vera читает t1 до публикации со статусом 404, публикует со статусом 200. После этого участник red читает published со статусом 200. Anna/t1 меняет только state задачи t1, число записей остаётся равным 3. Повторная публикация даёт 409 и не меняет опубликованный снимок. Длина проверяется на уровне функции, разрешение — доменного адаптера, токен — отдельной HTTP-интеграции, видимый статус — UI. assert.ok(response) не различает статусы: объект ошибки тоже является truthy.

**Ошибки:** Проверять только существование объекта ответа.; Считать право чтения эквивалентным праву публикации.; Назвать локальный модуль реальным API стендом.

### UI: локаторы, ожидания и наблюдаемый результат

Автор применил first(), sleep и проверил только наличие страницы. Нужно связать тест с конкретным действием и результатом.

1. Замените локатор на роль и имя в области editor.
2. Добавьте проверку draft до действия и published после.
3. Проверьте сохранение введённого Release checklist.
4. Укажите, какие слои не проверяет setContent, и зафиксируйте фактический статус запуска.

**Разбор:** Локаторы и ожидания показаны в полном dom-exercise.spec.ts. Сначала fill Task title значением Release checklist, затем status=draft, click конкретной кнопки, await toHaveText('published') и toHaveValue('Release checklist'). Не нужны first() и фиксированная пауза. Это тест DOM-обработчика в отдельной странице: не проверяет HTTP, session, store, БД и реальный портал. Пока Playwright не запускался, отчёт должен говорить Not run/не выполнено; подготовка курса не содержит фактического browser-runner результата.

**Ошибки:** Считать click доказательством сохранения в БД.; Заменять неоднозначность локатора произвольным first().; Увеличивать sleep вместо ожидания нужного условия.

### Изоляция, fixtures и асинхронность

Тест B ожидает draft, но получает published после теста A. Оба используют один store, созданный на уровне файла.

1. Перенесите создание store внутрь каждого сценария.
2. Добавьте проверку независимости двух фабрик.
3. Покажите, что изменение возвращённого body не мутирует store.
4. Опишите правильное ожидание publishLater и промежуточное состояние до release барьера.

**Разбор:** В каждом test: const store=createStore(). Изменение first не меняет second: у первого t1 published, у второго draft. После createTask('  New  ') изменение result.body.title='tampered' не должно затрагивать последующее чтение raw title '  New  '. Для async: const pending=store.publishLater('t1',anna,()=>gate); до release статус draft; release(); await pending; затем published. Барьер контролирует причинный порядок, а не секунды. Общий store и before-ссылка на внутренний объект недопустимы как независимый оракул.

**Ошибки:** Очищать shared state только в конце успешного теста.; Лечить 409 увеличением тайм-аута.; Забывать await и получать преждевременный зелёный результат.

### Диагностика падений и запуск в CI

Runner сообщает 4 FAIL на buggy. Менеджер предлагает завести четыре одинаковых дефекта и включить retry, чтобы получить зелёную ветку.

1. Сгруппируйте четыре отказа по трём причинам.
2. Для каждой причины назовите actual/expected и правило A.
3. Запишите основной зелёный job и отдельную команду buggy.
4. Объясните, какие данные нужно сохранить до retry и почему скриншот не заменяет exit code.

**Разбор:** Причины: 1) UTF-16 length вместо code points: actual=false, expected=true, нарушено A1; 2) чужой draft читается со статусом 200 вместо 404: нарушено A4, это вызывает два отказа; 3) Boris публикует задачу со статусом 200 вместо 403: нарушено A5. Основной запуск: node --test contract.test.mjs diagnostics.test.mjs. Для fixed ожидаются 20 PASS и exit 0. Отдельный запуск: node run-buggy.mjs. Ожидаются exit 1 и четыре заданных FAIL. Сохраняются версия, команда, реализация, полный вывод и первая разница каждого сценария. Retry не исправляет причину и не должен скрывать исходный отказ. Скриншот статуса job без тестового вывода не подтверждает, какой набор исполнялся.

**Ошибки:** Считать число упавших тестов числом уникальных дефектов.; Добавить || true и объявить сборку исправленной.; Выдать отсутствующий trace за созданный артефакт.

### Итоговый проект: надёжный набор публикации

Подготовьте пакет в новой папке, который выявляет известные дефекты и не зависит от порядка запуска или удалённой среды.

1. Составьте матрицу A1–A6 → test name → ожидаемое наблюдение → фактический статус.
2. Выполните основной fixed-набор и сохраните вывод и exit code.
3. Выполните buggy отдельно и объясните 4 FAIL через 3 причины.
4. Подтвердите независимость stores, копирование body и завершение async.
5. Добавьте план HTTP/UI-интеграций с честным Not run для отсутствующих запусков.

**Разбор:** Эталон пакета: orbitdesk.mjs, orbitdesk.buggy.mjs, contract.test.mjs, diagnostics.test.mjs, run-buggy.mjs, workbook.md, report.md. Основная команда для fixed даёт 20 PASS и exit 0. Отдельный buggy-прогон: 17 tests, 13 PASS, 4 FAIL, exit 1. Матрица отражает A1 — Unicode-длину; A3 — назначение автора моделью и сохранение исходного title; A4 — приватность draft; A5 — разрешения, состояние и отсутствие побочных эффектов; A6 — копии, изоляцию и await. Указаны пробелы, включая исчерпывающие границы description и настоящий HTTP. DOM-файл .spec независим от store; без запуска его статус — Not run. Каждая собственная модификация требует нового фактического подсчёта и объяснения вместо сохранения старых цифр ради формального совпадения.

**Ошибки:** Смешать buggy в основной зелёный job без явного ожидания exit 1.; Объявить DOM-тест сквозным backend-тестом.; Подменить независимый snapshot общей ссылкой.; Сдать цифры из разбора вместо собственного выполнения.

## Основной CI-job

В отдельном учебном репозитории положите все .mjs рядом, установите Node24 или 26 и выполните `node --test contract.test.mjs diagnostics.test.mjs`. Сохраните stdout/stderr и настоящий exit code. Установка пакетов для этого набора не нужна. Намеренный buggy-запуск оформляйте отдельным шагом с явным ожиданием exit 1; не подавляйте основной отказ через `|| true`.

20 включает 17 контрактных и 3 диагностических теста. Это не полный охват всех требований: не исчерпаны границы description, не реализованы настоящие HTTP, токены, БД и нагрузка. Ведите матрицу пробелов вместе с результатами.

## Первичные источники

- [Node.js Test runner](https://nodejs.org/api/test.html)
- [Node.js Assert](https://nodejs.org/api/assert.html)
- [Node.js ECMAScript modules](https://nodejs.org/api/esm.html)
- [Node.js CLI](https://nodejs.org/api/cli.html)
- [MDN String iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Symbol.iterator)
- [Playwright locators](https://playwright.dev/docs/locators)
- [Playwright auto-waiting](https://playwright.dev/docs/actionability)
- [Playwright assertions](https://playwright.dev/docs/test-assertions)
- [Playwright isolation](https://playwright.dev/docs/browser-contexts)
- [Playwright trace viewer](https://playwright.dev/docs/trace-viewer)
