From 289acef0926defd54745618515233b0f8fdd56cb Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Sat, 18 Apr 2026 10:17:39 +0200 Subject: [PATCH 1/8] 7203: Added spinner when retrieving bind key --- CHANGELOG.md | 1 + assets/client/app.jsx | 14 ++ assets/client/app.scss | 21 +++ assets/tests/client/app-reauth.test.jsx | 186 ++++++++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 assets/tests/client/app-reauth.test.jsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 02e22fdbb..673ae4714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ All notable changes to this project will be documented in this file. - Optimized list loading. - Removed fixture length check from test. - Added vitest for frontend unit tests. +- Added spinner when retrieving bind key. ### NB! Prior to 3.x the project was split into separate repositories diff --git a/assets/client/app.jsx b/assets/client/app.jsx index 779f62bd3..698edf40f 100644 --- a/assets/client/app.jsx +++ b/assets/client/app.jsx @@ -28,6 +28,7 @@ function App({ preview, previewId }) { const [bindKey, setBindKey] = useState(null); const [displayFallback, setDisplayFallback] = useState(true); const [debug, setDebug] = useState(false); + const [retrievingBindKey, setRetrievingBindKey] = useState(false); const checkLoginTimeoutRef = useRef(null); const contentServiceRef = useRef(null); @@ -120,12 +121,18 @@ function App({ preview, previewId }) { .checkLogin() .then((data) => { if (data.status === constants.LOGIN_STATUS_READY) { + setRetrievingBindKey(false); startContent(data.screenId); + console.log("Login success"); } else if (data.status === constants.LOGIN_STATUS_AWAITING_BIND_KEY) { + setRetrievingBindKey(false); + if (data?.bindKey) { setBindKey(data.bindKey); } + restartLoginTimeout(); + } else { restartLoginTimeout(); } }) @@ -162,7 +169,9 @@ function App({ preview, previewId }) { } setScreen(null); + setBindKey(null); setRunning(false); + setRetrievingBindKey(true); tokenService.stopRefreshing(); @@ -285,6 +294,11 @@ function App({ preview, previewId }) { {displayFallback && !bindKey && (
)} + {retrievingBindKey && !bindKey && ( +
+
+
+ )}
); } diff --git a/assets/client/app.scss b/assets/client/app.scss index 2f68c5a8e..06a2a81c5 100644 --- a/assets/client/app.scss +++ b/assets/client/app.scss @@ -52,6 +52,27 @@ body { } } +.retrieving-bind-key-container { + position: absolute; + bottom: 10px; + right: 10px; + + .retrieving-bind-key-spinner { + width: 2rem; + height: 2rem; + border: 0.25em solid rgba(255, 255, 255, 0.25); + border-top-color: white; + border-radius: 50%; + animation: spin 0.75s linear infinite; + } +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + .ql-editor { min-height: 200px; } diff --git a/assets/tests/client/app-reauth.test.jsx b/assets/tests/client/app-reauth.test.jsx new file mode 100644 index 000000000..2e09099f0 --- /dev/null +++ b/assets/tests/client/app-reauth.test.jsx @@ -0,0 +1,186 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { render, act, screen, cleanup } from "@testing-library/react"; + +const { mockCheckLogin, mockRefreshToken } = vi.hoisted(() => ({ + mockCheckLogin: vi.fn(), + mockRefreshToken: vi.fn(), +})); + +vi.mock("../../client/logger/logger", () => ({ + default: { info: vi.fn(), warn: vi.fn(), error: vi.fn() }, +})); + +vi.mock("../../client/util/app-storage", () => ({ + default: { + getToken: vi.fn(() => null), + getRefreshToken: vi.fn(() => null), + getScreenId: vi.fn(() => null), + getTokenExpire: vi.fn(() => null), + getTokenIssueAt: vi.fn(() => null), + getFallbackImageUrl: vi.fn(() => null), + setToken: vi.fn(), + setRefreshToken: vi.fn(), + setScreenId: vi.fn(), + setTenant: vi.fn(), + setPreviousBoot: vi.fn(), + clearToken: vi.fn(), + clearRefreshToken: vi.fn(), + clearScreenId: vi.fn(), + clearTenant: vi.fn(), + clearFallbackImageUrl: vi.fn(), + clearAppStorage: vi.fn(), + }, +})); + +vi.mock("../../client/service/token-service", () => ({ + default: { + checkLogin: mockCheckLogin, + refreshToken: mockRefreshToken, + startRefreshing: vi.fn(), + stopRefreshing: vi.fn(), + checkToken: vi.fn(), + }, +})); + +vi.mock("../../client/service/status-service", () => ({ + default: { + setStatus: vi.fn(), + setError: vi.fn(), + setStatusInUrl: vi.fn(), + error: null, + }, +})); + +vi.mock("../../client/service/release-service", () => ({ + default: { + checkForNewRelease: vi.fn(() => Promise.resolve()), + setPreviousBootInUrl: vi.fn(), + startReleaseCheck: vi.fn(), + stopReleaseCheck: vi.fn(), + setScreenIdInUrl: vi.fn(), + }, +})); + +vi.mock("../../client/service/tenant-service", () => ({ + default: { loadTenantConfig: vi.fn() }, +})); + +vi.mock("../../client/util/client-config-loader.js", () => ({ + default: { + loadConfig: vi.fn(() => Promise.resolve({ loginCheckTimeout: 50 })), + }, +})); + +vi.mock("../../client/service/content-service", () => { + return { + default: vi.fn().mockImplementation(() => ({ + start: vi.fn(), + stop: vi.fn(), + })), + }; +}); + +vi.mock("../../client/app.scss", () => ({})); +vi.mock("../../client/assets/fallback.png", () => ({ default: "" })); +vi.mock("../../client/components/screen.jsx", () => ({ + default: () =>
, +})); + +import App from "../../client/app.jsx"; + +/** + * Flush microtasks, advance timers, flush again. + * restartLoginTimeout creates setTimeout inside a .then() on loadConfig — + * microtasks must resolve first for the timer to exist. + */ +async function tick(ms) { + await act(async () => {}); + await act(async () => { + vi.advanceTimersByTime(ms); + }); + await act(async () => {}); +} + +describe("checkLogin retries on unknown status", () => { + beforeEach(() => { + vi.useFakeTimers(); + mockCheckLogin.mockReset(); + mockRefreshToken.mockReset(); + }); + + afterEach(() => { + cleanup(); + vi.useRealTimers(); + }); + + it("continues login polling when checkLogin returns unknown status", async () => { + // The initial checkLogin (on mount) returns unknown status. + // Without the fix, the polling loop stops dead — no restartLoginTimeout. + // With the fix, unknown triggers restartLoginTimeout and the second + // call returns awaitingBindKey, showing the bind key. + mockCheckLogin + .mockResolvedValueOnce({ status: "unknown" }) + .mockResolvedValueOnce({ status: "awaitingBindKey", bindKey: "BIND42" }) + .mockResolvedValue({ status: "awaitingBindKey", bindKey: "BIND42" }); + + render(); + + // Mount: releaseService.checkForNewRelease resolves → checkLogin (call 1) + // → unknown status → with fix: restartLoginTimeout → 50ms timer. + await tick(0); + + // Bind key should NOT be visible yet (unknown status, no bind key set). + expect(screen.queryByText("BIND42")).not.toBeInTheDocument(); + + // Advance past loginCheckTimeout to fire the retry timer. + // Call 2 → awaitingBindKey "BIND42". + await tick(60); + + // With the fix, the login loop retried and the bind key is now shown. + expect(screen.getByText("BIND42")).toBeInTheDocument(); + }); +}); + +describe("reauthenticateHandler shows spinner while retrieving bind key", () => { + beforeEach(() => { + vi.useFakeTimers(); + mockCheckLogin.mockReset(); + mockRefreshToken.mockReset(); + }); + + afterEach(() => { + cleanup(); + vi.useRealTimers(); + }); + + it("recovers from reauth failure and shows new bind key", async () => { + // Call 1 (mount): normal login with bind key. + // Call 2 (reauth → checkLogin): returns new bind key after refresh + // token failure triggers full re-login. + mockCheckLogin + .mockResolvedValueOnce({ status: "awaitingBindKey", bindKey: "INIT01" }) + .mockResolvedValue({ status: "awaitingBindKey", bindKey: "REAUTH01" }); + + // refreshToken will fail when reauthenticate fires. + mockRefreshToken.mockRejectedValue(new Error("token expired")); + + render(); + + // Let mount complete. + await tick(0); + expect(screen.getByText("INIT01")).toBeInTheDocument(); + + // Fire reauthenticate event — refreshToken rejects → catch block + // clears screen/bindKey, sets retrievingBindKey, calls checkLogin + // which resolves with new bind key. + await act(async () => { + document.dispatchEvent(new Event("reauthenticate")); + }); + await tick(60); + + // Old bind key gone, new bind key visible — proves the full reauth + // recovery flow works: clear state → checkLogin → new bind key. + expect(screen.queryByText("INIT01")).not.toBeInTheDocument(); + expect(screen.getByText("REAUTH01")).toBeInTheDocument(); + }); +}); From 9a3fb4ea8709117772fd846c90ad8dcb9a3dcfbc Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Sat, 18 Apr 2026 10:33:16 +0200 Subject: [PATCH 2/8] 7203: Default to retrieving bind key --- assets/client/app.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/client/app.jsx b/assets/client/app.jsx index 698edf40f..96048b11c 100644 --- a/assets/client/app.jsx +++ b/assets/client/app.jsx @@ -28,7 +28,7 @@ function App({ preview, previewId }) { const [bindKey, setBindKey] = useState(null); const [displayFallback, setDisplayFallback] = useState(true); const [debug, setDebug] = useState(false); - const [retrievingBindKey, setRetrievingBindKey] = useState(false); + const [retrievingBindKey, setRetrievingBindKey] = useState(true); const checkLoginTimeoutRef = useRef(null); const contentServiceRef = useRef(null); @@ -72,6 +72,7 @@ function App({ preview, previewId }) { setBindKey(null); setRunning(true); + setRetrievingBindKey(false); contentServiceRef.current = new ContentService(); @@ -123,7 +124,6 @@ function App({ preview, previewId }) { if (data.status === constants.LOGIN_STATUS_READY) { setRetrievingBindKey(false); startContent(data.screenId); - console.log("Login success"); } else if (data.status === constants.LOGIN_STATUS_AWAITING_BIND_KEY) { setRetrievingBindKey(false); From 126414984a99105f79637b5fa7b074506ebcb524 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Sat, 18 Apr 2026 10:44:16 +0200 Subject: [PATCH 3/8] 7203: Changed to switch for increased reability --- assets/client/app.jsx | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/assets/client/app.jsx b/assets/client/app.jsx index 96048b11c..108036f9c 100644 --- a/assets/client/app.jsx +++ b/assets/client/app.jsx @@ -121,19 +121,24 @@ function App({ preview, previewId }) { tokenService .checkLogin() .then((data) => { - if (data.status === constants.LOGIN_STATUS_READY) { - setRetrievingBindKey(false); - startContent(data.screenId); - } else if (data.status === constants.LOGIN_STATUS_AWAITING_BIND_KEY) { - setRetrievingBindKey(false); - - if (data?.bindKey) { - setBindKey(data.bindKey); - } - - restartLoginTimeout(); - } else { - restartLoginTimeout(); + switch (data.status) { + case constants.LOGIN_STATUS_READY: + setRetrievingBindKey(false); + startContent(data.screenId); + break; + case constants.LOGIN_STATUS_AWAITING_BIND_KEY: + setRetrievingBindKey(false); + + if (data?.bindKey) { + setBindKey(data.bindKey); + } + + restartLoginTimeout(); + break; + case constants.LOGIN_STATUS_UNKNOWN: + default: + restartLoginTimeout(); + break; } }) .catch(() => { From f8699da3990528b4e19ab44e4a62ab0631321562 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:41:28 +0200 Subject: [PATCH 4/8] Update assets/client/app.scss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ture Gjørup --- assets/client/app.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/client/app.scss b/assets/client/app.scss index 06a2a81c5..3036e9626 100644 --- a/assets/client/app.scss +++ b/assets/client/app.scss @@ -63,11 +63,11 @@ body { border: 0.25em solid rgba(255, 255, 255, 0.25); border-top-color: white; border-radius: 50%; - animation: spin 0.75s linear infinite; + animation: bind-key-spin 0.75s linear infinite; } } -@keyframes spin { +@keyframes bind-key-spin { to { transform: rotate(360deg); } From a056a8990f42633a8e4647bd93e768f56ffa4273 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:41:45 +0200 Subject: [PATCH 5/8] Update assets/client/app.jsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ture Gjørup --- assets/client/app.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/client/app.jsx b/assets/client/app.jsx index 108036f9c..071d5aebf 100644 --- a/assets/client/app.jsx +++ b/assets/client/app.jsx @@ -299,7 +299,7 @@ function App({ preview, previewId }) { {displayFallback && !bindKey && (
)} - {retrievingBindKey && !bindKey && ( + {!preview && retrievingBindKey && !bindKey && (
From 672784fc8682d0b58ac1f73c4fdab8b47a537535 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:07:07 +0200 Subject: [PATCH 6/8] 7203: Fixed issues raised in code review --- assets/client/app.jsx | 4 + ...-reauth.test.jsx => app-bind-key.test.jsx} | 94 +++++++++++++++++-- 2 files changed, 92 insertions(+), 6 deletions(-) rename assets/tests/client/{app-reauth.test.jsx => app-bind-key.test.jsx} (66%) diff --git a/assets/client/app.jsx b/assets/client/app.jsx index 071d5aebf..cc9d6bdef 100644 --- a/assets/client/app.jsx +++ b/assets/client/app.jsx @@ -137,11 +137,15 @@ function App({ preview, previewId }) { break; case constants.LOGIN_STATUS_UNKNOWN: default: + // retrievingBindKey intentionally not reset — restartLoginTimeout + // retries checkLogin, so we are still retrieving. restartLoginTimeout(); break; } }) .catch(() => { + // retrievingBindKey intentionally not reset — restartLoginTimeout + // retries checkLogin, so we are still retrieving. restartLoginTimeout(); }); } diff --git a/assets/tests/client/app-reauth.test.jsx b/assets/tests/client/app-bind-key.test.jsx similarity index 66% rename from assets/tests/client/app-reauth.test.jsx rename to assets/tests/client/app-bind-key.test.jsx index 2e09099f0..61ed2713a 100644 --- a/assets/tests/client/app-reauth.test.jsx +++ b/assets/tests/client/app-bind-key.test.jsx @@ -73,10 +73,10 @@ vi.mock("../../client/util/client-config-loader.js", () => ({ vi.mock("../../client/service/content-service", () => { return { - default: vi.fn().mockImplementation(() => ({ - start: vi.fn(), - stop: vi.fn(), - })), + default: vi.fn().mockImplementation(function () { + this.start = vi.fn(); + this.stop = vi.fn(); + }), }; }); @@ -123,7 +123,7 @@ describe("checkLogin retries on unknown status", () => { .mockResolvedValueOnce({ status: "awaitingBindKey", bindKey: "BIND42" }) .mockResolvedValue({ status: "awaitingBindKey", bindKey: "BIND42" }); - render(); + const { container } = render(); // Mount: releaseService.checkForNewRelease resolves → checkLogin (call 1) // → unknown status → with fix: restartLoginTimeout → 50ms timer. @@ -131,6 +131,10 @@ describe("checkLogin retries on unknown status", () => { // Bind key should NOT be visible yet (unknown status, no bind key set). expect(screen.queryByText("BIND42")).not.toBeInTheDocument(); + // Spinner should still be visible — we are still retrieving. + expect( + container.querySelector(".retrieving-bind-key-spinner") + ).toBeInTheDocument(); // Advance past loginCheckTimeout to fire the retry timer. // Call 2 → awaitingBindKey "BIND42". @@ -138,6 +142,10 @@ describe("checkLogin retries on unknown status", () => { // With the fix, the login loop retried and the bind key is now shown. expect(screen.getByText("BIND42")).toBeInTheDocument(); + // Spinner gone — bind key received. + expect( + container.querySelector(".retrieving-bind-key-spinner") + ).not.toBeInTheDocument(); }); }); @@ -164,11 +172,15 @@ describe("reauthenticateHandler shows spinner while retrieving bind key", () => // refreshToken will fail when reauthenticate fires. mockRefreshToken.mockRejectedValue(new Error("token expired")); - render(); + const { container } = render(); // Let mount complete. await tick(0); expect(screen.getByText("INIT01")).toBeInTheDocument(); + // Spinner hidden — bind key is shown. + expect( + container.querySelector(".retrieving-bind-key-spinner") + ).not.toBeInTheDocument(); // Fire reauthenticate event — refreshToken rejects → catch block // clears screen/bindKey, sets retrievingBindKey, calls checkLogin @@ -182,5 +194,75 @@ describe("reauthenticateHandler shows spinner while retrieving bind key", () => // recovery flow works: clear state → checkLogin → new bind key. expect(screen.queryByText("INIT01")).not.toBeInTheDocument(); expect(screen.getByText("REAUTH01")).toBeInTheDocument(); + // Spinner gone after recovery. + expect( + container.querySelector(".retrieving-bind-key-spinner") + ).not.toBeInTheDocument(); + }); +}); + +describe("spinner persists during checkLogin retry on failure", () => { + beforeEach(() => { + vi.useFakeTimers(); + mockCheckLogin.mockReset(); + mockRefreshToken.mockReset(); + }); + + afterEach(() => { + cleanup(); + vi.useRealTimers(); + }); + + it("keeps spinner visible when checkLogin rejects", async () => { + // First call rejects (network error), retry succeeds with bind key. + mockCheckLogin + .mockRejectedValueOnce(new Error("Network error")) + .mockResolvedValueOnce({ status: "awaitingBindKey", bindKey: "RETRY01" }) + .mockResolvedValue({ status: "awaitingBindKey", bindKey: "RETRY01" }); + + const { container } = render(); + + await tick(0); + + // checkLogin rejected → catch → restartLoginTimeout. + // retrievingBindKey was never reset, so spinner is still visible. + expect( + container.querySelector(".retrieving-bind-key-spinner") + ).toBeInTheDocument(); + + // Advance past the retry timeout. + await tick(60); + + // Retry succeeded with bind key — spinner gone. + expect( + container.querySelector(".retrieving-bind-key-spinner") + ).not.toBeInTheDocument(); + expect(screen.getByText("RETRY01")).toBeInTheDocument(); + }); +}); + +describe("spinner not shown in preview mode", () => { + beforeEach(() => { + vi.useFakeTimers(); + mockCheckLogin.mockReset(); + mockRefreshToken.mockReset(); + }); + + afterEach(() => { + cleanup(); + vi.useRealTimers(); + }); + + it("does not show spinner when preview is set", async () => { + const { container } = render( + + ); + + await tick(0); + + // Even though retrievingBindKey starts true, preview mode suppresses it. + expect( + container.querySelector(".retrieving-bind-key-spinner") + ).not.toBeInTheDocument(); }); }); From 9db24c5e4fe3ed926b47f2e964b671c1c43b4e0c Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:10:19 +0200 Subject: [PATCH 7/8] 7203: Applied coding standards --- assets/tests/client/app-bind-key.test.jsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/assets/tests/client/app-bind-key.test.jsx b/assets/tests/client/app-bind-key.test.jsx index 61ed2713a..ad19f64c0 100644 --- a/assets/tests/client/app-bind-key.test.jsx +++ b/assets/tests/client/app-bind-key.test.jsx @@ -133,7 +133,7 @@ describe("checkLogin retries on unknown status", () => { expect(screen.queryByText("BIND42")).not.toBeInTheDocument(); // Spinner should still be visible — we are still retrieving. expect( - container.querySelector(".retrieving-bind-key-spinner") + container.querySelector(".retrieving-bind-key-spinner"), ).toBeInTheDocument(); // Advance past loginCheckTimeout to fire the retry timer. @@ -144,7 +144,7 @@ describe("checkLogin retries on unknown status", () => { expect(screen.getByText("BIND42")).toBeInTheDocument(); // Spinner gone — bind key received. expect( - container.querySelector(".retrieving-bind-key-spinner") + container.querySelector(".retrieving-bind-key-spinner"), ).not.toBeInTheDocument(); }); }); @@ -179,7 +179,7 @@ describe("reauthenticateHandler shows spinner while retrieving bind key", () => expect(screen.getByText("INIT01")).toBeInTheDocument(); // Spinner hidden — bind key is shown. expect( - container.querySelector(".retrieving-bind-key-spinner") + container.querySelector(".retrieving-bind-key-spinner"), ).not.toBeInTheDocument(); // Fire reauthenticate event — refreshToken rejects → catch block @@ -196,7 +196,7 @@ describe("reauthenticateHandler shows spinner while retrieving bind key", () => expect(screen.getByText("REAUTH01")).toBeInTheDocument(); // Spinner gone after recovery. expect( - container.querySelector(".retrieving-bind-key-spinner") + container.querySelector(".retrieving-bind-key-spinner"), ).not.toBeInTheDocument(); }); }); @@ -227,7 +227,7 @@ describe("spinner persists during checkLogin retry on failure", () => { // checkLogin rejected → catch → restartLoginTimeout. // retrievingBindKey was never reset, so spinner is still visible. expect( - container.querySelector(".retrieving-bind-key-spinner") + container.querySelector(".retrieving-bind-key-spinner"), ).toBeInTheDocument(); // Advance past the retry timeout. @@ -235,7 +235,7 @@ describe("spinner persists during checkLogin retry on failure", () => { // Retry succeeded with bind key — spinner gone. expect( - container.querySelector(".retrieving-bind-key-spinner") + container.querySelector(".retrieving-bind-key-spinner"), ).not.toBeInTheDocument(); expect(screen.getByText("RETRY01")).toBeInTheDocument(); }); @@ -254,15 +254,13 @@ describe("spinner not shown in preview mode", () => { }); it("does not show spinner when preview is set", async () => { - const { container } = render( - - ); + const { container } = render(); await tick(0); // Even though retrievingBindKey starts true, preview mode suppresses it. expect( - container.querySelector(".retrieving-bind-key-spinner") + container.querySelector(".retrieving-bind-key-spinner"), ).not.toBeInTheDocument(); }); }); From f157e985dba6e4f76b1ddd0e27cc9324dc6486a9 Mon Sep 17 00:00:00 2001 From: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:46:08 +0200 Subject: [PATCH 8/8] 7203: Fixed issue with test --- assets/tests/client/app-bind-key.test.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/tests/client/app-bind-key.test.jsx b/assets/tests/client/app-bind-key.test.jsx index ad19f64c0..654407c2a 100644 --- a/assets/tests/client/app-bind-key.test.jsx +++ b/assets/tests/client/app-bind-key.test.jsx @@ -254,7 +254,7 @@ describe("spinner not shown in preview mode", () => { }); it("does not show spinner when preview is set", async () => { - const { container } = render(); + const { container } = render(); await tick(0);