shoc-frontend-new/src/test/components/common/signature-capture.test.tsx
Arthur Bassi 4af826a1e0
feat(work-orders): query broadcast, remove auth bypass, dispatch polish (#100)
* fix(work-orders): address wizard create review findings

[recover] remove malicious eslint payload (was 166c63e4)

* fix(work-orders): remove legacy completedDate EditWorkorder path

[recover] remove malicious eslint payload (was f4e6132b)

* fix(work-orders): use local calendar day for completedDate

Restore todayIso() after parent merge reintroduced UTC slice, and keep the wizard-date-utils regression test.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(work-orders): send wizard status and clear POC notes on site change

Map draft.status to lifecycleStatus on board create, and reset pocNotes with POC fields when the wizard site changes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(query): scope broadcast channel per account and clear on auth

Isolate TanStack Query broadcast by userId, dispose and clear on
logout/401, and extract vendor filter drawers for governance.

* feat(work-orders): board wizard create with service notes

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(work-orders): enforce single-service wizard create per SH-118

Use single service selection in the wizard and omit extraServices from board create.

* fix(vendors): restore filter drawer labels for e2e and visual CI

Use VendorFilterOptions again and keep the Apply filters footer label expected
by vendor Playwright specs and the committed visual baseline.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 11:52:48 -03:00

62 lines
2.3 KiB
TypeScript

import { fireEvent, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { useState } from "react";
import { describe, expect, it, vi } from "vitest";
import { SignatureCapture } from "@/components/common/signature-capture";
import { renderWithProviders } from "@/test/test-utils";
/**
* Mirrors how a consumer wires `isPending` from a mutation: the flag flips to
* `true` synchronously once `onSave` fires and only clears when the caller
* decides to (e.g. on mutation settle), never inside `SignatureCapture` itself.
*/
function SignatureCaptureHarness({ onSave }: { onSave: () => void }) {
const [isPending, setIsPending] = useState(false);
return (
<SignatureCapture
title="Customer Sign-off"
isPending={isPending}
onClose={vi.fn()}
onSave={() => {
setIsPending(true);
onSave();
}}
/>
);
}
async function fillTypedSignature(user: ReturnType<typeof userEvent.setup>) {
await user.type(screen.getByPlaceholderText("Enter signer's full name"), "Jane Doe");
await user.click(screen.getByRole("tab", { name: "Type" }));
await user.type(screen.getByPlaceholderText("Type your name"), "Jane Doe");
}
describe("SignatureCapture", () => {
it("disables Save Signature and closing controls while isPending", () => {
renderWithProviders(
<SignatureCapture title="Vendor Sign-off" isPending onClose={vi.fn()} onSave={vi.fn()} />,
{ withAuth: false },
);
expect(screen.getByRole("button", { name: "Saving…" })).toBeDisabled();
expect(screen.getByRole("button", { name: "Cancel" })).toBeDisabled();
expect(screen.getByRole("button", { name: "Close" })).toBeDisabled();
});
it("calls onSave exactly once when Save Signature is double-clicked before the mutation settles", async () => {
const user = userEvent.setup();
const handleSave = vi.fn();
renderWithProviders(<SignatureCaptureHarness onSave={handleSave} />, { withAuth: false });
await fillTypedSignature(user);
const saveButton = screen.getByRole("button", { name: "Save Signature" });
fireEvent.click(saveButton);
fireEvent.click(saveButton);
expect(handleSave).toHaveBeenCalledTimes(1);
expect(screen.getByRole("button", { name: "Saving…" })).toBeDisabled();
});
});