fix(rd21): expose committed revisions and refresh answer sources
This commit is contained in:
@@ -27,7 +27,7 @@ jest.mock("./components/ApplicationAssets", () => ({
|
||||
</div>
|
||||
),
|
||||
ApplicationCvSection: () => <div>CV section</div>,
|
||||
ApplicationPackageDraftsSection: ({ jobId, initialApplicationAnswer, initialRecruiterMessage, onDirtyChange }: { jobId: number; initialApplicationAnswer: string; initialRecruiterMessage: string; onDirtyChange?: (dirty: boolean) => void }) => <div>Application drafts section<output data-testid="answer-props">{jobId}|{initialApplicationAnswer}|{initialRecruiterMessage}</output><button onClick={() => onDirtyChange?.(true)}>Make answers dirty</button></div>,
|
||||
ApplicationPackageDraftsSection: ({ jobId, initialApplicationAnswer, initialRecruiterMessage, initialRevision, onDirtyChange }: { jobId: number; initialApplicationAnswer: string; initialRecruiterMessage: string; initialRevision: number; onDirtyChange?: (dirty: boolean) => void }) => <div>Application drafts section<output data-testid="answer-props">{jobId}|{initialApplicationAnswer}|{initialRecruiterMessage}|{initialRevision}</output><button onClick={() => onDirtyChange?.(true)}>Make answers dirty</button></div>,
|
||||
SubmittedPackagesSection: () => <div>Frozen packages section</div>,
|
||||
}));
|
||||
jest.mock("./components/InterviewPrep", () => ({ ApplicationInterviewPrep: () => <div>Interview section</div> }));
|
||||
@@ -77,6 +77,7 @@ const job = {
|
||||
|
||||
const overview = {
|
||||
id: 42,
|
||||
revision: 7,
|
||||
jobTitle: "Backend Developer",
|
||||
company: "Acme",
|
||||
location: "Oslo",
|
||||
@@ -151,6 +152,7 @@ function renderTable(path = "/jobs") {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockedApi.get.mockReset();
|
||||
window.localStorage.removeItem("uiLanguage");
|
||||
mockedApi.get.mockImplementation((url: string) => {
|
||||
if (url === "/companies") return Promise.resolve({ data: [{ id: 1, name: "Acme" }] } as any);
|
||||
@@ -411,3 +413,56 @@ test("holds answers until their aggregate matches the route and ignores a late p
|
||||
expect(mockedApi.post).not.toHaveBeenCalled();
|
||||
expect(mockedApi.put).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("entering Answers waits for a fresh coherent snapshot after sibling writes", async () => {
|
||||
renderTable("/jobs/42?section=application&sub=letter");
|
||||
await screen.findByRole("heading", { name: "Backend Developer" });
|
||||
let resolve!: (value: any) => void;
|
||||
const pending = new Promise<any>(done => { resolve = done; });
|
||||
mockedApi.get.mockReturnValueOnce(pending);
|
||||
fireEvent.click(screen.getByRole("tab", { name: "Answers & message draft" }));
|
||||
expect(screen.queryByTestId("answer-props")).not.toBeInTheDocument();
|
||||
await act(async () => { resolve({ data: { ...overview, revision: 8, applicationAnswerDraft: "Fresh answer", recruiterMessageDraft: "Fresh message" } }); await pending; });
|
||||
expect(await screen.findByTestId("answer-props")).toHaveTextContent("42|Fresh answer|Fresh message|8");
|
||||
expect(mockedApi.get.mock.calls.filter(([url]) => url === "/jobapplications/42/workspace")).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("a late previous Answers-entry read cannot enable the current editor", async () => {
|
||||
const { router } = renderTable("/jobs/42?section=application&sub=letter");
|
||||
await screen.findByRole("heading", { name: "Backend Developer" });
|
||||
let resolveOld!: (value: any) => void;
|
||||
let resolveNew!: (value: any) => void;
|
||||
const oldRead = new Promise<any>(done => { resolveOld = done; });
|
||||
const newRead = new Promise<any>(done => { resolveNew = done; });
|
||||
mockedApi.get.mockReturnValueOnce(oldRead).mockReturnValueOnce(newRead);
|
||||
await act(async () => { await router.navigate("/jobs/42?section=application&sub=answers"); });
|
||||
await act(async () => { await router.navigate("/jobs/42?section=application&sub=letter"); });
|
||||
await act(async () => { await router.navigate("/jobs/42?section=application&sub=answers"); });
|
||||
await act(async () => { resolveOld({ data: { ...overview, revision: 8 } }); await oldRead; });
|
||||
expect(screen.queryByTestId("answer-props")).not.toBeInTheDocument();
|
||||
await act(async () => { resolveNew({ data: { ...overview, revision: 9, applicationAnswerDraft: "Current answer" } }); await newRead; });
|
||||
expect(await screen.findByTestId("answer-props")).toHaveTextContent("42|Current answer|Saved recruiter message|9");
|
||||
});
|
||||
|
||||
test("a failed Answers-entry read stays closed until a successful retry", async () => {
|
||||
renderTable("/jobs/42?section=application&sub=letter");
|
||||
await screen.findByRole("heading", { name: "Backend Developer" });
|
||||
mockedApi.get.mockRejectedValueOnce({ response: { status: 503 } });
|
||||
fireEvent.click(screen.getByRole("tab", { name: "Answers & message draft" }));
|
||||
const retry = await screen.findByRole("button", { name: "Retry" });
|
||||
expect(screen.queryByTestId("answer-props")).not.toBeInTheDocument();
|
||||
mockedApi.get.mockResolvedValueOnce({ data: { ...overview, revision: 9, applicationAnswerDraft: "Recovered answer" } });
|
||||
fireEvent.click(retry);
|
||||
expect(await screen.findByTestId("answer-props")).toHaveTextContent("42|Recovered answer|Saved recruiter message|9");
|
||||
});
|
||||
|
||||
test("leaving a pending direct Answers entry still loads the workspace header", async () => {
|
||||
let resolve!: (value: any) => void;
|
||||
const pending = new Promise<any>(done => { resolve = done; });
|
||||
mockedApi.get.mockReturnValueOnce(pending);
|
||||
const { router } = renderTable("/jobs/42?section=application&sub=answers");
|
||||
await act(async () => { await router.navigate("/jobs/42?section=application&sub=letter"); });
|
||||
expect(await screen.findByRole("heading", { name: "Backend Developer" })).toBeInTheDocument();
|
||||
await act(async () => { resolve({ data: { ...overview, jobTitle: "Late header" } }); await pending; });
|
||||
expect(screen.queryByRole("heading", { name: "Late header" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user