From 0725a5373b5b70ecedf7b7636e2785cbfda6b0e4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 26 Jul 2025 07:20:35 +0000
Subject: [PATCH 1/4] Initial plan
From f54660e95d02eb4171b38e5e9d682dda2169cfcd Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 26 Jul 2025 07:35:59 +0000
Subject: [PATCH 2/4] Implement localStorage persistence for HAR viewer mode
selection
Co-authored-by: peckz <18050177+peckz@users.noreply.github.com>
---
.../pages/utilities/har-file-viewer.test.tsx | 98 +++++++++++++++++++
pages/utilities/har-file-viewer.tsx | 27 +++++
2 files changed, 125 insertions(+)
diff --git a/__tests__/pages/utilities/har-file-viewer.test.tsx b/__tests__/pages/utilities/har-file-viewer.test.tsx
index ef2bbb7..a5ea095 100644
--- a/__tests__/pages/utilities/har-file-viewer.test.tsx
+++ b/__tests__/pages/utilities/har-file-viewer.test.tsx
@@ -50,6 +50,11 @@ const mockHarData = {
};
describe("HARFileViewer", () => {
+ beforeEach(() => {
+ // Clear localStorage before each test
+ localStorage.clear();
+ });
+
test("should render the component and display the drop zone text", () => {
render();
@@ -121,4 +126,97 @@ describe("HARFileViewer", () => {
const row1 = within(rows[0]).getByTestId("column-status-code");
expect(row1).toHaveTextContent("200");
});
+
+ test("should default to table view when no localStorage value exists", async () => {
+ // Clear localStorage to ensure we test the default behavior
+ localStorage.clear();
+
+ const user = userEvent.setup();
+ render();
+
+ // Upload a HAR file first to show the view buttons
+ const file = new File([JSON.stringify(mockHarData)], "test.har", {
+ type: "application/json",
+ });
+ const fileInput = screen.getByTestId("input");
+ await user.upload(fileInput, file);
+
+ // Wait for the table rows to appear (this means HAR is loaded)
+ const rows = await screen.findAllByTestId("table-row");
+ expect(rows).toHaveLength(2);
+
+ // Check that localStorage has the default table view saved
+ expect(localStorage.getItem("har-viewer-view-mode")).toBe("table");
+ });
+
+ test("should load waterfall view from localStorage if previously saved", async () => {
+ // Set localStorage to waterfall view
+ localStorage.setItem("har-viewer-view-mode", "waterfall");
+
+ const user = userEvent.setup();
+ render();
+
+ // Upload a HAR file
+ const file = new File([JSON.stringify(mockHarData)], "test.har", {
+ type: "application/json",
+ });
+ const fileInput = screen.getByTestId("input");
+ await user.upload(fileInput, file);
+
+ // Wait for HAR data to load - in waterfall view we should see the HarWaterfall component
+ // Since waterfall view doesn't show table rows, we need to check for the component differently
+ // Let's wait for the filter buttons to appear which appear in both views
+ await screen.findByRole("button", { name: "All" });
+
+ // Verify localStorage still has waterfall
+ expect(localStorage.getItem("har-viewer-view-mode")).toBe("waterfall");
+ });
+
+ test("should save view mode to localStorage when user switches views", async () => {
+ localStorage.clear();
+
+ const user = userEvent.setup();
+ render();
+
+ // Upload a HAR file first
+ const file = new File([JSON.stringify(mockHarData)], "test.har", {
+ type: "application/json",
+ });
+ const fileInput = screen.getByTestId("input");
+ await user.upload(fileInput, file);
+
+ // Wait for the table rows to appear (means we're in table view initially)
+ const rows = await screen.findAllByTestId("table-row");
+ expect(rows).toHaveLength(2);
+
+ // Initially should be table view
+ expect(localStorage.getItem("har-viewer-view-mode")).toBe("table");
+
+ // Click waterfall button
+ const waterfallButton = screen.getByRole("button", { name: /waterfall/i });
+ await user.click(waterfallButton);
+
+ // Should save to localStorage
+ expect(localStorage.getItem("har-viewer-view-mode")).toBe("waterfall");
+
+ // Click table button
+ const tableButton = screen.getByRole("button", { name: /table view/i });
+ await user.click(tableButton);
+
+ // Should save to localStorage and we should see table rows again
+ expect(localStorage.getItem("har-viewer-view-mode")).toBe("table");
+ await screen.findAllByTestId("table-row");
+ });
+
+ test("should handle localStorage errors gracefully", () => {
+ // Mock localStorage to throw an error
+ const mockGetItem = jest.spyOn(Storage.prototype, "getItem").mockImplementation(() => {
+ throw new Error("localStorage not available");
+ });
+
+ // Should not crash when localStorage throws
+ expect(() => render()).not.toThrow();
+
+ mockGetItem.mockRestore();
+ });
});
diff --git a/pages/utilities/har-file-viewer.tsx b/pages/utilities/har-file-viewer.tsx
index 3a8b388..7fdf791 100644
--- a/pages/utilities/har-file-viewer.tsx
+++ b/pages/utilities/har-file-viewer.tsx
@@ -110,6 +110,33 @@ export default function HARFileViewer() {
const [activeFilter, setActiveFilter] = useState("All");
const [viewMode, setViewMode] = useState<"table" | "waterfall">("table");
const [statusFilter, setStatusFilter] = useState([]);
+ const [isInitialized, setIsInitialized] = useState(false);
+
+ // Load view mode from localStorage on component mount
+ useEffect(() => {
+ try {
+ const savedViewMode = localStorage.getItem("har-viewer-view-mode");
+ if (savedViewMode === "table" || savedViewMode === "waterfall") {
+ setViewMode(savedViewMode);
+ }
+ } catch (error) {
+ // localStorage not available or error occurred, use default
+ console.warn("Failed to load view mode from localStorage:", error);
+ }
+ setIsInitialized(true);
+ }, []);
+
+ // Save view mode to localStorage when it changes (but not on initial load)
+ useEffect(() => {
+ if (!isInitialized) return;
+
+ try {
+ localStorage.setItem("har-viewer-view-mode", viewMode);
+ } catch (error) {
+ // localStorage not available or error occurred
+ console.warn("Failed to save view mode to localStorage:", error);
+ }
+ }, [viewMode, isInitialized]);
const handleFileUpload = useCallback((file: File | undefined) => {
if (!file) {
From f287be146081bf4f2548745f075d771024637b79 Mon Sep 17 00:00:00 2001
From: peckz
Date: Sat, 26 Jul 2025 10:22:22 +0200
Subject: [PATCH 3/4] chore: fix formatting
---
.../pages/utilities/har-file-viewer.test.tsx | 42 ++++++++++---------
pages/utilities/har-file-viewer.tsx | 7 +++-
2 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/__tests__/pages/utilities/har-file-viewer.test.tsx b/__tests__/pages/utilities/har-file-viewer.test.tsx
index a5ea095..f797172 100644
--- a/__tests__/pages/utilities/har-file-viewer.test.tsx
+++ b/__tests__/pages/utilities/har-file-viewer.test.tsx
@@ -58,7 +58,9 @@ describe("HARFileViewer", () => {
test("should render the component and display the drop zone text", () => {
render();
- expect(screen.getByText("Drop your .har or .json file here")).toBeInTheDocument();
+ expect(
+ screen.getByText("Drop your .har or .json file here")
+ ).toBeInTheDocument();
});
test("should list all requests after uploading a har file", async () => {
@@ -130,10 +132,10 @@ describe("HARFileViewer", () => {
test("should default to table view when no localStorage value exists", async () => {
// Clear localStorage to ensure we test the default behavior
localStorage.clear();
-
+
const user = userEvent.setup();
render();
-
+
// Upload a HAR file first to show the view buttons
const file = new File([JSON.stringify(mockHarData)], "test.har", {
type: "application/json",
@@ -144,7 +146,7 @@ describe("HARFileViewer", () => {
// Wait for the table rows to appear (this means HAR is loaded)
const rows = await screen.findAllByTestId("table-row");
expect(rows).toHaveLength(2);
-
+
// Check that localStorage has the default table view saved
expect(localStorage.getItem("har-viewer-view-mode")).toBe("table");
});
@@ -152,10 +154,10 @@ describe("HARFileViewer", () => {
test("should load waterfall view from localStorage if previously saved", async () => {
// Set localStorage to waterfall view
localStorage.setItem("har-viewer-view-mode", "waterfall");
-
+
const user = userEvent.setup();
render();
-
+
// Upload a HAR file
const file = new File([JSON.stringify(mockHarData)], "test.har", {
type: "application/json",
@@ -167,17 +169,17 @@ describe("HARFileViewer", () => {
// Since waterfall view doesn't show table rows, we need to check for the component differently
// Let's wait for the filter buttons to appear which appear in both views
await screen.findByRole("button", { name: "All" });
-
+
// Verify localStorage still has waterfall
expect(localStorage.getItem("har-viewer-view-mode")).toBe("waterfall");
});
test("should save view mode to localStorage when user switches views", async () => {
localStorage.clear();
-
+
const user = userEvent.setup();
render();
-
+
// Upload a HAR file first
const file = new File([JSON.stringify(mockHarData)], "test.har", {
type: "application/json",
@@ -188,21 +190,21 @@ describe("HARFileViewer", () => {
// Wait for the table rows to appear (means we're in table view initially)
const rows = await screen.findAllByTestId("table-row");
expect(rows).toHaveLength(2);
-
+
// Initially should be table view
expect(localStorage.getItem("har-viewer-view-mode")).toBe("table");
-
+
// Click waterfall button
const waterfallButton = screen.getByRole("button", { name: /waterfall/i });
await user.click(waterfallButton);
-
+
// Should save to localStorage
expect(localStorage.getItem("har-viewer-view-mode")).toBe("waterfall");
-
+
// Click table button
const tableButton = screen.getByRole("button", { name: /table view/i });
await user.click(tableButton);
-
+
// Should save to localStorage and we should see table rows again
expect(localStorage.getItem("har-viewer-view-mode")).toBe("table");
await screen.findAllByTestId("table-row");
@@ -210,13 +212,15 @@ describe("HARFileViewer", () => {
test("should handle localStorage errors gracefully", () => {
// Mock localStorage to throw an error
- const mockGetItem = jest.spyOn(Storage.prototype, "getItem").mockImplementation(() => {
- throw new Error("localStorage not available");
- });
-
+ const mockGetItem = jest
+ .spyOn(Storage.prototype, "getItem")
+ .mockImplementation(() => {
+ throw new Error("localStorage not available");
+ });
+
// Should not crash when localStorage throws
expect(() => render()).not.toThrow();
-
+
mockGetItem.mockRestore();
});
});
diff --git a/pages/utilities/har-file-viewer.tsx b/pages/utilities/har-file-viewer.tsx
index 7fdf791..b0fdcc4 100644
--- a/pages/utilities/har-file-viewer.tsx
+++ b/pages/utilities/har-file-viewer.tsx
@@ -129,7 +129,7 @@ export default function HARFileViewer() {
// Save view mode to localStorage when it changes (but not on initial load)
useEffect(() => {
if (!isInitialized) return;
-
+
try {
localStorage.setItem("har-viewer-view-mode", viewMode);
} catch (error) {
@@ -169,7 +169,10 @@ export default function HARFileViewer() {
setStatus("hover");
const file = event.dataTransfer.files[0];
- if (!file || (!file.name.endsWith(".har") && !file.name.endsWith(".json"))) {
+ if (
+ !file ||
+ (!file.name.endsWith(".har") && !file.name.endsWith(".json"))
+ ) {
setStatus("unsupported");
return;
}
From 926f897f192a01c371eb929b1a03f177fe3fa1ce Mon Sep 17 00:00:00 2001
From: peckz
Date: Sat, 26 Jul 2025 10:28:11 +0200
Subject: [PATCH 4/4] chore: remove local storage tests
---
.../pages/utilities/har-file-viewer.test.tsx | 100 ------------------
pages/utilities/har-file-viewer.tsx | 2 -
2 files changed, 102 deletions(-)
diff --git a/__tests__/pages/utilities/har-file-viewer.test.tsx b/__tests__/pages/utilities/har-file-viewer.test.tsx
index f797172..8a62239 100644
--- a/__tests__/pages/utilities/har-file-viewer.test.tsx
+++ b/__tests__/pages/utilities/har-file-viewer.test.tsx
@@ -50,11 +50,6 @@ const mockHarData = {
};
describe("HARFileViewer", () => {
- beforeEach(() => {
- // Clear localStorage before each test
- localStorage.clear();
- });
-
test("should render the component and display the drop zone text", () => {
render();
@@ -128,99 +123,4 @@ describe("HARFileViewer", () => {
const row1 = within(rows[0]).getByTestId("column-status-code");
expect(row1).toHaveTextContent("200");
});
-
- test("should default to table view when no localStorage value exists", async () => {
- // Clear localStorage to ensure we test the default behavior
- localStorage.clear();
-
- const user = userEvent.setup();
- render();
-
- // Upload a HAR file first to show the view buttons
- const file = new File([JSON.stringify(mockHarData)], "test.har", {
- type: "application/json",
- });
- const fileInput = screen.getByTestId("input");
- await user.upload(fileInput, file);
-
- // Wait for the table rows to appear (this means HAR is loaded)
- const rows = await screen.findAllByTestId("table-row");
- expect(rows).toHaveLength(2);
-
- // Check that localStorage has the default table view saved
- expect(localStorage.getItem("har-viewer-view-mode")).toBe("table");
- });
-
- test("should load waterfall view from localStorage if previously saved", async () => {
- // Set localStorage to waterfall view
- localStorage.setItem("har-viewer-view-mode", "waterfall");
-
- const user = userEvent.setup();
- render();
-
- // Upload a HAR file
- const file = new File([JSON.stringify(mockHarData)], "test.har", {
- type: "application/json",
- });
- const fileInput = screen.getByTestId("input");
- await user.upload(fileInput, file);
-
- // Wait for HAR data to load - in waterfall view we should see the HarWaterfall component
- // Since waterfall view doesn't show table rows, we need to check for the component differently
- // Let's wait for the filter buttons to appear which appear in both views
- await screen.findByRole("button", { name: "All" });
-
- // Verify localStorage still has waterfall
- expect(localStorage.getItem("har-viewer-view-mode")).toBe("waterfall");
- });
-
- test("should save view mode to localStorage when user switches views", async () => {
- localStorage.clear();
-
- const user = userEvent.setup();
- render();
-
- // Upload a HAR file first
- const file = new File([JSON.stringify(mockHarData)], "test.har", {
- type: "application/json",
- });
- const fileInput = screen.getByTestId("input");
- await user.upload(fileInput, file);
-
- // Wait for the table rows to appear (means we're in table view initially)
- const rows = await screen.findAllByTestId("table-row");
- expect(rows).toHaveLength(2);
-
- // Initially should be table view
- expect(localStorage.getItem("har-viewer-view-mode")).toBe("table");
-
- // Click waterfall button
- const waterfallButton = screen.getByRole("button", { name: /waterfall/i });
- await user.click(waterfallButton);
-
- // Should save to localStorage
- expect(localStorage.getItem("har-viewer-view-mode")).toBe("waterfall");
-
- // Click table button
- const tableButton = screen.getByRole("button", { name: /table view/i });
- await user.click(tableButton);
-
- // Should save to localStorage and we should see table rows again
- expect(localStorage.getItem("har-viewer-view-mode")).toBe("table");
- await screen.findAllByTestId("table-row");
- });
-
- test("should handle localStorage errors gracefully", () => {
- // Mock localStorage to throw an error
- const mockGetItem = jest
- .spyOn(Storage.prototype, "getItem")
- .mockImplementation(() => {
- throw new Error("localStorage not available");
- });
-
- // Should not crash when localStorage throws
- expect(() => render()).not.toThrow();
-
- mockGetItem.mockRestore();
- });
});
diff --git a/pages/utilities/har-file-viewer.tsx b/pages/utilities/har-file-viewer.tsx
index b0fdcc4..706740e 100644
--- a/pages/utilities/har-file-viewer.tsx
+++ b/pages/utilities/har-file-viewer.tsx
@@ -121,7 +121,6 @@ export default function HARFileViewer() {
}
} catch (error) {
// localStorage not available or error occurred, use default
- console.warn("Failed to load view mode from localStorage:", error);
}
setIsInitialized(true);
}, []);
@@ -134,7 +133,6 @@ export default function HARFileViewer() {
localStorage.setItem("har-viewer-view-mode", viewMode);
} catch (error) {
// localStorage not available or error occurred
- console.warn("Failed to save view mode to localStorage:", error);
}
}, [viewMode, isInitialized]);