From cc185638a19bb33f07f9d8d43af86ec06f8547a4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 18 Mar 2026 11:19:10 +0000 Subject: [PATCH 1/6] test(save-button): reduce Redux setup boilerplate Extract a shared render helper for the mock Redux store and Provider wiring. This removes repetitive setup from each test case so scenarios focus on state differences instead of store plumbing, which aligns with Redux testing guidance to keep tests maintainable. Co-authored-by: Chris Zetter --- src/components/SaveButton/SaveButton.test.js | 67 ++++++-------------- 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/src/components/SaveButton/SaveButton.test.js b/src/components/SaveButton/SaveButton.test.js index 146720e0d..cdfa58ee5 100644 --- a/src/components/SaveButton/SaveButton.test.js +++ b/src/components/SaveButton/SaveButton.test.js @@ -6,6 +6,19 @@ import { triggerSave } from "../../redux/EditorSlice"; import SaveButton from "./SaveButton"; const logInHandler = jest.fn(); +const mockStore = configureStore([]); + +const renderSaveButton = (initialState) => { + const store = mockStore(initialState); + + render( + + + , + ); + + return store; +}; describe("When project is loaded", () => { beforeAll(() => { @@ -17,8 +30,6 @@ describe("When project is loaded", () => { describe("who doesn't own the project", () => { beforeEach(() => { - const middlewares = []; - const mockStore = configureStore(middlewares); const initialState = { editor: { loading: "success", @@ -36,12 +47,7 @@ describe("When project is loaded", () => { }, }, }; - store = mockStore(initialState); - render( - - - , - ); + store = renderSaveButton(initialState); }); test("Save button renders", () => { @@ -69,8 +75,6 @@ describe("When project is loaded", () => { describe("who does own the project", () => { beforeEach(() => { - const middlewares = []; - const mockStore = configureStore(middlewares); const initialState = { editor: { loading: "success", @@ -88,12 +92,7 @@ describe("When project is loaded", () => { }, }, }; - store = mockStore(initialState); - render( - - - , - ); + store = renderSaveButton(initialState); }); test("Does not render save button", () => { @@ -112,8 +111,6 @@ describe("When project is loaded", () => { let store; beforeEach(() => { - const middlewares = []; - const mockStore = configureStore(middlewares); const initialState = { editor: { loading: "success", @@ -121,12 +118,7 @@ describe("When project is loaded", () => { }, auth: {}, }; - store = mockStore(initialState); - render( - - - , - ); + store = renderSaveButton(initialState); }); test("Login to save button renders", () => { @@ -154,8 +146,6 @@ describe("When project is loaded", () => { let store; beforeEach(() => { - const middlewares = []; - const mockStore = configureStore(middlewares); const initialState = { editor: { loading: "success", @@ -163,12 +153,7 @@ describe("When project is loaded", () => { }, auth: {}, }; - store = mockStore(initialState); - render( - - - , - ); + store = renderSaveButton(initialState); }); test("Renders a secondary button", () => { @@ -181,8 +166,6 @@ describe("When project is loaded", () => { let store; beforeEach(() => { - const middlewares = []; - const mockStore = configureStore(middlewares); const initialState = { editor: { loading: "success", @@ -190,12 +173,7 @@ describe("When project is loaded", () => { }, auth: {}, }; - store = mockStore(initialState); - render( - - - , - ); + store = renderSaveButton(initialState); }); test("Renders a primary button", () => { @@ -211,17 +189,10 @@ describe("When project is loaded", () => { describe("When project is not loaded", () => { beforeEach(() => { - const middlewares = []; - const mockStore = configureStore(middlewares); - const store = mockStore({ + renderSaveButton({ editor: {}, auth: {}, }); - render( - - - , - ); }); test("Does not render a login to save button", () => { From 9067974847c7e34bc8bccba5e24a08b4a00e9ad6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 18 Mar 2026 11:20:21 +0000 Subject: [PATCH 2/6] test(save-button): use a real Redux Toolkit store Replace redux-mock-store with configureStore + root reducer so tests exercise real reducer behavior, matching Redux's recommended integration testing pattern. Assertions now verify observable store state changes instead of mocked dispatched-action arrays, which reduces false confidence and avoids mock-store deprecation warnings. Co-authored-by: Chris Zetter --- src/components/SaveButton/SaveButton.test.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/components/SaveButton/SaveButton.test.js b/src/components/SaveButton/SaveButton.test.js index cdfa58ee5..87790a5bc 100644 --- a/src/components/SaveButton/SaveButton.test.js +++ b/src/components/SaveButton/SaveButton.test.js @@ -1,15 +1,14 @@ import React from "react"; import { fireEvent, render, screen } from "@testing-library/react"; import { Provider } from "react-redux"; -import configureStore from "redux-mock-store"; -import { triggerSave } from "../../redux/EditorSlice"; +import { configureStore } from "@reduxjs/toolkit"; +import rootReducer from "../../redux/RootSlice"; import SaveButton from "./SaveButton"; const logInHandler = jest.fn(); -const mockStore = configureStore([]); -const renderSaveButton = (initialState) => { - const store = mockStore(initialState); +const renderSaveButton = (preloadedState) => { + const store = configureStore({ reducer: rootReducer, preloadedState }); render( @@ -60,10 +59,10 @@ describe("When project is loaded", () => { ).not.toBeInTheDocument(); }); - test("Clicking save dispatches trigger save action", () => { + test("Clicking save updates the save-triggered state", () => { const saveButton = screen.queryByText("header.save"); fireEvent.click(saveButton); - expect(store.getActions()).toEqual([triggerSave()]); + expect(store.getState().editor.saveTriggered).toBe(true); }); test("Clicking save triggers a logInHandler event", () => { @@ -129,10 +128,10 @@ describe("When project is loaded", () => { expect(screen.queryByText("header.save")).not.toBeInTheDocument(); }); - test("Clicking save dispatches trigger save action", () => { + test("Clicking save updates the save-triggered state", () => { const saveButton = screen.queryByText("header.loginToSave"); fireEvent.click(saveButton); - expect(store.getActions()).toEqual([triggerSave()]); + expect(store.getState().editor.saveTriggered).toBe(true); }); test("Clicking save triggers a logInHandler event", () => { From f4b9792f28ed6be36271c312e314748b1a6b5aa3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 18 Mar 2026 11:27:43 +0000 Subject: [PATCH 3/6] test(utils): add shared renderWithProviders helper Create a reusable Redux-aware render helper so component tests can share one Provider/store setup pattern. Updating SaveButton to use this keeps Redux test wiring consistent with the recommended real-store approach while reducing per-test boilerplate and making future migrations easier. Co-authored-by: Chris Zetter --- src/components/SaveButton/SaveButton.test.js | 15 +++---------- src/utils/renderWithProviders.js | 23 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 src/utils/renderWithProviders.js diff --git a/src/components/SaveButton/SaveButton.test.js b/src/components/SaveButton/SaveButton.test.js index 87790a5bc..32ee635e9 100644 --- a/src/components/SaveButton/SaveButton.test.js +++ b/src/components/SaveButton/SaveButton.test.js @@ -1,21 +1,12 @@ import React from "react"; -import { fireEvent, render, screen } from "@testing-library/react"; -import { Provider } from "react-redux"; -import { configureStore } from "@reduxjs/toolkit"; -import rootReducer from "../../redux/RootSlice"; +import { fireEvent, screen } from "@testing-library/react"; +import renderWithProviders from "../../utils/renderWithProviders"; import SaveButton from "./SaveButton"; const logInHandler = jest.fn(); const renderSaveButton = (preloadedState) => { - const store = configureStore({ reducer: rootReducer, preloadedState }); - - render( - - - , - ); - + const { store } = renderWithProviders(, { preloadedState }); return store; }; diff --git a/src/utils/renderWithProviders.js b/src/utils/renderWithProviders.js new file mode 100644 index 000000000..067bb4a28 --- /dev/null +++ b/src/utils/renderWithProviders.js @@ -0,0 +1,23 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import { Provider } from "react-redux"; +import { configureStore } from "@reduxjs/toolkit"; +import rootReducer from "../redux/RootSlice"; + +const renderWithProviders = ( + ui, + { preloadedState, store, reducer = rootReducer, ...renderOptions } = {}, +) => { + const testStore = store || configureStore({ reducer, preloadedState }); + + const Wrapper = ({ children }) => ( + {children} + ); + + return { + store: testStore, + ...render(ui, { wrapper: Wrapper, ...renderOptions }), + }; +}; + +export default renderWithProviders; From 179d0727593dcf65a6e213aa635fe619f7f5721d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 18 Mar 2026 11:34:16 +0000 Subject: [PATCH 4/6] test(save-button): inline shared renderWithProviders usage Remove the local renderSaveButton wrapper and call renderWithProviders directly in each setup block. This keeps the test aligned to the shared utility API, avoids an extra indirection layer, and makes the common pattern easier to copy into other Redux-connected tests. Co-authored-by: Chris Zetter --- src/components/SaveButton/SaveButton.test.js | 33 ++++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/components/SaveButton/SaveButton.test.js b/src/components/SaveButton/SaveButton.test.js index 32ee635e9..f6502ec51 100644 --- a/src/components/SaveButton/SaveButton.test.js +++ b/src/components/SaveButton/SaveButton.test.js @@ -5,11 +5,6 @@ import SaveButton from "./SaveButton"; const logInHandler = jest.fn(); -const renderSaveButton = (preloadedState) => { - const { store } = renderWithProviders(, { preloadedState }); - return store; -}; - describe("When project is loaded", () => { beforeAll(() => { document.addEventListener("editor-logIn", logInHandler); @@ -37,7 +32,9 @@ describe("When project is loaded", () => { }, }, }; - store = renderSaveButton(initialState); + ({ store } = renderWithProviders(, { + preloadedState: initialState, + })); }); test("Save button renders", () => { @@ -82,7 +79,9 @@ describe("When project is loaded", () => { }, }, }; - store = renderSaveButton(initialState); + ({ store } = renderWithProviders(, { + preloadedState: initialState, + })); }); test("Does not render save button", () => { @@ -108,7 +107,9 @@ describe("When project is loaded", () => { }, auth: {}, }; - store = renderSaveButton(initialState); + ({ store } = renderWithProviders(, { + preloadedState: initialState, + })); }); test("Login to save button renders", () => { @@ -143,7 +144,9 @@ describe("When project is loaded", () => { }, auth: {}, }; - store = renderSaveButton(initialState); + ({ store } = renderWithProviders(, { + preloadedState: initialState, + })); }); test("Renders a secondary button", () => { @@ -163,7 +166,9 @@ describe("When project is loaded", () => { }, auth: {}, }; - store = renderSaveButton(initialState); + ({ store } = renderWithProviders(, { + preloadedState: initialState, + })); }); test("Renders a primary button", () => { @@ -179,9 +184,11 @@ describe("When project is loaded", () => { describe("When project is not loaded", () => { beforeEach(() => { - renderSaveButton({ - editor: {}, - auth: {}, + renderWithProviders(, { + preloadedState: { + editor: {}, + auth: {}, + }, }); }); From e32d54be25b2f1e980d0ddaa1027b3738af60c9c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 18 Mar 2026 11:40:47 +0000 Subject: [PATCH 5/6] test(save-button): rename state fixtures to preloadedState Use preloadedState naming and object shorthand when calling renderWithProviders. This matches Redux Toolkit terminology and removes repetitive key:value syntax, making each setup block clearer and more consistent. Co-authored-by: Chris Zetter --- src/components/SaveButton/SaveButton.test.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/SaveButton/SaveButton.test.js b/src/components/SaveButton/SaveButton.test.js index f6502ec51..0ea4193ab 100644 --- a/src/components/SaveButton/SaveButton.test.js +++ b/src/components/SaveButton/SaveButton.test.js @@ -15,7 +15,7 @@ describe("When project is loaded", () => { describe("who doesn't own the project", () => { beforeEach(() => { - const initialState = { + const preloadedState = { editor: { loading: "success", webComponent: true, @@ -33,7 +33,7 @@ describe("When project is loaded", () => { }, }; ({ store } = renderWithProviders(, { - preloadedState: initialState, + preloadedState, })); }); @@ -62,7 +62,7 @@ describe("When project is loaded", () => { describe("who does own the project", () => { beforeEach(() => { - const initialState = { + const preloadedState = { editor: { loading: "success", webComponent: true, @@ -80,7 +80,7 @@ describe("When project is loaded", () => { }, }; ({ store } = renderWithProviders(, { - preloadedState: initialState, + preloadedState, })); }); @@ -100,7 +100,7 @@ describe("When project is loaded", () => { let store; beforeEach(() => { - const initialState = { + const preloadedState = { editor: { loading: "success", webComponent: false, @@ -108,7 +108,7 @@ describe("When project is loaded", () => { auth: {}, }; ({ store } = renderWithProviders(, { - preloadedState: initialState, + preloadedState, })); }); @@ -137,7 +137,7 @@ describe("When project is loaded", () => { let store; beforeEach(() => { - const initialState = { + const preloadedState = { editor: { loading: "success", webComponent: false, @@ -145,7 +145,7 @@ describe("When project is loaded", () => { auth: {}, }; ({ store } = renderWithProviders(, { - preloadedState: initialState, + preloadedState, })); }); @@ -159,7 +159,7 @@ describe("When project is loaded", () => { let store; beforeEach(() => { - const initialState = { + const preloadedState = { editor: { loading: "success", webComponent: true, @@ -167,7 +167,7 @@ describe("When project is loaded", () => { auth: {}, }; ({ store } = renderWithProviders(, { - preloadedState: initialState, + preloadedState, })); }); From ff165eca71df412bc87d72d7a3b5fc8ef052ff9e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 18 Mar 2026 15:21:02 +0000 Subject: [PATCH 6/6] test(save-button): fix lint warnings in setup blocks Remove unused store bindings in scenarios that only verify rendered button classes. This resolves no-unused-vars warnings from yarn lint and keeps the setup focused on the behavior each test actually asserts. Co-authored-by: Chris Zetter --- src/components/SaveButton/SaveButton.test.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/SaveButton/SaveButton.test.js b/src/components/SaveButton/SaveButton.test.js index 0ea4193ab..6ee83cd4f 100644 --- a/src/components/SaveButton/SaveButton.test.js +++ b/src/components/SaveButton/SaveButton.test.js @@ -134,8 +134,6 @@ describe("When project is loaded", () => { }); describe("with webComponent=false", () => { - let store; - beforeEach(() => { const preloadedState = { editor: { @@ -144,9 +142,9 @@ describe("When project is loaded", () => { }, auth: {}, }; - ({ store } = renderWithProviders(, { + renderWithProviders(, { preloadedState, - })); + }); }); test("Renders a secondary button", () => { @@ -156,8 +154,6 @@ describe("When project is loaded", () => { }); describe("with webComponent=true", () => { - let store; - beforeEach(() => { const preloadedState = { editor: { @@ -166,9 +162,9 @@ describe("When project is loaded", () => { }, auth: {}, }; - ({ store } = renderWithProviders(, { + renderWithProviders(, { preloadedState, - })); + }); }); test("Renders a primary button", () => {