Skip to content

Commit 1696916

Browse files
github-actions[bot]Test ImproverCopilotshm11C3
authored
test: add branch coverage for formatter and useUpdater edge cases (#1201)
- formatBytes: test negative decimals clamped to 0 (decimals<0 branch) - formatDuration: test zero-hours/minutes cases and days-only durations (uncovered ternary branches on the template-literal return line) - useUpdater: test percent=null when total===0n (BigInt zero-guard branch) - useUpdater: test install() when contentLength is absent (total stays null) - useUpdater: test fetchUpdate error path (meta stays null on non-ok result) formatter.ts branches: 82.35% → 100% useAppUpdate.ts branches: 76.92% → 92.3% Co-authored-by: Test Improver <copilot-test-improver@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: shm <m11c3.sh@gmail.com>
1 parent 57ea76b commit 1696916

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

src/features/updater/hooks/useAppUpdate.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,65 @@ describe("useUpdater", () => {
120120
expect(result.current.total).toBeNull();
121121
expect(result.current.percent).toBeNull();
122122
});
123+
124+
it("percent: null when total is 0n", async () => {
125+
// Edge case: started event sends contentLength "0", total becomes 0n
126+
(commands.installUpdate as Mock).mockImplementation(async (ch: unknown) => {
127+
const channel = ch as TestChannel;
128+
channel.onmessage?.({ event: "started", data: { contentLength: "0" } });
129+
channel.onmessage?.({ event: "finished", data: {} });
130+
return { status: "ok", data: null };
131+
});
132+
133+
const { result } = renderHook(() => useUpdater());
134+
135+
await act(async () => {
136+
await result.current.install();
137+
});
138+
139+
await waitFor(() => result.current.isFinished === true);
140+
// total is 0n, so percent must be null (division by zero guard)
141+
expect(result.current.percent).toBeNull();
142+
});
143+
144+
it("install: total stays null when contentLength is absent", async () => {
145+
// started event without contentLength → setTotal(null)
146+
(commands.installUpdate as Mock).mockImplementation(async (ch: unknown) => {
147+
const channel = ch as TestChannel;
148+
channel.onmessage?.({
149+
event: "started",
150+
// contentLength omitted → falsy
151+
data: { contentLength: "" },
152+
});
153+
channel.onmessage?.({ event: "progress", data: { chunkLength: "50" } });
154+
channel.onmessage?.({ event: "finished", data: {} });
155+
return { status: "ok", data: null };
156+
});
157+
158+
const { result } = renderHook(() => useUpdater());
159+
160+
await act(async () => {
161+
await result.current.install();
162+
});
163+
164+
await waitFor(() => result.current.isFinished === true);
165+
// total should remain null (unknown download size)
166+
expect(result.current.total).toBeNull();
167+
expect(result.current.percent).toBeNull();
168+
});
169+
170+
it("fetchUpdate: meta stays null when result is not ok", async () => {
171+
(commands.fetchUpdate as Mock).mockResolvedValue({
172+
status: "error",
173+
error: "network failure",
174+
});
175+
176+
const { result } = renderHook(() => useUpdater());
177+
178+
await act(async () => {
179+
await new Promise((resolve) => setTimeout(resolve, 50));
180+
});
181+
182+
expect(result.current.meta).toBeNull();
183+
});
123184
});

src/lib/formatter.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ describe("formatBytes", () => {
1616
expect(formatBytes(1024 ** 2)).toEqual([1, "MB"]);
1717
expect(formatBytes(1024 ** 3)).toEqual([1, "GB"]);
1818
});
19+
20+
it("should clamp negative decimals to 0", () => {
21+
// decimals < 0 branch: should behave as decimals = 0
22+
const [value] = formatBytes(1024, -1);
23+
expect(Number.isInteger(value)).toBe(true);
24+
expect(value).toBe(1);
25+
});
1926
});
2027

2128
describe("formatDuration", () => {
@@ -26,6 +33,21 @@ describe("formatDuration", () => {
2633
it("should format duration in Japanese", () => {
2734
expect(formatDuration(90061, "ja-JP")).toBe("1日 1時間 1分 1秒");
2835
});
36+
37+
it("should omit zero hours and minutes", () => {
38+
// h = 0, m = 0 — only seconds
39+
expect(formatDuration(45, "en-US")).toBe("45second");
40+
});
41+
42+
it("should omit zero hours when only minutes and seconds", () => {
43+
// h = 0, m > 0
44+
expect(formatDuration(125, "en-US")).toBe("2minute 5second");
45+
});
46+
47+
it("should format days without hours in English", () => {
48+
// d > 0, h = 0, m = 0
49+
expect(formatDuration(86400, "en-US")).toBe("1day 0second");
50+
});
2951
});
3052

3153
describe("formatBytesBigint", () => {

0 commit comments

Comments
 (0)