dappress 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -1
- package/dist/actions.js +19 -8
- package/dist/browser.js +2 -2
- package/dist/metamask-pages.d.ts +10 -0
- package/dist/metamask-pages.js +31 -4
- package/dist/metamask.d.ts +7 -0
- package/dist/metamask.js +52 -20
- package/dist/page-helpers.d.ts +8 -0
- package/dist/page-helpers.js +17 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ What changes for the users of Dappress, version by version. The format follows [
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.6.1] - 2026-10-05
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `cy.confirmTransaction({ gas })` no longer confirms, now and then, with the fee MetaMask estimated in place of the one asked. MetaMask's confirmation carries the new fee a moment after the fee editor closes, and confirming meanwhile sent it the old one, which it kept. The command now waits for the "Network fee" row to change, as it did for the spending cap.
|
|
12
|
+
|
|
13
|
+
## [0.6.0] - 2026-10-05
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- The peer dependency on Cypress asks for 15.10 or later, the first version with `Cypress.expose()`, which the commands read their options with. Earlier versions were accepted at install, then failed as soon as the support file loaded.
|
|
18
|
+
- A command sent right after another was answered in the popup no longer fails on "Timed out waiting for confirm-footer-button". The popup closes a moment after its button goes, and the next command could find it, still listed on the request just answered, then lose it. A decision now waits for the popup to close, and a confirmation that closes before it is acted on is looked for again.
|
|
19
|
+
|
|
7
20
|
## [0.5.1] - 2026-10-05
|
|
8
21
|
|
|
9
22
|
### Changed
|
|
@@ -85,7 +98,9 @@ What changes for the users of Dappress, version by version. The format follows [
|
|
|
85
98
|
- The wallet setup file, `cypress/wallet.setup.ts`, and the opt-in profile cache.
|
|
86
99
|
- TypeScript declarations.
|
|
87
100
|
|
|
88
|
-
[Unreleased]: https://github.com/blassaut/dappress/compare/v0.
|
|
101
|
+
[Unreleased]: https://github.com/blassaut/dappress/compare/v0.6.1...HEAD
|
|
102
|
+
[0.6.1]: https://github.com/blassaut/dappress/compare/v0.6.0...v0.6.1
|
|
103
|
+
[0.6.0]: https://github.com/blassaut/dappress/compare/v0.5.1...v0.6.0
|
|
89
104
|
[0.5.1]: https://github.com/blassaut/dappress/compare/v0.5.0...v0.5.1
|
|
90
105
|
[0.5.0]: https://github.com/blassaut/dappress/compare/v0.4.0...v0.5.0
|
|
91
106
|
[0.4.0]: https://github.com/blassaut/dappress/compare/v0.3.3...v0.4.0
|
package/dist/actions.js
CHANGED
|
@@ -86,16 +86,27 @@ function createTasks(options) {
|
|
|
86
86
|
await metamask.leaveUnlockForm(page, options);
|
|
87
87
|
return state;
|
|
88
88
|
}
|
|
89
|
-
/**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Find the confirmation and act on it. One that closes before it is acted
|
|
91
|
+
* on was the popup of the request before, found as it closed: the request's
|
|
92
|
+
* own is looked for once more.
|
|
93
|
+
*/
|
|
94
|
+
async function onConfirmation(browser, act) {
|
|
95
|
+
for (let attempt = 0;; attempt++) {
|
|
96
|
+
const page = await (0, metamask_pages_1.getConfirmationPage)(browser, await metamaskId(browser), options.timeout);
|
|
97
|
+
try {
|
|
98
|
+
return await act(page);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (attempt > 0 || !(error instanceof metamask.ConfirmationClosed))
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
93
105
|
}
|
|
106
|
+
/** Approve the prompt a network change raised: "Add network" or the permission to switch. */
|
|
107
|
+
const approveNetworkChange = (browser) => onConfirmation(browser, (page) => metamask.approveNetworkChange(page, options.timeout));
|
|
94
108
|
/** The task for a decision: find the confirmation, press its button. */
|
|
95
|
-
const decide = (decision) =>
|
|
96
|
-
const page = await (0, metamask_pages_1.getConfirmationPage)(browser, await metamaskId(browser), options.timeout);
|
|
97
|
-
await metamask.decide(decision, page, options.timeout, argument);
|
|
98
|
-
};
|
|
109
|
+
const decide = (decision) => (browser, argument) => onConfirmation(browser, (page) => metamask.decide(decision, page, options.timeout, argument));
|
|
99
110
|
const actions = {
|
|
100
111
|
setupWallet,
|
|
101
112
|
approveNetworkChange,
|
package/dist/browser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// Connects Puppeteer to the browser Cypress launched, through the debugging
|
|
3
|
-
// URL Cypress hands out in after:browser:launch (Cypress
|
|
3
|
+
// URL Cypress hands out in after:browser:launch (Cypress 15.10+).
|
|
4
4
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
5
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
6
|
};
|
|
@@ -20,7 +20,7 @@ function captureDebuggerUrl(on) {
|
|
|
20
20
|
*/
|
|
21
21
|
async function withBrowser(action) {
|
|
22
22
|
if (!debuggerUrl) {
|
|
23
|
-
throw new Error('[dappress] No browser to connect to. Is configureDappress() called in setupNodeEvents, with Cypress
|
|
23
|
+
throw new Error('[dappress] No browser to connect to. Is configureDappress() called in setupNodeEvents, with Cypress 15.10 or later?');
|
|
24
24
|
}
|
|
25
25
|
const browser = await puppeteer_core_1.default.connect({ browserWSEndpoint: debuggerUrl, defaultViewport: null });
|
|
26
26
|
try {
|
package/dist/metamask-pages.d.ts
CHANGED
|
@@ -12,6 +12,16 @@ export declare function getRequestPages(browser: Browser, extensionId: string):
|
|
|
12
12
|
* The page a dapp request is shown on. Waits for the popup to open, or takes
|
|
13
13
|
* the side panel. A popup still on its home route is one closing after the
|
|
14
14
|
* previous request, or one that hasn't routed to the request yet: skipped.
|
|
15
|
+
* The route is read from the page, which follows a change of hash at once,
|
|
16
|
+
* where the target can still name the request just answered.
|
|
15
17
|
*/
|
|
16
18
|
export declare function getConfirmationPage(browser: Browser, extensionId: string, timeout: number): Promise<Page>;
|
|
19
|
+
/**
|
|
20
|
+
* Once the request shown on a popup is answered, wait for the popup to close,
|
|
21
|
+
* at most `timeout` ms. MetaMask closes it a moment after its button goes, and
|
|
22
|
+
* left to close on its own, the next command could find it, still listed on
|
|
23
|
+
* `request`, and act on a page that vanishes under it. A popup that goes on
|
|
24
|
+
* to another request instead is left to it; so is the side panel, which stays.
|
|
25
|
+
*/
|
|
26
|
+
export declare function waitForDismissal(page: Page, request: string, timeout: number): Promise<void>;
|
|
17
27
|
export declare function withUsableWindow(page: Page): Promise<Page>;
|
package/dist/metamask-pages.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.findExtensionId = findExtensionId;
|
|
|
7
7
|
exports.getHomePage = getHomePage;
|
|
8
8
|
exports.getRequestPages = getRequestPages;
|
|
9
9
|
exports.getConfirmationPage = getConfirmationPage;
|
|
10
|
+
exports.waitForDismissal = waitForDismissal;
|
|
10
11
|
exports.withUsableWindow = withUsableWindow;
|
|
11
12
|
const page_helpers_1 = require("./page-helpers");
|
|
12
13
|
const HOME_PATH = '/home.html';
|
|
@@ -97,14 +98,20 @@ async function getRequestPages(browser, extensionId) {
|
|
|
97
98
|
* The page a dapp request is shown on. Waits for the popup to open, or takes
|
|
98
99
|
* the side panel. A popup still on its home route is one closing after the
|
|
99
100
|
* previous request, or one that hasn't routed to the request yet: skipped.
|
|
101
|
+
* The route is read from the page, which follows a change of hash at once,
|
|
102
|
+
* where the target can still name the request just answered.
|
|
100
103
|
*/
|
|
101
104
|
async function getConfirmationPage(browser, extensionId, timeout) {
|
|
102
105
|
const deadline = Date.now() + timeout;
|
|
103
106
|
while (Date.now() < deadline) {
|
|
104
107
|
for (const pathname of CONFIRMATION_PATHS) {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
for (const target of pagesOf(browser, extensionId, pathname)) {
|
|
109
|
+
// A target closing while its page is taken has none: not a confirmation either
|
|
110
|
+
const page = await target.page().catch(() => null);
|
|
111
|
+
if (!page || page.isClosed() || isHomeRoute(page.url()))
|
|
112
|
+
continue;
|
|
113
|
+
return pathname === '/notification.html' ? withUsableWindow(page) : page;
|
|
114
|
+
}
|
|
108
115
|
}
|
|
109
116
|
await (0, page_helpers_1.sleep)(250);
|
|
110
117
|
}
|
|
@@ -115,6 +122,26 @@ async function getConfirmationPage(browser, extensionId, timeout) {
|
|
|
115
122
|
.join(', ');
|
|
116
123
|
throw new Error(`[dappress] MetaMask showed no confirmation within ${timeout}ms. Did the dapp send a request? MetaMask pages seen: ${seen || 'none'}`);
|
|
117
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Once the request shown on a popup is answered, wait for the popup to close,
|
|
127
|
+
* at most `timeout` ms. MetaMask closes it a moment after its button goes, and
|
|
128
|
+
* left to close on its own, the next command could find it, still listed on
|
|
129
|
+
* `request`, and act on a page that vanishes under it. A popup that goes on
|
|
130
|
+
* to another request instead is left to it; so is the side panel, which stays.
|
|
131
|
+
*/
|
|
132
|
+
async function waitForDismissal(page, request, timeout) {
|
|
133
|
+
if (!request.includes('/notification.html'))
|
|
134
|
+
return;
|
|
135
|
+
const deadline = Date.now() + timeout;
|
|
136
|
+
while (Date.now() < deadline) {
|
|
137
|
+
if (page.isClosed())
|
|
138
|
+
return;
|
|
139
|
+
const url = page.url();
|
|
140
|
+
if (url !== request && !isHomeRoute(url))
|
|
141
|
+
return;
|
|
142
|
+
await (0, page_helpers_1.sleep)(100);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
118
145
|
// MetaMask asks Chrome for a 400x620 popup. Under Xvfb on a GitHub runner the
|
|
119
146
|
// window it gets is 1x1: nothing is laid out, the pane of a confirmation that
|
|
120
147
|
// must be read to the end has no height, so it can never be scrolled there and
|
|
@@ -126,7 +153,7 @@ async function withUsableWindow(page) {
|
|
|
126
153
|
const { windowId, bounds } = await session.send('Browser.getWindowForTarget');
|
|
127
154
|
if (bounds.windowState === 'normal' && (bounds.width ?? 0) >= 200 && (bounds.height ?? 0) >= 200)
|
|
128
155
|
return page;
|
|
129
|
-
console.log(`[dappress] MetaMask's popup is ${bounds.width}x${bounds.height} (${bounds.windowState}); resizing it to ${POPUP.width}x${POPUP.height}`);
|
|
156
|
+
console.log(`[dappress] MetaMask's popup is ${bounds.width}x${bounds.height} (${bounds.windowState}), as under Xvfb without a window manager; resizing it to ${POPUP.width}x${POPUP.height}`);
|
|
130
157
|
if (bounds.windowState !== 'normal')
|
|
131
158
|
await session.send('Browser.setWindowBounds', { windowId, bounds: { windowState: 'normal' } });
|
|
132
159
|
await session.send('Browser.setWindowBounds', { windowId, bounds: { ...POPUP } });
|
package/dist/metamask.d.ts
CHANGED
|
@@ -48,6 +48,13 @@ export declare function disconnectSite(page: Page, origin: string): Promise<void
|
|
|
48
48
|
type CommandOptions = ConnectOptions & TransactionOptions;
|
|
49
49
|
/** Press the button of `decision` on the confirmation shown on `page`, once it is set as `options` ask. */
|
|
50
50
|
export declare function decide(decision: Decision, page: Page, timeout: number, options?: CommandOptions): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Thrown when the confirmation page closed before its button was pressed: it
|
|
53
|
+
* was the popup of the request before, found as it closed. The request's own
|
|
54
|
+
* popup is there to be looked for.
|
|
55
|
+
*/
|
|
56
|
+
export declare class ConfirmationClosed extends Error {
|
|
57
|
+
}
|
|
51
58
|
/** wallet_addEthereumChain on a network MetaMask knows behaves like a switch: either prompt may show. */
|
|
52
59
|
export declare function approveNetworkChange(page: Page, timeout: number): Promise<void>;
|
|
53
60
|
export {};
|
package/dist/metamask.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// first, then a fallback on the button's English text. When a MetaMask release
|
|
8
8
|
// moves something, this is the file to fix.
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.decisions = void 0;
|
|
10
|
+
exports.ConfirmationClosed = exports.decisions = void 0;
|
|
11
11
|
exports.walletState = walletState;
|
|
12
12
|
exports.onboard = onboard;
|
|
13
13
|
exports.unlock = unlock;
|
|
@@ -20,6 +20,7 @@ exports.disconnectSite = disconnectSite;
|
|
|
20
20
|
exports.decide = decide;
|
|
21
21
|
exports.approveNetworkChange = approveNetworkChange;
|
|
22
22
|
const page_helpers_1 = require("./page-helpers");
|
|
23
|
+
const metamask_pages_1 = require("./metamask-pages");
|
|
23
24
|
const testId = (id) => `[data-testid="${id}"]`;
|
|
24
25
|
// XPath rather than Puppeteer's ::-p-text(): the latter needs MutationObserver,
|
|
25
26
|
// which MetaMask's sandbox (LavaMoat) blocks in its pages.
|
|
@@ -136,6 +137,8 @@ const selectors = {
|
|
|
136
137
|
gasForm: testId('gas-fee-advanced-eip1559-modal'),
|
|
137
138
|
gasField: (id) => [`#${id}`, `${testId(id)} input`],
|
|
138
139
|
gasSave: [testId('gas-fee-modal-save-button'), buttonText('Save')],
|
|
140
|
+
// The "Network fee" row: its amount and the name of its estimate
|
|
141
|
+
feeSection: testId('gas-fee-section'),
|
|
139
142
|
},
|
|
140
143
|
// The older footer, still used by the permission update a network switch
|
|
141
144
|
// asks for, by "Add suggested tokens", and by template confirmations
|
|
@@ -553,11 +556,30 @@ async function decide(decision, page, timeout, options) {
|
|
|
553
556
|
const adjust = adjustments[decision];
|
|
554
557
|
if (!adjust)
|
|
555
558
|
throw new Error(`[dappress] ${decision} takes no options`);
|
|
556
|
-
await (
|
|
559
|
+
await waitForButton(page, exports.decisions[decision], timeout);
|
|
557
560
|
await adjust(page, options, timeout);
|
|
558
561
|
}
|
|
559
562
|
return pressAndWaitForDismissal(page, exports.decisions[decision], timeout);
|
|
560
563
|
}
|
|
564
|
+
/**
|
|
565
|
+
* Thrown when the confirmation page closed before its button was pressed: it
|
|
566
|
+
* was the popup of the request before, found as it closed. The request's own
|
|
567
|
+
* popup is there to be looked for.
|
|
568
|
+
*/
|
|
569
|
+
class ConfirmationClosed extends Error {
|
|
570
|
+
}
|
|
571
|
+
exports.ConfirmationClosed = ConfirmationClosed;
|
|
572
|
+
// The button of a confirmation, or ConfirmationClosed if the page went meanwhile
|
|
573
|
+
async function waitForButton(page, button, timeout) {
|
|
574
|
+
try {
|
|
575
|
+
await (0, page_helpers_1.waitFor)(page, button, { timeout });
|
|
576
|
+
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
if (page.isClosed())
|
|
579
|
+
throw new ConfirmationClosed(`[dappress] The confirmation closed before "${(0, page_helpers_1.describe)(button)}" was pressed`);
|
|
580
|
+
throw error;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
561
583
|
/** wallet_addEthereumChain on a network MetaMask knows behaves like a switch: either prompt may show. */
|
|
562
584
|
function approveNetworkChange(page, timeout) {
|
|
563
585
|
return pressAndWaitForDismissal(page, [...selectors.confirmation.confirm, ...selectors.pageContainer.confirm], timeout);
|
|
@@ -617,7 +639,7 @@ async function setSpendingCap(page, cap) {
|
|
|
617
639
|
const s = selectors.transaction;
|
|
618
640
|
if (!(await (0, page_helpers_1.isVisible)(page, s.editSpendingCap, 10000)))
|
|
619
641
|
throw await (0, page_helpers_1.failure)(page, 'MetaMask shows no spending cap to edit: spendingCap is for an ERC-20 approval');
|
|
620
|
-
const shown = await textOf(page, s.spendingCap);
|
|
642
|
+
const shown = await (0, page_helpers_1.textOf)(page, s.spendingCap);
|
|
621
643
|
const asked = await openSpendingCap(page);
|
|
622
644
|
await type(page, s.spendingCapInput, cap, 'spending cap');
|
|
623
645
|
if (!(await pressToClose(page, s.spendingCapSave, s.spendingCapInput)))
|
|
@@ -625,9 +647,7 @@ async function setSpendingCap(page, cap) {
|
|
|
625
647
|
// The modal closes before the confirmation has the new cap, and confirming
|
|
626
648
|
// meanwhile approves the old one. Two caps may read the same once MetaMask
|
|
627
649
|
// has rounded them, so a row that stays as it was is not a failure.
|
|
628
|
-
|
|
629
|
-
while (Date.now() < deadline && (await textOf(page, s.spendingCap)) === shown)
|
|
630
|
-
await (0, page_helpers_1.sleep)(250);
|
|
650
|
+
await (0, page_helpers_1.waitForTextChange)(page, s.spendingCap, shown, Number(asked) === Number(cap) ? 0 : 5000);
|
|
631
651
|
}
|
|
632
652
|
/** Open the spending cap's modal. Yields the cap the dapp asked for, which its field starts with. */
|
|
633
653
|
async function openSpendingCap(page) {
|
|
@@ -648,11 +668,14 @@ async function setGas(page, gas) {
|
|
|
648
668
|
const s = selectors.transaction;
|
|
649
669
|
if (!(await (0, page_helpers_1.isVisible)(page, s.editGas, 10000)))
|
|
650
670
|
throw await (0, page_helpers_1.failure)(page, 'MetaMask shows no network fee to edit on this confirmation');
|
|
671
|
+
const shown = await (0, page_helpers_1.textOf)(page, s.feeSection);
|
|
651
672
|
await openFeeEditor(page);
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
673
|
+
const changed = typeof gas === 'string' ? await pickGasEstimate(page, gas) : await fillGasForm(page, gas);
|
|
674
|
+
// The editor closes before the confirmation carries the new fee, and
|
|
675
|
+
// confirming meanwhile sends MetaMask the old one, which it keeps: the row
|
|
676
|
+
// is waited for to change. A fee set as it already was changes nothing.
|
|
677
|
+
if (changed)
|
|
678
|
+
await (0, page_helpers_1.waitForTextChange)(page, s.feeSection, shown, 5000);
|
|
656
679
|
}
|
|
657
680
|
async function openFeeEditor(page) {
|
|
658
681
|
const s = selectors.transaction;
|
|
@@ -667,32 +690,45 @@ async function openFeeEditor(page) {
|
|
|
667
690
|
// Picking an estimate closes the editor. MetaMask lists the estimates it has
|
|
668
691
|
// for the network: Low, Market and Aggressive where it has fee estimates,
|
|
669
692
|
// "Network suggested" where it only has a gas price, as on a local node.
|
|
693
|
+
// Yields whether the transaction had another estimate before.
|
|
670
694
|
async function pickGasEstimate(page, name) {
|
|
671
695
|
const s = selectors.transaction;
|
|
672
696
|
const option = s.gasOption(...gasEstimates[name]);
|
|
673
697
|
if (!(await (0, page_helpers_1.isVisible)(page, option, 5000)))
|
|
674
698
|
throw await (0, page_helpers_1.failure)(page, `MetaMask offers no "${name}" fee for this transaction`);
|
|
699
|
+
const selected = await hasEstimate(page, option);
|
|
675
700
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
676
701
|
await (0, page_helpers_1.click)(page, option);
|
|
677
702
|
if (await (0, page_helpers_1.isGone)(page, s.gasEstimates, 10000))
|
|
678
|
-
return;
|
|
703
|
+
return !selected;
|
|
679
704
|
}
|
|
680
705
|
throw await (0, page_helpers_1.failure)(page, `MetaMask did not take the "${name}" fee`);
|
|
681
706
|
}
|
|
707
|
+
// The estimate the transaction has is marked by a class on its row
|
|
708
|
+
async function hasEstimate(page, option) {
|
|
709
|
+
const element = await (0, page_helpers_1.waitFor)(page, option);
|
|
710
|
+
return element.evaluate((el) => el.className.includes('--selected')).catch(() => false);
|
|
711
|
+
}
|
|
712
|
+
// Yields whether a field is given another value than it had
|
|
682
713
|
async function fillGasForm(page, values) {
|
|
683
714
|
const s = selectors.transaction;
|
|
684
715
|
await openGasForm(page);
|
|
716
|
+
const asked = Object.keys(gasFields).filter((field) => values[field] !== undefined);
|
|
717
|
+
let changed = false;
|
|
718
|
+
for (const field of asked) {
|
|
719
|
+
if (Number(await valueOf(page, s.gasField(gasFields[field]))) !== Number(values[field]))
|
|
720
|
+
changed = true;
|
|
721
|
+
}
|
|
685
722
|
// The form checks each fee against the other as it stood, and keeps the old
|
|
686
723
|
// value of a fee it refused: the max base fee is typed again once the
|
|
687
724
|
// priority fee is in, for two fees that both move below or above the old ones.
|
|
688
|
-
const fields =
|
|
689
|
-
if (values.maxBaseFee !== undefined && values.priorityFee !== undefined)
|
|
690
|
-
fields.push('maxBaseFee');
|
|
725
|
+
const fields = values.maxBaseFee !== undefined && values.priorityFee !== undefined ? [...asked, 'maxBaseFee'] : asked;
|
|
691
726
|
for (const field of fields)
|
|
692
727
|
await type(page, s.gasField(gasFields[field]), values[field], field);
|
|
693
728
|
// "Save" stays disabled on a value the form refuses, and the error quotes what the form says of it
|
|
694
729
|
if (!(await pressToClose(page, s.gasSave, s.gasForm)))
|
|
695
730
|
throw await (0, page_helpers_1.failure)(page, 'MetaMask did not save the network fee');
|
|
731
|
+
return changed;
|
|
696
732
|
}
|
|
697
733
|
// "Advanced", in the list of estimates, replaces the list with the form. A
|
|
698
734
|
// transaction with a gas price gets another form, which has none of these fields.
|
|
@@ -728,10 +764,6 @@ async function valueOf(page, selector) {
|
|
|
728
764
|
const field = await (0, page_helpers_1.waitFor)(page, selector);
|
|
729
765
|
return field.evaluate((el) => el.value);
|
|
730
766
|
}
|
|
731
|
-
async function textOf(page, selector) {
|
|
732
|
-
const element = await page.$(selector);
|
|
733
|
-
return element ? element.evaluate((el) => (el.textContent ?? '').trim()).catch(() => null) : null;
|
|
734
|
-
}
|
|
735
767
|
/**
|
|
736
768
|
* Press a footer button, then wait for the confirmation to go away: the
|
|
737
769
|
* popup closes, the side panel goes back to the home screen. A click that
|
|
@@ -741,7 +773,7 @@ async function textOf(page, selector) {
|
|
|
741
773
|
* what MetaMask logged.
|
|
742
774
|
*/
|
|
743
775
|
async function pressAndWaitForDismissal(page, button, timeout) {
|
|
744
|
-
await (
|
|
776
|
+
await waitForButton(page, button, timeout);
|
|
745
777
|
await dismissModal(page);
|
|
746
778
|
const logged = recordErrors(page);
|
|
747
779
|
const request = page.url();
|
|
@@ -751,7 +783,7 @@ async function pressAndWaitForDismissal(page, button, timeout) {
|
|
|
751
783
|
if (await (0, page_helpers_1.isVisible)(page, selectors.alert.acknowledge, 500))
|
|
752
784
|
await (0, page_helpers_1.click)(page, selectors.alert.acknowledge);
|
|
753
785
|
if (await (0, page_helpers_1.isGone)(page, button, 3000))
|
|
754
|
-
return;
|
|
786
|
+
return (0, metamask_pages_1.waitForDismissal)(page, request, 3000);
|
|
755
787
|
await dismissModal(page);
|
|
756
788
|
await (0, page_helpers_1.dispatchClick)(page, button, { timeout: 3000 }).catch(() => { });
|
|
757
789
|
}
|
package/dist/page-helpers.d.ts
CHANGED
|
@@ -29,5 +29,13 @@ export declare function fill(page: Page, selector: Selector, text: string, optio
|
|
|
29
29
|
* words for the log and as a screenshot to look at.
|
|
30
30
|
*/
|
|
31
31
|
export declare function failure(page: Page, message: string): Promise<Error>;
|
|
32
|
+
/** The text of the first element `selector` matches, or null when there is none. */
|
|
33
|
+
export declare function textOf(page: Page, selector: string): Promise<string | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Wait for the text of `selector` to differ from `shown`, at most `timeout`
|
|
36
|
+
* ms. For a value MetaMask sets on its page a moment after a modal that
|
|
37
|
+
* edits it closes: acting on the page meanwhile acts on the old value.
|
|
38
|
+
*/
|
|
39
|
+
export declare function waitForTextChange(page: Page, selector: string, shown: string | null, timeout: number): Promise<void>;
|
|
32
40
|
export declare function sleep(ms: number): Promise<void>;
|
|
33
41
|
export {};
|
package/dist/page-helpers.js
CHANGED
|
@@ -18,6 +18,8 @@ exports.clickWhenEnabled = clickWhenEnabled;
|
|
|
18
18
|
exports.dispatchClick = dispatchClick;
|
|
19
19
|
exports.fill = fill;
|
|
20
20
|
exports.failure = failure;
|
|
21
|
+
exports.textOf = textOf;
|
|
22
|
+
exports.waitForTextChange = waitForTextChange;
|
|
21
23
|
exports.sleep = sleep;
|
|
22
24
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
23
25
|
const node_os_1 = __importDefault(require("node:os"));
|
|
@@ -194,6 +196,21 @@ async function pageText(page) {
|
|
|
194
196
|
return '';
|
|
195
197
|
}
|
|
196
198
|
}
|
|
199
|
+
/** The text of the first element `selector` matches, or null when there is none. */
|
|
200
|
+
async function textOf(page, selector) {
|
|
201
|
+
const element = await page.$(selector);
|
|
202
|
+
return element ? element.evaluate((el) => (el.textContent ?? '').trim()).catch(() => null) : null;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Wait for the text of `selector` to differ from `shown`, at most `timeout`
|
|
206
|
+
* ms. For a value MetaMask sets on its page a moment after a modal that
|
|
207
|
+
* edits it closes: acting on the page meanwhile acts on the old value.
|
|
208
|
+
*/
|
|
209
|
+
async function waitForTextChange(page, selector, shown, timeout) {
|
|
210
|
+
const deadline = Date.now() + timeout;
|
|
211
|
+
while (Date.now() < deadline && (await textOf(page, selector)) === shown)
|
|
212
|
+
await sleep(250);
|
|
213
|
+
}
|
|
197
214
|
function sleep(ms) {
|
|
198
215
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
199
216
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dappress",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "MetaMask automation for Cypress: load the real extension, import a test wallet, answer dapp requests with cy.* commands. Tracks the latest MetaMask release.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Benjamin Lassaut",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"format:check": "prettier --check ."
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"cypress": ">=
|
|
64
|
+
"cypress": ">=15.10.0"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@scure/bip39": "^1.6.0",
|