diff --git a/AGENTS.md b/AGENTS.md
index 04bd24c..14c9572 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,7 +1,7 @@
# AGENTS.md
Это учебный HTTP-сервер, на который **живьём ходят студенты** из курсов Хекслета:
-HTTP API, протокол HTTP, Postman, js-playwright. Уроки и самостоятельные работы
+HTTP API, протокол HTTP, Postman, js-playwright, курсы Selenium и Playwright. Уроки и самостоятельные работы
цитируют его ответы дословно, вплоть до значений полей.
Отсюда главное следствие, из которого растёт почти всё остальное в этом файле:
@@ -269,6 +269,16 @@ make deploy APP=http_example HOST=timeweb SKIP=caddy,cron
**Значения полей только на латинице.** Сервер один на все локали курсов, и русский текст в ответе читался бы как ошибка у испанского студента. По-русски здесь пишутся комментарии в коде и спецификациях, не данные.
+## Учебные приложения для UI-тестов это тоже текст уроков
+
+Префиксы `/java-selenium`, `/python-selenium` и `/python-playwright` обслуживают курсы Selenium и Playwright, ru и es. У каждого курса своё приложение (библиотека, трекер задач, табло вылетов), описанное в `apps` в `custom-server/src/ui-pages.ts`. Общее у них только устройство входа, сессии и флеш-сообщений. Уроки находят элементы по `id`, классам и `data-test` и сравнивают надписи дословно, поэтому переименование `#username`, `#book-moby-dick`, `.figcaption h5` или текста флеш-сообщения ломает примеры курса. Страницы и локаторы перечислены в README, их держит `test/ui-pages.test.ts`.
+
+- **Тема приложения выбрана под уроки курса.** Новая страница появляется из сценария приложения: в библиотеке перетаскивают книгу на полку, на табло время зависит от часового пояса. Страница-тренажёр без сценария сюда не идёт.
+- **Слова Hexlet в приложениях нет.** Это вымышленные сервисы, и студент тестирует их как чужой продукт.
+- **Перетаскивание собрано на `mousedown` и `mouseup`.** `Actions.dragAndDrop` в Selenium эмулирует мышь, а события HTML5 `dragstart` и `drop` браузер на неё не шлёт, поэтому `draggable` пример молча не срабатывал бы.
+- **Сессия это подписанная кука с путём курса, и курс записан в её значении.** Урок про куки сохраняет их после входа, открывает страницу в новом браузере, подкладывает куки руками и идёт в закрытый раздел. Кука одного курса не открывает раздел другого, неподписанное значение не открывает ничего.
+- **Учётки напечатаны на страницах входа и в уроках.** Их смена это правка курсов.
+
## Набор данных не меняется
`POST` и `DELETE` отвечают 201 и 204, но список задач остаётся прежним: создание
diff --git a/README.md b/README.md
index 0e9a591..bc7469d 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# http-example
-Учебный HTTP-сервер Хекслета: [https://http.hexlet.app](https://http.hexlet.app). На него **живьём ходят студенты** из курсов HTTP API, протокол HTTP, Postman и js-playwright, а уроки и самостоятельные работы цитируют его ответы дословно, вплоть до значений полей.
+Учебный HTTP-сервер Хекслета: [https://http.hexlet.app](https://http.hexlet.app). На него **живьём ходят студенты** из курсов HTTP API, протокол HTTP, Postman, js-playwright и курсов Selenium и Playwright, а уроки и самостоятельные работы цитируют его ответы дословно, вплоть до значений полей.
Отсюда главное свойство проекта: **ответы сервера это контент курса**, а не деталь реализации. Правка данных здесь делает неверным текст урока в другом репозитории, и по коду это не видно. Поэтому правила работы с проектом собраны в [AGENTS.md](./AGENTS.md), и читать их стоит до первой правки — там же разобрано, почему наборы данных такого размера, почему они не меняются и какие коды ответов несут уроки.
@@ -19,6 +19,39 @@
Обработчик `body` принимает JSON, `application/x-www-form-urlencoded` и multipart. Для примера с файлом он возвращает его содержимое как строку UTF-8. Лимиты multipart допускают один файл до 64 КиБ и до десяти текстовых полей по 4 КиБ. Исходное тестовое изображение взято из [httpbin](https://httpbin.org/image/png), который распространяется под [лицензией MIT](https://github.com/postmanlabs/httpbin/blob/master/LICENSE).
+## Учебные приложения для UI-тестов
+
+Курсы Selenium и Playwright тестируют маленькие приложения под префиксом своего курса. У каждого своя предметная область, и страницы в нём появились из её сценариев. Уроки ищут элементы и проверяют тексты дословно, поэтому локаторы и надписи этих страниц тоже контент курса.
+
+**`/java-selenium`, библиотека City Library.** Курс разбирает действия пользователя, окна и фреймы, и у каждого действия в библиотеке есть смысл.
+
+| Путь | Сценарий |
+| --- | --- |
+| `/java-selenium` | каталог, `h1` «City Library» |
+| `/java-selenium/login` → `/my-shelf` | вход читателя `reader` / `books123`, полка с выданными книгами |
+| `/java-selenium/covers` | обложки `.figure`, название `.figcaption h5` видно при наведении, двойной клик добавляет книгу в избранное |
+| `/java-selenium/shelves` | книга `#book-moby-dick` перетаскивается на полку `#shelf-reading` |
+| `/java-selenium/search` | поиск `#search` фильтрует каталог, Enter печатает число найденного в `#result` |
+| `/java-selenium/return` | кнопка `#return-book` спрашивает `confirm` перед возвратом |
+| `/java-selenium/preview` | «Open preview» открывает отрывок в новом окне |
+| `/java-selenium/reader` | глава книги во фрейме `chapter`, абзац `#content` |
+
+**`/python-selenium`, трекер Task Board.** Курс разбирает сессию, куки, ожидания и мобильное меню.
+
+| Путь | Сценарий |
+| --- | --- |
+| `/python-selenium` | промо-страница, первая ссылка «Sign in» после названия |
+| `/python-selenium/login` → `/tasks` | вход `student` / `tasks123`, список задач, меню `#menu-button` с пунктами `.menu-item` |
+
+**`/python-playwright`, табло Departures.** Курс настраивает контекст браузера, и табло на эти настройки отвечает.
+
+| Путь | Сценарий |
+| --- | --- |
+| `/python-playwright` | табло `#departures`, время вылета печатается в часовом поясе браузера, на узком экране строки складываются в карточки |
+| `/python-playwright/login` → `/trips` | вход пассажира `passenger` / `flight123`, «My trips» |
+
+У всех трёх одинаковое устройство входа. Форма `#login` с полями `#username` и `#password` и кнопкой `button.primary[data-test=login-button]`, флеш-сообщение `#flash`, выход по `/<курс>/logout`. Приложения описаны конфигом `apps` в `custom-server/src/ui-pages.ts`, страницы лежат фрагментами в `custom-server/assets/ui-pages/<курс>/`, префикс в них записан как `{{prefix}}`.
+
## Prerequisites
- [mise](https://mise.jdx.dev) (ставит Node, pnpm, Caddy и jactionlint по `mise.toml`)
diff --git a/custom-server/assets/ui-pages/java-selenium/covers.html b/custom-server/assets/ui-pages/java-selenium/covers.html
new file mode 100644
index 0000000..7b773c8
--- /dev/null
+++ b/custom-server/assets/ui-pages/java-selenium/covers.html
@@ -0,0 +1,15 @@
+
+
Book covers
+
Hover over a cover to see the book. Double-click the title to add it to favorites.
+ Open preview
+
diff --git a/custom-server/assets/ui-pages/java-selenium/preview_moby_dick.html b/custom-server/assets/ui-pages/java-selenium/preview_moby_dick.html
new file mode 100644
index 0000000..78ac37c
--- /dev/null
+++ b/custom-server/assets/ui-pages/java-selenium/preview_moby_dick.html
@@ -0,0 +1,4 @@
+
+
Moby Dick: preview
+
Call me Ishmael. Some years ago, never mind how long precisely, having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world.