Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ export default [
},
},
{
ignores: ['node_modules/**', 'dist/**', 'coverage/**'],
ignores: ['node_modules/**', 'dist/**', 'coverage/**', 'src/__snapshots__/**'],
},
];
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"serve": "vite preview",
"test": "jest",
"format": "prettier src/* --write",
"lint": "eslint src/*",
"lint": "eslint",
"typecheck": "tsc --noEmit"
},
"eslintConfig": {
Expand Down
224 changes: 136 additions & 88 deletions src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,22 @@
import "@testing-library/jest-dom";
import { render, createEvent, fireEvent } from "@testing-library/react";
import App from "./App";
import { SAMPLE_LOG_DATA_1, SAMPLE_LOG_DATA_2 } from "./test-data";

describe("App", () => {
it("does not throw", () => {
render(<App />);

const inputEl = document.getElementById("input-text");
expect(inputEl).toBeTruthy();
expect(inputEl).toBeVisible();
});
const DOM_EL_FAIL = "DOM Element missing";

describe("App", () => {
it("renders the correct starting elements", () => {
render(<App />);

const exampleLinkEl = document.getElementById("example-link");
expect(exampleLinkEl?.innerHTML).toBe("Load an example log");

const inputEl = document.getElementById("input-text");
expect(inputEl?.getAttribute("placeholder")).toBe(
"Paste a verifier log here or choose a file",
);
expect(inputEl?.tagName).toBe("TEXTAREA");

const fileInputEl = document.getElementById("file-input");
expect(fileInputEl?.tagName).toBe("INPUT");

const gotoLineEl = document.getElementById("goto-line-input");
expect(gotoLineEl?.tagName).toBe("INPUT");

const gotoStartEl = document.getElementById("goto-start");
expect(gotoStartEl?.tagName).toBe("BUTTON");

const gotoEndEl = document.getElementById("goto-end");
expect(gotoEndEl?.tagName).toBe("BUTTON");

const clearEl = document.getElementById("clear");
expect(clearEl?.tagName).toBe("BUTTON");
const { container } = render(<App />);
expect(container).toMatchSnapshot();
});

it("renders the log visualizer when text is pasted", async () => {
render(<App />);
const { container, rerender } = render(<App />);

const inputEl = document.getElementById("input-text");
if (!inputEl) {
fail();
throw new Error(DOM_EL_FAIL);
}

fireEvent(
Expand All @@ -60,90 +32,60 @@ describe("App", () => {
}),
);

const logContainerEl = document.getElementById("log-container");
expect(logContainerEl).toBeTruthy();
expect(logContainerEl).toBeVisible();

const logLinesEl = document.getElementById("line-numbers-idx");
expect(logLinesEl?.innerHTML).toBe(
'<div class="line-numbers-line">1</div>',
);

const logLinesPcEl = document.getElementById("line-numbers-pc");
expect(logLinesPcEl?.innerHTML).toBe(
'<div class="line-numbers-line">314:</div>',
);
rerender(<App />);
expect(container).toMatchSnapshot();

expect(document.getElementById("c-source-container")).toBeTruthy();
expect(document.getElementById("formatted-log-lines")).toBeTruthy();

const firstLine = document.getElementById("line-0");
expect(firstLine?.innerHTML).toBe(
'*(u8 *)(<span id="mem-slot-r7-line-0" class="mem-slot r7" data-id="r7">r7</span> +1303)&nbsp;=&nbsp;<span id="mem-slot-r1-line-0" class="mem-slot r1" data-id="r1">r1</span>',
);

const hintSelectedLineEl = document.getElementById("hint-selected-line");
expect(hintSelectedLineEl?.innerHTML).toBe(
"<span>[selected raw line] 1:</span>&nbsp;314: (73) *(u8 *)(r7 +1303) = r1 ; frame1: R1_w=0 R7=map_value(off=0,ks=4,vs=2808,imm=0)",
);

expect(document.getElementById("state-panel-shown")).toBeTruthy();

const statePanelHeader = document.getElementById("state-panel-header");
expect(statePanelHeader?.innerHTML).toBe(
"<div>Line: 1</div><div>PC: 314</div><div>Frame: 0</div>",
);

//TODO: add tests for state panel content
expect(document.getElementById("state-panel")).toBeTruthy();

// Hit the clear button and make sure we go back to the intial state
const clearEl = document.getElementById("clear");
if (!clearEl) {
fail();
throw new Error(DOM_EL_FAIL);
}
fireEvent(clearEl, createEvent.click(clearEl));
expect(document.getElementById("log-container")).toBeFalsy();
expect(document.getElementById("state-panel-shown")).toBeFalsy();
expect(document.getElementById("input-text")).toBeTruthy();
expect(document.getElementById("input-text")).toBeVisible();

rerender(<App />);
expect(container).toMatchSnapshot();

expect(document.getElementById("c-source-container")).toBeFalsy();
expect(document.getElementById("formatted-log-lines")).toBeFalsy();
expect(document.getElementById("state-panel")).toBeFalsy();
});

it("jumps to the next/prev instruction on key up/down", async () => {
render(<App />);

const inputEl = document.getElementById("input-text");
if (!inputEl) {
fail();
throw new Error(DOM_EL_FAIL);
}

fireEvent(
inputEl,
createEvent.paste(inputEl, {
clipboardData: {
getData: () =>
`
0: (18) r1 = 0x11 ; R1_w=17
2: (b7) r2 = 0 ; R2_w=0
3: (85) call bpf_obj_new_impl#54651 ; R0_w=ptr_or_null_node_data(id=2,ref_obj_id=2) refs=2
4: (bf) r6 = r0 ; R0_w=ptr_or_null_node_data(id=2,ref_obj_id=2) R6_w=ptr_or_null_node_data(id=2,ref_obj_id=2) refs=2
5: (b7) r7 = 1 ; R7_w=1 refs=2
; if (!n) @ rbtree.c:199
6: (15) if r6 == 0x0 goto pc+104 ; R6_w=ptr_node_data(ref_obj_id=2) refs=2
7: (b7) r1 = 4 ; R1_w=4 ref
`,
getData: () => SAMPLE_LOG_DATA_1,
},
}),
);

// Need to show all log lines
const checkboxEl = document.getElementById("show-full-log");
if (!checkboxEl) {
throw new Error(DOM_EL_FAIL);
}
fireEvent(checkboxEl, createEvent.click(checkboxEl));

const logContainerEl = document.getElementById("log-container");
expect(logContainerEl).toBeTruthy();
expect(logContainerEl).toBeVisible();

const line5 = document.getElementById("line-5");
const line6 = document.getElementById("line-6");
const line7 = document.getElementById("line-7");

if (!line5 || !line6 || !line7 || !logContainerEl) {
fail();
if (!line5 || !line7 || !line6 || !logContainerEl) {
throw new Error(DOM_EL_FAIL);
}

expect(line5.innerHTML).toBe(
Expand Down Expand Up @@ -174,4 +116,110 @@ describe("App", () => {
expect(line6.classList.contains("selected-line")).toBeFalsy();
expect(line7.classList.contains("selected-line")).toBeFalsy();
});

it("c lines and state panel containers are collapsible", async () => {
render(<App />);

const inputEl = document.getElementById("input-text");
if (!inputEl) {
throw new Error("Input text is missing");
}

fireEvent(
inputEl,
createEvent.paste(inputEl, {
clipboardData: {
getData: () => SAMPLE_LOG_DATA_1,
},
}),
);

const cSourceEl = document.getElementById("c-source-container");
const cSourceFile = cSourceEl?.querySelector(".c-source-file");

expect(cSourceFile).toBeVisible();

const cSourceHideShow = cSourceEl?.querySelector(".hide-show-button");

if (!cSourceHideShow) {
throw new Error(DOM_EL_FAIL);
}

fireEvent(cSourceHideShow, createEvent.click(cSourceHideShow));
expect(cSourceFile).not.toBeVisible();

const statePanelEl = document.getElementById("state-panel");
const statePanelHeader = document.getElementById("state-panel-header");

expect(statePanelHeader).toBeVisible();

const statePanelHideShow = statePanelEl?.querySelector(".hide-show-button");

if (!statePanelHideShow) {
throw new Error(DOM_EL_FAIL);
}

fireEvent(statePanelHideShow, createEvent.click(statePanelHideShow));
expect(statePanelHeader).not.toBeVisible();
});

it("highlights the associated c source or log line(s) when the other is clicked ", async () => {
render(<App />);

const inputEl = document.getElementById("input-text");
if (!inputEl) {
throw new Error(DOM_EL_FAIL);
}

fireEvent(
inputEl,
createEvent.paste(inputEl, {
clipboardData: {
getData: () => SAMPLE_LOG_DATA_2,
},
}),
);

const line4El = document.getElementById("line-4");
const cLineEl = document.getElementById("line-rbtree.c:198");
if (!line4El || !cLineEl) {
throw new Error(DOM_EL_FAIL);
}

expect(line4El.classList).not.toContain("selected-line");
expect(cLineEl.classList).not.toContain("selected-line");

// Click on the first instruction log line
fireEvent(line4El, createEvent.click(line4El));

expect(line4El.classList).toContain("selected-line");
expect(cLineEl.classList).toContain("selected-line");

// Click on another log line
const line10El = document.getElementById("line-10");
if (!line10El) {
throw new Error(DOM_EL_FAIL);
}

fireEvent(line10El, createEvent.click(line10El));

expect(line4El.classList).not.toContain("selected-line");
expect(cLineEl.classList).not.toContain("selected-line");

// Click on the first c source line
fireEvent(cLineEl, createEvent.click(cLineEl));

expect(line4El.classList).toContain("selected-line");
expect(cLineEl.classList).toContain("selected-line");

// The other instructions for this source line should also be selected
const followingIns = ["line-5", "line-6", "line-7", "line-8"];
followingIns.forEach((lineId) => {
const el = document.getElementById(lineId);
if (!el) {
throw new Error(DOM_EL_FAIL);
}
expect(el.classList).toContain("selected-line");
});
});
});
Loading