Задача 1: интернет-магазин (v0)
Первая задача Арены. Нарочно маленькая: пять экранов, один сценарий покупки, простая админка. Достаточно, чтобы различать варианты, и мало, чтобы один прогон занимал вечер, а не неделю.
Что строим
Магазин одного продавца. Покупатель смотрит каталог, кладёт в корзину, оформляет заказ. Продавец управляет товарами в админке. Оплаты нет, заказ фиксируется со статусом «новый».
Экраны
| Экран | Что на нём |
|---|---|
Каталог / |
Список товаров: фото, название, цена. Фильтр по категории. Поиск по названию. Пагинация или «показать ещё» по 12. |
Товар /p/:slug |
Фото, название, цена, описание, остаток. Кнопка «В корзину» неактивна при остатке 0. |
Корзина /cart |
Строки с количеством (можно менять), сумма, кнопка «Оформить». Пустая корзина показывает подсказку. |
Оформление /checkout |
Имя, телефон, город, комментарий. Валидация. После отправки страница «Заказ №N принят». |
Админка /admin |
Таблица товаров. Создать, изменить, удалить. Поля: название, slug, цена, категория, остаток, фото (URL), описание. Вход по одному паролю из env. |
Данные
- Товар: id, slug, название, цена (целое, копейки), категория, остаток, фото URL, описание, активен.
- Категория: id, название, slug.
- Заказ: id, номер, дата, покупатель (имя, телефон, город, комментарий), строки (товар, количество, цена на момент заказа), сумма, статус.
- Хранилище на усмотрение исполнителя: SQLite, JSON-файл, что угодно. Сид из 30 товаров и 4 категорий даётся в задаче.
Ограничения
- Корзина переживает перезагрузку страницы.
- Цена в заказе фиксируется на момент заказа, изменение цены товара потом не меняет заказ.
- Нельзя заказать больше, чем остаток. После заказа остаток уменьшается.
- Slug уникален. Удалённый товар не открывается, но в старых заказах остаётся.
- Адаптив до 375px, без горизонтального скролла.
- Никаких внешних сервисов: всё поднимается одной командой локально.
Требования (контракт для тестов и графа)
Это и есть requirements.md. Исполнитель получает этот список. Тесты ниже он не получает.
| id | Требование |
|---|---|
| REQ-shop-01 | Каталог показывает только активные товары |
| REQ-shop-02 | Фильтр по категории оставляет товары только этой категории |
| REQ-shop-03 | Поиск по названию без учёта регистра |
| REQ-shop-04 | Страница товара открывается по slug, несуществующий slug даёт 404 |
| REQ-shop-05 | «В корзину» недоступна при остатке 0 |
| REQ-shop-06 | Добавление в корзину увеличивает счётчик в шапке |
| REQ-shop-07 | Корзина сохраняется между перезагрузками |
| REQ-shop-08 | Изменение количества пересчитывает сумму |
| REQ-shop-09 | Количество в корзине не превышает остаток |
| REQ-shop-10 | Удаление строки из корзины |
| REQ-shop-11 | Пустая корзина не даёт перейти к оформлению |
| REQ-shop-12 | Оформление требует имя и телефон, телефон валидируется |
| REQ-shop-13 | Успешный заказ получает номер и показывает его |
| REQ-shop-14 | После заказа остаток товара уменьшен |
| REQ-shop-15 | Цена в заказе не меняется при смене цены товара |
| REQ-shop-16 | Админка закрыта без пароля |
| REQ-shop-17 | Создание товара делает его видимым в каталоге |
| REQ-shop-18 | Редактирование товара меняет данные на его странице |
| REQ-shop-19 | Удаление товара убирает его из каталога, страница даёт 404 |
| REQ-shop-20 | Дублирующий slug отклоняется |
| REQ-shop-21 | Каталог на 375px без горизонтального скролла |
| REQ-shop-22 | Проект поднимается одной командой из README |
Скрытые e2e-сценарии (Playwright)
Один сценарий на требование, имя теста содержит id. Каждый начинается с чистого сида.
REQ-shop-01деактивировать товар через админку, проверить что в каталоге его нет.REQ-shop-02выбрать категорию «Кофе», все карточки из неё, счётчик совпадает с сидом.REQ-shop-03ввести «ЧАЙ», найти товары с «чай» в названии.REQ-shop-04открыть/p/net-takogo, получить 404.REQ-shop-05открыть товар с остатком 0, кнопка disabled.REQ-shop-06добавить два товара, счётчик в шапке «2».REQ-shop-07добавить товар, перезагрузить, корзина не пуста.REQ-shop-08поставить количество 3, сумма = цена × 3.REQ-shop-09остаток 2, попытка поставить 5, в корзине 2 и сообщение.REQ-shop-10удалить строку, сумма пересчитана, при пустой корзине подсказка.REQ-shop-11зайти на/checkoutс пустой корзиной, редирект или блок.REQ-shop-12отправить без имени и с телефоном «123», две ошибки валидации.REQ-shop-13оформить, увидеть «Заказ №» и число.REQ-shop-14остаток 5, купить 2, на странице товара остаток 3.REQ-shop-15оформить заказ, в админке поменять цену, заказ в хранилище с прежней ценой (проверка через админку заказов или API, на усмотрение).REQ-shop-16открыть/adminбез пароля, получить форму входа или 401.REQ-shop-17создать товар в админке, найти в каталоге.REQ-shop-18поменять название, проверить на странице товара.REQ-shop-19удалить товар, в каталоге нет, страница 404.REQ-shop-20создать товар с существующим slug, ошибка.REQ-shop-21viewport 375×812,document.scrollingElement.scrollWidth <= 375.REQ-shop-22выполняется раннером: команда из README поднимает сервер за 60 секунд.
Что получает исполнитель
Папку tasks/shop/: brief.md (экраны, данные, ограничения), requirements.md (таблица REQ), rules.md (архитектурные правила, см. scoring.md), сид seed.json. И одну инструкцию: «Сделай, чтобы проект поднимался командой из README. В коммитах и тестах указывай REQ-id».
Контракт запуска (обязателен для любого варианта)
- Команды:
npm install,npm run build,npm start. Послеnpm startприложение слушаетhttp://localhost:3000не позже чем через 60 секунд. - Данные при старте читаются из
seed.json. Хранилище любое, без внешних сервисов. - Переменные окружения:
ADMIN_PASSWORD(по умолчаниюarena),ARENA_TEST=1включает тестовый режим. - В тестовом режиме есть
POST /api/test/reset, который возвращает данные к сиду и отвечает 200. Вне тестового режима маршрут отвечает 404. - Обязательные
data-testidна элементах перечислены вtasks/shop/brief.mdрепоarena. Без них скрытые тесты не найдут элементы, и это засчитывается как провал требования.
Зачем контракт: раннер и тесты не знают стек варианта. Единственное, на что они опираются, это порт, команда запуска, сброс данных и data-testid. Это цена за независимость от стека.