Skip to content

Commit 51beef1

Browse files
test: work in progress
1 parent 6032fb0 commit 51beef1

17 files changed

Lines changed: 1440 additions & 37 deletions

tests/api-mocking/MockServerE2E.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { createLogger, LogLevel } from '../framework/logger.ts';
66
import {
77
MockApiEndpoint,
88
MockEventsObject,
9+
PlatformDetector,
910
Resource,
1011
ServerStatus,
1112
TestSpecificMock,
@@ -407,10 +408,9 @@ export default class MockServerE2E implements Resource {
407408
};
408409
}
409410

410-
let updatedUrl =
411-
device.getPlatform() === 'android'
412-
? urlEndpoint.replace('localhost', '127.0.0.1')
413-
: urlEndpoint;
411+
let updatedUrl = (await PlatformDetector.isAndroid())
412+
? urlEndpoint.replace('localhost', '127.0.0.1')
413+
: urlEndpoint;
414414

415415
// Translate fallback ports to actual allocated ports (host-side forwarding)
416416
updatedUrl = translateFallbackPortToActual(updatedUrl);

tests/flows/general.flow.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,12 @@ export const dismissDevScreens = async (): Promise<void> => {
5757
await Gestures.tap(fastRefreshButton, {
5858
elemDescription: 'Dev Menu Fast Refresh Button',
5959
});
60-
} catch {
61-
logger.error('Dev screens dismiss error');
60+
} catch (error) {
61+
logger.debug(
62+
`Dev screens were not dismissed (best effort): ${
63+
error instanceof Error ? error.message : String(error)
64+
}`,
65+
);
6266
}
6367
};
6468

@@ -105,8 +109,12 @@ export const dismissDevScreensPlaywright = async (): Promise<void> => {
105109
description: 'Dev Menu Fast Refresh Button should be visible',
106110
});
107111
await PlaywrightGestures.waitAndTap(fastRefreshButton);
108-
} catch {
109-
logger.error('Dev screens dismiss error');
112+
} catch (error) {
113+
logger.debug(
114+
`Playwright dev screens were not dismissed (best effort): ${
115+
error instanceof Error ? error.message : String(error)
116+
}`,
117+
);
110118
}
111119
};
112120

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import PlaywrightUtilities from './PlaywrightUtilities';
2+
import type { CurrentDeviceDetails } from './fixture';
3+
4+
describe('PlaywrightUtilities.launchApp', () => {
5+
const executeMock = jest.fn();
6+
7+
const androidDevice: CurrentDeviceDetails = {
8+
platform: 'android',
9+
deviceName: 'Pixel_5',
10+
udid: 'emulator-5554',
11+
packageName: 'io.metamask',
12+
launchableActivity: 'io.metamask.MainActivity',
13+
isBrowserstack: false,
14+
};
15+
16+
beforeEach(() => {
17+
jest.useFakeTimers();
18+
executeMock.mockResolvedValue(undefined);
19+
globalThis.driver = {
20+
execute: executeMock,
21+
} as unknown as WebdriverIO.Browser;
22+
});
23+
24+
afterEach(() => {
25+
delete globalThis.driver;
26+
jest.useRealTimers();
27+
jest.clearAllMocks();
28+
});
29+
30+
it('launches Android apps with Appium mobile startActivity intent parameters', async () => {
31+
const launchPromise = PlaywrightUtilities.launchApp(androidDevice, {
32+
launchArgs: {
33+
fixtureServerPort: '1234',
34+
},
35+
});
36+
37+
await jest.advanceTimersByTimeAsync(1000);
38+
await launchPromise;
39+
40+
expect(executeMock).toHaveBeenCalledWith(
41+
'mobile: startActivity',
42+
expect.objectContaining({
43+
component: 'io.metamask/io.metamask.MainActivity',
44+
action: 'android.intent.action.MAIN',
45+
categories: ['android.intent.category.LAUNCHER'],
46+
stop: true,
47+
wait: true,
48+
extras: expect.arrayContaining([['s', 'fixtureServerPort', '1234']]),
49+
}),
50+
);
51+
expect(executeMock).not.toHaveBeenCalledWith(
52+
'mobile: startActivity',
53+
expect.objectContaining({
54+
appPackage: expect.any(String),
55+
}),
56+
);
57+
expect(executeMock).not.toHaveBeenCalledWith(
58+
'mobile: startActivity',
59+
expect.objectContaining({
60+
appActivity: expect.any(String),
61+
}),
62+
);
63+
expect(executeMock).not.toHaveBeenCalledWith(
64+
'mobile: startActivity',
65+
expect.objectContaining({
66+
optionalIntentArguments: expect.any(String),
67+
}),
68+
);
69+
});
70+
});

tests/framework/PlaywrightUtilities.ts

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import Utilities from './Utilities';
1212
import { ACCOUNT_ACTIVITY_WS } from '../websocket/constants.ts';
1313
// eslint-disable-next-line import-x/no-nodejs-modules
1414
import { execSync } from 'child_process';
15-
import { CurrentDeviceDetails } from './fixture';
15+
import type { CurrentDeviceDetails } from './fixture';
1616

1717
// eslint-disable-next-line @typescript-eslint/no-var-requires, import-x/no-commonjs, @typescript-eslint/no-require-imports
1818
const deviceMatrix: DeviceMatrix = require('../performance/device-matrix.json');
1919

20+
type AndroidIntentExtra = ['s', string, string];
21+
2022
/**
2123
* Get the driver instance.
2224
* @returns The driver instance.
@@ -270,29 +272,28 @@ class PlaywrightUtilities {
270272
}
271273

272274
/**
273-
* Builds `optionalIntentArguments` for Android `am start` from {@link LaunchArgs}.
274-
* Each defined string value is passed as a string extra (`--es`) using the same keys
275-
* as Detox `launchArgs` / `react-native-launch-arguments`.
275+
* Builds Android string intent extras from {@link LaunchArgs}.
276+
* Each defined string value is passed as a string extra (`--es`) using the
277+
* same keys as Detox `launchArgs` / `react-native-launch-arguments`.
276278
*/
277-
private static buildAndroidOptionalIntentArguments(
279+
private static buildAndroidIntentExtras(
278280
{ launchArgs }: { launchArgs?: Partial<LaunchArgs> } = {
279281
launchArgs: {} as Partial<LaunchArgs>,
280282
},
281-
): string | undefined {
283+
): AndroidIntentExtra[] {
282284
const resolved = PlaywrightUtilities.buildResolvedLaunchArgs({
283285
launchArgs,
284286
});
285287

286-
const segments: string[] = [];
288+
const extras: AndroidIntentExtra[] = [];
287289
for (const [key, value] of Object.entries(resolved)) {
288290
if (value === undefined || value === '') {
289291
continue;
290292
}
291-
const escaped = String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
292-
segments.push(`--es ${key} "${escaped}"`);
293+
extras.push(['s', key, String(value)]);
293294
}
294295

295-
return segments.length > 0 ? segments.join(' ') : undefined;
296+
return extras;
296297
}
297298

298299
/**
@@ -340,16 +341,17 @@ class PlaywrightUtilities {
340341
);
341342
}
342343

343-
const optionalIntentArguments =
344-
PlaywrightUtilities.buildAndroidOptionalIntentArguments({
345-
launchArgs,
346-
});
344+
const extras = PlaywrightUtilities.buildAndroidIntentExtras({
345+
launchArgs,
346+
});
347+
347348
await drv.execute('mobile: startActivity', {
348-
appPackage: pkg,
349-
appActivity: activity,
350-
...(optionalIntentArguments !== undefined
351-
? { optionalIntentArguments }
352-
: {}),
349+
component: `${pkg}/${activity}`,
350+
action: 'android.intent.action.MAIN',
351+
categories: ['android.intent.category.LAUNCHER'],
352+
stop: true,
353+
wait: true,
354+
...(extras.length > 0 ? { extras } : {}),
353355
});
354356
}
355357

@@ -402,6 +404,7 @@ class PlaywrightUtilities {
402404
if (!currentDeviceDetails?.packageName && !currentDeviceDetails?.appId) {
403405
throw new Error('Package name or app id is not available');
404406
}
407+
405408
if (currentDeviceDetails.platform === 'android') {
406409
await this.launchAppAndroid(currentDeviceDetails, {
407410
launchArgs,

tests/framework/fixtures/FixtureHelper.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import {
6767
} from '../../websocket/account-activity-mocks';
6868
import { FrameworkDetector } from '../FrameworkDetector';
6969
import PlaywrightUtilities from '../PlaywrightUtilities';
70+
import { DeviceCommandHandler } from '../services/device-commands';
7071

7172
const logger = createLogger({
7273
name: 'FixtureHelper',
@@ -523,6 +524,10 @@ export async function withFixtures(
523524
analyticsExpectations,
524525
currentDeviceDetails,
525526
} = options;
527+
const deviceCommands =
528+
currentDeviceDetails && !currentDeviceDetails.isBrowserstack
529+
? new DeviceCommandHandler({ currentDeviceDetails, logger })
530+
: undefined;
526531

527532
// Clean up any stale port forwarding from previous failed tests
528533
// This ensures we start with a clean slate on Android
@@ -655,19 +660,36 @@ export async function withFixtures(
655660
throw new Error('currentDeviceDetails is not available');
656661
}
657662
const testArgs = {
658-
fixtureServerPort: `${getFixturesServerPort()}`,
659-
commandQueueServerPort: `${commandQueueServer.getServerPort()}`,
663+
fixtureServerPort: isAndroid
664+
? `${FALLBACK_FIXTURE_SERVER_PORT}`
665+
: `${getFixturesServerPort()}`,
666+
commandQueueServerPort: isAndroid
667+
? `${FALLBACK_COMMAND_QUEUE_SERVER_PORT}`
668+
: `${commandQueueServer.getServerPort()}`,
660669
detoxURLBlacklistRegex: Utilities.BlacklistURLs,
661-
mockServerPort: `${mockServerPort}`,
662-
[ACCOUNT_ACTIVITY_WS.launchArgKey]: `${accountActivityWsServer.getServerPort()}`,
670+
mockServerPort: isAndroid
671+
? `${FALLBACK_MOCKSERVER_PORT}`
672+
: `${mockServerPort}`,
673+
[ACCOUNT_ACTIVITY_WS.launchArgKey]: isAndroid
674+
? `${ACCOUNT_ACTIVITY_WS.fallbackPort}`
675+
: `${accountActivityWsServer.getServerPort()}`,
663676
...(launchArgs || {}),
664677
};
665-
console.log('currentDeviceDetails', currentDeviceDetails);
666-
console.log('args', testArgs);
667678

668-
await PlaywrightUtilities.launchApp(currentDeviceDetails, {
669-
launchArgs: testArgs,
670-
});
679+
if (deviceCommands) {
680+
await deviceCommands.clearAppData();
681+
}
682+
683+
const appStateRequest = fixtureServer.waitForNextStateRequest();
684+
try {
685+
await PlaywrightUtilities.launchApp(currentDeviceDetails, {
686+
launchArgs: testArgs,
687+
});
688+
await appStateRequest;
689+
} catch (error) {
690+
appStateRequest.catch(() => undefined);
691+
throw error;
692+
}
671693
} else {
672694
throw new Error(`Unsupported test runner: ${framework}`);
673695
}
@@ -687,6 +709,7 @@ export async function withFixtures(
687709
mockServer: mockServerInstance.server,
688710
localNodes,
689711
commandQueueServer,
712+
deviceCommands,
690713
});
691714
} catch (error) {
692715
testError = error as Error;

0 commit comments

Comments
 (0)