From 585664f9d20fd83ef142d880e19c898c64ae5f53 Mon Sep 17 00:00:00 2001 From: clawbot Date: Tue, 11 Aug 2026 12:37:08 +0000 Subject: [PATCH] fix: move the UTC Timestamps checkbox into the Display well (closes #212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `settings-utc-timestamps` checkbox sat inside the Token Spam Protection well, immediately after the dust-threshold input. It is a display preference and has nothing to do with spam filtering, so it now renders in the Display well next to the theme selector. Markup move only. The checkbox is wired by id — `$("settings-utc-timestamps")` in `src/popup/views/settings.js`, where `$` is `document.getElementById` — for both the initial `checked` assignment and the `change` handler, so nothing in the wiring depends on the element's parent or siblings. `state.utcTimestamps`, `saveState`/`loadState` and the `helpers.js` formatting path are untouched. Both Settings descriptions follow the markup: the `README.md` Screen Map and the `docs/README.md` Settings list each now name the UTC control under Display and no longer under Token Spam Protection. `tests/settingsUtcTimestamps.test.js` pins both halves down: the checkbox appears exactly once and inside the Display well rather than the spam well, and the setting still round-trips through storage. --- README.md | 5 +- TODO.md | 3 + docs/README.md | 8 +- src/popup/index.html | 12 +-- tests/settingsUtcTimestamps.test.js | 111 ++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 13 deletions(-) create mode 100644 tests/settingsUtcTimestamps.test.js diff --git a/README.md b/README.md index b8df696..5fe1597 100644 --- a/README.md +++ b/README.md @@ -695,8 +695,8 @@ screen, including ExportPrivKey, falls back to Home. `[x]` delete button, plus a "+ Add wallet" button - Tracked Tokens: one row per tracked token with an `[x]` remove button, plus a "+ Add token" button - - Display: "Show tracked tokens with zero balance" checkbox and a Theme - selector (System / Light / Dark) + - Display: "Show tracked tokens with zero balance" checkbox, "UTC + Timestamps" checkbox, and a Theme selector (System / Light / Dark) - Network: network selector (Ethereum Mainnet / Sepolia Testnet); switching resets the RPC and Blockscout endpoints to that network's defaults - Ethereum RPC: endpoint URL input + "Save" button (validated against @@ -707,7 +707,6 @@ screen, including ExportPrivKey, falls back to Home. - "Hide tokens with fewer than 1,000 holders" checkbox - "Hide transactions from detected fraud contracts" checkbox - "Hide dust transactions below N gwei" checkbox + threshold input - - "UTC Timestamps" checkbox - Allowed Sites: list with remove buttons - Denied Sites: list with remove buttons - About: project link, license, author, version, release date, and the diff --git a/TODO.md b/TODO.md index d202884..c898966 100644 --- a/TODO.md +++ b/TODO.md @@ -44,6 +44,9 @@ undefined identifiers, which is how # Completed Steps +- 2026-08-11: UTC Timestamps checkbox moved from the Token Spam Protection well + into Display, next to the theme selector + ([#212](https://git.eeqj.de/sneak/AutistMask/issues/212)). - 2026-08-11: Three `README.md` claims corrected against the code — blocklist attribution, token-display rule, navigation model ([#213](https://git.eeqj.de/sneak/AutistMask/issues/213)). diff --git a/docs/README.md b/docs/README.md index 7244b8e..08f244a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -359,16 +359,16 @@ Click the gear icon on the home screen to access settings: - **Wallets**: Your wallets, and "+ Add wallet". - **Tracked Tokens**: The ERC-20 tokens tracked across all addresses, and "+ Add token". -- **Display**: Toggle whether tracked tokens with zero balance are shown, and - choose the theme (System, Light, or Dark). +- **Display**: Toggle whether tracked tokens with zero balance are shown, switch + timestamps to UTC, and choose the theme (System, Light, or Dark). - **Network**: Switch between Ethereum Mainnet and Sepolia Testnet. Switching resets the RPC and Blockscout endpoints to that network's defaults. - **Ethereum RPC**: Change the Ethereum node endpoint. Default is a public RPC. You can use your own node for maximum privacy. - **Blockscout API**: Change the Blockscout instance used for token balances and transaction history. You can use a self-hosted instance. -- **Token Spam Protection**: Toggle individual scam filters, set the dust - transaction threshold, and switch timestamps to UTC. +- **Token Spam Protection**: Toggle individual scam filters and set the dust + transaction threshold. - **Allowed Sites / Denied Sites**: View and manage web3 site permissions. - **About**: License, author, version, release date, and a link to the commit this build came from. diff --git a/src/popup/index.html b/src/popup/index.html index a38e901..d05f91e 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -869,6 +869,12 @@ /> Show tracked tokens with zero balance +
- UTC Timestamps -
diff --git a/tests/settingsUtcTimestamps.test.js b/tests/settingsUtcTimestamps.test.js new file mode 100644 index 0000000..91526db --- /dev/null +++ b/tests/settingsUtcTimestamps.test.js @@ -0,0 +1,111 @@ +// Tests for the UTC Timestamps setting. +// +// The checkbox was moved out of the Token Spam Protection well and into the +// Display well next to the theme selector. It is wired by id through the $() +// helper, so the move cannot break the handler — but nothing in the suite said +// so. These tests pin both halves down: the markup lives in Display and +// nowhere else, and the value still round-trips through storage. + +const fs = require("fs"); +const path = require("path"); + +const POPUP_HTML = fs.readFileSync( + path.join(__dirname, "..", "src", "popup", "index.html"), + "utf8", +); + +// The body of one `
` well, selected by its heading. +function wellWithHeading(html, heading) { + const headingIndex = html.indexOf( + '

' + heading + "

", + ); + expect(headingIndex).toBeGreaterThan(-1); + const start = html.lastIndexOf('
{ + test("the checkbox appears exactly once in the popup markup", () => { + const matches = POPUP_HTML.match(/id="settings-utc-timestamps"/g); + expect(matches).toHaveLength(1); + }); + + test("it renders in the Display well, alongside the theme selector", () => { + const display = wellWithHeading(POPUP_HTML, "Display"); + + expect(display).toContain('id="settings-utc-timestamps"'); + expect(display).toContain('id="settings-theme"'); + }); + + test("it does not render in the Token Spam Protection well", () => { + const spam = wellWithHeading(POPUP_HTML, "Token Spam Protection"); + + expect(spam).not.toContain('id="settings-utc-timestamps"'); + // The filters that do belong there are untouched. + expect(spam).toContain('id="settings-hide-low-holders"'); + expect(spam).toContain('id="settings-hide-fraud-contracts"'); + expect(spam).toContain('id="settings-hide-dust"'); + expect(spam).toContain('id="settings-dust-threshold"'); + }); +}); + +describe("the UTC Timestamps setting round-trips through storage", () => { + let store; + + function loadStateModule() { + store = {}; + global.chrome = { + storage: { + local: { + get: async (key) => + key in store ? { [key]: store[key] } : {}, + set: async (obj) => Object.assign(store, obj), + }, + }, + }; + jest.resetModules(); + return require("../src/shared/state"); + } + + afterEach(() => { + delete global.chrome; + }); + + test("defaults to off with nothing persisted", async () => { + const { state, loadState } = loadStateModule(); + + await loadState(); + + expect(state.utcTimestamps).toBe(false); + }); + + test("an enabled checkbox is persisted and read back", async () => { + const first = loadStateModule(); + + // What the change handler in views/settings.js does. + first.state.utcTimestamps = true; + await first.saveState(); + expect(store.autistmask.utcTimestamps).toBe(true); + + // A fresh popup load sees it. + jest.resetModules(); + const second = require("../src/shared/state"); + expect(second.state.utcTimestamps).toBe(false); + await second.loadState(); + expect(second.state.utcTimestamps).toBe(true); + }); + + test("turning it back off is persisted too", async () => { + const { state, saveState, loadState } = loadStateModule(); + + state.utcTimestamps = true; + await saveState(); + state.utcTimestamps = false; + await saveState(); + + state.utcTimestamps = true; + await loadState(); + expect(state.utcTimestamps).toBe(false); + }); +});