Skip to content

Commit fb9f899

Browse files
committed
Test: 10 more widget components (E)
1 parent 68bb131 commit fb9f899

10 files changed

Lines changed: 727 additions & 0 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// @vitest-environment jsdom
2+
3+
import { screen } from "@testing-library/react";
4+
import { beforeEach, describe, expect, it, vi } from "vitest";
5+
6+
import { renderWithProviders } from "test-utils/render-with-providers";
7+
import { findServiceBlockByLabel } from "test-utils/widget-assertions";
8+
9+
const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() }));
10+
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
11+
12+
import Component from "./component";
13+
14+
function expectBlockValue(container, label, value) {
15+
const block = findServiceBlockByLabel(container, label);
16+
expect(block, `missing block for ${label}`).toBeTruthy();
17+
expect(block.textContent).toContain(String(value));
18+
}
19+
20+
describe("widgets/gitea/component", () => {
21+
beforeEach(() => {
22+
vi.clearAllMocks();
23+
});
24+
25+
it("renders placeholders while loading", () => {
26+
useWidgetAPI
27+
.mockReturnValueOnce({ data: undefined, error: undefined }) // notifications
28+
.mockReturnValueOnce({ data: undefined, error: undefined }) // issues
29+
.mockReturnValueOnce({ data: undefined, error: undefined }); // repositories
30+
31+
const { container } = renderWithProviders(<Component service={{ widget: { type: "gitea", url: "http://x" } }} />, {
32+
settings: { hideErrors: false },
33+
});
34+
35+
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
36+
expect(screen.getByText("gitea.notifications")).toBeInTheDocument();
37+
expect(screen.getByText("gitea.issues")).toBeInTheDocument();
38+
expect(screen.getByText("gitea.pulls")).toBeInTheDocument();
39+
expect(screen.getByText("gitea.repositories")).toBeInTheDocument();
40+
expect(screen.getAllByText("-")).toHaveLength(4);
41+
});
42+
43+
it("renders error UI when any endpoint errors", () => {
44+
useWidgetAPI
45+
.mockReturnValueOnce({ data: undefined, error: undefined })
46+
.mockReturnValueOnce({ data: undefined, error: { message: "nope" } })
47+
.mockReturnValueOnce({ data: undefined, error: undefined });
48+
49+
renderWithProviders(<Component service={{ widget: { type: "gitea", url: "http://x" } }} />, {
50+
settings: { hideErrors: false },
51+
});
52+
53+
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
54+
expect(screen.getByText("nope")).toBeInTheDocument();
55+
});
56+
57+
it("renders computed counts when loaded", () => {
58+
useWidgetAPI
59+
.mockReturnValueOnce({ data: [{ id: 1 }, { id: 2 }], error: undefined })
60+
.mockReturnValueOnce({
61+
data: { issues: [{ id: 1 }], pulls: [{ id: 1 }, { id: 2 }, { id: 3 }] },
62+
error: undefined,
63+
})
64+
.mockReturnValueOnce({ data: { data: [{ id: 1 }] }, error: undefined });
65+
66+
const { container } = renderWithProviders(<Component service={{ widget: { type: "gitea", url: "http://x" } }} />, {
67+
settings: { hideErrors: false },
68+
});
69+
70+
expectBlockValue(container, "gitea.notifications", 2);
71+
expectBlockValue(container, "gitea.issues", 1);
72+
expectBlockValue(container, "gitea.pulls", 3);
73+
expectBlockValue(container, "gitea.repositories", 1);
74+
});
75+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// @vitest-environment jsdom
2+
3+
import { screen } from "@testing-library/react";
4+
import { beforeEach, describe, expect, it, vi } from "vitest";
5+
6+
import { renderWithProviders } from "test-utils/render-with-providers";
7+
import { findServiceBlockByLabel } from "test-utils/widget-assertions";
8+
9+
const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() }));
10+
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
11+
12+
import Component from "./component";
13+
14+
function expectBlockValue(container, label, value) {
15+
const block = findServiceBlockByLabel(container, label);
16+
expect(block, `missing block for ${label}`).toBeTruthy();
17+
expect(block.textContent).toContain(String(value));
18+
}
19+
20+
describe("widgets/gitlab/component", () => {
21+
beforeEach(() => {
22+
vi.clearAllMocks();
23+
});
24+
25+
it("renders placeholders while loading", () => {
26+
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
27+
28+
const { container } = renderWithProviders(<Component service={{ widget: { type: "gitlab", url: "http://x" } }} />, {
29+
settings: { hideErrors: false },
30+
});
31+
32+
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
33+
expect(screen.getByText("gitlab.groups")).toBeInTheDocument();
34+
expect(screen.getByText("gitlab.issues")).toBeInTheDocument();
35+
expect(screen.getByText("gitlab.merges")).toBeInTheDocument();
36+
expect(screen.getByText("gitlab.projects")).toBeInTheDocument();
37+
expect(screen.getAllByText("-")).toHaveLength(4);
38+
});
39+
40+
it("renders error UI when widget API errors", () => {
41+
useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } });
42+
43+
renderWithProviders(<Component service={{ widget: { type: "gitlab", url: "http://x" } }} />, {
44+
settings: { hideErrors: false },
45+
});
46+
47+
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
48+
expect(screen.getByText("nope")).toBeInTheDocument();
49+
});
50+
51+
it("renders counts when loaded", () => {
52+
useWidgetAPI.mockReturnValue({
53+
data: { groups_count: 1, issues_count: 2, merge_requests_count: 3, projects_count: 4 },
54+
error: undefined,
55+
});
56+
57+
const { container } = renderWithProviders(<Component service={{ widget: { type: "gitlab", url: "http://x" } }} />, {
58+
settings: { hideErrors: false },
59+
});
60+
61+
expectBlockValue(container, "gitlab.groups", 1);
62+
expectBlockValue(container, "gitlab.issues", 2);
63+
expectBlockValue(container, "gitlab.merges", 3);
64+
expectBlockValue(container, "gitlab.projects", 4);
65+
});
66+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// @vitest-environment jsdom
2+
3+
import { screen } from "@testing-library/react";
4+
import { describe, expect, it, vi } from "vitest";
5+
6+
import { renderWithProviders } from "test-utils/render-with-providers";
7+
8+
vi.mock("./metrics/info", () => ({ default: () => <div>glances-info</div> }));
9+
vi.mock("./metrics/memory", () => ({ default: () => <div>glances-memory</div> }));
10+
vi.mock("./metrics/process", () => ({ default: () => <div>glances-process</div> }));
11+
vi.mock("./metrics/containers", () => ({ default: () => <div>glances-containers</div> }));
12+
vi.mock("./metrics/cpu", () => ({ default: () => <div>glances-cpu</div> }));
13+
vi.mock("./metrics/net", () => ({ default: () => <div>glances-net</div> }));
14+
vi.mock("./metrics/sensor", () => ({ default: () => <div>glances-sensor</div> }));
15+
vi.mock("./metrics/disk", () => ({ default: () => <div>glances-disk</div> }));
16+
vi.mock("./metrics/gpu", () => ({ default: () => <div>glances-gpu</div> }));
17+
vi.mock("./metrics/fs", () => ({ default: () => <div>glances-fs</div> }));
18+
19+
import Component from "./component";
20+
21+
describe("widgets/glances/component", () => {
22+
it("routes metric=info to Info", () => {
23+
renderWithProviders(<Component service={{ widget: { type: "glances", metric: "info" } }} />, {
24+
settings: { hideErrors: false },
25+
});
26+
27+
expect(screen.getByText("glances-info")).toBeInTheDocument();
28+
});
29+
30+
it("routes metric=cpu to Cpu", () => {
31+
renderWithProviders(<Component service={{ widget: { type: "glances", metric: "cpu" } }} />, {
32+
settings: { hideErrors: false },
33+
});
34+
35+
expect(screen.getByText("glances-cpu")).toBeInTheDocument();
36+
});
37+
38+
it("routes metric patterns (network:, sensor:, disk:, gpu:, fs:) to their modules", () => {
39+
const { rerender } = renderWithProviders(
40+
<Component service={{ widget: { type: "glances", metric: "network:eth0" } }} />,
41+
{
42+
settings: { hideErrors: false },
43+
},
44+
);
45+
expect(screen.getByText("glances-net")).toBeInTheDocument();
46+
47+
rerender(<Component service={{ widget: { type: "glances", metric: "sensor:temp" } }} />);
48+
expect(screen.getByText("glances-sensor")).toBeInTheDocument();
49+
50+
rerender(<Component service={{ widget: { type: "glances", metric: "disk:sda" } }} />);
51+
expect(screen.getByText("glances-disk")).toBeInTheDocument();
52+
53+
rerender(<Component service={{ widget: { type: "glances", metric: "gpu:nvidia" } }} />);
54+
expect(screen.getByText("glances-gpu")).toBeInTheDocument();
55+
56+
rerender(<Component service={{ widget: { type: "glances", metric: "fs:/" } }} />);
57+
expect(screen.getByText("glances-fs")).toBeInTheDocument();
58+
});
59+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// @vitest-environment jsdom
2+
3+
import { screen } from "@testing-library/react";
4+
import { beforeEach, describe, expect, it, vi } from "vitest";
5+
6+
import { renderWithProviders } from "test-utils/render-with-providers";
7+
import { findServiceBlockByLabel } from "test-utils/widget-assertions";
8+
9+
const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() }));
10+
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
11+
12+
import Component from "./component";
13+
14+
function expectBlockValue(container, label, value) {
15+
const block = findServiceBlockByLabel(container, label);
16+
expect(block, `missing block for ${label}`).toBeTruthy();
17+
expect(block.textContent).toContain(String(value));
18+
}
19+
20+
describe("widgets/gluetun/component", () => {
21+
beforeEach(() => {
22+
vi.clearAllMocks();
23+
});
24+
25+
it("defaults fields and filters to 3 blocks while loading (no port_forwarded)", () => {
26+
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
27+
28+
const service = { widget: { type: "gluetun", url: "http://x" } };
29+
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
30+
31+
expect(service.widget.fields).toEqual(["public_ip", "region", "country"]);
32+
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
33+
expect(screen.getByText("gluetun.public_ip")).toBeInTheDocument();
34+
expect(screen.getByText("gluetun.region")).toBeInTheDocument();
35+
expect(screen.getByText("gluetun.country")).toBeInTheDocument();
36+
expect(screen.queryByText("gluetun.port_forwarded")).toBeNull();
37+
});
38+
39+
it("renders error UI when widget API errors", () => {
40+
useWidgetAPI.mockReturnValue({ data: undefined, error: { message: "nope" } });
41+
42+
renderWithProviders(<Component service={{ widget: { type: "gluetun", url: "http://x" } }} />, {
43+
settings: { hideErrors: false },
44+
});
45+
46+
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
47+
expect(screen.getByText("nope")).toBeInTheDocument();
48+
});
49+
50+
it("includes port_forwarded and uses the v2 endpoint when widget.version > 1", () => {
51+
useWidgetAPI
52+
.mockReturnValueOnce({ data: { public_ip: "1.2.3.4", region: "CA", country: "US" }, error: undefined })
53+
.mockReturnValueOnce({ data: { port: 12345 }, error: undefined });
54+
55+
const service = {
56+
widget: {
57+
type: "gluetun",
58+
url: "http://x",
59+
version: 2,
60+
fields: ["public_ip", "region", "country", "port_forwarded"],
61+
},
62+
};
63+
64+
const { container } = renderWithProviders(<Component service={service} />, { settings: { hideErrors: false } });
65+
66+
expect(useWidgetAPI.mock.calls[0][1]).toBe("ip");
67+
expect(useWidgetAPI.mock.calls[1][1]).toBe("port_forwarded_v2");
68+
69+
expect(container.querySelectorAll(".service-block")).toHaveLength(4);
70+
expectBlockValue(container, "gluetun.public_ip", "1.2.3.4");
71+
expectBlockValue(container, "gluetun.region", "CA");
72+
expectBlockValue(container, "gluetun.country", "US");
73+
expectBlockValue(container, "gluetun.port_forwarded", 12345);
74+
});
75+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// @vitest-environment jsdom
2+
3+
import { screen } from "@testing-library/react";
4+
import { beforeEach, describe, expect, it, vi } from "vitest";
5+
6+
import { renderWithProviders } from "test-utils/render-with-providers";
7+
import { findServiceBlockByLabel } from "test-utils/widget-assertions";
8+
9+
const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() }));
10+
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
11+
12+
import Component from "./component";
13+
14+
function expectBlockValue(container, label, value) {
15+
const block = findServiceBlockByLabel(container, label);
16+
expect(block, `missing block for ${label}`).toBeTruthy();
17+
expect(block.textContent).toContain(String(value));
18+
}
19+
20+
describe("widgets/gotify/component", () => {
21+
beforeEach(() => {
22+
vi.clearAllMocks();
23+
});
24+
25+
it("renders placeholders while loading", () => {
26+
useWidgetAPI
27+
.mockReturnValueOnce({ data: undefined, error: undefined }) // application
28+
.mockReturnValueOnce({ data: undefined, error: undefined }) // message
29+
.mockReturnValueOnce({ data: undefined, error: undefined }); // client
30+
31+
const { container } = renderWithProviders(<Component service={{ widget: { type: "gotify", url: "http://x" } }} />, {
32+
settings: { hideErrors: false },
33+
});
34+
35+
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
36+
expect(screen.getByText("gotify.apps")).toBeInTheDocument();
37+
expect(screen.getByText("gotify.clients")).toBeInTheDocument();
38+
expect(screen.getByText("gotify.messages")).toBeInTheDocument();
39+
expect(screen.getAllByText("-")).toHaveLength(3);
40+
});
41+
42+
it("renders error UI when any endpoint errors", () => {
43+
useWidgetAPI
44+
.mockReturnValueOnce({ data: undefined, error: undefined })
45+
.mockReturnValueOnce({ data: undefined, error: { message: "nope" } })
46+
.mockReturnValueOnce({ data: undefined, error: undefined });
47+
48+
renderWithProviders(<Component service={{ widget: { type: "gotify", url: "http://x" } }} />, {
49+
settings: { hideErrors: false },
50+
});
51+
52+
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
53+
expect(screen.getByText("nope")).toBeInTheDocument();
54+
});
55+
56+
it("renders computed counts when loaded", () => {
57+
useWidgetAPI
58+
.mockReturnValueOnce({ data: [{ id: 1 }, { id: 2 }], error: undefined })
59+
.mockReturnValueOnce({ data: { messages: [{ id: 1 }] }, error: undefined })
60+
.mockReturnValueOnce({ data: [{ id: 1 }, { id: 2 }, { id: 3 }], error: undefined });
61+
62+
const { container } = renderWithProviders(<Component service={{ widget: { type: "gotify", url: "http://x" } }} />, {
63+
settings: { hideErrors: false },
64+
});
65+
66+
expectBlockValue(container, "gotify.apps", 2);
67+
expectBlockValue(container, "gotify.clients", 3);
68+
expectBlockValue(container, "gotify.messages", 1);
69+
});
70+
});

0 commit comments

Comments
 (0)