|
|
|
@@ -93,6 +93,20 @@ test("popup loads and reaches the welcome view", async (env) => {
|
|
|
|
|
assert(title === "AutistMask", "unexpected popup title: " + title);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Chrome's own stylesheet for extension pages sets the font on body; see the
|
|
|
|
|
// comment above <body> in src/popup/index.html.
|
|
|
|
|
test("the popup is drawn in the monospace font it declares (#418)", async (env) => {
|
|
|
|
|
const font = await env.page.evaluate(
|
|
|
|
|
() => getComputedStyle(document.body).fontFamily,
|
|
|
|
|
);
|
|
|
|
|
// --font-mono in src/popup/styles/main.css, as the browser writes it out.
|
|
|
|
|
assert(
|
|
|
|
|
font ===
|
|
|
|
|
'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
|
|
|
|
|
"the popup is drawn in " + font + ", not in --font-mono",
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// The empirical half of #182. The manifest change is only a claim about
|
|
|
|
|
// what the CSP permits; this is the observation. Two things have to hold
|
|
|
|
|
// together, and the run covers both: the popup realm compiles WASM (here),
|
|
|
|
@@ -352,6 +366,7 @@ test("the native ETH transaction detail still renders (#151)", async (env) => {
|
|
|
|
|
// the assertions below are as much about that row staying hidden as
|
|
|
|
|
// about the screen coming up.
|
|
|
|
|
env.routeOpts.seedNativeTransfer = true;
|
|
|
|
|
try {
|
|
|
|
|
await env.page.reload();
|
|
|
|
|
await openAddressDetail(env.page);
|
|
|
|
|
|
|
|
|
@@ -368,7 +383,9 @@ test("the native ETH transaction detail still renders (#151)", async (env) => {
|
|
|
|
|
"the native transaction detail shows the wrong hash: " + hash,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const type = (await env.page.locator("#tx-detail-type").innerText()).trim();
|
|
|
|
|
const type = (
|
|
|
|
|
await env.page.locator("#tx-detail-type").innerText()
|
|
|
|
|
).trim();
|
|
|
|
|
assert(
|
|
|
|
|
type === "Native ETH Transfer",
|
|
|
|
|
"the native transaction was classified " + JSON.stringify(type),
|
|
|
|
@@ -393,13 +410,15 @@ test("the native ETH transaction detail still renders (#151)", async (env) => {
|
|
|
|
|
|
|
|
|
|
assert(
|
|
|
|
|
!(await env.page.isVisible("#tx-detail-token-contract-section")),
|
|
|
|
|
"the token contract row is showing on a transfer that has no token " +
|
|
|
|
|
"contract",
|
|
|
|
|
"the token contract row is showing on a transfer that has no " +
|
|
|
|
|
"token contract",
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Back to one seeded transaction for everything after this: the tests
|
|
|
|
|
// below were written against a list holding the token transfer alone.
|
|
|
|
|
} finally {
|
|
|
|
|
// Back to one seeded transaction for everything after this: the
|
|
|
|
|
// tests below were written against a list holding the token transfer
|
|
|
|
|
// alone.
|
|
|
|
|
env.routeOpts.seedNativeTransfer = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("tap-to-copy on the transaction detail screen copies the address (#151)", async (env) => {
|
|
|
|
@@ -1771,6 +1790,7 @@ test("ConfirmTx blocks sending while the fee estimate is pending (#238)", async
|
|
|
|
|
expectToken: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Released by the next test, which watches the estimate land.
|
|
|
|
|
env.routeOpts.holdGasEstimate = true;
|
|
|
|
|
await goToConfirm(env.page, {
|
|
|
|
|
token: "ETH",
|
|
|
|
@@ -1933,6 +1953,7 @@ test("ConfirmTx refuses to send when the fee estimate fails, with its own messag
|
|
|
|
|
);
|
|
|
|
|
env.routeOpts.failGasEstimate = true;
|
|
|
|
|
env.routeOpts.holdGasEstimate = true;
|
|
|
|
|
try {
|
|
|
|
|
await goToConfirm(env.page, {
|
|
|
|
|
token: "ETH",
|
|
|
|
|
balance: FUNDED_ETH_TEXT + " ETH",
|
|
|
|
@@ -1949,10 +1970,10 @@ test("ConfirmTx refuses to send when the fee estimate fails, with its own messag
|
|
|
|
|
await waitForEstimate(env.page);
|
|
|
|
|
|
|
|
|
|
const st = await confirmState(env.page);
|
|
|
|
|
env.routeOpts.failGasEstimate = false;
|
|
|
|
|
assert(
|
|
|
|
|
st.fee === "Unable to estimate",
|
|
|
|
|
"the fee line does not report the failure: " + JSON.stringify(st.fee),
|
|
|
|
|
"the fee line does not report the failure: " +
|
|
|
|
|
JSON.stringify(st.fee),
|
|
|
|
|
);
|
|
|
|
|
assert(
|
|
|
|
|
!st.reserveShown,
|
|
|
|
@@ -1978,10 +1999,15 @@ test("ConfirmTx refuses to send when the fee estimate fails, with its own messag
|
|
|
|
|
st.height +
|
|
|
|
|
"px",
|
|
|
|
|
);
|
|
|
|
|
} finally {
|
|
|
|
|
env.routeOpts.failGasEstimate = false;
|
|
|
|
|
env.routeOpts.holdGasEstimate = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("ConfirmTx drives the ERC-20 path from pending to funded (#238)", async (env) => {
|
|
|
|
|
env.routeOpts.holdGasEstimate = true;
|
|
|
|
|
try {
|
|
|
|
|
await goToConfirm(env.page, {
|
|
|
|
|
token: STUB_TOKEN.address,
|
|
|
|
|
balance: TOKEN_BALANCE_TEXT + " " + STUB_TOKEN.symbol,
|
|
|
|
@@ -1990,9 +2016,12 @@ test("ConfirmTx drives the ERC-20 path from pending to funded (#238)", async (en
|
|
|
|
|
|
|
|
|
|
const pending = await confirmState(env.page);
|
|
|
|
|
env.erc20PendingHeight = pending.height;
|
|
|
|
|
console.log("# confirm-tx ERC-20 view height: " + pending.height + "px");
|
|
|
|
|
console.log(
|
|
|
|
|
"# confirm-tx ERC-20 view height: " + pending.height + "px",
|
|
|
|
|
);
|
|
|
|
|
assert(
|
|
|
|
|
pending.type === "ERC-20 token transfer (" + STUB_TOKEN.symbol + ")",
|
|
|
|
|
pending.type ===
|
|
|
|
|
"ERC-20 token transfer (" + STUB_TOKEN.symbol + ")",
|
|
|
|
|
"unexpected transaction type: " + JSON.stringify(pending.type),
|
|
|
|
|
);
|
|
|
|
|
assert(
|
|
|
|
@@ -2020,7 +2049,10 @@ test("ConfirmTx drives the ERC-20 path from pending to funded (#238)", async (en
|
|
|
|
|
"the ERC-20 fee block does not quote the reserve: " +
|
|
|
|
|
JSON.stringify(st.reserve),
|
|
|
|
|
);
|
|
|
|
|
assert(!st.sendDisabled, "Send is disabled for a funded ERC-20 transfer");
|
|
|
|
|
assert(
|
|
|
|
|
!st.sendDisabled,
|
|
|
|
|
"Send is disabled for a funded ERC-20 transfer",
|
|
|
|
|
);
|
|
|
|
|
assert(
|
|
|
|
|
st.height === pending.height,
|
|
|
|
|
"the ERC-20 view changed height when the estimate landed: " +
|
|
|
|
@@ -2029,6 +2061,9 @@ test("ConfirmTx drives the ERC-20 path from pending to funded (#238)", async (en
|
|
|
|
|
st.height +
|
|
|
|
|
"px",
|
|
|
|
|
);
|
|
|
|
|
} finally {
|
|
|
|
|
env.routeOpts.holdGasEstimate = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("ConfirmTx refuses an ERC-20 send that exceeds the token balance (#238)", async (env) => {
|
|
|
|
@@ -2130,6 +2165,7 @@ test("ConfirmTx reports a failed ERC-20 estimate as unknown, not as a fee proble
|
|
|
|
|
/gas estimation failed/,
|
|
|
|
|
);
|
|
|
|
|
env.routeOpts.failGasEstimate = true;
|
|
|
|
|
try {
|
|
|
|
|
await goToConfirm(env.page, {
|
|
|
|
|
token: STUB_TOKEN.address,
|
|
|
|
|
balance: TOKEN_BALANCE_TEXT + " " + STUB_TOKEN.symbol,
|
|
|
|
@@ -2138,7 +2174,6 @@ test("ConfirmTx reports a failed ERC-20 estimate as unknown, not as a fee proble
|
|
|
|
|
await waitForEstimate(env.page);
|
|
|
|
|
|
|
|
|
|
const st = await confirmState(env.page);
|
|
|
|
|
env.routeOpts.failGasEstimate = false;
|
|
|
|
|
assert(
|
|
|
|
|
st.fee === "Unable to estimate",
|
|
|
|
|
"the ERC-20 fee line does not report the failure: " +
|
|
|
|
@@ -2164,6 +2199,9 @@ test("ConfirmTx reports a failed ERC-20 estimate as unknown, not as a fee proble
|
|
|
|
|
st.height +
|
|
|
|
|
"px",
|
|
|
|
|
);
|
|
|
|
|
} finally {
|
|
|
|
|
env.routeOpts.failGasEstimate = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ------------------------- the popup's own send, end to end (#305)
|
|
|
|
@@ -2220,6 +2258,34 @@ async function fillPasswordAndSend(page) {
|
|
|
|
|
await page.click("#btn-confirm-send");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Back to the address screen from wherever a send stopped, which is where a
|
|
|
|
|
// passing send test leaves the popup for the next one. The success and failure
|
|
|
|
|
// screens are left by their Done button. The wait for a receipt has no button
|
|
|
|
|
// and asks only every ten seconds; a reopened popup resumes it and asks at
|
|
|
|
|
// once, and seedReceipt has the stub node confirm the transaction.
|
|
|
|
|
async function backToAddressAfterSend(env) {
|
|
|
|
|
if (await env.page.isVisible("#view-wait-tx")) {
|
|
|
|
|
env.routeOpts.seedReceipt = true;
|
|
|
|
|
await waitForPersisted(
|
|
|
|
|
env.page,
|
|
|
|
|
"currentView",
|
|
|
|
|
"wait-tx",
|
|
|
|
|
"before closing the popup",
|
|
|
|
|
);
|
|
|
|
|
await env.page.close();
|
|
|
|
|
env.page = await openPopup(env.ctx, env.popupUrl);
|
|
|
|
|
await visible(env.page, "#view-success-tx");
|
|
|
|
|
env.routeOpts.seedReceipt = false;
|
|
|
|
|
}
|
|
|
|
|
for (const done of ["#btn-success-tx-done", "#btn-error-tx-done"]) {
|
|
|
|
|
if (await env.page.isVisible(done)) {
|
|
|
|
|
await env.page.click(done);
|
|
|
|
|
await visible(env.page, "#view-address");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
await backToAddress(env.page);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function goToTokenConfirm(env) {
|
|
|
|
|
await goToConfirm(env.page, {
|
|
|
|
|
token: STUB_TOKEN.address,
|
|
|
|
@@ -2240,13 +2306,14 @@ test("the popup's own ERC-20 send broadcasts the amount it displayed (#305)", as
|
|
|
|
|
// The previous test left the ETH balance at the fee-only fixture, which
|
|
|
|
|
// blocks sending outright; this one has to be able to press Send.
|
|
|
|
|
env.routeOpts.ethBalanceWei = toHexWei(FUNDED_ETH_WEI);
|
|
|
|
|
// Confirm the transaction once it is broadcast, so the wait screen
|
|
|
|
|
// resolves to the success view instead of polling for the rest of the run.
|
|
|
|
|
env.routeOpts.seedReceipt = true;
|
|
|
|
|
try {
|
|
|
|
|
await settleOnMain(env, { ethWei: FUNDED_ETH_WEI, expectToken: true });
|
|
|
|
|
const shown = await goToTokenConfirm(env);
|
|
|
|
|
|
|
|
|
|
const before = env.routeOpts.broadcastTransactions.length;
|
|
|
|
|
// Confirm the transaction once it is broadcast, so the wait screen
|
|
|
|
|
// resolves to the success view instead of polling for the rest of the run.
|
|
|
|
|
env.routeOpts.seedReceipt = true;
|
|
|
|
|
await fillPasswordAndSend(env.page);
|
|
|
|
|
await visible(env.page, "#view-wait-tx", 60000);
|
|
|
|
|
|
|
|
|
@@ -2260,9 +2327,9 @@ test("the popup's own ERC-20 send broadcasts the amount it displayed (#305)", as
|
|
|
|
|
broadcast[broadcast.length - 1],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// The measurement, printed on every run: the amount the user read, and
|
|
|
|
|
// what the signed bytes mean at each of the two candidate scales. Under
|
|
|
|
|
// the defect these three lines disagree.
|
|
|
|
|
// The measurement, printed on every run: the amount the user read,
|
|
|
|
|
// and what the signed bytes mean at each of the two candidate scales.
|
|
|
|
|
// Under the defect these three lines disagree.
|
|
|
|
|
console.log(
|
|
|
|
|
"# erc-20 send artifact: displayed=" +
|
|
|
|
|
JSON.stringify(shown) +
|
|
|
|
@@ -2318,29 +2385,38 @@ test("the popup's own ERC-20 send broadcasts the amount it displayed (#305)", as
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await visible(env.page, "#view-success-tx", 60000);
|
|
|
|
|
await env.page.click("#btn-success-tx-done");
|
|
|
|
|
await visible(env.page, "#view-address");
|
|
|
|
|
} finally {
|
|
|
|
|
env.routeOpts.seedReceipt = false;
|
|
|
|
|
await backToAddressAfterSend(env);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test("a token that lies about decimals() at signing time broadcasts nothing (#305)", async (env) => {
|
|
|
|
|
try {
|
|
|
|
|
const shown = await goToTokenConfirm(env);
|
|
|
|
|
|
|
|
|
|
// Only now, with the screen already built and its estimate already taken
|
|
|
|
|
// at the explorer's scale, does the contract start answering differently.
|
|
|
|
|
// This is the whole shape of the defect: a value read at signing time that
|
|
|
|
|
// nothing on screen was ever derived from.
|
|
|
|
|
// Only now, with the screen already built and its estimate already
|
|
|
|
|
// taken at the explorer's scale, does the contract start answering
|
|
|
|
|
// differently. This is the whole shape of the defect: a value read at
|
|
|
|
|
// signing time that nothing on screen was ever derived from.
|
|
|
|
|
env.routeOpts.tokenDecimalsOverride = LYING_DECIMALS;
|
|
|
|
|
const before = env.routeOpts.broadcastTransactions.length;
|
|
|
|
|
await fillPasswordAndSend(env.page);
|
|
|
|
|
await visible(env.page, "#view-error-tx", 60000);
|
|
|
|
|
env.routeOpts.tokenDecimalsOverride = null;
|
|
|
|
|
|
|
|
|
|
// The count is asserted as soon as the send has ended either way, and
|
|
|
|
|
// the screen only after it: a transfer that got through shows as the
|
|
|
|
|
// wait for its receipt, never as the failure screen expected here.
|
|
|
|
|
await visible(
|
|
|
|
|
env.page,
|
|
|
|
|
"#view-wait-tx:not(.hidden), #view-error-tx:not(.hidden)",
|
|
|
|
|
60000,
|
|
|
|
|
);
|
|
|
|
|
assert(
|
|
|
|
|
env.routeOpts.broadcastTransactions.length === before,
|
|
|
|
|
"a transfer encoded against a contract that contradicts the " +
|
|
|
|
|
"confirmation screen still reached the RPC (#305)",
|
|
|
|
|
);
|
|
|
|
|
await visible(env.page, "#view-error-tx");
|
|
|
|
|
|
|
|
|
|
const message = (
|
|
|
|
|
await env.page.locator("#error-tx-message").innerText()
|
|
|
|
@@ -2363,9 +2439,10 @@ test("a token that lies about decimals() at signing time broadcasts nothing (#30
|
|
|
|
|
/^[A-Z].*\.$/s.test(message),
|
|
|
|
|
"the refusal is not a full sentence: " + JSON.stringify(message),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await env.page.click("#btn-error-tx-done");
|
|
|
|
|
await visible(env.page, "#view-address");
|
|
|
|
|
} finally {
|
|
|
|
|
env.routeOpts.tokenDecimalsOverride = null;
|
|
|
|
|
await backToAddressAfterSend(env);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------- hostile token symbol (#307)
|
|
|
|
@@ -2437,6 +2514,7 @@ test("a token whose symbol() returns markup renders as text (#307)", async (env)
|
|
|
|
|
"# stub token symbol() now returns: " + JSON.stringify(HOSTILE_SYMBOL),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Close and reopen so the refresh that runs on open fetches balances
|
|
|
|
|
// with the hostile symbol in them.
|
|
|
|
|
await reopenPopup(env, "address");
|
|
|
|
@@ -2499,12 +2577,15 @@ test("a token whose symbol() returns markup renders as text (#307)", async (env)
|
|
|
|
|
);
|
|
|
|
|
assert(
|
|
|
|
|
!st.rowText.includes("z-index"),
|
|
|
|
|
"the uncapped symbol reached the screen: " + JSON.stringify(st.rowText),
|
|
|
|
|
"the uncapped symbol reached the screen: " +
|
|
|
|
|
JSON.stringify(st.rowText),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Put the fixture back before the next test reads it, and let the
|
|
|
|
|
// stored balances be rewritten with the honest symbol.
|
|
|
|
|
} finally {
|
|
|
|
|
// Put the fixture back before the next test reads it.
|
|
|
|
|
env.routeOpts.tokenSymbolOverride = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Let the stored balances be rewritten with the honest symbol.
|
|
|
|
|
await reopenPopup(env, "main");
|
|
|
|
|
await env.page.waitForFunction(
|
|
|
|
|
(addr) => {
|
|
|
|
|