From 0deb9daa0be10ce0504f4e5eed5b8d55bc8fe107 Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 29 Oct 2026 16:36:15 +0200 Subject: [PATCH 1/2] fix: re-query containers while waiting for text --- test/helpers/actions.ts | 59 ++++++++++++------------ test/unit/activity-text.test.ts | 79 +++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 29 deletions(-) create mode 100644 test/unit/activity-text.test.ts diff --git a/test/helpers/actions.ts b/test/helpers/actions.ts index 77c624b..2a11ab3 100644 --- a/test/helpers/actions.ts +++ b/test/helpers/actions.ts @@ -201,32 +201,15 @@ export async function expectTextWithin( strategy?: RetrieveStrategy; } = {} ) { - const parent = elementById(ancestorId); - await parent.waitForDisplayed({ timeout }); - - if (driver.isIOS) { - const parentLabel = await parent.getAttribute('label'); - const parentValue = await parent.getAttribute('value'); - const matchesParent = (() => { - if (strategy === 'exact') { - return ( - (typeof parentLabel === 'string' && parentLabel === text) || - (typeof parentValue === 'string' && parentValue === text) - ); - } - return ( - (typeof parentLabel === 'string' && parentLabel.includes(text)) || - (typeof parentValue === 'string' && parentValue.includes(text)) - ); - })(); - - if (matchesParent) { - if (!visible) { - await parent.waitForDisplayed({ reverse: true, timeout }); - } - return; - } - } + const matchesParent = async (parent: ChainablePromiseElement) => { + if (!driver.isIOS) return false; + const label = await parent.getAttribute('label'); + const value = await parent.getAttribute('value'); + return [label, value].some( + (value) => + typeof value === 'string' && (strategy === 'exact' ? value === text : value.includes(text)) + ); + }; const needle = (() => { if (driver.isAndroid) { @@ -238,10 +221,28 @@ export async function expectTextWithin( : `.//*[self::XCUIElementTypeStaticText or self::XCUIElementTypeTextView or self::XCUIElementTypeTextField or self::XCUIElementTypeButton][contains(@label,'${text}') or contains(@value,'${text}')]`; })(); - if (!visible) { - await parent.$(needle).waitForDisplayed({ reverse: true, timeout }); + const parent = elementById(ancestorId); + await parent.waitForDisplayed({ timeout }); + + if (visible) { + await browser.waitUntil( + async () => { + // A list-position identifier can move to another element while its contents load. + const currentParent = elementById(ancestorId); + if (!(await currentParent.isDisplayed())) return false; + return ( + (await matchesParent(currentParent)) || (await currentParent.$(needle).isDisplayed()) + ); + }, + { timeout, timeoutMsg: `Expected "${text}" within "${ancestorId}"` } + ); + return; + } + + if (await matchesParent(parent)) { + await parent.waitForDisplayed({ reverse: true, timeout }); } else { - await parent.$(needle).waitForDisplayed({ timeout }); + await parent.$(needle).waitForDisplayed({ reverse: true, timeout }); } } diff --git a/test/unit/activity-text.test.ts b/test/unit/activity-text.test.ts new file mode 100644 index 0000000..401f4ff --- /dev/null +++ b/test/unit/activity-text.test.ts @@ -0,0 +1,79 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { expectTextWithin } from '../helpers/actions.ts'; + +function changingRow({ isAndroid = false, missing = false, removing = false } = {}) { + const original = { label: '', children: removing ? ['9 000'] : ['1 000'] }; + let current = original; + let queries = 0; + const waitUntil = async (probe: () => Promise) => { + for (let attempt = 0; attempt < 3; attempt++) { + if (await probe()) return; + if (removing) { + original.children = []; + } else { + current = missing + ? { label: '9 0000', children: [] } + : isAndroid + ? { label: '', children: ['Transfer + 9 000'] } + : { label: '9 000', children: [] }; + } + } + throw new Error('Text did not appear before the timeout'); + }; + const select = () => { + queries++; + const row = current; + return { + isDisplayed: async () => true, + waitForDisplayed: async () => {}, + getAttribute: async (attribute: string) => (attribute === 'label' ? row.label : null), + $: () => ({ + isDisplayed: async () => row.children.some((text) => text.includes('9 000')), + waitForDisplayed: async ({ reverse = false } = {}) => + waitUntil(async () => row.children.some((text) => text.includes('9 000')) !== reverse), + }), + }; + }; + return { + $: select, + browser: { waitUntil }, + driver: { isAndroid, isIOS: !isAndroid }, + queries: () => queries, + }; +} + +for (const isAndroid of [false, true]) { + test(`${isAndroid ? 'Android' : 'iOS'}: follows the row currently holding the activity identifier`, async (t) => { + const previous = { browser: globalThis.browser, driver: globalThis.driver, $: globalThis.$ }; + const screen = changingRow({ isAndroid }); + Object.assign(globalThis, { browser: screen.browser, driver: screen.driver, $: screen.$ }); + t.after(() => Object.assign(globalThis, previous)); + + await expectTextWithin('ActivityShort-0', '9 000', { + strategy: isAndroid ? 'contains' : 'exact', + }); + assert.ok(screen.queries() > 1); + }); +} + +test('keeps exact matching and fails when the requested text never appears', async (t) => { + const previous = { browser: globalThis.browser, driver: globalThis.driver, $: globalThis.$ }; + const screen = changingRow({ missing: true }); + Object.assign(globalThis, { browser: screen.browser, driver: screen.driver, $: screen.$ }); + t.after(() => Object.assign(globalThis, previous)); + + await assert.rejects( + expectTextWithin('ActivityShort-0', '9 000', { strategy: 'exact' }), + /timeout/ + ); +}); + +test('still waits for a descendant to disappear', async (t) => { + const previous = { browser: globalThis.browser, driver: globalThis.driver, $: globalThis.$ }; + const screen = changingRow({ removing: true }); + Object.assign(globalThis, { browser: screen.browser, driver: screen.driver, $: screen.$ }); + t.after(() => Object.assign(globalThis, previous)); + + await expectTextWithin('ActivityShort-0', '9 000', { visible: false }); +}); From 0affc99619cafe524583fe1be6c2b1129d8a597e Mon Sep 17 00:00:00 2001 From: benk10 Date: Thu, 29 Oct 2026 18:14:16 +0200 Subject: [PATCH 2/2] test: return from connections before opening wallet --- test/specs/lightning.e2e.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/specs/lightning.e2e.ts b/test/specs/lightning.e2e.ts index 70c11ff..e8d794d 100644 --- a/test/specs/lightning.e2e.ts +++ b/test/specs/lightning.e2e.ts @@ -279,9 +279,7 @@ describe('@lightning @ios_gate - Lightning', () => { await mineBlocks(6); await electrum?.waitForSync(); await elementById('Channel').waitForDisplayed({ reverse: true }); - if (driver.isAndroid) { - await tap('NavigationBack'); - } + await tap('NavigationBack'); await doNavigationClose(); await expectTextWithin('ActivityShort-0', '9 000');