feat(tests): agregar pruebas E2E para la gestión de líneas en proformas, incluyendo adición, edición y eliminación
This commit is contained in:
parent
b6aa20e52b
commit
026e373f52
@ -0,0 +1,42 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
|
||||
import { LineEditor } from "./line-editor";
|
||||
|
||||
test("asocia el selector de añadir línea al botón de añadir al final", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<LineEditor
|
||||
actionsLabel="Acciones"
|
||||
addAtEndLabel="Añadir al final"
|
||||
addAtEndTestId="proforma-add-item"
|
||||
addAtStartLabel="Añadir al inicio"
|
||||
columns={[]}
|
||||
duplicateLabel="Duplicar"
|
||||
getLineKey={(line) => line}
|
||||
insertAfterLabel="Insertar después"
|
||||
insertBeforeLabel="Insertar antes"
|
||||
lines={[] as string[]}
|
||||
moveDownLabel="Mover abajo"
|
||||
moveUpLabel="Mover arriba"
|
||||
onAddAfter={() => undefined}
|
||||
onAddAtEnd={() => undefined}
|
||||
onAddAtStart={() => undefined}
|
||||
onAddBefore={() => undefined}
|
||||
onDuplicate={() => undefined}
|
||||
onMoveDown={() => undefined}
|
||||
onMoveUp={() => undefined}
|
||||
onRemove={() => undefined}
|
||||
removeLabel="Eliminar"
|
||||
title="Líneas"
|
||||
/>
|
||||
);
|
||||
|
||||
const addAtStartLabelIndex = html.indexOf("Añadir al inicio");
|
||||
const addAtEndTestIdIndex = html.indexOf('data-testid="proforma-add-item"');
|
||||
const addAtEndLabelIndex = html.indexOf("Añadir al final");
|
||||
|
||||
assert.ok(addAtStartLabelIndex < addAtEndTestIdIndex);
|
||||
assert.ok(addAtEndTestIdIndex < addAtEndLabelIndex);
|
||||
});
|
||||
@ -38,6 +38,7 @@ export interface LineEditorProps<TLine> {
|
||||
description?: React.ReactNode;
|
||||
lines: TLine[];
|
||||
getLineKey: (line: TLine, index: number) => React.Key;
|
||||
getLineTestId?: (line: TLine, index: number) => string;
|
||||
|
||||
columns: LineEditorColumn<TLine>[];
|
||||
|
||||
@ -64,6 +65,10 @@ export interface LineEditorProps<TLine> {
|
||||
moveDownLabel: string;
|
||||
removeLabel: string;
|
||||
|
||||
addAtEndTestId?: string;
|
||||
getLineActionsTestId?: (line: TLine, index: number) => string;
|
||||
getLineRemoveTestId?: (line: TLine, index: number) => string;
|
||||
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
@ -73,6 +78,7 @@ export const LineEditor = <TLine,>({
|
||||
description,
|
||||
lines,
|
||||
getLineKey,
|
||||
getLineTestId,
|
||||
|
||||
columns,
|
||||
|
||||
@ -99,6 +105,10 @@ export const LineEditor = <TLine,>({
|
||||
moveDownLabel,
|
||||
removeLabel,
|
||||
|
||||
addAtEndTestId,
|
||||
getLineActionsTestId,
|
||||
getLineRemoveTestId,
|
||||
|
||||
disabled = false,
|
||||
|
||||
className,
|
||||
@ -124,6 +134,7 @@ export const LineEditor = <TLine,>({
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
data-testid={addAtEndTestId}
|
||||
disabled={disabled}
|
||||
onClick={onAddAtEnd}
|
||||
size="sm"
|
||||
@ -171,6 +182,7 @@ export const LineEditor = <TLine,>({
|
||||
"group",
|
||||
hasLineError && "bg-destructive/5 hover:bg-destructive/5"
|
||||
)}
|
||||
data-testid={getLineTestId?.(line, index)}
|
||||
>
|
||||
<TableCell className="text-muted-foreground text-sm font-medium align-top pt-4">
|
||||
{index + 1}
|
||||
@ -189,6 +201,7 @@ export const LineEditor = <TLine,>({
|
||||
<Button
|
||||
aria-label={actionsLabel}
|
||||
className="size-8"
|
||||
data-testid={getLineActionsTestId?.(line, index)}
|
||||
size="icon"
|
||||
type="button"
|
||||
variant="ghost"
|
||||
@ -235,6 +248,7 @@ export const LineEditor = <TLine,>({
|
||||
|
||||
<DropdownMenuItem
|
||||
className="text-destructive focus:text-destructive"
|
||||
data-testid={getLineRemoveTestId?.(line, index)}
|
||||
onClick={() => onRemove(index)}
|
||||
>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
|
||||
@ -260,6 +260,7 @@ export const ProformaLineEditor = ({
|
||||
<LineEditor
|
||||
actionsLabel={t("common.actions", "Acciones")}
|
||||
addAtEndLabel={t("common.add_at_end", "Al final")}
|
||||
addAtEndTestId="proforma-add-item"
|
||||
addAtStartLabel={t("common.add_at_start", "Al inicio")}
|
||||
className={className}
|
||||
columns={columns}
|
||||
@ -269,11 +270,14 @@ export const ProformaLineEditor = ({
|
||||
)}
|
||||
disabled={disabled}
|
||||
duplicateLabel={t("common.duplicate", "Duplicar")}
|
||||
getLineActionsTestId={(field) => `proforma-item-actions-${field.fieldId}`}
|
||||
getLineErrorMessage={(_, index) => {
|
||||
const message = getItemErrorMessage(index);
|
||||
return message ? t(message, message) : undefined;
|
||||
}}
|
||||
getLineKey={(field) => field.fieldId}
|
||||
getLineRemoveTestId={(field) => `proforma-item-delete-${field.fieldId}`}
|
||||
getLineTestId={(field) => `proforma-item-row-${field.fieldId}`}
|
||||
insertAfterLabel={t("common.insert_after", "Insertar después")}
|
||||
insertBeforeLabel={t("common.insert_before", "Insertar antes")}
|
||||
lines={fields}
|
||||
|
||||
@ -50,7 +50,7 @@ test("conecta la alta rápida directa y la alta rápida desde selector con callb
|
||||
namedExports: {
|
||||
CancelActionButton: () => null,
|
||||
PercentageField: () => null,
|
||||
RdxEditorStickyFooter: () => null,
|
||||
RdxEditorActionBar: () => null,
|
||||
RdxPageContainer: ({ children }: { children: ReactNode }) => children,
|
||||
RdxPageFrame: ({
|
||||
children,
|
||||
|
||||
@ -83,7 +83,7 @@ test("RdxFormGrid mantiene la retícula base y combina className", () => {
|
||||
assert.match(html, /custom-grid/);
|
||||
});
|
||||
|
||||
test("RdxEditorStickyFooter renderiza regiones presentes sin wrappers vacíos", () => {
|
||||
test("RdxEditorActionBar renderiza regiones presentes sin wrappers vacíos", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<RdxEditorActionBar
|
||||
className="custom-sticky"
|
||||
@ -104,7 +104,7 @@ test("RdxEditorStickyFooter renderiza regiones presentes sin wrappers vacíos",
|
||||
assert.ok(html.indexOf("Cancel") < html.indexOf("Save"));
|
||||
});
|
||||
|
||||
test("RdxEditorStickyFooter omite regiones opcionales vacías", () => {
|
||||
test("RdxEditorActionBar omite regiones opcionales vacías", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<RdxEditorActionBar primary={<button type="submit">Save</button>} />
|
||||
);
|
||||
|
||||
127
tests/e2e/customer-invoices/proformas/proforma-items.spec.ts
Normal file
127
tests/e2e/customer-invoices/proformas/proforma-items.spec.ts
Normal file
@ -0,0 +1,127 @@
|
||||
import type { Page, TestInfo } from "@playwright/test";
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { loginViaUi } from "../../support/login-via-ui";
|
||||
|
||||
import { createProformaViaUi } from "./support/create-proforma-via-ui";
|
||||
|
||||
const itemRows = (page: Page) => page.getByTestId(/^proforma-item-row-/);
|
||||
|
||||
const saveProforma = async (page: Page, id: string) => {
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.request().method() === "PUT" &&
|
||||
new URL(response.url()).pathname.endsWith(`/proformas/${id}`)
|
||||
);
|
||||
|
||||
await page.getByTestId("proforma-update-submit").click();
|
||||
expect((await responsePromise).status()).toBe(200);
|
||||
};
|
||||
|
||||
const addItem = async (page: Page, description: string) => {
|
||||
await page.getByTestId("proforma-add-item").click();
|
||||
const row = itemRows(page).last();
|
||||
await expect(row).toBeVisible();
|
||||
const descriptionInput = row.locator('[name$=".description"]');
|
||||
await descriptionInput.fill(description);
|
||||
};
|
||||
|
||||
const expectItemDescriptions = async (page: Page, descriptions: string[]) => {
|
||||
const rows = itemRows(page);
|
||||
await expect(rows).toHaveCount(descriptions.length);
|
||||
|
||||
for (const [index, description] of descriptions.entries()) {
|
||||
await expect(rows.nth(index).locator('[name$=".description"]')).toHaveValue(description);
|
||||
}
|
||||
};
|
||||
|
||||
const createProformaWithItemsViaUi = async (
|
||||
page: Page,
|
||||
testInfo: TestInfo,
|
||||
descriptions: string[]
|
||||
) => {
|
||||
const proforma = await createProformaViaUi(page, testInfo);
|
||||
|
||||
for (const description of descriptions) {
|
||||
await addItem(page, description);
|
||||
}
|
||||
|
||||
await saveProforma(page, proforma.id);
|
||||
return proforma;
|
||||
};
|
||||
|
||||
test("CUSTOMER-INVOICES-PROFORMA-ITEMS-001: añade una línea a un draft vacío y conserva sus datos", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await loginViaUi(page);
|
||||
const proforma = await createProformaViaUi(page, testInfo);
|
||||
const description = `E2E ITEM ADD ${proforma.token}`;
|
||||
|
||||
await addItem(page, description);
|
||||
await saveProforma(page, proforma.id);
|
||||
await expectItemDescriptions(page, [description]);
|
||||
await page.reload();
|
||||
await expectItemDescriptions(page, [description]);
|
||||
});
|
||||
|
||||
test("CUSTOMER-INVOICES-PROFORMA-ITEMS-002: edita una línea persistida sin duplicarla", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await loginViaUi(page);
|
||||
const original = `E2E ITEM ORIGINAL ${testInfo.testId}`;
|
||||
const proforma = await createProformaWithItemsViaUi(page, testInfo, [original]);
|
||||
const updated = `E2E ITEM UPDATED ${proforma.token}`;
|
||||
|
||||
const row = itemRows(page).first();
|
||||
await row.locator('[name$=".description"]').fill(updated);
|
||||
await saveProforma(page, proforma.id);
|
||||
await expectItemDescriptions(page, [updated]);
|
||||
await page.reload();
|
||||
await expectItemDescriptions(page, [updated]);
|
||||
});
|
||||
|
||||
test("CUSTOMER-INVOICES-PROFORMA-ITEMS-003: elimina la última línea y conserva el draft vacío", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await loginViaUi(page);
|
||||
const proforma = await createProformaWithItemsViaUi(page, testInfo, [
|
||||
`E2E ITEM DELETE ${testInfo.testId}`,
|
||||
]);
|
||||
|
||||
const row = itemRows(page).first();
|
||||
await row.getByTestId(/^proforma-item-actions-/).click();
|
||||
await page.getByTestId(/^proforma-item-delete-/).click();
|
||||
await expect(itemRows(page)).toHaveCount(0);
|
||||
|
||||
const requestPromise = page.waitForRequest(
|
||||
(request) =>
|
||||
request.method() === "PUT" &&
|
||||
new URL(request.url()).pathname.endsWith(`/proformas/${proforma.id}`)
|
||||
);
|
||||
await saveProforma(page, proforma.id);
|
||||
expect((await requestPromise).postDataJSON()).toMatchObject({ items: [] });
|
||||
await page.reload();
|
||||
await expect(itemRows(page)).toHaveCount(0);
|
||||
await expect(page.locator('input[name="reference"]')).toBeVisible();
|
||||
});
|
||||
|
||||
test("CUSTOMER-INVOICES-PROFORMA-ITEMS-004: conserva varias líneas y su orden tras recargar", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await loginViaUi(page);
|
||||
const proforma = await createProformaViaUi(page, testInfo);
|
||||
const descriptions = [
|
||||
`E2E ITEM AAA ${proforma.token}`,
|
||||
`E2E ITEM BBB ${proforma.token}`,
|
||||
`E2E ITEM CCC ${proforma.token}`,
|
||||
];
|
||||
|
||||
for (const description of descriptions) {
|
||||
await addItem(page, description);
|
||||
}
|
||||
|
||||
await saveProforma(page, proforma.id);
|
||||
await expectItemDescriptions(page, descriptions);
|
||||
await page.reload();
|
||||
await expectItemDescriptions(page, descriptions);
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user