1import { describe, expect, it } from "vite-plus/test";
2
3describe("composerChipSizeSuffix", () => {
4 it("labels attachment records with a human size, matching web's chip", async () => {
5 const { composerChipSizeSuffix } = await import("./composerChipPresentation");
6 expect(composerChipSizeSuffix({ kind: "file", sizeBytes: 1024 })).toBe("1 KB");
7 expect(composerChipSizeSuffix({ kind: "file", sizeBytes: 3_700_000 })).toBe("3.5 MB");
8 expect(composerChipSizeSuffix({ kind: "image", sizeBytes: 2048 })).toBe("2 KB");
9 });
10
11 it("adds nothing for records that carry no bytes", async () => {
12 const { composerChipSizeSuffix } = await import("./composerChipPresentation");
13 // Terminal/review/PR chips have no size to show.
14 expect(composerChipSizeSuffix({ kind: "terminal" })).toBe("");
15 expect(composerChipSizeSuffix({ kind: "file" })).toBe("");
16 expect(composerChipSizeSuffix(undefined)).toBe("");
17 });
18});
19
20describe("contextChipPresentation image detection", () => {
21 it("treats a picture attached through the file picker as an image", async () => {
22 const { contextChipPresentation } = await import("./composerChipPresentation");
23 // The document picker types every pick as `file`, so the name has to carry the intent.
24 expect(
25 contextChipPresentation("file", { kind: "file", name: "IMG_4997.PNG", mimeType: "" }),
26 ).toEqual({ accent: "#d55665", symbol: "photo" });
27 expect(
28 contextChipPresentation("file", {
29 kind: "file",
30 name: "shot",
31 mimeType: "image/jpeg",
32 }),
33 ).toEqual({ accent: "#d55665", symbol: "photo" });
34 });
35
36 it("leaves genuine documents and videos alone", async () => {
37 const { contextChipPresentation } = await import("./composerChipPresentation");
38 expect(
39 contextChipPresentation("file", { kind: "file", name: "notes.txt", mimeType: "text/plain" }),
40 ).toEqual({ accent: "#0090cd", symbol: "doc" });
41 expect(
42 contextChipPresentation("file", { kind: "file", name: "clip.mp4", mimeType: "video/mp4" }),
43 ).toEqual({ accent: "#d06217", symbol: "play.rectangle" });
44 });
45});
46
47describe("pull request chip status", () => {
48 const chip = async (state: string, isDraft = false) => {
49 const { contextChipPresentation } = await import("./composerChipPresentation");
50 return contextChipPresentation("review-comment", {
51 kind: "review-comment",
52 sectionId: "pull-request:10978",
53 pullRequest: { state, isDraft },
54 });
55 };
56
57 it("colours a pull request by its state, the way web and the forge do", async () => {
58 expect((await chip("open")).accent).toBe("#009f6e");
59 expect((await chip("open", true)).accent).toBe("#7f8793");
60 expect((await chip("merged")).accent).toBe("#8a70dd");
61 expect((await chip("closed")).accent).toBe("#d55665");
62 });
63
64 it("keeps one glyph across every state, so only colour carries the status", async () => {
65 // Web draws a fixed `git-pull-request` and encodes state in colour alone. A per-state glyph
66 // here would put mobile out of step with it.
67 const symbols = await Promise.all(
68 [chip("open"), chip("open", true), chip("merged"), chip("closed")].map(
69 async (pending) => (await pending).symbol,
70 ),
71 );
72 expect(new Set(symbols)).toEqual(new Set(["git-pull-request"]));
73 });
74
75 it("falls back to the generic pull request chip when the state is unknown", async () => {
76 const { contextChipPresentation } = await import("./composerChipPresentation");
77 // An older server may send no metadata at all; the chip still has to render.
78 expect(
79 contextChipPresentation("review-comment", {
80 kind: "review-comment",
81 sectionId: "pull-request:1",
82 }).accent,
83 ).toBe("#7079e4");
84 });
85});