# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: workshop.spec.js >> form: Форма: проверить сохранение
- Location: tests\workshop.spec.js:3:1

# Error details

```
Error: expect(locator).toContainText(expected) failed

Locator: getByTestId('created-request')
Expected substring: "Команда Кедр"
Timeout: 1800ms
Error: element(s) not found

Call log:
  - Expect "toContainText" getByTestId('created-request') with timeout 1800ms
  - waiting for getByTestId('created-request')

```

```yaml
- link "Перейти к практической задаче":
  - /url: "#laboratory"
- banner:
  - link "Playwright — лаборатория качества":
    - /url: "#top"
    - text: Playwright ЛАБОРАТОРИЯ КАЧЕСТВА
  - text: 9 задач · реальные тесты в исходниках
  - paragraph: Проверять результат, а не надеяться
  - heading "Нажали. Дождались. Убедились." [level=1]:
    - text: Нажали. Дождались.
    - emphasis: Убедились.
  - paragraph: Попробуйте знакомые действия в интерфейсе. Включите неисправность и посмотрите, какое утверждение теста её поймает. Затем запустите этот тест в настоящем браузере из готового проекта.
  - link "Начать с формы ↓":
    - /url: "#laboratory"
  - link "Скачать проект ↗":
    - /url: https://ae-nekrasov.ru/wp-content/plugins/aen-playwright-workshop/playwright-project.zip
  - text: 01 ДЕЙСТВИЕ
  - strong: Отправить форму
  - text: 02 ОЖИДАНИЕ
  - strong: Получить результат
  - text: 03 ПРОВЕРКА
  - strong: Новая заявка в списке
  - paragraph: Кнопка нажалась — ещё не значит, что нужное действие выполнено.
- main:
  - paragraph:
    - strong: Здесь — живой учебный интерфейс.
    - text: Вы выполняете действия самостоятельно. Playwright запускается в скачанном проекте или CI; эта страница не запускает браузерный движок. Ниже показан отдельно сохранённый результат настоящего прогона.
  - region "Девять проверок для веб-сервиса":
    - paragraph: От привычного действия — к полезному тесту
    - heading "Девять проверок для веб-сервиса" [level=2]
    - text: 01 — 09
    - complementary:
      - paragraph: ПРАКТИЧЕСКИЕ ЗАДАЧИ
      - navigation "Практические задачи":
        - 'button "01 Форма: проверить сохранение"':
          - text: "01"
          - strong: "Форма: проверить сохранение"
        - 'button "02 Поиск: проверить найденные строки"':
          - text: "02"
          - strong: "Поиск: проверить найденные строки"
        - 'button "03 Диалог: отменить и подтвердить"':
          - text: "03"
          - strong: "Диалог: отменить и подтвердить"
        - 'button "04 Файл: проверить выбранный документ"':
          - text: "04"
          - strong: "Файл: проверить выбранный документ"
        - 'button "05 Скачивание: проверить содержимое CSV"':
          - text: "05"
          - strong: "Скачивание: проверить содержимое CSV"
        - 'button "06 Мобильное меню: перейти и закрыть"':
          - text: "06"
          - strong: "Мобильное меню: перейти и закрыть"
        - 'button "07 Сеть: ошибка и повтор"':
          - text: "07"
          - strong: "Сеть: ошибка и повтор"
        - 'button "08 Ожидание: дождаться результата"':
          - text: "08"
          - strong: "Ожидание: дождаться результата"
        - 'button "09 Сессии: проверить независимое состояние"':
          - text: "09"
          - strong: "Сессии: проверить независимое состояние"
    - paragraph: 01 / 09
    - 'heading "Форма: проверить сохранение" [level=3]'
    - paragraph: Создайте заявку и убедитесь, что она появилась в списке.
    - button "Начать заново ↺"
    - strong: Где пригодится
    - text: Обратная связь, регистрация и оформление заказа.
    - switch "Включить неисправность" [checked]
    - text: Включить неисправность
    - paragraph: Появляется сообщение об успехе, но запись не добавляется.
    - text: Неисправность включена
    - region "Попробуйте интерфейс":
      - text: A
      - heading "Попробуйте интерфейс" [level=4]
      - text: Учебные данные
      - form "Учебная заявка":
        - text: Имя клиента
        - textbox "Имя клиента":
          - /placeholder: Например, Команда Кедр
          - text: Команда Кедр
        - text: Email
        - textbox "Email":
          - /placeholder: cedar@example.test
          - text: cedar@example.test
        - text: Услуга
        - combobox "Услуга":
          - option "Аудит интерфейса" [selected]
          - option "Поддержка сайта"
          - option "Дизайн страницы"
        - button "Создать заявку"
      - paragraph: Учебная форма сохраняет одну запись только в этом сценарии. Используйте вымышленные данные.
      - status: Заявка создана
    - region "Что проверяет тест":
      - paragraph: Наблюдаемый результат
      - heading "Что проверяет тест" [level=4]
      - paragraph: Новая заявка содержит переданные имя, email и услугу.
      - list:
        - listitem: Введите имя и email.
        - listitem: Выберите услугу и создайте заявку.
        - listitem: Проверьте новую строку, а не только зелёное сообщение.
      - strong: Проверьте сами
      - paragraph: "Неисправность включена намеренно. Код теста остаётся тем же: утверждение должно обнаружить неправильный результат."
    - button "← Предыдущая" [disabled]
    - paragraph: Один сценарий — одно проверяемое поведение
    - button "Следующая →"
  - region "Тест для выбранной задачи":
    - paragraph: Заберите воспроизводимую проверку
    - heading "Тест для выбранной задачи" [level=2]
    - paragraph: Код соответствует учебному интерфейсу выше. Он запускается через Playwright Test в проекте из архива.
    - button "Скачать тест .spec.js ↓"
    - text: tests/form.spec.js
    - button "Копировать тест"
    - code: "import {test, expect} from '@playwright/test'; test('form: Форма: проверить сохранение', async ({page, browser, baseURL}, testInfo) => { const bug = testInfo.project.metadata.bug ? '&bug=1' : ''; await page.goto('/?case=form' + bug); await page.getByLabel('Имя клиента', {exact:true}).fill('Команда Кедр'); await page.getByLabel('Email', {exact:true}).fill('cedar@example.test'); await page.getByLabel('Услуга', {exact:true}).selectOption('audit'); await page.getByRole('button', {name:'Создать заявку', exact:true}).click(); await expect(page.getByText('Заявка создана', {exact:true})).toBeVisible(); const created = page.getByTestId('created-request'); await expect(created).toContainText('Команда Кедр'); await expect(created).toContainText('cedar@example.test'); await expect(created).toContainText('Аудит интерфейса'); });"
    - paragraph:
      - text: Обратите внимание на локаторы по роли и подписи, ожидания
      - code: expect
      - text: и проверку самого результата. Искусственная пауза не заменяет ожидание нужного состояния.
  - region "Какие проверки мы запускали":
    - paragraph: Сохранённый реальный прогон
    - heading "Какие проверки мы запускали" [level=2]
    - paragraph: Это результат запуска тестов проекта, а не оценка ваших действий на странице. Дата, версия и детали сохраняются в открытом JSON-файле.
    - link "Открыть результаты прогона ↗":
      - /url: evidence/results.json
    - strong: 18/18
    - text: проверок исправного стенда
    - paragraph: "Намеренных дефектов обнаружено: 9 из 9."
    - list:
      - listitem: "chromium: 9 успешно, 0 ошибок"
      - listitem: "webkit: 9 успешно, 0 ошибок"
      - listitem: "firefox: не проверен в этом окружении."
    - text: Playwright 1.65.0 · Node v24.19.0 · 06.11.2026, 20:46:36 UTC
  - region "Запустите проект у себя":
    - paragraph: Теперь — в настоящем браузере
    - heading "Запустите проект у себя" [level=2]
    - paragraph:
      - text: Распакуйте архив, откройте терминал в папке с
      - code: package.json
      - text: и установите зависимости и браузеры. В проекте есть инструкция, исходники интерфейса, тесты и отдельный прогон с намеренными неисправностями.
    - link "Скачать исходники ZIP ↓":
      - /url: https://ae-nekrasov.ru/wp-content/plugins/aen-playwright-workshop/playwright-project.zip
    - text: В корне проекта
    - button "Копировать"
    - code: npm ci npx playwright install npm test npx playwright show-report
    - paragraph:
      - strong: "Поиграться локально:"
      - code: npm start
      - text: ", затем"
      - code: http://127.0.0.1:8794
      - text: . Для тестов сервер запускается автоматически.
  - text: Практика Александра Некрасова · только вымышленные данные
  - link "Подробная статья ↗":
    - /url: https://ae-nekrasov.ru/development/playwright-testirovanie-sajta-masterskaya-primery/
- status
```

