|
|
@ -1,11 +1,23 @@ |
|
|
import { render, screen, fireEvent, waitFor, cleanup } from "@testing-library/react"; |
|
|
|
|
|
import { describe, it, expect, afterEach, beforeEach, vi } from "vitest"; |
|
|
|
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; |
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; |
|
|
import { QuestionAnswersProvider, useQuestionAnswers } from "./question-answer-storage"; |
|
|
|
|
|
import { convertSchemaToFrontendItems } from "@/lib/schema-adapter"; |
|
|
|
|
|
|
|
|
import { |
|
|
|
|
|
cleanup, |
|
|
|
|
|
fireEvent, |
|
|
|
|
|
render, |
|
|
|
|
|
screen, |
|
|
|
|
|
waitFor, |
|
|
|
|
|
} from "@testing-library/react"; |
|
|
|
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; |
|
|
import { useFormSchemaQuery } from "@/hooks/marriage/use-form-schema"; |
|
|
import { useFormSchemaQuery } from "@/hooks/marriage/use-form-schema"; |
|
|
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; |
|
|
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; |
|
|
import { useMarriageSectionDataQuery, useUpdateMarriageSectionDataMutation } from "@/hooks/marriage/use-section-data"; |
|
|
|
|
|
|
|
|
import { |
|
|
|
|
|
useMarriageSectionDataQuery, |
|
|
|
|
|
useUpdateMarriageSectionDataMutation, |
|
|
|
|
|
} from "@/hooks/marriage/use-section-data"; |
|
|
|
|
|
import { convertSchemaToFrontendItems } from "@/lib/schema-adapter"; |
|
|
|
|
|
import { |
|
|
|
|
|
QuestionAnswersProvider, |
|
|
|
|
|
useQuestionAnswers, |
|
|
|
|
|
} from "./question-answer-storage"; |
|
|
|
|
|
|
|
|
vi.mock("@/hooks/marriage/use-form-schema", () => ({ |
|
|
vi.mock("@/hooks/marriage/use-form-schema", () => ({ |
|
|
useFormSchemaQuery: vi.fn(), |
|
|
useFormSchemaQuery: vi.fn(), |
|
|
@ -14,6 +26,7 @@ vi.mock("@/hooks/marriage/use-profile-main", () => ({ |
|
|
useMarriageProfileQuery: vi.fn(), |
|
|
useMarriageProfileQuery: vi.fn(), |
|
|
})); |
|
|
})); |
|
|
vi.mock("@/hooks/marriage/use-section-data", () => ({ |
|
|
vi.mock("@/hooks/marriage/use-section-data", () => ({ |
|
|
|
|
|
applyProfilePatchResultToCache: vi.fn(), |
|
|
useMarriageSectionDataQuery: vi.fn(), |
|
|
useMarriageSectionDataQuery: vi.fn(), |
|
|
useUpdateMarriageSectionDataMutation: vi.fn(), |
|
|
useUpdateMarriageSectionDataMutation: vi.fn(), |
|
|
})); |
|
|
})); |
|
|
@ -28,8 +41,22 @@ function TestComponent({ slug }: { slug: string }) { |
|
|
data-testid="set-radio" |
|
|
data-testid="set-radio" |
|
|
onClick={() => |
|
|
onClick={() => |
|
|
setAnswerValue( |
|
|
setAnswerValue( |
|
|
{ id: "q1", type: "radio", title: "Q1", order: 1, required: true, baseRequired: true, isVisible: true, description: "", tooltip: "", extras: { placeHolder: "", options: [], range: [0,0] }, options: [{ id: "opt1", value: "A", label: "Option A", order: 1 }] }, |
|
|
|
|
|
"opt1" |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
id: "q1", |
|
|
|
|
|
type: "radio", |
|
|
|
|
|
title: "Q1", |
|
|
|
|
|
order: 1, |
|
|
|
|
|
required: true, |
|
|
|
|
|
baseRequired: true, |
|
|
|
|
|
isVisible: true, |
|
|
|
|
|
description: "", |
|
|
|
|
|
tooltip: "", |
|
|
|
|
|
extras: { placeHolder: "", options: [], range: [0, 0] }, |
|
|
|
|
|
options: [ |
|
|
|
|
|
{ id: "opt1", value: "A", label: "Option A", order: 1 }, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
|
|
|
"opt1", |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
> |
|
|
> |
|
|
@ -40,8 +67,23 @@ function TestComponent({ slug }: { slug: string }) { |
|
|
data-testid="set-checkbox" |
|
|
data-testid="set-checkbox" |
|
|
onClick={() => |
|
|
onClick={() => |
|
|
setAnswerValue( |
|
|
setAnswerValue( |
|
|
{ id: "q2", type: "checkbox", title: "Q2", order: 2, required: true, baseRequired: true, isVisible: true, description: "", tooltip: "", extras: { placeHolder: "", options: [], range: [0,0] }, options: [{ id: "opt2", value: "B", label: "Option B", order: 1 }, { id: "opt3", value: "C", label: "Option C", order: 2 }] }, |
|
|
|
|
|
["opt2", "opt3"] |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
id: "q2", |
|
|
|
|
|
type: "checkbox", |
|
|
|
|
|
title: "Q2", |
|
|
|
|
|
order: 2, |
|
|
|
|
|
required: true, |
|
|
|
|
|
baseRequired: true, |
|
|
|
|
|
isVisible: true, |
|
|
|
|
|
description: "", |
|
|
|
|
|
tooltip: "", |
|
|
|
|
|
extras: { placeHolder: "", options: [], range: [0, 0] }, |
|
|
|
|
|
options: [ |
|
|
|
|
|
{ id: "opt2", value: "B", label: "Option B", order: 1 }, |
|
|
|
|
|
{ id: "opt3", value: "C", label: "Option C", order: 2 }, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
|
|
|
["opt2", "opt3"], |
|
|
) |
|
|
) |
|
|
} |
|
|
} |
|
|
> |
|
|
> |
|
|
@ -87,19 +129,35 @@ describe("Question Answer & Schema Integration", () => { |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
it("should send option_id instead of label/value for radio", async () => { |
|
|
it("should send option_id instead of label/value for radio", async () => { |
|
|
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); |
|
|
|
|
|
|
|
|
const queryClient = new QueryClient({ |
|
|
|
|
|
defaultOptions: { queries: { retry: false } }, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
render( |
|
|
render( |
|
|
<QueryClientProvider client={queryClient}> |
|
|
<QueryClientProvider client={queryClient}> |
|
|
<QuestionAnswersProvider |
|
|
<QuestionAnswersProvider |
|
|
slug="test_slug" |
|
|
slug="test_slug" |
|
|
questions={[ |
|
|
questions={[ |
|
|
{ id: "q1", type: "radio", title: "Q1", order: 1, required: true, baseRequired: true, isVisible: true, description: "", tooltip: "", extras: { placeHolder: "", options: [], range: [0,0] }, options: [{ id: "opt1", value: "A", label: "Option A", order: 1 }] }, |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
id: "q1", |
|
|
|
|
|
type: "radio", |
|
|
|
|
|
title: "Q1", |
|
|
|
|
|
order: 1, |
|
|
|
|
|
required: true, |
|
|
|
|
|
baseRequired: true, |
|
|
|
|
|
isVisible: true, |
|
|
|
|
|
description: "", |
|
|
|
|
|
tooltip: "", |
|
|
|
|
|
extras: { placeHolder: "", options: [], range: [0, 0] }, |
|
|
|
|
|
options: [ |
|
|
|
|
|
{ id: "opt1", value: "A", label: "Option A", order: 1 }, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
]} |
|
|
]} |
|
|
> |
|
|
> |
|
|
<TestComponent slug="test_slug" /> |
|
|
<TestComponent slug="test_slug" /> |
|
|
</QuestionAnswersProvider> |
|
|
</QuestionAnswersProvider> |
|
|
</QueryClientProvider> |
|
|
|
|
|
|
|
|
</QueryClientProvider>, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
fireEvent.click(screen.getByTestId("set-radio")); |
|
|
fireEvent.click(screen.getByTestId("set-radio")); |
|
|
@ -114,19 +172,36 @@ describe("Question Answer & Schema Integration", () => { |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
it("should send array of option_ids for checkbox", async () => { |
|
|
it("should send array of option_ids for checkbox", async () => { |
|
|
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); |
|
|
|
|
|
|
|
|
const queryClient = new QueryClient({ |
|
|
|
|
|
defaultOptions: { queries: { retry: false } }, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
render( |
|
|
render( |
|
|
<QueryClientProvider client={queryClient}> |
|
|
<QueryClientProvider client={queryClient}> |
|
|
<QuestionAnswersProvider |
|
|
<QuestionAnswersProvider |
|
|
slug="test_slug" |
|
|
slug="test_slug" |
|
|
questions={[ |
|
|
questions={[ |
|
|
{ id: "q2", type: "checkbox", title: "Q2", order: 2, required: true, baseRequired: true, isVisible: true, description: "", tooltip: "", extras: { placeHolder: "", options: [], range: [0,0] }, options: [{ id: "opt2", value: "B", label: "Option B", order: 1 }, { id: "opt3", value: "C", label: "Option C", order: 2 }] }, |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
id: "q2", |
|
|
|
|
|
type: "checkbox", |
|
|
|
|
|
title: "Q2", |
|
|
|
|
|
order: 2, |
|
|
|
|
|
required: true, |
|
|
|
|
|
baseRequired: true, |
|
|
|
|
|
isVisible: true, |
|
|
|
|
|
description: "", |
|
|
|
|
|
tooltip: "", |
|
|
|
|
|
extras: { placeHolder: "", options: [], range: [0, 0] }, |
|
|
|
|
|
options: [ |
|
|
|
|
|
{ id: "opt2", value: "B", label: "Option B", order: 1 }, |
|
|
|
|
|
{ id: "opt3", value: "C", label: "Option C", order: 2 }, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
]} |
|
|
]} |
|
|
> |
|
|
> |
|
|
<TestComponent slug="test_slug" /> |
|
|
<TestComponent slug="test_slug" /> |
|
|
</QuestionAnswersProvider> |
|
|
</QuestionAnswersProvider> |
|
|
</QueryClientProvider> |
|
|
|
|
|
|
|
|
</QueryClientProvider>, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
fireEvent.click(screen.getByTestId("set-checkbox")); |
|
|
fireEvent.click(screen.getByTestId("set-checkbox")); |
|
|
@ -139,6 +214,61 @@ describe("Question Answer & Schema Integration", () => { |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
it("batches all dirty answers into one mutation", async () => { |
|
|
|
|
|
const queryClient = new QueryClient({ |
|
|
|
|
|
defaultOptions: { queries: { retry: false } }, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
render( |
|
|
|
|
|
<QueryClientProvider client={queryClient}> |
|
|
|
|
|
<QuestionAnswersProvider |
|
|
|
|
|
slug="test_slug" |
|
|
|
|
|
questions={[ |
|
|
|
|
|
{ |
|
|
|
|
|
id: "q1", |
|
|
|
|
|
type: "radio", |
|
|
|
|
|
title: "Q1", |
|
|
|
|
|
order: 1, |
|
|
|
|
|
required: true, |
|
|
|
|
|
baseRequired: true, |
|
|
|
|
|
isVisible: true, |
|
|
|
|
|
description: "", |
|
|
|
|
|
tooltip: "", |
|
|
|
|
|
extras: { placeHolder: "", options: [], range: [0, 0] }, |
|
|
|
|
|
options: [ |
|
|
|
|
|
{ id: "opt1", value: "A", label: "Option A", order: 1 }, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
id: "q2", |
|
|
|
|
|
type: "checkbox", |
|
|
|
|
|
title: "Q2", |
|
|
|
|
|
order: 2, |
|
|
|
|
|
required: true, |
|
|
|
|
|
baseRequired: true, |
|
|
|
|
|
isVisible: true, |
|
|
|
|
|
description: "", |
|
|
|
|
|
tooltip: "", |
|
|
|
|
|
extras: { placeHolder: "", options: [], range: [0, 0] }, |
|
|
|
|
|
options: [ |
|
|
|
|
|
{ id: "opt2", value: "B", label: "Option B", order: 1 }, |
|
|
|
|
|
{ id: "opt3", value: "C", label: "Option C", order: 2 }, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
|
|
|
]} |
|
|
|
|
|
> |
|
|
|
|
|
<TestComponent slug="test_slug" /> |
|
|
|
|
|
</QuestionAnswersProvider> |
|
|
|
|
|
</QueryClientProvider>, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
fireEvent.click(screen.getByTestId("set-radio")); |
|
|
|
|
|
fireEvent.click(screen.getByTestId("set-checkbox")); |
|
|
|
|
|
fireEvent.click(screen.getByTestId("save")); |
|
|
|
|
|
|
|
|
|
|
|
await waitFor(() => expect(capturedPayload?.fields).toHaveLength(2)); |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
it("should sort schema questions and options by order correctly", () => { |
|
|
it("should sort schema questions and options by order correctly", () => { |
|
|
const mockSchema = { |
|
|
const mockSchema = { |
|
|
sections: [ |
|
|
sections: [ |
|
|
@ -155,25 +285,52 @@ describe("Question Answer & Schema Integration", () => { |
|
|
title: "Card", |
|
|
title: "Card", |
|
|
order: 2, |
|
|
order: 2, |
|
|
questions: [ |
|
|
questions: [ |
|
|
{ id: "q1", title: "Q1", type: "text", order: 10, required: true, is_visible: true, ui_config: {}, options: [] }, |
|
|
|
|
|
{ id: "q2", title: "Q2", type: "text", order: 5, required: true, is_visible: true, ui_config: {}, options: [ |
|
|
|
|
|
{ id: "opt1", value: "A", label: "Option A", order: 2 }, |
|
|
|
|
|
{ id: "opt2", value: "B", label: "Option B", order: 1 } |
|
|
|
|
|
] }, |
|
|
|
|
|
] |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
id: "q1", |
|
|
|
|
|
title: "Q1", |
|
|
|
|
|
type: "text", |
|
|
|
|
|
order: 10, |
|
|
|
|
|
required: true, |
|
|
|
|
|
is_visible: true, |
|
|
|
|
|
ui_config: {}, |
|
|
|
|
|
options: [], |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
id: "q2", |
|
|
|
|
|
title: "Q2", |
|
|
|
|
|
type: "text", |
|
|
|
|
|
order: 5, |
|
|
|
|
|
required: true, |
|
|
|
|
|
is_visible: true, |
|
|
|
|
|
ui_config: {}, |
|
|
|
|
|
options: [ |
|
|
|
|
|
{ id: "opt1", value: "A", label: "Option A", order: 2 }, |
|
|
|
|
|
{ id: "opt2", value: "B", label: "Option B", order: 1 }, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
|
|
|
], |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
id: "card2", |
|
|
id: "card2", |
|
|
title: "Card 2", |
|
|
title: "Card 2", |
|
|
order: 1, |
|
|
order: 1, |
|
|
questions: [ |
|
|
questions: [ |
|
|
{ id: "q3", title: "Q3", type: "text", order: 1, required: true, is_visible: true, ui_config: {}, options: [] }, |
|
|
|
|
|
] |
|
|
|
|
|
} |
|
|
|
|
|
] |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
id: "q3", |
|
|
|
|
|
title: "Q3", |
|
|
|
|
|
type: "text", |
|
|
|
|
|
order: 1, |
|
|
|
|
|
required: true, |
|
|
|
|
|
is_visible: true, |
|
|
|
|
|
ui_config: {}, |
|
|
|
|
|
options: [], |
|
|
|
|
|
}, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
], |
|
|
], |
|
|
progress: { sections_progress: {} } |
|
|
|
|
|
|
|
|
progress: { sections_progress: {} }, |
|
|
} as any; |
|
|
} as any; |
|
|
|
|
|
|
|
|
const frontendItems = convertSchemaToFrontendItems(mockSchema, "en"); |
|
|
const frontendItems = convertSchemaToFrontendItems(mockSchema, "en"); |
|
|
@ -193,14 +350,24 @@ describe("Question Answer & Schema Integration", () => { |
|
|
data: { |
|
|
data: { |
|
|
slug: "test_slug", |
|
|
slug: "test_slug", |
|
|
data: [ |
|
|
data: [ |
|
|
{ key: "q_radio", type: "radio", value: "Server Canonical Value", option_id: "opt_radio" }, |
|
|
|
|
|
{ key: "q_check", type: "checkbox", value: ["Server Val 1", "Server Val 2"], option_id: ["opt_check1", "opt_check2"] }, |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
key: "q_radio", |
|
|
|
|
|
type: "radio", |
|
|
|
|
|
value: "Server Canonical Value", |
|
|
|
|
|
option_id: "opt_radio", |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
key: "q_check", |
|
|
|
|
|
type: "checkbox", |
|
|
|
|
|
value: ["Server Val 1", "Server Val 2"], |
|
|
|
|
|
option_id: ["opt_check1", "opt_check2"], |
|
|
|
|
|
}, |
|
|
], |
|
|
], |
|
|
}, |
|
|
}, |
|
|
isLoading: false, |
|
|
isLoading: false, |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
let capturedValues: any = {}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const capturedValues: any = {}; |
|
|
|
|
|
|
|
|
function HydrationTestComponent() { |
|
|
function HydrationTestComponent() { |
|
|
const { getAnswerValue } = useQuestionAnswers(); |
|
|
const { getAnswerValue } = useQuestionAnswers(); |
|
|
@ -209,14 +376,16 @@ describe("Question Answer & Schema Integration", () => { |
|
|
return <div />; |
|
|
return <div />; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); |
|
|
|
|
|
|
|
|
const queryClient = new QueryClient({ |
|
|
|
|
|
defaultOptions: { queries: { retry: false } }, |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
render( |
|
|
render( |
|
|
<QueryClientProvider client={queryClient}> |
|
|
<QueryClientProvider client={queryClient}> |
|
|
<QuestionAnswersProvider slug="test_slug" questions={[]}> |
|
|
<QuestionAnswersProvider slug="test_slug" questions={[]}> |
|
|
<HydrationTestComponent /> |
|
|
<HydrationTestComponent /> |
|
|
</QuestionAnswersProvider> |
|
|
</QuestionAnswersProvider> |
|
|
</QueryClientProvider> |
|
|
|
|
|
|
|
|
</QueryClientProvider>, |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
// Give it a moment to reconcile useEffect in QuestionAnswersProvider
|
|
|
// Give it a moment to reconcile useEffect in QuestionAnswersProvider
|
|
|
@ -242,13 +411,23 @@ describe("Question Answer & Schema Integration", () => { |
|
|
title: "Card", |
|
|
title: "Card", |
|
|
order: 1, |
|
|
order: 1, |
|
|
questions: [ |
|
|
questions: [ |
|
|
{ id: "q1", title: "Q1", type: "text", order: 1, required: false, is_required: true, is_visible: true, ui_config: {}, options: [] }, |
|
|
|
|
|
] |
|
|
|
|
|
} |
|
|
|
|
|
] |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
id: "q1", |
|
|
|
|
|
title: "Q1", |
|
|
|
|
|
type: "text", |
|
|
|
|
|
order: 1, |
|
|
|
|
|
required: false, |
|
|
|
|
|
is_required: true, |
|
|
|
|
|
is_visible: true, |
|
|
|
|
|
ui_config: {}, |
|
|
|
|
|
options: [], |
|
|
|
|
|
}, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
|
|
|
], |
|
|
|
|
|
}, |
|
|
], |
|
|
], |
|
|
progress: { sections_progress: {} } |
|
|
|
|
|
|
|
|
progress: { sections_progress: {} }, |
|
|
} as any; |
|
|
} as any; |
|
|
|
|
|
|
|
|
const frontendItems = convertSchemaToFrontendItems(mockSchema, "en"); |
|
|
const frontendItems = convertSchemaToFrontendItems(mockSchema, "en"); |
|
|
|