Skip to content

Commit

Permalink
test(unit): fix issue with base64 encoding & additional coverage
Browse files Browse the repository at this point in the history
test(unit): restore mock

test(unit): fix issue with base64 encoding and context

minor changes

fix issue with base64 encoding

re-enable hashed rekord test

fix test for HashedRekord component

fix breaking Entry test

add mocks for explorer test

add mocks for explorer test

add data-testids and additional tests for explorer

fix tests, add additional

fix lint issue
  • Loading branch information
kahboom committed Apr 4, 2024
1 parent 8f23e31 commit 1ba840a
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 54 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
"**/__tests__/**/*.[jt]s?(x)",
"**/?(*.)+(spec|test).[jt]s?(x)"
],
"extends": ["plugin:testing-library/react"]
"extends": ["plugin:testing-library/react"],
"rules": {
"testing-library/no-debugging-utils": "off"
}
}
],
"rules": {
Expand Down
11 changes: 8 additions & 3 deletions src/__mocks__/atobMock.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
const atobMock = () => {
window.atob = jest.fn().mockImplementation(str => {
const decoded = Buffer.from(str, "base64").toString("utf-8");
console.log(`Decoding: ${str}, Result: ${decoded}`);
const base64Pattern =
/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/;

return decoded;
if (!base64Pattern.test(str)) {
// return if string is not base64 encoded
return str;
}

return Buffer.from(str, "base64").toString("utf-8");
});
};

Expand Down
57 changes: 57 additions & 0 deletions src/modules/api/context.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const originalEnv = process.env;

beforeEach(() => {
jest.resetModules();
process.env = {
...originalEnv,
NEXT_PUBLIC_REKOR_DEFAULT_DOMAIN: "https://example.com",
};
});

afterEach(() => {
process.env = originalEnv;
});

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {
RekorClientProvider,
useRekorClient,
useRekorBaseUrl,
} from "./context";

const TestConsumerComponent = () => {
useRekorClient();
const [baseUrl, setBaseUrl] = useRekorBaseUrl();

return (
<div>
<button onClick={() => setBaseUrl("https://new.example.com")}>
Change Base URL
</button>
<p>Base URL: {baseUrl}</p>
</div>
);
};

describe("RekorClientContext", () => {
beforeAll(() => jest.clearAllMocks());

it("provides a RekorClient instance and manages base URL", async () => {
render(
<RekorClientProvider>
<TestConsumerComponent />
</RekorClientProvider>,
);

expect(
screen.getByText(/Base URL: https:\/\/example.com/),
).toBeInTheDocument();

await userEvent.click(screen.getByText(/Change Base URL/));

expect(
screen.getByText(/Base URL: https:\/\/new.example.com/),
).toBeInTheDocument();
});
});
20 changes: 11 additions & 9 deletions src/modules/components/DSSE.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
jest.mock("next/router");
// @ts-ignore
// @ts-nocheck
import atobMock from "../../__mocks__/atobMock";
import decodex509Mock from "../../__mocks__/decodex509Mock";

jest.mock("next/router");

jest.mock("../x509/decode", () => ({
decodex509: decodex509Mock,
}));

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";

describe("DSSEViewer Component", () => {
beforeAll(() => {
jest.clearAllMocks();
atobMock();
});

Expand All @@ -32,11 +38,7 @@ describe("DSSEViewer Component", () => {
};

it("renders without crashing", () => {
render(
<RekorClientProvider>
<DSSEViewer dsse={mockDSSE} />
</RekorClientProvider>,
);
render(<DSSEViewer dsse={mockDSSE} />);
expect(screen.getByText("Hash")).toBeInTheDocument();
});

Expand All @@ -56,7 +58,7 @@ describe("DSSEViewer Component", () => {
).toBeInTheDocument();
});

it.skip("displays the public key certificate title and content correctly", () => {
it("displays the public key certificate title and content correctly", () => {
render(<DSSEViewer dsse={mockDSSE} />);
expect(screen.getByText("Public Key Certificate")).toBeInTheDocument();
});
Expand Down
55 changes: 38 additions & 17 deletions src/modules/components/Entry.test.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,51 @@
// @ts-nocheck
jest.mock("react-syntax-highlighter/dist/cjs/styles/prism", () => ({}));
jest.mock("../utils/date", () => ({
toRelativeDateString: jest.fn().mockReturnValue("Some Date"),
}));
jest.mock("./HashedRekord", () => ({
HashedRekordViewer: () => <div>MockedHashedRekordViewer</div>,
}));

import atobMock from "../../__mocks__/atobMock";

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.skip("renders and toggles the accordion content", () => {
beforeAll(() => {
atobMock();
});

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

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",
signature: {
publicKey: {
content: window.btoa(
"-----BEGIN CERTIFICATE-----certContent-----END CERTIFICATE-----",
), // base64 encode
},
},
},
};

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

expect(screen.getByText("apiVersion")).not.toBeVisible();

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

Expand All @@ -31,9 +54,7 @@ describe("Entry", () => {
fireEvent.click(toggleButton);

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

Expand Down
84 changes: 66 additions & 18 deletions src/modules/components/Explorer.test.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,58 @@
jest.mock("next/router");
import { NextRouter, useRouter } from "next/router";

import { fireEvent, render, screen, waitFor } from "@testing-library/react";
jest.mock("next/router", () => ({
useRouter: jest.fn(),
}));

beforeEach(() => {
jest.resetAllMocks();

(useRouter as jest.Mock).mockImplementation(
(): Partial<NextRouter> => ({
query: {},
pathname: "/",
asPath: "/",
}),
);
});

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

describe("Explorer", () => {
jest.mock("../api/rekor_api", () => ({
useRekorSearch: jest.fn(() =>
jest.fn().mockImplementation(() => {
return Promise.resolve({ entries: [], totalCount: 0 });
}),
),
}));
it("should render search form and display search button", () => {
render(
<RekorClientProvider>
<Explorer />
</RekorClientProvider>,
);

expect(screen.getByLabelText("Attribute")).toBeInTheDocument();
expect(screen.getByLabelText("Email")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Search" })).toBeInTheDocument();
});

it("should handle invalid logIndex query parameter", () => {
const mockRouter = {
query: {
logIndex: "invalid",
},
push: jest.fn(),
};

(useRouter as jest.Mock).mockImplementation(
(): Partial<NextRouter> => mockRouter,
);

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

expect(screen.getByText("Search")).toBeInTheDocument();
expect(mockRouter.push).not.toHaveBeenCalled();
});

it("displays loading indicator when fetching data", async () => {
Expand All @@ -31,16 +63,32 @@ describe("Explorer", () => {
);

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

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

expect(
screen
.findByLabelText("Showing" || "No matching entries found")
.then(res => {
expect(res).toBeInTheDocument();
}),
screen.findByLabelText("Showing").then(res => {
screen.debug();
console.log(res);
expect(res).toBeInTheDocument();
}),
);
});

describe("RekorError", () => {
it("should render an Alert component if the error parameter is undefined", () => {
render(<RekorError error={undefined} />);
const alert = screen.getByRole("alert");
expect(alert).toBeInTheDocument();
expect(alert).toHaveTextContent("Unknown error");
});

it("should render an Alert component if error parameter is an empty object", () => {
render(<RekorError error={{}} />);
const alert = screen.getByRole("alert");
expect(alert).toBeInTheDocument();
expect(alert).toHaveTextContent("Unknown error");
});
});
});
5 changes: 3 additions & 2 deletions src/modules/components/Explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function isRekorError(error: unknown): error is RekorError {
return !!error && typeof error === "object";
}

function Error({ error }: { error: unknown }) {
export function RekorError({ error }: { error: unknown }) {
let title = "Unknown error";
let detail: string | undefined;

Expand All @@ -47,6 +47,7 @@ function Error({ error }: { error: unknown }) {
style={{ margin: "1em auto" }}
title={title}
variant={"danger"}
role={"alert"}
>
{detail}
</Alert>
Expand Down Expand Up @@ -181,7 +182,7 @@ export function Explorer() {
/>

{error ? (
<Error error={error} />
<RekorError error={error} />
) : loading ? (
<LoadingIndicator />
) : (
Expand Down
16 changes: 13 additions & 3 deletions src/modules/components/HashedRekord.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
// @ts-nocheck
jest.mock("next/router");
jest.mock("react-syntax-highlighter/dist/cjs/styles/prism");

import decodex509Mock from "../../__mocks__/decodex509Mock";

jest.mock("../x509/decode", () => ({
decodex509: decodex509Mock,
}));

import { HashedRekordViewer } from "./HashedRekord";
import { render, screen } from "@testing-library/react";
import { HashedRekorV001Schema } from "rekor";
Expand Down Expand Up @@ -30,7 +37,7 @@ describe("HashedRekordViewer", () => {
expect(screen.getByText("mockedPublicKeyContent")).toBeInTheDocument();
});

it.skip("renders the component with a public key certificate", () => {
it("renders the component with a public key certificate", () => {
const mockedRekordWithCert = {
// simulate a certificate
data: {},
Expand All @@ -45,7 +52,10 @@ describe("HashedRekordViewer", () => {

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

// verify that the decoded certificate content is displayed
expect(screen.getByText(/Decoded:/)).toBeInTheDocument();
expect(
screen.getByText(
/'-----BEGIN CERTIFICATE-----Mocked Certificate-----END CERTIFICATE-----'/,
),
).toBeInTheDocument();
});
});
2 changes: 1 addition & 1 deletion src/modules/components/Intoto.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("IntotoViewer", () => {
},
};

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

// verify the hash link is rendered correctly
Expand Down
Loading

0 comments on commit 1ba840a

Please sign in to comment.