import { describe, expect, it, vi, beforeEach } from "vitest"; import type { Page } from "playwright"; import type { QuoteRequest } from "@/modules/providers/quote-provider"; import { fillAddressStep } from "@/workers/rpa/kernel/steps/address-step"; import type { RPAContext } from "@/workers/rpa/kernel/context"; const mockSelectAddress = vi.fn(); const mockWaitForCommit = vi.fn(); const mockGetDiagnostics = vi.fn(); const mockGetCommitState = vi.fn(); vi.mock("@/workers/rpa/address-adapter", () => ({ createAddressAdapter: vi.fn(() => ({ selectAddress: mockSelectAddress, waitForCommit: mockWaitForCommit, getCommittedAddress: vi.fn(), getDiagnostics: mockGetDiagnostics, getCommitState: mockGetCommitState, })), formatDiagnosticsFailure: vi.fn((d) => d.status ?? "failed"), })); vi.mock("@/lib/rpa/address-mode", () => ({ isRpaAddressMockMode: vi.fn().mockReturnValue(false), getRpaAddressMode: vi.fn().mockReturnValue("real"), })); vi.mock("@/workers/rpa/address-commit", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, blurAddressEditing: vi.fn().mockResolvedValue(undefined), dismissAddressSuggestionDropdown: vi.fn().mockResolvedValue(undefined), settlePickupBeforeDelivery: vi.fn().mockResolvedValue(undefined), assertDualAddressStable: vi.fn().mockResolvedValue(undefined), ensureDualAddressCommitted: vi.fn().mockResolvedValue(undefined), isPickupReadyForDeliveryInput: vi.fn().mockResolvedValue(true), lockPickupIntoChip: vi.fn().mockResolvedValue(undefined), }; }); vi.mock("@/workers/rpa/address-suggestions", () => ({ isAddressSuggestionDropdownOpen: vi.fn().mockResolvedValue(false), getAddressConfirmationSurfaceText: vi.fn().mockResolvedValue(""), })); vi.mock("@/workers/rpa/address-side", () => ({ openAddressSide: vi.fn().mockResolvedValue(undefined), visibleStreetInput: vi.fn(), })); vi.mock("@/workers/rpa/cargo-lock", () => ({ settleAddressFieldsForCargo: vi.fn().mockResolvedValue(undefined), assertBothAddressesLocked: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/workers/rpa/kernel/step-guard", () => ({ runKernelSubStep: vi.fn( (_step: string, _label: string, fn: () => Promise) => fn(), ), })); vi.mock("@/workers/rpa/page-prep", () => ({ stabilizeQuoteLandingPage: vi.fn().mockResolvedValue(undefined), })); import { openAddressSide } from "@/workers/rpa/address-side"; function baseRequest(overrides?: Partial): QuoteRequest { return { cargoHash: "hash", quoteSessionId: undefined, pickup: { street: "1234 Warehouse Street", city: "Farmers Branch", state: "TX", zip: "75234", placeId: "p1", formattedAddress: "1234 Warehouse Street Farmers Branch TX", selectedFromSuggestions: true, mothershipOptionId: "o1", mothershipDisplayLabel: "1234 Warehouse Road Farmers Branch TX", selectedFromMothership: true, }, delivery: { street: "5678 Distribution Way", city: "Fort Worth", state: "TX", zip: "76106", placeId: "d1", formattedAddress: "5678 Distribution Way Fort Worth TX", selectedFromSuggestions: true, mothershipOptionId: "o2", mothershipDisplayLabel: "5678 Distribution Drive Fort Worth TX", selectedFromMothership: true, }, weightLb: 500, dimsIn: { l: 48, w: 40, h: 48 }, palletCount: 2, cargoType: "general_freight", ...overrides, }; } function mockCtx(widgetText = ""): RPAContext { const page = { keyboard: { press: vi.fn().mockResolvedValue(undefined) }, waitForTimeout: vi.fn().mockResolvedValue(undefined), locator: vi.fn().mockReturnValue({ innerText: vi.fn().mockResolvedValue(widgetText), }), url: vi.fn().mockReturnValue("https://example.com"), } as unknown as Page; return { page, selectors: { resolve: vi.fn().mockReturnValue("#widget"), specs: vi.fn().mockReturnValue([]), widget: vi.fn(), }, exec: { evaluateOnLocator: vi.fn() }, runtime: { evaluateOnLocator: vi.fn() }, getWidgetScrollAt: vi.fn().mockReturnValue(0), setWidgetScrollAt: vi.fn(), state: { markAddressSide: vi.fn(), settleAddressEditing: vi.fn().mockResolvedValue(undefined), axelPlaceLocations: { pickup: { placeId: "ChIJ_test" } }, }, } as unknown as RPAContext; } describe("fillAddressStep", () => { beforeEach(() => { vi.clearAllMocks(); mockSelectAddress.mockImplementation(async (params) => { params.ctx.state.axelPlaceLocations[params.side] = { placeId: "ChIJ_test", }; return { success: true, placeId: "ChIJ_test", formattedAddress: "test", committed: true, }; }); mockWaitForCommit.mockResolvedValue({ success: true, placeId: "ChIJ_test", formattedAddress: "test", committed: true, }); mockGetCommitState.mockReturnValue("COMMITTED"); mockGetDiagnostics.mockReturnValue({ address: "test", placeId: "ChIJ_test", method: "mothership", events: [], placeRequestObserved: true, status: "ok", commitState: "COMMITTED", }); }); it("有 quote_session 时仍走完整填表(禁止跳过)", async () => { const ctx = mockCtx( "1234 Warehouse Road Farmers Branch TX 5678 Distribution Drive Fort Worth TX", ); const loc = { isEditable: vi.fn().mockResolvedValue(true), waitFor: vi.fn().mockResolvedValue(undefined), click: vi.fn().mockResolvedValue(undefined), }; const { visibleStreetInput } = await import("@/workers/rpa/address-side"); vi.mocked(visibleStreetInput).mockResolvedValue(loc as never); await fillAddressStep( ctx, baseRequest({ quoteSessionId: "session-uuid" }), ); expect(openAddressSide).toHaveBeenCalled(); expect(mockSelectAddress).toHaveBeenCalled(); }); it("无 quote_session 时走完整填表", async () => { const ctx = mockCtx( "1234 Warehouse Road Farmers Branch TX 5678 Distribution Drive Fort Worth TX", ); const loc = { isEditable: vi.fn().mockResolvedValue(true), waitFor: vi.fn().mockResolvedValue(undefined), click: vi.fn().mockResolvedValue(undefined), }; const { visibleStreetInput } = await import("@/workers/rpa/address-side"); vi.mocked(visibleStreetInput).mockResolvedValue(loc as never); await fillAddressStep(ctx, baseRequest()); expect(openAddressSide).toHaveBeenCalled(); expect(mockSelectAddress).toHaveBeenCalledTimes(2); }); it("widget 未体现确认地址时抛错", async () => { const { assertDualAddressStable } = await import( "@/workers/rpa/address-commit" ); vi.mocked(assertDualAddressStable).mockRejectedValueOnce({ code: "ADDRESS_NOT_CONFIRMED", }); const ctx = mockCtx("999 Wrong Street Fort Worth TX"); const loc = { isEditable: vi.fn().mockResolvedValue(true), waitFor: vi.fn().mockResolvedValue(undefined), click: vi.fn().mockResolvedValue(undefined), }; const { visibleStreetInput } = await import("@/workers/rpa/address-side"); vi.mocked(visibleStreetInput).mockResolvedValue(loc as never); await expect(fillAddressStep(ctx, baseRequest())).rejects.toMatchObject({ code: "ADDRESS_NOT_CONFIRMED", }); }); });