1import { describe, expect, it } from "vite-plus/test";
2import * as Schema from "effect/Schema";
3import {
4 clearSubmittedReview,
5 pullRequestReviewKey,
6 ReviewDraftDocument,
7 type ReviewDraft,
8} from "./pullRequestReviewDrafts";
9const decodeDocument = Schema.decodeUnknownSync(ReviewDraftDocument);
10const comment = {
11 id: "one",
12 path: "renamed.ts",
13 oldPath: "old.ts",
14 position: { kind: "deleted" as const, oldLine: 9 },
15 body: "Keep this",
16 headSha: "abc",
17 code: "old line",
18};
19const draft: ReviewDraft = {
20 summary: "Review",
21 comment: "Separate PR comment",
22 comments: [comment],
23};
24describe("mobile review draft recovery", () => {
25 it("round trips renamed/deleted line anchors and a separate unsent PR comment", () => {
26 const saved = JSON.stringify({ version: 1, drafts: { key: draft } });
27 expect(decodeDocument(JSON.parse(saved)).drafts.key).toEqual(draft);
28 });
29 it("rejects a damaged document instead of replacing drafts with an empty file", () => {
30 expect(() =>
31 decodeDocument({
32 version: 1,
33 drafts: {
34 key: { ...draft, comments: [{ ...comment, position: { kind: "deleted", oldLine: 0 } }] },
35 },
36 }),
37 ).toThrow();
38 });
39 it("clears a submitted snapshot while retaining edits made during the request", () => {
40 const current = {
41 ...draft,
42 summary: "New summary",
43 comments: [
44 { ...comment, body: "New feedback" },
45 { ...comment, id: "two" },
46 ],
47 };
48 expect(clearSubmittedReview(current, draft)).toEqual(current);
49 expect(clearSubmittedReview(draft, draft)).toEqual({
50 summary: "",
51 comment: draft.comment,
52 comments: [],
53 });
54 });
55 it("does not mix identical PR numbers across repositories, hosts, or environments", () => {
56 const ref = {
57 environmentId: "a",
58 projectId: "p",
59 host: "github.com",
60 repository: "one/repo",
61 number: 1,
62 };
63 expect(
64 new Set(
65 [
66 ref,
67 { ...ref, environmentId: "b" },
68 { ...ref, host: "github.enterprise" },
69 { ...ref, repository: "two/repo" },
70 ].map(pullRequestReviewKey),
71 ).size,
72 ).toBe(4);
73 });
74 it("retains a draft reanchored while its earlier position was being submitted", () => {
75 const current = {
76 ...draft,
77 comments: [{ ...comment, position: { kind: "deleted" as const, oldLine: 12 } }],
78 };
79 expect(clearSubmittedReview(current, draft).comments).toEqual(current.comments);
80 });
81});