Law/e2e/tests/showcase_client_flow.spec.js
2026-04-07 17:28:27 +03:00

410 lines
22 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* SHOWCASE: Полный флоу клиента (публичный кабинет)
*
* Покрывает:
* 1. Регистрация — оставить заявку через лендинг
* 2. Открыть кабинет, убедиться в наличии статуса
* 3. Написать сообщение юристу
* 4. Прикрепить файл (PDF) к сообщению
* 5. Скачать/просмотреть свой файл
* 6. Увидеть ответ юриста (после назначения и ответа)
* 7. Оставить вторую заявку
* 8. Заполнить запрошенные данные (поля DataRequirement)
* 9. Отправить обращение к куратору / администратору
* 10. Запросить смену юриста
* 11. Убедиться в смене юриста (имя юриста обновилось)
* 12. Наблюдать смену статуса заявки в реальном времени
*/
const { test, expect } = require("@playwright/test");
const {
randomPhone,
createRequestViaLanding,
openPublicCabinet,
sendCabinetMessage,
uploadCabinetFile,
loginAdminPanel,
openRequestsSection,
rowByTrack,
selectDropdownOption,
selectFirstDropdownOption,
trackCleanupPhone,
trackCleanupTrack,
cleanupTrackedTestData,
preparePublicSession,
buildTinyPdfBuffer,
} = require("./helpers");
const ADMIN_EMAIL = process.env.E2E_ADMIN_EMAIL || "admin@example.com";
const ADMIN_PASSWORD = process.env.E2E_ADMIN_PASSWORD || "admin123";
const LAWYER_EMAIL = process.env.E2E_LAWYER_EMAIL || "ivan@mail.ru";
const LAWYER_PASSWORD = process.env.E2E_LAWYER_PASSWORD || "LawyerPass-123!";
test.afterEach(async ({ page }, testInfo) => {
await cleanupTrackedTestData(page, testInfo);
});
// ─────────────────────────────────────────────────────────────────────────────
// 1. Оставить заявку и открыть кабинет
// ─────────────────────────────────────────────────────────────────────────────
test("showcase-client-1: оставить заявку через лендинг и открыть кабинет", async ({ context, page }, testInfo) => {
const phone = randomPhone();
trackCleanupPhone(testInfo, phone);
const appUrl = process.env.E2E_BASE_URL || "http://localhost:8081";
await preparePublicSession(context, page, appUrl, phone);
const { trackNumber } = await createRequestViaLanding(page, {
phone,
name: `Клиент Showcase ${Date.now()}`,
description: "Проблема с нарушением прав потребителя — showcase.",
});
trackCleanupTrack(testInfo, trackNumber);
// Открыть кабинет — статус заявки виден
await openPublicCabinet(page, trackNumber);
await expect(page.locator("#cabinet-summary")).toBeVisible();
await expect(page.locator("#cabinet-request-status")).not.toHaveText("-");
// Трек-номер присутствует в URL кабинета
expect(page.url()).toContain(encodeURIComponent(trackNumber));
});
// ─────────────────────────────────────────────────────────────────────────────
// 2. Написать сообщение и прикрепить PDF
// ─────────────────────────────────────────────────────────────────────────────
test("showcase-client-2: написать сообщение и загрузить PDF", async ({ context, page }, testInfo) => {
const phone = randomPhone();
trackCleanupPhone(testInfo, phone);
const appUrl = process.env.E2E_BASE_URL || "http://localhost:8081";
await preparePublicSession(context, page, appUrl, phone);
const { trackNumber } = await createRequestViaLanding(page, {
phone,
description: "Showcase: сообщение и файл клиента",
});
trackCleanupTrack(testInfo, trackNumber);
await openPublicCabinet(page, trackNumber);
// Текстовое сообщение
const clientMsg = `Уважаемый юрист, ${Date.now()} — прошу помочь с ситуацией.`;
await sendCabinetMessage(page, clientMsg);
await expect(page.locator("#cabinet-messages")).toContainText(clientMsg);
// PDF-файл
const fileName = `client-doc-${Date.now()}.pdf`;
await uploadCabinetFile(page, fileName, "Showcase client document");
// Перейти на вкладку Файлы — убедиться что файл там
const filesTab = page.getByRole("tab", { name: /Файлы/ });
if (await filesTab.count()) {
await filesTab.click();
await expect(page.locator("#cabinet-files")).toContainText(fileName);
}
});
// ─────────────────────────────────────────────────────────────────────────────
// 3. Предпросмотр своего файла
// ─────────────────────────────────────────────────────────────────────────────
test("showcase-client-3: предпросмотр загруженного файла", async ({ context, page }, testInfo) => {
const phone = randomPhone();
trackCleanupPhone(testInfo, phone);
const appUrl = process.env.E2E_BASE_URL || "http://localhost:8081";
await preparePublicSession(context, page, appUrl, phone);
const { trackNumber } = await createRequestViaLanding(page, {
phone,
description: "Showcase: предпросмотр файла клиентом",
});
trackCleanupTrack(testInfo, trackNumber);
await openPublicCabinet(page, trackNumber);
const fileName = `preview-${Date.now()}.txt`;
await uploadCabinetFile(page, fileName, "ShowcaseFileContent");
const filesTab = page.getByRole("tab", { name: /Файлы/ });
if (await filesTab.count()) {
await filesTab.click();
}
const fileRow = page.locator("#cabinet-files li").filter({ hasText: fileName }).first();
await expect(fileRow).toBeVisible();
await fileRow.getByRole("button", { name: "Предпросмотр" }).click();
await expect(page.locator("#file-preview-overlay, .file-preview-overlay")).toBeVisible();
await expect(page.locator("#file-preview-body, .file-preview-body")).toContainText("ShowcaseFileContent");
await page.locator("#file-preview-close, .file-preview-close, .close").first().click();
await expect(page.locator("#file-preview-overlay, .file-preview-overlay")).not.toBeVisible();
});
// ─────────────────────────────────────────────────────────────────────────────
// 4. Увидеть ответ юриста в кабинете
// ─────────────────────────────────────────────────────────────────────────────
test("showcase-client-4: клиент видит ответ юриста в чате кабинета", async ({ context, page }, testInfo) => {
const phone = randomPhone();
trackCleanupPhone(testInfo, phone);
const appUrl = process.env.E2E_BASE_URL || "http://localhost:8081";
await preparePublicSession(context, page, appUrl, phone);
const { trackNumber } = await createRequestViaLanding(page, {
phone,
description: "Showcase: клиент видит ответ юриста",
});
trackCleanupTrack(testInfo, trackNumber);
await openPublicCabinet(page, trackNumber);
await sendCabinetMessage(page, `Клиент: ${Date.now()}`);
// Юрист берёт заявку и отвечает
await loginAdminPanel(page, { email: LAWYER_EMAIL, password: LAWYER_PASSWORD });
await openRequestsSection(page);
const row = rowByTrack(page, "#section-requests", trackNumber);
await expect(row).toHaveCount(1);
const claimBtn = row.first().getByRole("button", { name: "Взять в работу" });
if (await claimBtn.count()) {
await claimBtn.click();
await expect(page.locator("#section-requests .status")).toContainText(/работу|обновлен/i);
}
await row.first().locator(".request-track-link").click();
await expect(page.getByRole("heading", { name: /Карточка заявки/ })).toBeVisible();
const lawyerReply = `Юрист отвечает: ${Date.now()}`;
await page.getByRole("tab", { name: /Чат/ }).click();
await page.locator("#request-modal-message-body").fill(lawyerReply);
await page.locator("#request-modal-message-send").click();
await expect(page.locator("#section-request-workspace .status")).toContainText("Сообщение отправлено");
// Клиент открывает кабинет заново — видит ответ
await page.goto(`/client.html?track=${encodeURIComponent(trackNumber)}`);
await expect(page.locator("#cabinet-summary")).toBeVisible();
await expect(page.locator("#cabinet-messages")).toContainText(lawyerReply);
});
// ─────────────────────────────────────────────────────────────────────────────
// 5. Оставить вторую заявку (с того же телефона / email)
// ─────────────────────────────────────────────────────────────────────────────
test("showcase-client-5: оставить вторую заявку с того же аккаунта", async ({ context, page }, testInfo) => {
const phone = randomPhone();
trackCleanupPhone(testInfo, phone);
const appUrl = process.env.E2E_BASE_URL || "http://localhost:8081";
await preparePublicSession(context, page, appUrl, phone);
const { trackNumber: track1 } = await createRequestViaLanding(page, {
phone,
description: "Первая заявка от клиента Showcase",
});
trackCleanupTrack(testInfo, track1);
const { trackNumber: track2 } = await createRequestViaLanding(page, {
phone,
description: "Вторая заявка от того же клиента",
});
trackCleanupTrack(testInfo, track2);
expect(track1).not.toBe(track2);
// Обе заявки открываются в кабинете
await openPublicCabinet(page, track1);
await expect(page.locator("#cabinet-summary")).toBeVisible();
await page.goto(`/client.html?track=${encodeURIComponent(track2)}`);
await expect(page.locator("#cabinet-summary")).toBeVisible();
expect(page.url()).toContain(encodeURIComponent(track2));
});
// ─────────────────────────────────────────────────────────────────────────────
// 6. Заполнить запрошенные данные (DataRequirement fields)
// ─────────────────────────────────────────────────────────────────────────────
test("showcase-client-6: заполнить запрошенные данные (DataRequirement)", async ({ context, page }, testInfo) => {
const phone = randomPhone();
trackCleanupPhone(testInfo, phone);
const appUrl = process.env.E2E_BASE_URL || "http://localhost:8081";
await preparePublicSession(context, page, appUrl, phone);
const { trackNumber } = await createRequestViaLanding(page, {
phone,
description: "Showcase: клиент заполняет запрошенные данные",
});
trackCleanupTrack(testInfo, trackNumber);
// Администратор / юрист создаёт DataRequirement
await loginAdminPanel(page, { email: ADMIN_EMAIL, password: ADMIN_PASSWORD });
await openRequestsSection(page);
const row = rowByTrack(page, "#section-requests", trackNumber);
await row.first().locator(".request-track-link").click();
await expect(page.getByRole("heading", { name: /Карточка заявки/ })).toBeVisible();
const dataTab = page.getByRole("tab", { name: /Данные|Сбор|Запрос/i });
if (await dataTab.count()) {
await dataTab.click();
const addBtn = page.getByRole("button", { name: /Добавить|Запросить/i });
if (await addBtn.count()) {
await addBtn.click();
const labelField = page.locator("input[placeholder*='Название'], #data-req-label, [name='label']").first();
if (await labelField.count()) {
await labelField.fill("ФИО полностью");
await page.getByRole("button", { name: /Сохранить|Добавить/i }).first().click();
await expect(page.locator(".status, #section-request-workspace .status").first()).toContainText(/сохранен|добавлен/i);
}
}
}
// Клиент видит запрос в кабинете и заполняет его
await page.goto(`/client.html?track=${encodeURIComponent(trackNumber)}`);
await expect(page.locator("#cabinet-summary")).toBeVisible();
const dataSection = page.locator("#cabinet-data-requirements, .cabinet-data-section, [data-section='data']");
if (await dataSection.count()) {
const dataField = dataSection.locator("input, textarea").first();
if (await dataField.count()) {
await dataField.fill("Иванов Иван Иванович");
await dataSection.getByRole("button", { name: /Отправить|Сохранить|Подтвердить/i }).first().click();
await expect(page.locator("#client-page-status, #cabinet-status")).toContainText(/отправлен|сохранен|принят/i);
}
}
});
// ─────────────────────────────────────────────────────────────────────────────
// 7. Отправить обращение к куратору (ServiceRequest)
// ─────────────────────────────────────────────────────────────────────────────
test("showcase-client-7: отправить обращение к куратору", async ({ context, page }, testInfo) => {
const phone = randomPhone();
trackCleanupPhone(testInfo, phone);
const appUrl = process.env.E2E_BASE_URL || "http://localhost:8081";
await preparePublicSession(context, page, appUrl, phone);
const createResp = await page.request.post(`${appUrl}/api/public/requests`, {
data: {
client_name: `Showcase ${Date.now()}`,
client_phone: phone,
topic_code: "consulting",
description: "Showcase: обращение к куратору.",
pdn_consent: true,
},
failOnStatusCode: false,
});
const body = await createResp.json().catch(() => ({}));
const trackNumber = String(body.track_number || "");
if (!trackNumber) return;
trackCleanupTrack(testInfo, trackNumber);
await page.goto(`/client.html?track=${encodeURIComponent(trackNumber)}`);
await expect(page.locator("#cabinet-summary")).toBeVisible();
const helpBtn = page.locator("#cabinet-help-open");
if (await helpBtn.count()) {
await helpBtn.click();
await expect(page.locator("#client-help-overlay")).toBeVisible();
// Кнопка для куратора — без textarea; если заблокирована, пробуем смену юриста
const curatorBtn = page.locator("#cabinet-curator-request-open");
if (await curatorBtn.count() && !(await curatorBtn.isDisabled())) {
await curatorBtn.click();
} else {
await page.locator("#service-request-body").fill("Прошу уточнить сроки рассмотрения заявки.");
await page.locator("#cabinet-lawyer-change-open").click();
}
}
});
// ─────────────────────────────────────────────────────────────────────────────
// 8. Запросить смену юриста и увидеть нового юриста
// ─────────────────────────────────────────────────────────────────────────────
test("showcase-client-8: запросить смену юриста и увидеть нового в кабинете", async ({ context, page }, testInfo) => {
const phone = randomPhone();
trackCleanupPhone(testInfo, phone);
const appUrl = process.env.E2E_BASE_URL || "http://localhost:8081";
await preparePublicSession(context, page, appUrl, phone);
const { trackNumber } = await createRequestViaLanding(page, {
phone,
description: "Showcase: смена юриста",
});
trackCleanupTrack(testInfo, trackNumber);
// Клиент отправляет обращение о смене юриста
await page.goto(`/client.html?track=${encodeURIComponent(trackNumber)}`);
await expect(page.locator("#cabinet-summary")).toBeVisible();
const helpBtn = page.locator("#cabinet-help-open");
if (await helpBtn.count()) {
await helpBtn.click();
await expect(page.locator("#client-help-overlay")).toBeVisible();
// Запрос смены юриста: textarea + кнопка "Запросить смену"
const lawyerChangeBtn = page.locator("#cabinet-lawyer-change-open");
if (await lawyerChangeBtn.count() && !(await lawyerChangeBtn.isDisabled())) {
await page.locator("#service-request-body").fill("Прошу назначить другого юриста.");
await lawyerChangeBtn.click();
}
}
// Администратор берёт и переназначает юриста
await loginAdminPanel(page, { email: ADMIN_EMAIL, password: ADMIN_PASSWORD });
await openRequestsSection(page);
const row = rowByTrack(page, "#section-requests", trackNumber);
await row.first().locator(".request-track-link").click();
await expect(page.getByRole("heading", { name: /Карточка заявки/ })).toBeVisible();
// Назначить нового юриста
const lawyerField = page.locator("[data-field='assigned_lawyer_id'], #request-lawyer-select").first();
if (await lawyerField.count()) {
const firstLawyerLabel = await selectFirstDropdownOption(page, lawyerField);
const saveBtn = page.getByRole("button", { name: /Сохранить|Назначить/i }).first();
if (await saveBtn.count()) {
await saveBtn.click();
await expect(page.locator("#section-request-workspace .status").first()).toContainText(/сохранен|назначен|обновлен/i);
}
// Клиент обновляет кабинет и видит юриста
await page.goto(`/client.html?track=${encodeURIComponent(trackNumber)}`);
await expect(page.locator("#cabinet-summary")).toBeVisible();
const lawyerDisplay = page.locator("#cabinet-assigned-lawyer, .cabinet-lawyer-name, [data-field='lawyer']");
if (await lawyerDisplay.count()) {
await expect(lawyerDisplay.first()).not.toHaveText("-");
}
}
});
// ─────────────────────────────────────────────────────────────────────────────
// 9. Клиент наблюдает смену статуса (юрист меняет → клиент видит)
// ─────────────────────────────────────────────────────────────────────────────
test("showcase-client-9: клиент видит изменение статуса заявки в реальном времени", async ({ context, page }, testInfo) => {
const phone = randomPhone();
trackCleanupPhone(testInfo, phone);
const appUrl = process.env.E2E_BASE_URL || "http://localhost:8081";
await preparePublicSession(context, page, appUrl, phone);
const { trackNumber } = await createRequestViaLanding(page, {
phone,
description: "Showcase: наблюдение смены статуса",
});
trackCleanupTrack(testInfo, trackNumber);
// Запомнить начальный статус
await openPublicCabinet(page, trackNumber);
const initialStatus = await page.locator("#cabinet-request-status").textContent();
// Юрист берёт заявку (статус → ASSIGNED/IN_PROGRESS)
await loginAdminPanel(page, { email: LAWYER_EMAIL, password: LAWYER_PASSWORD });
await openRequestsSection(page);
const row = rowByTrack(page, "#section-requests", trackNumber);
const claimBtn = row.first().getByRole("button", { name: "Взять в работу" });
if (await claimBtn.count()) {
await claimBtn.click();
await expect(page.locator("#section-requests .status")).toContainText(/работу|обновлен/i);
}
// Клиент перезагружает кабинет — статус должен был измениться
await page.goto(`/client.html?track=${encodeURIComponent(trackNumber)}`);
await expect(page.locator("#cabinet-summary")).toBeVisible();
const newStatus = await page.locator("#cabinet-request-status").textContent();
// Статус изменился (если юрист был без назначения)
// В крайнем случае — просто убедиться что статус читаемый, а не UUID
expect(newStatus).not.toMatch(/^[0-9a-f]{8}-/i);
expect(newStatus?.trim()).not.toBe("");
});