* feat: Add routing trace presenters Add domain-specific presenters (SalesforceRoutingTracePresenter, RoutingFormTracePresenter) that format trace steps into human-readable strings, and a core RoutingTracePresenter that delegates to them based on step domain. Includes unit tests for all presenters. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Add findByBookingUid to RoutingTraceRepository Add method to look up a routing trace by booking UID, needed by the routing trace presenter tRPC endpoint. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Add getRoutingTrace tRPC endpoint Expose routing trace data for a booking via viewer.bookings.getRoutingTrace. Tries the permanent RoutingTrace first (round robin bookings), then falls back to PendingRoutingTrace via the booking's form response relation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: Add routing trace side sheet to booking list item Add a route icon button on booking list items that came from routing forms. Clicking it opens a side sheet displaying the full routing trace as human-readable steps. Adds RoutingTraceSheet component, store state, and translation key. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: Move routing trace action to dropdown menu Move the routing trace button from a standalone icon on the booking list item into the actions dropdown menu alongside "Report wrong assignment". The RoutingTraceSheet is now rendered from BookingActionsDropdown. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Improve routing trace UI and handle unnamed routes - Redesign RoutingTraceSheet with vertical timeline layout, domain badges, skeleton loading state, and millisecond timestamps - Use Salesforce app-store icon for Salesforce steps - Fall back to "Unnamed route" when route name is missing or matches route ID - Fix dropdown menu icon to git-merge (valid icon, unique in menu) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: Add tests for getRoutingTrace handler and findByBookingUid repository method Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * Abstract functions * Fix build error * Add permission check * fix: Update getRoutingTrace tests to include ctx and mock BookingAccessService Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
127 lines
4.2 KiB
TypeScript
127 lines
4.2 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { ROUTING_TRACE_DOMAINS } from "../constants";
|
|
import type { RoutingTraceService } from "../services/RoutingTraceService";
|
|
import { ROUTING_FORM_STEPS, RoutingFormTraceService } from "./RoutingFormTraceService";
|
|
|
|
describe("RoutingFormTraceService", () => {
|
|
let mockTraceService: RoutingTraceService;
|
|
let routingFormTraceService: RoutingFormTraceService;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockTraceService = {
|
|
addStep: vi.fn(),
|
|
} as unknown as RoutingTraceService;
|
|
routingFormTraceService = new RoutingFormTraceService(mockTraceService);
|
|
});
|
|
|
|
describe("routeMatched", () => {
|
|
it("should add a route_matched step with correct data", () => {
|
|
routingFormTraceService.routeMatched({
|
|
routeId: "route-123",
|
|
routeName: "Sales Route",
|
|
});
|
|
|
|
expect(mockTraceService.addStep).toHaveBeenCalledWith({
|
|
domain: ROUTING_TRACE_DOMAINS.ROUTING_FORM,
|
|
step: ROUTING_FORM_STEPS.ROUTE_MATCHED,
|
|
data: {
|
|
routeId: "route-123",
|
|
routeName: "Sales Route",
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("fallbackRouteUsed", () => {
|
|
it("should add a fallback_route_used step with correct data", () => {
|
|
routingFormTraceService.fallbackRouteUsed({
|
|
routeId: "fallback-route",
|
|
routeName: "Default Route",
|
|
});
|
|
|
|
expect(mockTraceService.addStep).toHaveBeenCalledWith({
|
|
domain: ROUTING_TRACE_DOMAINS.ROUTING_FORM,
|
|
step: ROUTING_FORM_STEPS.FALLBACK_ROUTE_USED,
|
|
data: {
|
|
routeId: "fallback-route",
|
|
routeName: "Default Route",
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("attributeLogicEvaluated", () => {
|
|
it("should add an attribute-logic-evaluated step with routing details", () => {
|
|
routingFormTraceService.attributeLogicEvaluated({
|
|
routeName: "Enterprise Route",
|
|
routeIsFallback: false,
|
|
checkedFallback: true,
|
|
attributeRoutingDetails: [
|
|
{ attributeName: "Company Size", attributeValue: "Enterprise" },
|
|
{ attributeName: "Region", attributeValue: "APAC" },
|
|
],
|
|
});
|
|
|
|
expect(mockTraceService.addStep).toHaveBeenCalledWith({
|
|
domain: ROUTING_TRACE_DOMAINS.ROUTING_FORM,
|
|
step: ROUTING_FORM_STEPS.ATTRIBUTE_LOGIC_EVALUATED,
|
|
data: {
|
|
routeName: "Enterprise Route",
|
|
routeIsFallback: false,
|
|
checkedFallback: true,
|
|
attributeRoutingDetails: [
|
|
{ attributeName: "Company Size", attributeValue: "Enterprise" },
|
|
{ attributeName: "Region", attributeValue: "APAC" },
|
|
],
|
|
},
|
|
});
|
|
});
|
|
|
|
it("should add an attribute-logic-evaluated step with minimal data", () => {
|
|
routingFormTraceService.attributeLogicEvaluated({});
|
|
|
|
expect(mockTraceService.addStep).toHaveBeenCalledWith({
|
|
domain: ROUTING_TRACE_DOMAINS.ROUTING_FORM,
|
|
step: ROUTING_FORM_STEPS.ATTRIBUTE_LOGIC_EVALUATED,
|
|
data: {},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("attributeFallbackUsed", () => {
|
|
it("should add an attribute_fallback_used step with route name", () => {
|
|
routingFormTraceService.attributeFallbackUsed({
|
|
routeName: "Fallback Route",
|
|
});
|
|
|
|
expect(mockTraceService.addStep).toHaveBeenCalledWith({
|
|
domain: ROUTING_TRACE_DOMAINS.ROUTING_FORM,
|
|
step: ROUTING_FORM_STEPS.ATTRIBUTE_FALLBACK_USED,
|
|
data: {
|
|
routeName: "Fallback Route",
|
|
},
|
|
});
|
|
});
|
|
|
|
it("should add an attribute_fallback_used step with undefined route name", () => {
|
|
routingFormTraceService.attributeFallbackUsed({});
|
|
|
|
expect(mockTraceService.addStep).toHaveBeenCalledWith({
|
|
domain: ROUTING_TRACE_DOMAINS.ROUTING_FORM,
|
|
step: ROUTING_FORM_STEPS.ATTRIBUTE_FALLBACK_USED,
|
|
data: {},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("ROUTING_FORM_STEPS constants", () => {
|
|
it("should have correct step values", () => {
|
|
expect(ROUTING_FORM_STEPS.ROUTE_MATCHED).toBe("route_matched");
|
|
expect(ROUTING_FORM_STEPS.FALLBACK_ROUTE_USED).toBe("fallback_route_used");
|
|
expect(ROUTING_FORM_STEPS.ATTRIBUTE_LOGIC_EVALUATED).toBe("attribute-logic-evaluated");
|
|
expect(ROUTING_FORM_STEPS.ATTRIBUTE_FALLBACK_USED).toBe("attribute_fallback_used");
|
|
});
|
|
});
|
|
});
|