Skip to content

Commit 6a8cde0

Browse files
tests: migrate components tests (#63)
Signed-off-by: Carlos Feria <[email protected]>
1 parent 93f2fa8 commit 6a8cde0

File tree

13 files changed

+428
-0
lines changed

13 files changed

+428
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
3+
import atobMock from "../__mocks__/atobMock";
4+
import decodex509Mock from "../__mocks__/decodex509Mock";
5+
6+
import { render, screen } from "@testing-library/react";
7+
import { IntotoViewer001 } from "./Intoto001";
8+
9+
vi.mock("react-router-dom", () => ({ Link: ({ children }: any) => <a>{children}</a> }));
10+
11+
vi.mock("../x509/decode", () => ({
12+
decodex509: decodex509Mock,
13+
}));
14+
15+
describe("IntotoViewer001", () => {
16+
beforeAll(() => {
17+
atobMock();
18+
});
19+
20+
afterAll(() => {
21+
vi.restoreAllMocks();
22+
});
23+
24+
it("should render the payload hash and provide a link to the hash page", () => {
25+
const intoto = {
26+
content: {
27+
payloadHash: {
28+
algorithm: "sha256",
29+
value: "abc123",
30+
},
31+
},
32+
publicKey: "123",
33+
};
34+
35+
// @ts-expect-error allowed
36+
render(<IntotoViewer001 intoto={intoto} />);
37+
38+
const hashLink = screen.getByText("Hash");
39+
expect(hashLink).toBeInTheDocument();
40+
41+
const hashValue = screen.getByText("sha256:abc123");
42+
expect(hashValue).toBeInTheDocument();
43+
});
44+
});
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
3+
import atobMock from "../__mocks__/atobMock";
4+
import decodex509Mock from "../__mocks__/decodex509Mock";
5+
6+
import { render, screen } from "@testing-library/react";
7+
import { IntotoViewer002 } from "./Intoto002";
8+
import type { IntotoV002Schema } from "rekor";
9+
10+
vi.mock("react-router-dom", () => ({ Link: ({ children }: any) => <a>{children}</a> }));
11+
12+
const pemCertificate = `-----BEGIN CERTIFICATE-----\n${Buffer.from("Mocked Public Key").toString(
13+
"base64"
14+
)}\n-----END CERTIFICATE-----`;
15+
16+
vi.mock("../Template/x509/decode", () => ({
17+
decodex509: decodex509Mock,
18+
}));
19+
20+
describe("IntotoViewer", () => {
21+
beforeAll(() => {
22+
atobMock();
23+
});
24+
25+
afterAll(() => {
26+
vi.restoreAllMocks();
27+
});
28+
29+
const mockIntoto: IntotoV002Schema = {
30+
content: {
31+
envelope: {
32+
payloadType: "application/vnd.in-toto+json",
33+
signatures: [
34+
{
35+
publicKey: pemCertificate,
36+
sig: Buffer.from("signature content", "utf-8").toString("base64"),
37+
},
38+
],
39+
},
40+
payloadHash: {
41+
algorithm: "sha256",
42+
value: "hashValue",
43+
},
44+
},
45+
};
46+
47+
it("renders the component with payload hash, signature, and certificate", () => {
48+
render(<IntotoViewer002 intoto={mockIntoto} />);
49+
50+
// verify the hash link is rendered correctly
51+
expect(screen.getByText("Hash")).toBeInTheDocument();
52+
expect(screen.getByText("sha256:hashValue")).toBeInTheDocument();
53+
54+
// verify the signature is rendered & decoded
55+
expect(screen.getByText("signature content")).toBeInTheDocument();
56+
expect(screen.getByText(/BEGIN CERTIFICATE/)).toBeInTheDocument();
57+
});
58+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* eslint-disable no-useless-escape, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-return */
2+
3+
const atobMock = () => {
4+
window.atob = vi.fn().mockImplementation((str) => {
5+
const base64Pattern = /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/;
6+
7+
if (!base64Pattern.test(str)) {
8+
// return if string is not base64 encoded
9+
return str;
10+
}
11+
12+
return Buffer.from(str, "base64").toString("utf-8");
13+
});
14+
};
15+
16+
export default atobMock;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const decodex509Mock = vi.fn().mockReturnValue({
2+
publicKey: "-----BEGIN CERTIFICATE-----Mocked Certificate-----END CERTIFICATE-----",
3+
subject: "Mocked Subject",
4+
});
5+
6+
export default decodex509Mock;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = "test-file-stub";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vi.mock("react-syntax-highlighter/dist/cjs/styles/prism", () => ({}));
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = {};
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
3+
import atobMock from "../__mocks__/atobMock";
4+
import decodex509Mock from "../__mocks__/decodex509Mock";
5+
6+
vi.mock("react-router-dom", () => ({ Link: ({ children }: any) => <a>{children}</a> }));
7+
8+
vi.mock("../Template/x509/decode", () => ({
9+
decodex509: decodex509Mock,
10+
}));
11+
12+
import { render, screen } from "@testing-library/react";
13+
import "@testing-library/jest-dom";
14+
import { DSSEViewer } from "./DSSEViewer";
15+
import type { DSSEV001Schema } from "rekor";
16+
17+
describe("DSSEViewer Component", () => {
18+
beforeAll(() => {
19+
vi.clearAllMocks();
20+
atobMock();
21+
});
22+
23+
afterAll(() => {
24+
vi.restoreAllMocks();
25+
});
26+
27+
const mockDSSE: DSSEV001Schema = {
28+
payloadHash: {
29+
algorithm: "sha256",
30+
value: "exampleHashValue",
31+
},
32+
signatures: [
33+
{
34+
signature: "exampleSignature",
35+
verifier: "-----BEGIN CERTIFICATE-----\nexamplePublicKey\n-----END CERTIFICATE-----",
36+
},
37+
],
38+
};
39+
40+
it("renders without crashing", () => {
41+
render(<DSSEViewer dsse={mockDSSE} />);
42+
expect(screen.getByText("Hash")).toBeInTheDocument();
43+
});
44+
45+
it("displays the payload hash correctly", () => {
46+
render(<DSSEViewer dsse={mockDSSE} />);
47+
expect(screen.getByText(`${mockDSSE.payloadHash?.algorithm}:${mockDSSE.payloadHash?.value}`)).toBeInTheDocument();
48+
});
49+
50+
it("displays the signature correctly", () => {
51+
render(<DSSEViewer dsse={mockDSSE} />);
52+
expect(screen.getByText(mockDSSE.signatures![0].signature)).toBeInTheDocument();
53+
});
54+
55+
it("displays the public key certificate title and content correctly", () => {
56+
render(<DSSEViewer dsse={mockDSSE} />);
57+
expect(screen.getByText("Public Key Certificate")).toBeInTheDocument();
58+
});
59+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
3+
vi.mock("react-router-dom", () => ({ Link: ({ children }: any) => <a>{children}</a> }));
4+
5+
vi.mock("react-syntax-highlighter/dist/cjs/styles/prism", () => ({ atomDark: {} }));
6+
vi.mock("../utils/date", () => ({
7+
toRelativeDateString: vi.fn().mockReturnValue("Some Date"),
8+
}));
9+
vi.mock("./HashedRekord", () => ({
10+
HashedRekordViewer: () => <div>MockedHashedRekordViewer</div>,
11+
}));
12+
13+
import atobMock from "../__mocks__/atobMock";
14+
15+
import { fireEvent, render, screen } from "@testing-library/react";
16+
import { Entry, EntryCard } from "./Entry";
17+
18+
describe("Entry", () => {
19+
beforeAll(() => {
20+
atobMock();
21+
});
22+
23+
afterAll(() => {
24+
vi.restoreAllMocks();
25+
});
26+
27+
const mockEntry = {
28+
someUuid: {
29+
body: Buffer.from(JSON.stringify({ kind: "hashedrekord", apiVersion: "v1", spec: {} })).toString("base64"),
30+
attestation: { data: Buffer.from("{}").toString("base64") },
31+
logID: "123",
32+
logIndex: 123,
33+
integratedTime: 1618886400,
34+
publicKey: "mockedPublicKey",
35+
signature: {
36+
publicKey: {
37+
content: window.btoa("-----BEGIN CERTIFICATE-----certContent-----END CERTIFICATE-----"), // base64 encode
38+
},
39+
},
40+
},
41+
};
42+
43+
it("renders and toggles the accordion content", () => {
44+
render(<Entry entry={mockEntry} />);
45+
46+
expect(screen.getByText("apiVersion")).not.toBeVisible();
47+
48+
// check if UUID link is rendered
49+
expect(screen.getByText("someUuid")).toBeInTheDocument();
50+
51+
// simulate clicking the accordion toggle
52+
const toggleButton = screen.getByText("Raw Body");
53+
fireEvent.click(toggleButton);
54+
55+
// now the accordion content should be visible
56+
expect(screen.getByText("apiVersion")).toBeVisible();
57+
});
58+
});
59+
60+
describe("EntryCard", () => {
61+
it("renders the title and content", () => {
62+
render(<EntryCard title="Test Title" content="Test Content" />);
63+
expect(screen.getByText("Test Title")).toBeInTheDocument();
64+
expect(screen.getByText("Test Content")).toBeInTheDocument();
65+
});
66+
});
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { useLocation, useNavigate, type Path } from "react-router-dom";
2+
3+
vi.mock("react-router-dom", () => ({
4+
useNavigate: vi.fn(),
5+
useLocation: vi.fn(),
6+
}));
7+
8+
beforeEach(() => {
9+
vi.resetAllMocks();
10+
11+
(useLocation as Mock).mockImplementation(
12+
(): Path => ({
13+
pathname: "/",
14+
search: "",
15+
hash: "",
16+
})
17+
);
18+
});
19+
20+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
21+
import type { Mock } from "vitest";
22+
import { RekorClientProvider } from "../../../api/context";
23+
import { Explorer } from "./Explorer";
24+
25+
describe("Explorer", () => {
26+
it("renders without issues", () => {
27+
render(
28+
<RekorClientProvider>
29+
<Explorer />
30+
</RekorClientProvider>
31+
);
32+
33+
expect(screen.getByText("Search")).toBeInTheDocument();
34+
});
35+
36+
it("should render search form and display search button", () => {
37+
render(
38+
<RekorClientProvider>
39+
<Explorer />
40+
</RekorClientProvider>
41+
);
42+
43+
expect(screen.getByLabelText("Attribute")).toBeInTheDocument();
44+
expect(screen.getByRole("textbox", { name: "Email input field" })).toBeInTheDocument();
45+
expect(screen.getByRole("button", { name: "Search" })).toBeInTheDocument();
46+
});
47+
48+
it("should handle invalid logIndex query parameter", () => {
49+
const mockNavigate = vi.fn();
50+
51+
(useNavigate as Mock).mockImplementation(() => mockNavigate);
52+
expect(mockNavigate).not.toHaveBeenCalled();
53+
});
54+
55+
it("displays loading indicator when fetching data", async () => {
56+
render(
57+
<RekorClientProvider>
58+
<Explorer />
59+
</RekorClientProvider>
60+
);
61+
62+
const button = screen.getByText("Search");
63+
fireEvent.click(button);
64+
65+
await waitFor(() => expect(screen.queryByRole("status")).toBeNull());
66+
67+
expect(
68+
screen.findByLabelText("Showing").then((res) => {
69+
expect(res).toBeInTheDocument();
70+
})
71+
);
72+
});
73+
});

0 commit comments

Comments
 (0)