mirror of
https://github.com/TronoSfera/Law.git
synced 2026-05-18 18:13:46 +03:00
410 lines
22 KiB
JavaScript
410 lines
22 KiB
JavaScript
/**
|
||
* 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("");
|
||
});
|