diff --git a/README.md b/README.md index 7a049bd..d86e7e1 100644 --- a/README.md +++ b/README.md @@ -680,8 +680,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 @@ -692,7 +692,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 608523b..785095c 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: README Screen Map rebuilt from the code — every screen, element and transition re-verified against `src/popup/` ([#164](https://git.eeqj.de/sneak/AutistMask/issues/164)). 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); + }); +});