mirror of
https://github.com/gethomepage/homepage.git
synced 2026-02-07 16:30:52 +08:00
Fix linkwarden widget stats + add component test
This commit is contained in:
@@ -1,35 +1,23 @@
|
||||
import Block from "components/services/widget/block";
|
||||
import Container from "components/services/widget/container";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { widget } = service;
|
||||
|
||||
const [stats, setStats] = useState({
|
||||
totalLinks: null,
|
||||
collections: { total: null },
|
||||
tags: { total: null },
|
||||
});
|
||||
|
||||
const { data: collectionsStatsData, error: collectionsStatsError } = useWidgetAPI(widget, "collections");
|
||||
const { data: tagsStatsData, error: tagsStatsError } = useWidgetAPI(widget, "tags");
|
||||
|
||||
useEffect(() => {
|
||||
if (collectionsStatsData?.response && tagsStatsData?.response) {
|
||||
setStats({
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
totalLinks: collectionsStatsData.response.reduce((sum, collection) => sum + (collection._count?.links || 0), 0),
|
||||
collections: {
|
||||
total: collectionsStatsData.response.length,
|
||||
},
|
||||
tags: {
|
||||
total: tagsStatsData.response.length,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [collectionsStatsData, tagsStatsData]);
|
||||
// Some APIs return raw arrays, others wrap the payload (e.g. { response: [...] }).
|
||||
const collections = collectionsStatsData?.response ?? collectionsStatsData;
|
||||
const tags = tagsStatsData?.response ?? tagsStatsData;
|
||||
|
||||
const totalLinks = Array.isArray(collections)
|
||||
? collections.reduce((sum, collection) => sum + (collection._count?.links || 0), 0)
|
||||
: null;
|
||||
const collectionsTotal = Array.isArray(collections) ? collections.length : null;
|
||||
const tagsTotal = Array.isArray(tags) ? tags.length : null;
|
||||
|
||||
if (collectionsStatsError || tagsStatsError) {
|
||||
return <Container service={service} error={collectionsStatsError || tagsStatsError} />;
|
||||
@@ -47,9 +35,9 @@ export default function Component({ service }) {
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="linkwarden.links" value={stats.totalLinks} />
|
||||
<Block label="linkwarden.collections" value={stats.collections.total} />
|
||||
<Block label="linkwarden.tags" value={stats.tags.total} />
|
||||
<Block label="linkwarden.links" value={totalLinks} />
|
||||
<Block label="linkwarden.collections" value={collectionsTotal} />
|
||||
<Block label="linkwarden.tags" value={tagsTotal} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
81
src/widgets/linkwarden/component.test.jsx
Normal file
81
src/widgets/linkwarden/component.test.jsx
Normal file
@@ -0,0 +1,81 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { screen } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { renderWithProviders } from "test-utils/render-with-providers";
|
||||
import { findServiceBlockByLabel } from "test-utils/widget-assertions";
|
||||
|
||||
const { useWidgetAPI } = vi.hoisted(() => ({ useWidgetAPI: vi.fn() }));
|
||||
vi.mock("utils/proxy/use-widget-api", () => ({ default: useWidgetAPI }));
|
||||
|
||||
import Component from "./component";
|
||||
|
||||
function expectBlockValue(container, label, value) {
|
||||
const block = findServiceBlockByLabel(container, label);
|
||||
expect(block, `missing block for ${label}`).toBeTruthy();
|
||||
expect(block.textContent).toContain(String(value));
|
||||
}
|
||||
|
||||
describe("widgets/linkwarden/component", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders placeholders while loading", () => {
|
||||
useWidgetAPI.mockReturnValue({ data: undefined, error: undefined });
|
||||
|
||||
const { container } = renderWithProviders(<Component service={{ widget: { type: "linkwarden" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(container.querySelectorAll(".service-block")).toHaveLength(3);
|
||||
expect(screen.getByText("linkwarden.links")).toBeInTheDocument();
|
||||
expect(screen.getByText("linkwarden.collections")).toBeInTheDocument();
|
||||
expect(screen.getByText("linkwarden.tags")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders error UI when either endpoint errors", () => {
|
||||
useWidgetAPI.mockImplementation((_widget, endpoint) => {
|
||||
if (endpoint === "tags") return { data: undefined, error: { message: "nope" } };
|
||||
return { data: undefined, error: undefined };
|
||||
});
|
||||
|
||||
renderWithProviders(<Component service={{ widget: { type: "linkwarden" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expect(screen.getAllByText(/widget\.api_error/i).length).toBeGreaterThan(0);
|
||||
expect(screen.getByText("nope")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("computes totals from collections + tags arrays", async () => {
|
||||
useWidgetAPI.mockImplementation((_widget, endpoint) => {
|
||||
if (endpoint === "collections") {
|
||||
return {
|
||||
data: [
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
{ _count: { links: 2 } },
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
{ _count: { links: 3 } },
|
||||
],
|
||||
error: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (endpoint === "tags") {
|
||||
return { data: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }], error: undefined };
|
||||
}
|
||||
|
||||
return { data: undefined, error: undefined };
|
||||
});
|
||||
|
||||
const { container } = renderWithProviders(<Component service={{ widget: { type: "linkwarden" } }} />, {
|
||||
settings: { hideErrors: false },
|
||||
});
|
||||
|
||||
expectBlockValue(container, "linkwarden.links", 5);
|
||||
expectBlockValue(container, "linkwarden.collections", 2);
|
||||
expectBlockValue(container, "linkwarden.tags", 4);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user