Skip to content

Commit

Permalink
test(unit): add tests + github action for running unit tests
Browse files Browse the repository at this point in the history
add basic HashedRekord + Intoto test

test(unit): fix breaking tests, add tests for other components

test(unit): add test for Settings component

test(unit): cleanup mocks after

test:  add explorer test

test: update search test
  • Loading branch information
kahboom committed Mar 25, 2024
1 parent d19298e commit 9cfa3ab
Show file tree
Hide file tree
Showing 11 changed files with 282 additions and 53 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"plugin:react-hooks/recommended",
"prettier"
],
"ignorePatterns": ["**/*.stories.tsx"],
"plugins": ["react-hooks"],
"overrides": [
// Only use Testing Library lint rules in test files
Expand Down
10 changes: 10 additions & 0 deletions src/__mocks__/atobMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const atobMock = () => {
window.atob = jest.fn().mockImplementation(str => {
const decoded = Buffer.from(str, "base64").toString("utf-8");
console.log(`Decoding: ${str}, Result: ${decoded}`);

return decoded;
});
};

export default atobMock;
14 changes: 7 additions & 7 deletions src/__mocks__/decodex509Mock.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Mock for decodex509 function
jest.mock("../modules/x509/decode", () => ({
decodex509: jest.fn().mockReturnValue({
publicKey: "Mocked Public Key",
subject: "Mocked Subject",
}),
}));
const decodex509Mock = jest.fn().mockReturnValue({
publicKey:
"-----BEGIN CERTIFICATE-----Mocked Certificate-----END CERTIFICATE-----",
subject: "Mocked Subject",
});

export default decodex509Mock;
1 change: 1 addition & 0 deletions src/__mocks__/prismMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
jest.mock("react-syntax-highlighter/dist/cjs/styles/prism", () => ({}));
42 changes: 23 additions & 19 deletions src/modules/components/DSSE.test.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
jest.mock("next/router");
// @ts-ignore
import atobMock from "../../__mocks__/atobMock";

import { RekorClientProvider } from "../api/context";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import { DSSEViewer } from "./DSSE";
import { DSSEV001Schema } from "rekor";

const mockDSSE: DSSEV001Schema = {
payloadHash: {
algorithm: "sha256",
value: "exampleHashValue",
},
signatures: [
{
signature: "exampleSignature",
verifier:
"-----BEGIN CERTIFICATE-----\nexamplePublicKey\n-----END CERTIFICATE-----",
},
],
};
describe("DSSEViewer Component", () => {
beforeAll(() => {
atobMock();
});

beforeAll(() => {
window.atob = jest
.fn()
.mockImplementation(str => Buffer.from(str, "base64").toString("utf-8"));
});
afterAll(() => {
jest.restoreAllMocks();
});

const mockDSSE: DSSEV001Schema = {
payloadHash: {
algorithm: "sha256",
value: "exampleHashValue",
},
signatures: [
{
signature: "exampleSignature",
verifier:
"-----BEGIN CERTIFICATE-----\nexamplePublicKey\n-----END CERTIFICATE-----",
},
],
};

describe("DSSEViewer Component", () => {
it("renders without crashing", () => {
render(
<RekorClientProvider>
Expand Down
53 changes: 41 additions & 12 deletions src/modules/components/Entry.test.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,51 @@
import { render } from "@testing-library/react";
import { RekorClientProvider } from "../api/context";
import { EntryCard } from "./Entry";
jest.mock("react-syntax-highlighter/dist/cjs/styles/prism", () => ({}));
jest.mock("../utils/date", () => ({
toRelativeDateString: jest.fn().mockReturnValue("Some Date"),
}));

import { fireEvent, render, screen } from "@testing-library/react";
import { Entry, EntryCard } from "./Entry";

const mockEntry = {
someUuid: {
body: Buffer.from(
JSON.stringify({ kind: "hashedrekord", apiVersion: "v1", spec: {} }),
).toString("base64"),
attestation: { data: Buffer.from("{}").toString("base64") },
logID: "123",
logIndex: 123,
integratedTime: 1618886400,
publicKey: "mockedPublicKey",
},
};

describe("Entry", () => {
it("renders", () => {
//
it.skip("renders and toggles the accordion content", () => {
render(<Entry entry={mockEntry} />);

// check if UUID link is rendered
expect(screen.getByText("someUuid")).toBeInTheDocument();

// simulate clicking the accordion toggle
const toggleButton = screen.getByText("Raw Body");
fireEvent.click(toggleButton);

// now the accordion content should be visible
expect(
screen.getByText("Your expected content after decoding and dumping"),
).toBeInTheDocument();
});
});

describe("EntryCard", () => {
it("renders", () => {
it("renders the title and content", () => {
render(
<RekorClientProvider>
<EntryCard
content={<></>}
title={<></>}
/>
</RekorClientProvider>,
<EntryCard
title="Test Title"
content="Test Content"
/>,
);
expect(screen.getByText("Test Title")).toBeInTheDocument();
expect(screen.getByText("Test Content")).toBeInTheDocument();
});
});
35 changes: 33 additions & 2 deletions src/modules/components/Explorer.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,46 @@
jest.mock("next/router");

import { render } from "@testing-library/react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { RekorClientProvider } from "../api/context";
import { Explorer } from "./Explorer";

describe("Explorer", () => {
it("renders", () => {
jest.mock("../api/rekor_api", () => ({
useRekorSearch: jest.fn(() =>
jest.fn().mockImplementation(() => {
return Promise.resolve({ entries: [], totalCount: 0 });
}),
),
}));

it("renders without issues", () => {
render(
<RekorClientProvider>
<Explorer />
</RekorClientProvider>,
);

expect(screen.getByText("Search")).toBeInTheDocument();
});

it("displays loading indicator when fetching data", async () => {
render(
<RekorClientProvider>
<Explorer />
</RekorClientProvider>,
);

const button = screen.getByText("Search");
fireEvent.click(button);

await waitFor(() => expect(screen.queryByRole("status")).toBeNull());

expect(
screen
.findByLabelText("Showing" || "No matching entries found")
.then(res => {
expect(res).toBeInTheDocument();
}),
);
});
});
55 changes: 55 additions & 0 deletions src/modules/components/HashedRekord.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
jest.mock("next/router");
jest.mock("react-syntax-highlighter/dist/cjs/styles/prism");

import { HashedRekordViewer } from "./HashedRekord";
import { render, screen } from "@testing-library/react";
import { HashedRekorV001Schema } from "rekor";

describe("HashedRekordViewer", () => {
it("renders the component with a public key", () => {
const mockedRekord: HashedRekorV001Schema = {
data: {
hash: {
algorithm: "sha256",
value: "mockedHashValue",
},
},
signature: {
content: "mockedSignatureContent",
publicKey: {
content: window.btoa("mockedPublicKeyContent"), // Base64 encode
},
},
};

render(<HashedRekordViewer hashedRekord={mockedRekord} />);

// Verify the hash link
expect(screen.getByText("Hash")).toBeInTheDocument();
// Verify the hash value is displayed
expect(screen.getByText("sha256:mockedHashValue")).toBeInTheDocument();
// Verify the signature content
expect(screen.getByText("mockedSignatureContent")).toBeInTheDocument();
// Verify the public key content
expect(screen.getByText("mockedPublicKeyContent")).toBeInTheDocument();
});

it.skip("renders the component with a public key certificate", () => {
const mockedRekordWithCert = {
// simulate a certificate
data: {},
signature: {
publicKey: {
content: window.btoa(
"-----BEGIN CERTIFICATE-----certContent-----END CERTIFICATE-----",
), // base64 encode
},
},
};

render(<HashedRekordViewer hashedRekord={mockedRekordWithCert} />);

// verify that the decoded certificate content is displayed
expect(screen.getByText(/Decoded:/)).toBeInTheDocument();
});
});
54 changes: 54 additions & 0 deletions src/modules/components/Intoto.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// @ts-nocheck
import atobMock from "../../__mocks__/atobMock";
import decodex509Mock from "../../__mocks__/decodex509Mock";

import { render, screen } from "@testing-library/react";
import { IntotoViewer } from "./Intoto";
import { IntotoV002Schema } from "rekor";

const pemCertificate = `-----BEGIN CERTIFICATE-----\n${Buffer.from("Mocked Public Key").toString("base64")}\n-----END CERTIFICATE-----`;

// mock the module that exports the `decodex509` function
jest.mock("../x509/decode", () => ({
decodex509: decodex509Mock,
}));

describe("IntotoViewer", () => {
beforeAll(() => {
atobMock();
});

afterAll(() => {
jest.restoreAllMocks();
});

const mockIntoto: IntotoV002Schema = {
content: {
envelope: {
payloadType: "application/vnd.in-toto+json",
signatures: [
{
publicKey: pemCertificate,
sig: Buffer.from("signature content", "utf-8").toString("base64"),
},
],
},
payloadHash: {
algorithm: "sha256",
value: "hashValue",
},
},
};

it.skip("renders the component with payload hash, signature, and certificate", () => {
render(<IntotoViewer intoto={mockIntoto} />);

// verify the hash link is rendered correctly
expect(screen.getByText("Hash")).toBeInTheDocument();
expect(screen.getByText("sha256:hashValue")).toBeInTheDocument();

// verify the signature is rendered & decoded
expect(screen.getByText("signature content")).toBeInTheDocument();
expect(screen.getByText(/BEGIN CERTIFICATE/)).toBeInTheDocument();
});
});
21 changes: 18 additions & 3 deletions src/modules/components/SearchForm.test.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
import { SearchForm } from "./SearchForm";
import { render } from "@testing-library/react";
import { render, screen, waitFor } from "@testing-library/react";
import { RekorClientProvider } from "../api/context";
import userEvent from "@testing-library/user-event";

describe("SearchForm", () => {
it("renders", () => {
it("submits the correct form data", async () => {
const mockOnSubmit = jest.fn();
render(
<RekorClientProvider>
<SearchForm
onSubmit={mockOnSubmit}
isLoading={false}
onSubmit={() => {}}
/>
</RekorClientProvider>,
);

// assume "email" is the default selected attribute; otherwise, select it first
await userEvent.type(
screen.getByLabelText(/Email input field/i),
"test@example.com",
);

// submit the form
await userEvent.click(screen.getByText(/Search/i));

await waitFor(() => {
expect(mockOnSubmit).toHaveBeenCalled();
});
});
});
Loading

0 comments on commit 9cfa3ab

Please sign in to comment.