Compare commits
2
Commits
6c3c0ca660
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e48308c5b6 | ||
|
|
aa10a2a5f5 |
@@ -362,6 +362,14 @@ are outside `make check`. Neither opens the popup from the toolbar button: both
|
|||||||
load its page in an ordinary tab, so what the toolbar popup itself adds, its
|
load its page in an ordinary tab, so what the toolbar popup itself adds, its
|
||||||
size and its closing when it loses focus, is covered by neither.
|
size and its closing when it loses focus, is covered by neither.
|
||||||
|
|
||||||
|
Each run builds its image under a tag of its own, `autistmask-e2e-chrome:` or
|
||||||
|
`autistmask-e2e-firefox:` followed by twelve random hex digits, so two clones
|
||||||
|
can run the same suite at once without one build taking the other's image away.
|
||||||
|
The run removes that image, and its container if one is still running, after the
|
||||||
|
suite passes or fails, on Ctrl-C, on `kill`, and when the terminal or SSH
|
||||||
|
session it runs in closes. A run killed outright (`kill -9`) cannot clean up:
|
||||||
|
its image stays under its tag until removed by hand with `docker image rm`.
|
||||||
|
|
||||||
### Chrome (`make test-e2e`)
|
### Chrome (`make test-e2e`)
|
||||||
|
|
||||||
`make test-e2e` builds `dist/chrome/` and drives the **real popup in a real
|
`make test-e2e` builds `dist/chrome/` and drives the **real popup in a real
|
||||||
@@ -1544,8 +1552,14 @@ view would leave a wallet one click from deletion.
|
|||||||
- **Transitions**:
|
- **Transitions**:
|
||||||
- "Import" with a valid entry and a matching password of at least 12
|
- "Import" with a valid entry and a matching password of at least 12
|
||||||
characters → creates the wallet, clears the navigation stack, and →
|
characters → creates the wallet, clears the navigation stack, and →
|
||||||
**Home**. The phrase and xprv modes then scan for further used addresses
|
**Home**. When no address is active yet, as on the first wallet, the new
|
||||||
and report the count as a flash message.
|
wallet's first address becomes the active one. The phrase and xprv modes
|
||||||
|
then scan for further used addresses and report the count as a flash
|
||||||
|
message. When the scan finds two or more used addresses, they replace the
|
||||||
|
wallet's list, which then holds the first address only if it was used. If
|
||||||
|
the first address was active and is not in the new list, the new list's
|
||||||
|
first address becomes the active one and `AUTISTMASK_ACTIVE_CHANGED` is
|
||||||
|
broadcast.
|
||||||
- "Import" with a missing, short or mismatched password → full-sentence
|
- "Import" with a missing, short or mismatched password → full-sentence
|
||||||
error on the error line, no screen change
|
error on the error line, no screen change
|
||||||
- "Import" with an invalid entry or a duplicate wallet or address → flash
|
- "Import" with an invalid entry or a duplicate wallet or address → flash
|
||||||
|
|||||||
+30
-17
@@ -22,13 +22,29 @@ set -eu
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
||||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
||||||
|
|
||||||
IMAGE="$("$SCRIPT_DIR/projectname")-e2e-chrome"
|
# Each run builds, runs and then removes an image under a tag of its own.
|
||||||
|
# Under one tag shared by every run, a second clone's build moves the tag
|
||||||
IIDFILE=""
|
# off this run's image, and the image store can then delete that untagged
|
||||||
|
# image before or while this run uses it. The tag is random rather than
|
||||||
|
# $$ because CI jobs share one docker daemon, but each job's container
|
||||||
|
# numbers its processes from scratch.
|
||||||
|
SUFFIX="$(od -An -N6 -tx1 /dev/urandom | tr -d ' \n')"
|
||||||
|
NAME="$("$SCRIPT_DIR/projectname")-e2e-chrome"
|
||||||
|
IMAGE="$NAME:$SUFFIX"
|
||||||
|
# Each docker run below uses this container name: docker run --rm returns
|
||||||
|
# only once its container is removed, so no run meets the one before it.
|
||||||
|
CONTAINER="$NAME-$SUFFIX"
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if [ -n "$IIDFILE" ]; then
|
# After a third Ctrl-C docker's client exits whether or not the
|
||||||
rm -f "$IIDFILE"
|
# container has stopped, and a container still running keeps the image
|
||||||
|
# in use.
|
||||||
|
if docker container inspect "$CONTAINER" >/dev/null 2>&1; then
|
||||||
|
docker container rm -f "$CONTAINER" >/dev/null
|
||||||
|
fi
|
||||||
|
# No image to remove when the build failed or was interrupted.
|
||||||
|
if docker image inspect "$IMAGE" >/dev/null 2>&1; then
|
||||||
|
docker image rm "$IMAGE" >/dev/null
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,22 +56,16 @@ main() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
IIDFILE="$(mktemp)"
|
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
trap 'cleanup; exit 130' INT TERM
|
# HUP: the terminal or SSH session running the suite closed.
|
||||||
|
trap 'cleanup; exit 130' HUP INT TERM
|
||||||
|
|
||||||
echo "Building the Chrome e2e image (extension included)..."
|
echo "Building the Chrome e2e image (extension included)..."
|
||||||
# --no-cache: the image build runs make build and its checks, and a
|
# --no-cache: the image build runs make build and its checks, and a
|
||||||
# cached layer is a check that did not run.
|
# cached layer is a check that did not run.
|
||||||
docker build --no-cache --iidfile "$IIDFILE" -t "$IMAGE" \
|
docker build --no-cache -t "$IMAGE" -f tests/e2e/Dockerfile .
|
||||||
-f tests/e2e/Dockerfile .
|
|
||||||
|
|
||||||
echo "Running e2e suite in the pinned Playwright container..."
|
echo "Running e2e suite in the pinned Playwright container..."
|
||||||
# The image is run by ID, not by tag: where two clones of this repo run
|
|
||||||
# the suite at once, the other build can move the tag between this
|
|
||||||
# build and this run, and the suite would then silently test the other
|
|
||||||
# checkout.
|
|
||||||
#
|
|
||||||
# --ipc=host: Chromium's shared-memory needs more than the default
|
# --ipc=host: Chromium's shared-memory needs more than the default
|
||||||
# 64MB /dev/shm or renderers crash.
|
# 64MB /dev/shm or renderers crash.
|
||||||
# HOME=/tmp: the image's root home is not a reliable place for the
|
# HOME=/tmp: the image's root home is not a reliable place for the
|
||||||
@@ -75,11 +85,12 @@ main() {
|
|||||||
# probe. The image is pinned by digest, so this can only ever bite
|
# probe. The image is pinned by digest, so this can only ever bite
|
||||||
# on a deliberate bump.
|
# on a deliberate bump.
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
|
--name "$CONTAINER" \
|
||||||
--ipc=host \
|
--ipc=host \
|
||||||
-e HOME=/tmp \
|
-e HOME=/tmp \
|
||||||
-e PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 \
|
-e PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 \
|
||||||
-e "E2E_TRACE_NETWORK=${E2E_TRACE_NETWORK:-0}" \
|
-e "E2E_TRACE_NETWORK=${E2E_TRACE_NETWORK:-0}" \
|
||||||
"$(cat "$IIDFILE")" \
|
"$IMAGE" \
|
||||||
node tests/e2e/run.js
|
node tests/e2e/run.js
|
||||||
|
|
||||||
# Where chrome.storage.local lives, and what moves it: two unpacked loads
|
# Where chrome.storage.local lives, and what moves it: two unpacked loads
|
||||||
@@ -93,9 +104,10 @@ main() {
|
|||||||
# --host-resolver-rules=MAP * ~NOTFOUND so nothing it does can leave.
|
# --host-resolver-rules=MAP * ~NOTFOUND so nothing it does can leave.
|
||||||
echo "Running the extension-id and storage-partition observations..."
|
echo "Running the extension-id and storage-partition observations..."
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
|
--name "$CONTAINER" \
|
||||||
--ipc=host \
|
--ipc=host \
|
||||||
-e HOME=/tmp \
|
-e HOME=/tmp \
|
||||||
"$(cat "$IIDFILE")" \
|
"$IMAGE" \
|
||||||
node tests/e2e/storagePartition.js
|
node tests/e2e/storagePartition.js
|
||||||
|
|
||||||
# Approvals left open for seven minutes in minimized windows, past the
|
# Approvals left open for seven minutes in minimized windows, past the
|
||||||
@@ -105,9 +117,10 @@ main() {
|
|||||||
# attached to. It reaches nothing but its own loopback server.
|
# attached to. It reaches nothing but its own loopback server.
|
||||||
echo "Running the approvals left open for seven minutes..."
|
echo "Running the approvals left open for seven minutes..."
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
|
--name "$CONTAINER" \
|
||||||
--ipc=host \
|
--ipc=host \
|
||||||
-e HOME=/tmp \
|
-e HOME=/tmp \
|
||||||
"$(cat "$IIDFILE")" \
|
"$IMAGE" \
|
||||||
node tests/e2e/slowApproval.js
|
node tests/e2e/slowApproval.js
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-16
@@ -21,13 +21,23 @@ set -eu
|
|||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
|
||||||
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
|
||||||
|
|
||||||
IMAGE="$("$SCRIPT_DIR/projectname")-e2e-firefox"
|
# A tag and a container name of this run's own, as in script/test-e2e,
|
||||||
|
# which says why.
|
||||||
IIDFILE=""
|
SUFFIX="$(od -An -N6 -tx1 /dev/urandom | tr -d ' \n')"
|
||||||
|
NAME="$("$SCRIPT_DIR/projectname")-e2e-firefox"
|
||||||
|
IMAGE="$NAME:$SUFFIX"
|
||||||
|
CONTAINER="$NAME-$SUFFIX"
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if [ -n "$IIDFILE" ]; then
|
# After a third Ctrl-C docker's client exits whether or not the
|
||||||
rm -f "$IIDFILE"
|
# container has stopped, and a container still running keeps the image
|
||||||
|
# in use.
|
||||||
|
if docker container inspect "$CONTAINER" >/dev/null 2>&1; then
|
||||||
|
docker container rm -f "$CONTAINER" >/dev/null
|
||||||
|
fi
|
||||||
|
# No image to remove when the build failed or was interrupted.
|
||||||
|
if docker image inspect "$IMAGE" >/dev/null 2>&1; then
|
||||||
|
docker image rm "$IMAGE" >/dev/null
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,22 +49,16 @@ main() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
IIDFILE="$(mktemp)"
|
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT
|
||||||
trap 'cleanup; exit 130' INT TERM
|
# HUP: the terminal or SSH session running the suite closed.
|
||||||
|
trap 'cleanup; exit 130' HUP INT TERM
|
||||||
|
|
||||||
echo "Building the pinned Firefox e2e image (extension included)..."
|
echo "Building the pinned Firefox e2e image (extension included)..."
|
||||||
# --no-cache: the image build runs make build and its checks, and a
|
# --no-cache: the image build runs make build and its checks, and a
|
||||||
# cached layer is a check that did not run.
|
# cached layer is a check that did not run.
|
||||||
docker build --no-cache --iidfile "$IIDFILE" -t "$IMAGE" \
|
docker build --no-cache -t "$IMAGE" -f tests/e2e/firefox/Dockerfile .
|
||||||
-f tests/e2e/firefox/Dockerfile .
|
|
||||||
|
|
||||||
echo "Running the Firefox e2e suite..."
|
echo "Running the Firefox e2e suite..."
|
||||||
# The image is run by ID, not by tag: where two clones of this repo run
|
|
||||||
# the suite at once, the other build can move the tag between this
|
|
||||||
# build and this run, and the suite would then silently test the other
|
|
||||||
# checkout.
|
|
||||||
#
|
|
||||||
# --shm-size=1g: Firefox needs more than the default 64MB /dev/shm.
|
# --shm-size=1g: Firefox needs more than the default 64MB /dev/shm.
|
||||||
# --network none: the suite stubs nothing, so this is what keeps the
|
# --network none: the suite stubs nothing, so this is what keeps the
|
||||||
# run offline and deterministic. The extension swallows its own
|
# run offline and deterministic. The extension swallows its own
|
||||||
@@ -69,10 +73,11 @@ main() {
|
|||||||
# "CanCreateUserNamespace() clone() failure: EPERM" on startup here;
|
# "CanCreateUserNamespace() clone() failure: EPERM" on startup here;
|
||||||
# it is cosmetic and headless Firefox runs fine without it.
|
# it is cosmetic and headless Firefox runs fine without it.
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
|
--name "$CONTAINER" \
|
||||||
--shm-size=1g \
|
--shm-size=1g \
|
||||||
--network none \
|
--network none \
|
||||||
-e HOME=/tmp \
|
-e HOME=/tmp \
|
||||||
"$(cat "$IIDFILE")" \
|
"$IMAGE" \
|
||||||
node tests/e2e/firefox/run.js dist/firefox
|
node tests/e2e/firefox/run.js dist/firefox
|
||||||
|
|
||||||
# The install/uninstall/re-install property, against the packaged XPI
|
# The install/uninstall/re-install property, against the packaged XPI
|
||||||
@@ -82,10 +87,11 @@ main() {
|
|||||||
# and the suite above shares one session throughout.
|
# and the suite above shares one session throughout.
|
||||||
echo "Running the Firefox re-install suite against the packaged XPI..."
|
echo "Running the Firefox re-install suite against the packaged XPI..."
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
|
--name "$CONTAINER" \
|
||||||
--shm-size=1g \
|
--shm-size=1g \
|
||||||
--network none \
|
--network none \
|
||||||
-e HOME=/tmp \
|
-e HOME=/tmp \
|
||||||
"$(cat "$IIDFILE")" \
|
"$IMAGE" \
|
||||||
node tests/e2e/firefox/reinstall.js
|
node tests/e2e/firefox/reinstall.js
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const {
|
|||||||
const { encryptWithPassword } = require("../../shared/vault");
|
const { encryptWithPassword } = require("../../shared/vault");
|
||||||
const { state, saveState } = require("../../shared/state");
|
const { state, saveState } = require("../../shared/state");
|
||||||
const { scanForAddresses } = require("../../shared/balances");
|
const { scanForAddresses } = require("../../shared/balances");
|
||||||
|
const { notify } = require("../../shared/browserApi");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an address already exists in ANY wallet (hd, xprv, or key).
|
* Check if an address already exists in ANY wallet (hd, xprv, or key).
|
||||||
@@ -194,6 +195,9 @@ async function importMnemonic(ctx) {
|
|||||||
};
|
};
|
||||||
state.wallets.push(wallet);
|
state.wallets.push(wallet);
|
||||||
state.hasWallet = true;
|
state.hasWallet = true;
|
||||||
|
if (state.activeAddress === null) {
|
||||||
|
state.activeAddress = firstAddress;
|
||||||
|
}
|
||||||
await saveState();
|
await saveState();
|
||||||
clearViewStack();
|
clearViewStack();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
@@ -215,8 +219,16 @@ async function importMnemonic(ctx) {
|
|||||||
tokenBalances: [],
|
tokenBalances: [],
|
||||||
}));
|
}));
|
||||||
wallet.nextIndex = scan.nextIndex;
|
wallet.nextIndex = scan.nextIndex;
|
||||||
|
// The new list leaves out the first address when the scan found it
|
||||||
|
// unused. If it was the active one, the new list's first takes over,
|
||||||
|
// and the background is told as when the user picks one on Home.
|
||||||
|
const activeMoved =
|
||||||
|
state.activeAddress === firstAddress &&
|
||||||
|
wallet.addresses[0].address !== firstAddress;
|
||||||
|
if (activeMoved) state.activeAddress = wallet.addresses[0].address;
|
||||||
await saveState();
|
await saveState();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
|
if (activeMoved) notify({ type: "AUTISTMASK_ACTIVE_CHANGED" });
|
||||||
showFlash("Found " + scan.addresses.length + " addresses.");
|
showFlash("Found " + scan.addresses.length + " addresses.");
|
||||||
} else {
|
} else {
|
||||||
showFlash("Ready.", 1000);
|
showFlash("Ready.", 1000);
|
||||||
@@ -254,6 +266,9 @@ async function importPrivateKey(ctx) {
|
|||||||
addresses: [{ address: addr, balance: "0.0000", tokenBalances: [] }],
|
addresses: [{ address: addr, balance: "0.0000", tokenBalances: [] }],
|
||||||
});
|
});
|
||||||
state.hasWallet = true;
|
state.hasWallet = true;
|
||||||
|
if (state.activeAddress === null) {
|
||||||
|
state.activeAddress = addr;
|
||||||
|
}
|
||||||
await saveState();
|
await saveState();
|
||||||
clearViewStack();
|
clearViewStack();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
@@ -315,6 +330,9 @@ async function importXprvKey(ctx) {
|
|||||||
};
|
};
|
||||||
state.wallets.push(wallet);
|
state.wallets.push(wallet);
|
||||||
state.hasWallet = true;
|
state.hasWallet = true;
|
||||||
|
if (state.activeAddress === null) {
|
||||||
|
state.activeAddress = firstAddress;
|
||||||
|
}
|
||||||
await saveState();
|
await saveState();
|
||||||
clearViewStack();
|
clearViewStack();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
@@ -336,8 +354,15 @@ async function importXprvKey(ctx) {
|
|||||||
tokenBalances: [],
|
tokenBalances: [],
|
||||||
}));
|
}));
|
||||||
wallet.nextIndex = scan.nextIndex;
|
wallet.nextIndex = scan.nextIndex;
|
||||||
|
// As for a recovery phrase: an active first address the scan left
|
||||||
|
// out gives way to the new list's first.
|
||||||
|
const activeMoved =
|
||||||
|
state.activeAddress === firstAddress &&
|
||||||
|
wallet.addresses[0].address !== firstAddress;
|
||||||
|
if (activeMoved) state.activeAddress = wallet.addresses[0].address;
|
||||||
await saveState();
|
await saveState();
|
||||||
ctx.renderWalletList();
|
ctx.renderWalletList();
|
||||||
|
if (activeMoved) notify({ type: "AUTISTMASK_ACTIVE_CHANGED" });
|
||||||
showFlash("Found " + scan.addresses.length + " addresses.");
|
showFlash("Found " + scan.addresses.length + " addresses.");
|
||||||
} else {
|
} else {
|
||||||
showFlash("Ready.", 1000);
|
showFlash("Ready.", 1000);
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
// Adding the first wallet left no address active until the popup was reopened,
|
||||||
|
// so Send and Receive on Home only said "No active address selected."
|
||||||
|
// (https://git.eeqj.de/sneak/AutistMask/issues/512). Each test adds a wallet
|
||||||
|
// through one of the three import tabs and checks which address is active by
|
||||||
|
// opening Receive from Home in the same popup.
|
||||||
|
|
||||||
|
const { HDNodeWallet, Mnemonic, Wallet } = require("ethers");
|
||||||
|
|
||||||
|
const {
|
||||||
|
bootPopup,
|
||||||
|
cleanupPopup,
|
||||||
|
unversionedValidProfile,
|
||||||
|
ADDRESS,
|
||||||
|
} = require("./support/popupBoot");
|
||||||
|
|
||||||
|
const PHRASE =
|
||||||
|
"abandon abandon abandon abandon abandon abandon " +
|
||||||
|
"abandon abandon abandon abandon abandon about";
|
||||||
|
const PRIVATE_KEY = "0x" + "11".repeat(32);
|
||||||
|
// The master key of PHRASE, so its first address is the phrase's.
|
||||||
|
const XPRV = HDNodeWallet.fromSeed(
|
||||||
|
Mnemonic.fromPhrase(PHRASE).computeSeed(),
|
||||||
|
).extendedKey;
|
||||||
|
const PASSWORD = "correct horse battery staple";
|
||||||
|
|
||||||
|
const PHRASE_ADDRESS = HDNodeWallet.fromPhrase(PHRASE).address;
|
||||||
|
|
||||||
|
// Addresses 1 and 2 of PHRASE used, address 0 not: a scan that answers these
|
||||||
|
// replaces the wallet's list with one that leaves out its first address.
|
||||||
|
const account = HDNodeWallet.fromPhrase(PHRASE, "", "m/44'/60'/0'/0");
|
||||||
|
const used = [1, 2].map((index) => ({
|
||||||
|
address: account.deriveChild(index).address,
|
||||||
|
index,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TABS = [
|
||||||
|
{
|
||||||
|
tab: "tab-mnemonic",
|
||||||
|
field: "wallet-mnemonic",
|
||||||
|
secret: PHRASE,
|
||||||
|
address: PHRASE_ADDRESS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: "tab-privkey",
|
||||||
|
field: "import-private-key",
|
||||||
|
secret: PRIVATE_KEY,
|
||||||
|
address: new Wallet(PRIVATE_KEY).address,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: "tab-xprv",
|
||||||
|
field: "import-xprv-key",
|
||||||
|
secret: XPRV,
|
||||||
|
address: PHRASE_ADDRESS,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Fills in the open AddWallet screen and presses "Import".
|
||||||
|
async function importOn(page, { tab, field, secret }) {
|
||||||
|
await page.click(tab);
|
||||||
|
page.node(field).value = secret;
|
||||||
|
page.node("add-wallet-password").value = PASSWORD;
|
||||||
|
page.node("add-wallet-password-confirm").value = PASSWORD;
|
||||||
|
await page.click("btn-add-wallet-confirm");
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanupPopup();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("the first wallet's first address is active right away", () => {
|
||||||
|
test.each(TABS)("added from $tab, Receive opens on it", async (entry) => {
|
||||||
|
const page = await bootPopup(undefined);
|
||||||
|
expect(page.pageErrors).toEqual([]);
|
||||||
|
await page.click("btn-welcome-add");
|
||||||
|
await importOn(page, entry);
|
||||||
|
expect(page.visibleViews()).toEqual(["main"]);
|
||||||
|
|
||||||
|
await page.click("btn-main-receive");
|
||||||
|
expect(page.visibleViews()).toEqual(["receive"]);
|
||||||
|
expect(page.node("receive-address-block").dataset.full).toBe(
|
||||||
|
entry.address,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("a scan that leaves out the first address", () => {
|
||||||
|
// The key tab runs no scan.
|
||||||
|
test.each(TABS.filter((t) => t.tab !== "tab-privkey"))(
|
||||||
|
"added from $tab, the scan's first address is active",
|
||||||
|
async (entry) => {
|
||||||
|
const page = await bootPopup(undefined);
|
||||||
|
const { scanForAddresses } = require("../src/shared/balances");
|
||||||
|
scanForAddresses.mockResolvedValueOnce({
|
||||||
|
addresses: used,
|
||||||
|
nextIndex: 3,
|
||||||
|
});
|
||||||
|
await page.click("btn-welcome-add");
|
||||||
|
await importOn(page, entry);
|
||||||
|
|
||||||
|
expect(page.storage.read("autistmask").activeAddress).toBe(
|
||||||
|
used[0].address,
|
||||||
|
);
|
||||||
|
expect(globalThis.chrome.runtime.sendMessage).toHaveBeenCalledWith({
|
||||||
|
type: "AUTISTMASK_ACTIVE_CHANGED",
|
||||||
|
});
|
||||||
|
await page.click("btn-main-receive");
|
||||||
|
expect(page.node("receive-address-block").dataset.full).toBe(
|
||||||
|
used[0].address,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("adding a wallet while an address is active", () => {
|
||||||
|
test.each(TABS)(
|
||||||
|
"added from $tab, the active address is unchanged",
|
||||||
|
async (entry) => {
|
||||||
|
const page = await bootPopup(unversionedValidProfile());
|
||||||
|
expect(page.pageErrors).toEqual([]);
|
||||||
|
// The phrase and xprv scans leave out the new wallet's first
|
||||||
|
// address, which was never active here. The key tab runs no scan.
|
||||||
|
const { scanForAddresses } = require("../src/shared/balances");
|
||||||
|
scanForAddresses.mockResolvedValueOnce({
|
||||||
|
addresses: used,
|
||||||
|
nextIndex: 3,
|
||||||
|
});
|
||||||
|
await page.click("btn-add-wallet-bottom");
|
||||||
|
await importOn(page, entry);
|
||||||
|
expect(page.visibleViews()).toEqual(["main"]);
|
||||||
|
|
||||||
|
expect(page.storage.read("autistmask").activeAddress).toBe(ADDRESS);
|
||||||
|
expect(
|
||||||
|
globalThis.chrome.runtime.sendMessage,
|
||||||
|
).not.toHaveBeenCalledWith({ type: "AUTISTMASK_ACTIVE_CHANGED" });
|
||||||
|
await page.click("btn-main-receive");
|
||||||
|
expect(page.node("receive-address-block").dataset.full).toBe(
|
||||||
|
ADDRESS,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -265,12 +265,17 @@ async function bootPopup(stored, options) {
|
|||||||
formatAddressTotal: () => "",
|
formatAddressTotal: () => "",
|
||||||
getAddressValue: () => ({ usd: null, partial: false }),
|
getAddressValue: () => ({ usd: null, partial: false }),
|
||||||
}));
|
}));
|
||||||
|
// scanForAddresses() answers in the real one's shape, a scan that found
|
||||||
|
// no used address: AddWallet reads `addresses` from it.
|
||||||
jest.doMock("../../src/shared/balances", () => ({
|
jest.doMock("../../src/shared/balances", () => ({
|
||||||
fetchTokenBalances: jest.fn(async () => []),
|
fetchTokenBalances: jest.fn(async () => []),
|
||||||
refreshBalances: jest.fn(async () => {}),
|
refreshBalances: jest.fn(async () => {}),
|
||||||
lookupTokenInfo: jest.fn(async () => null),
|
lookupTokenInfo: jest.fn(async () => null),
|
||||||
getProvider: () => ({}),
|
getProvider: () => ({}),
|
||||||
scanForAddresses: jest.fn(async () => []),
|
scanForAddresses: jest.fn(async () => ({
|
||||||
|
addresses: [],
|
||||||
|
nextIndex: 1,
|
||||||
|
})),
|
||||||
}));
|
}));
|
||||||
// filterTransactions() answers in the real one's shape: Home,
|
// filterTransactions() answers in the real one's shape: Home,
|
||||||
// AddressDetail and AddressToken read both fields, and a bare list makes
|
// AddressDetail and AddressToken read both fields, and a bare list makes
|
||||||
|
|||||||
Reference in New Issue
Block a user