// 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)"); });