# Test source

```ts
  1   | import {test, expect} from '@playwright/test';
  2   | 
  3   | test('form: Форма: проверить сохранение', async ({page, browser, baseURL}, testInfo) => {
  4   |   const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  5   |   await page.goto('/?case=form' + bug);
  6   |   await page.getByLabel('Имя клиента', {exact:true}).fill('Команда Кедр');
  7   |   await page.getByLabel('Email', {exact:true}).fill('cedar@example.test');
  8   |   await page.getByLabel('Услуга', {exact:true}).selectOption('audit');
  9   |   await page.getByRole('button', {name:'Создать заявку', exact:true}).click();
  10  |   await expect(page.getByText('Заявка создана', {exact:true})).toBeVisible();
  11  |   const created = page.getByTestId('created-request');
> 12  |   await expect(created).toContainText('Команда Кедр');
      |                         ^ Error: expect(locator).toContainText(expected) failed
  13  |   await expect(created).toContainText('cedar@example.test');
  14  |   await expect(created).toContainText('Аудит интерфейса');
  15  | });
  16  | 
  17  | test('filter: Поиск: проверить найденные строки', async ({page, browser, baseURL}, testInfo) => {
  18  |   const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  19  |   await page.goto('/?case=filter' + bug);
  20  |   const rows = page.getByTestId('request-row');
  21  |   await expect(rows).toHaveCount(6);
  22  |   await page.getByLabel('Найти заявку', {exact:true}).fill('Кедр');
  23  |   await expect(rows).toHaveCount(1);
  24  |   await expect(rows.first()).toContainText('Кедр');
  25  | });
  26  | 
  27  | test('dialog: Диалог: отменить и подтвердить', async ({page, browser, baseURL}, testInfo) => {
  28  |   const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  29  |   await page.goto('/?case=dialog' + bug);
  30  |   const draft = page.getByTestId('draft-item');
  31  |   await page.getByRole('button', {name:'Удалить черновик', exact:true}).click();
  32  |   const dialog = page.getByRole('dialog');
  33  |   await expect(dialog).toBeVisible();
  34  |   await dialog.getByRole('button', {name:'Отмена', exact:true}).click();
  35  |   await expect(dialog).not.toBeVisible();
  36  |   await expect(draft).toContainText('Коммерческое предложение');
  37  |   await page.getByRole('button', {name:'Удалить черновик', exact:true}).click();
  38  |   await dialog.getByRole('button', {name:'Да, удалить', exact:true}).click();
  39  |   await expect(draft).not.toBeVisible();
  40  | });
  41  | 
  42  | test('upload: Файл: проверить выбранный документ', async ({page, browser, baseURL}, testInfo) => {
  43  |   const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  44  |   await page.goto('/?case=upload' + bug);
  45  |   await page.getByLabel('Учебный файл', {exact:true}).setInputFiles({
  46  |     name:'synthetic.txt', mimeType:'text/plain',
  47  |     buffer:Buffer.from('Only synthetic educational content.\n', 'utf8')
  48  |   });
  49  |   await expect(page.getByTestId('file-name')).toHaveText('synthetic.txt');
  50  | });
  51  | 
  52  | test('download: Скачивание: проверить содержимое CSV', async ({page, browser, baseURL}, testInfo) => {
  53  |   const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  54  |   await page.goto('/?case=download' + bug);
  55  |   const downloadPromise = page.waitForEvent('download');
  56  |   await page.getByRole('button', {name:'Скачать CSV', exact:true}).click();
  57  |   const download = await downloadPromise;
  58  |   expect(download.suggestedFilename()).toBe('requests.csv');
  59  |   const {readFile} = await import('node:fs/promises');
  60  |   const csv = (await readFile(await download.path(), 'utf8')).replace(/^\uFEFF/, '');
  61  |   expect(csv.split(/\r?\n/).filter(Boolean)).toHaveLength(4);
  62  |   expect(csv).toContain('RQ-103');
  63  |   expect(csv).toContain('Бюро Маяк');
  64  | });
  65  | 
  66  | test('navigation: Мобильное меню: перейти и закрыть', async ({page, browser, baseURL}, testInfo) => {
  67  |   const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  68  |   await page.goto('/?case=navigation' + bug);
  69  |   await page.setViewportSize({width:390, height:844});
  70  |   const menu = page.getByRole('button', {name:'Меню разделов', exact:true});
  71  |   await expect(menu).toHaveAttribute('aria-expanded', 'false');
  72  |   await menu.click();
  73  |   await page.getByRole('link', {name:'Отчёты', exact:true}).click();
  74  |   await expect(page.getByTestId('navigation-page')).toHaveText('Отчёты сервиса');
  75  |   await expect(menu).toHaveAttribute('aria-expanded', 'false');
  76  | });
  77  | 
  78  | test('network: Сеть: ошибка и повтор', async ({page, browser, baseURL}, testInfo) => {
  79  |   const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  80  |   await page.goto('/?case=network' + bug);
  81  |   let attempts = 0;
  82  |   await page.route('**/api/demo-status.json', async route => {
  83  |     attempts++;
  84  |     await route.fulfill({status:attempts===1?503:200,
  85  |       contentType:'application/json',
  86  |       body:JSON.stringify(attempts===1
  87  |         ? {ok:false, message:'Учебный сбой'}
  88  |         : {ok:true, message:'Сервис доступен', requests:14})
  89  |     });
  90  |   });
  91  |   await page.getByRole('button', {name:'Проверить соединение', exact:true}).click();
  92  |   await expect(page.getByText('Не удалось загрузить данные', {exact:true})).toBeVisible();
  93  |   await page.getByRole('button', {name:'Повторить', exact:true}).click();
  94  |   await expect(page.getByText('Сервис доступен', {exact:true})).toBeVisible();
  95  |   expect(attempts).toBe(2);
  96  | });
  97  | 
  98  | test('waiting: Ожидание: дождаться результата', async ({page, browser, baseURL}, testInfo) => {
  99  |   const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  100 |   await page.goto('/?case=waiting' + bug);
  101 |   const button = page.getByRole('button', {name:'Сформировать отчёт', exact:true});
  102 |   await button.click();
  103 |   await expect(page.getByText('Отчёт готов', {exact:true})).toBeVisible();
  104 |   await expect(page.getByTestId('report-count')).toHaveText('14');
  105 |   await expect(button).toBeEnabled();
  106 | });
  107 | 
  108 | test('isolation: Сессии: проверить независимое состояние', async ({page, browser, baseURL}, testInfo) => {
  109 |   const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  110 |   await page.goto('/?case=isolation' + bug);
  111 |   const density = page.getByRole('checkbox', {name:'Плотный вид', exact:true});
  112 |   await density.check();
```