Files
AutistMask/tests/timestampDisplay.test.js
T
sneak af757eb552
check / check (push) Failing after 2s
e2e / e2e-chrome (push) Failing after 2s
e2e / e2e-firefox (push) Failing after 2s
chore: remove dead exports and share copied view helpers (closes #168)
AddressDetail and AddressToken defined their own isoDate() and
timeAgo(), hiding the shared pair in helpers.js, so a fix there would
not have reached them. The copies were identical and are deleted;
blockieHtml() and tokenLabel(), each defined twice, move to helpers.js.
A new test shows the history rows and the transaction detail view
write the time with the shared pair.

Deleted as never called: explorerLink(), ETHEREUM_SEPOLIA_CHAIN_ID,
getWalletValue() and getTotalValue() with their tests. Home's "Total:"
is the active address's total, as README.md already says.
addressColor() and etherscanAddressUrl() are no longer exported.

Model: opus-5-5
2026-10-05 06:44:16 +00:00

160 lines
5.2 KiB
JavaScript

// A transaction's time is written by isoDate() and timeAgo() in
// src/popup/views/helpers.js on every screen that shows one (README, Display
// Consistency; https://git.eeqj.de/sneak/AutistMask/issues/168). AddressDetail
// and AddressToken used to define their own copies, so a fix to the shared pair
// would not have reached them.
//
// The pair is replaced before the views are loaded, because a view takes it
// when it loads. A view that writes the time with a copy of its own shows the
// real time instead of the replacement.
//
// Driven against a minimal DOM stub in the shape
// tests/contractCreation.test.js uses.
jest.mock("../src/shared/log", () => ({
log: {
debugf: () => {},
infof: () => {},
warnf: () => {},
errorf: () => {},
},
// The transaction detail view fetches on-chain details after drawing; an
// answer that is not ok leaves the drawn lines as they are.
debugFetch: async () => ({ ok: false }),
setRuntimeDebug: () => {},
isDebug: () => false,
}));
// The history lists ask the explorer for their transactions and resolve ENS
// names for them; here the explorer answers with mockHistory and no name
// resolves.
let mockHistory = [];
jest.mock("../src/shared/transactions", () => ({
...jest.requireActual("../src/shared/transactions"),
fetchRecentTransactions: async () => mockHistory,
}));
jest.mock("../src/shared/ens", () => ({
...jest.requireActual("../src/shared/ens"),
resolveEnsNames: async () => new Map(),
}));
globalThis.chrome = {
storage: { local: { get: async () => ({}), set: async () => {} } },
};
const helpers = require("../src/popup/views/helpers");
jest.spyOn(helpers, "isoDate").mockReturnValue("SHARED-ISO-DATE");
jest.spyOn(helpers, "timeAgo").mockReturnValue("SHARED-TIME-AGO");
const { state } = require("../src/shared/state");
const addressDetail = require("../src/popup/views/addressDetail");
const addressToken = require("../src/popup/views/addressToken");
const transactionDetail = require("../src/popup/views/transactionDetail");
const FROM = "0x0000000000000000000000000000000000000a11";
const RECIPIENT = "0x66133E8ea0f5D1d612D2502a968757D1048c214a";
function makeElement(id) {
const el = {
id,
textContent: "",
value: "",
innerHTML: "",
style: {},
dataset: {},
classList: {
add: () => {},
remove: () => {},
contains: () => false,
toggle: () => false,
},
addEventListener: () => {},
querySelectorAll: () => [],
appendChild: () => {},
};
// Views reach for .parentElement to hide whole sections.
Object.defineProperty(el, "parentElement", {
get: () => node(id + "-parent"),
});
return el;
}
function makeDocument() {
const els = new Map();
return {
getElementById(id) {
// The debug banner is created on demand by helpers.js; absent
// is the state a non-debug, non-testnet popup is in.
if (id === "debug-banner") return null;
if (!els.has(id)) els.set(id, makeElement(id));
return els.get(id);
},
createElement: () => makeElement("created"),
body: { prepend: () => {} },
};
}
function node(id) {
return globalThis.document.getElementById(id);
}
// A transaction FROM sent, as the history lists hold it.
function historyTx() {
return {
hash: "0x85215772ed26ea8b39c2b3b18779030487efbe0b5fd7e882592b2f62b837be84",
from: FROM,
to: RECIPIENT,
value: "0.0000",
exactValue: "0.0",
rawAmount: "0",
rawUnit: "wei",
symbol: "ETH",
timestamp: 1790000000,
isError: false,
directionLabel: "Sent",
direction: "sent",
contractAddress: null,
};
}
beforeEach(() => {
globalThis.document = makeDocument();
globalThis.window = { location: { search: "" } };
state.wallets = [
{
name: "Main",
type: "key",
addresses: [{ address: FROM, balance: "0.0000" }],
},
];
state.trackedTokens = [];
state.viewData = {};
state.viewStack = [];
state.currentView = null;
state.selectedWallet = 0;
state.selectedAddress = 0;
state.selectedToken = "ETH";
});
describe.each([
["AddressDetail", "tx-list", () => addressDetail.show()],
["AddressToken", "address-token-tx-list", () => addressToken.show()],
])("a transaction history row on %s", (_name, listId, open) => {
test("shows the time written by the shared isoDate() and timeAgo()", async () => {
mockHistory = [historyTx()];
open();
// The list is drawn once the history has been fetched.
await new Promise((resolve) => setTimeout(resolve, 0));
const html = node(listId).innerHTML;
expect(html).toContain('title="SHARED-ISO-DATE"');
expect(html).toContain(">SHARED-TIME-AGO<");
});
});
test("the transaction detail view shows the time written by the shared isoDate() and timeAgo()", () => {
transactionDetail.show(historyTx());
const html = node("tx-detail-time").innerHTML;
expect(html).toContain("SHARED-ISO-DATE");
expect(html).toContain("(SHARED-TIME-AGO)");
});