Skip to content

[web] theme.js crashes all Node.js tests that import clock-tree-widget #9912

@oharboe

Description

@oharboe

Description

@maliberty This is a regression. I'll let you nurse CI on this one.

Tested on origin/master 56355d0.

Apply the patch at the bottom and run:

bazelisk test //src/web/test:clock_tree_widget_test

You'll see:

ReferenceError: localStorage is not defined
    at file:///.../src/web/src/theme.js:6:20

ca12251 ("web: add light/dark theme toggle with persistence") added import { getThemeColors } from './theme.js' to clock-tree-widget.js. theme.js calls localStorage.getItem('theme') and document.documentElement.dataset.theme at module top level, which doesn't exist in Node.js. Any js_test that transitively imports clock-tree-widget.js now crashes before a single test case runs.

The fix guards both the top-level initialization and getThemeColors() with typeof checks. In Node.js, getThemeColors() returns hardcoded dark-theme defaults — these are only used by canvas rendering code that never runs in tests anyway.


Fix (git am on origin/master 56355d0):

From cea3b57d753a87cf5c91ced1f3554c3939dccaf1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Harboe?= <oyvind.harboe@zylin.com>
Date: Tue, 24 Mar 2026 15:53:14 +0100
Subject: [PATCH] fix: guard theme.js against missing browser globals
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

theme.js accesses localStorage and document at module top level,
which crashes in Node.js (used by js_test). Guard both the
initialization and getThemeColors() with typeof checks, falling
back to dark-theme defaults in non-browser environments.

Fixes clock_tree_widget_test failure after ca12251 added
`import { getThemeColors } from './theme.js'` to clock-tree-widget.js.

Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
---
 src/web/src/theme.js | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/src/web/src/theme.js b/src/web/src/theme.js
index 97f36fbb97..4ee2c20fc4 100644
--- a/src/web/src/theme.js
+++ b/src/web/src/theme.js
@@ -3,12 +3,22 @@

 // Theme initialization and helpers.

-const savedTheme = localStorage.getItem('theme')
-    || (matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
-document.documentElement.dataset.theme = savedTheme;
+if (typeof localStorage !== 'undefined') {
+    const savedTheme = localStorage.getItem('theme')
+        || (matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark');
+    document.documentElement.dataset.theme = savedTheme;
+}

 // Read current CSS custom property values for canvas-based widgets.
 export function getThemeColors() {
+    if (typeof document === 'undefined') {
+        return {
+            canvasBg: '#1a1a1a', canvasText: '#ccc', canvasAxis: '#555',
+            canvasLabel: '#aaa', canvasGrid: '#333', canvasTitle: '#fff',
+            fgPrimary: '#e0e0e0', fgMuted: '#888', bgPanel: '#252525',
+            bgMap: '#1a1a1a',
+        };
+    }
     const s = getComputedStyle(document.documentElement);
     const v = (name) => s.getPropertyValue(name).trim();
     return {
--
2.51.0

Suggested Solution

No response

Additional Context

No response

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions