Skip to content

Commit

Permalink
feat: created new test-utils directory to contain dummy live apps
Browse files Browse the repository at this point in the history
  • Loading branch information
ggilchrist-ledger committed Jul 11, 2023
1 parent 9801101 commit be3d133
Show file tree
Hide file tree
Showing 39 changed files with 511 additions and 909 deletions.
1 change: 1 addition & 0 deletions apps/ledger-live-desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"@ledgerhq/live-network": "workspace:^",
"@ledgerhq/logs": "workspace:^",
"@ledgerhq/react-ui": "workspace:^",
"@ledgerhq/test-utils": "workspace:^",
"@ledgerhq/types-cryptoassets": "workspace:^",
"@ledgerhq/types-devices": "workspace:^",
"@ledgerhq/types-live": "workspace:^",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import test from "../../fixtures/common";
import { expect } from "@playwright/test";
import * as server from "../../utils/serve-dummy-app";
import { Layout } from "../../models/Layout";
import { DiscoverPage } from "../../models/DiscoverPage";
import { PortfolioPage } from "../../models/PortfolioPage";
Expand All @@ -9,6 +8,7 @@ import { AccountsPage } from "../../models/AccountsPage";
import { AccountPage } from "../../models/AccountPage";
import { SettingsPage } from "../../models/SettingsPage";
import { MarketPage } from "../../models/MarketPage";
import { getLiveAppManifest, startDummyServer, stopDummyServer } from "@ledgerhq/test-utils";

test.use({
userdata: "1AccountBTC1AccountETH",
Expand All @@ -26,14 +26,14 @@ let continueTest = false;
test.beforeAll(async ({ request }) => {
// Check that dummy app in tests/utils/dummy-ptx-app has been started successfully
try {
const port = await server.start("dummy-ptx-app/public");
const port = await startDummyServer("dummy-ptx-app/public");
const url = `http://localhost:${port}`;
const response = await request.get(url);
if (response.ok() && port) {
continueTest = true;
console.info(`========> Dummy test app successfully running on port ${port}! <=========`);
process.env.MOCK_REMOTE_LIVE_MANIFEST = JSON.stringify(
server.liveAppManifest({
getLiveAppManifest({
id: "multibuy",
url,
name: "Dummy Buy / Sell App",
Expand Down Expand Up @@ -65,7 +65,7 @@ test.beforeAll(async ({ request }) => {
});

test.afterAll(async () => {
await server.stop();
await stopDummyServer();
console.info(`========> Dummy test app stopped <=========`);
delete process.env.MOCK_REMOTE_LIVE_MANIFEST;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Layout } from "../../models/Layout";
import { Drawer } from "../../models/Drawer";
import { Modal } from "../../models/Modal";
import { DeviceAction } from "../../models/DeviceAction";
import * as server from "../../utils/serve-dummy-app";
import { startDummyServer, getLiveAppManifest, stopDummyServer } from "@ledgerhq/test-utils";

test.use({ userdata: "1AccountBTC1AccountETH" });

Expand All @@ -14,14 +14,14 @@ let continueTest = false;
test.beforeAll(async ({ request }) => {
// Check that dummy app in tests/utils/dummy-app-build has been started successfully
try {
const port = await server.start("dummy-live-app/build");
const port = await startDummyServer("dummy-live-app/build");
const url = `http://localhost:${port}`;
const response = await request.get(url);
if (response.ok()) {
continueTest = true;
console.info(`========> Dummy test app successfully running on port ${port}! <=========`);
process.env.MOCK_REMOTE_LIVE_MANIFEST = JSON.stringify(
server.liveAppManifest({
getLiveAppManifest({
id: "dummy-live-app",
url,
permissions: [{ method: "*" }],
Expand All @@ -45,7 +45,7 @@ test.beforeAll(async ({ request }) => {
});

test.afterAll(async () => {
await server.stop();
await stopDummyServer();
console.info(`========> Dummy test app stopped <=========`);
delete process.env.MOCK_REMOTE_LIVE_MANIFEST;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Layout } from "../../models/Layout";
import { Drawer } from "../../models/Drawer";
import { Modal } from "../../models/Modal";
import { DeviceAction } from "../../models/DeviceAction";
import * as server from "../../utils/serve-dummy-app";
import { startDummyServer, getLiveAppManifest, stopDummyServer } from "@ledgerhq/test-utils";
import { randomUUID } from "crypto";

test.use({ userdata: "1AccountBTC1AccountETH" });
Expand All @@ -15,7 +15,7 @@ let continueTest = false;
test.beforeAll(async ({ request }) => {
// Check that dummy app in tests/utils/dummy-app-build has been started successfully
try {
const port = await server.start("dummy-wallet-app/build");
const port = await startDummyServer("dummy-wallet-app/build");
const url = `http://localhost:${port}`;
const response = await request.get(url);
if (response.ok()) {
Expand All @@ -24,7 +24,7 @@ test.beforeAll(async ({ request }) => {
`========> Dummy Wallet API app successfully running on port ${port}! <=========`,
);
process.env.MOCK_REMOTE_LIVE_MANIFEST = JSON.stringify(
server.liveAppManifest({
getLiveAppManifest({
id: "dummy-live-app",
url,
name: "Dummy Wallet API Live App",
Expand All @@ -49,7 +49,7 @@ test.beforeAll(async ({ request }) => {
});

test.afterAll(async () => {
await server.stop();
await stopDummyServer();
console.info(`========> Dummy Wallet API app stopped <=========`);
delete process.env.MOCK_REMOTE_LIVE_MANIFEST;
});
Expand All @@ -70,7 +70,7 @@ test("Wallet API methods", async ({ page }) => {
await drawer.waitForDrawerToDisappear();

const id = randomUUID();
const resPromise = discoverPage.send({
const response = discoverPage.send({
jsonrpc: "2.0",
id,
method: "account.request",
Expand All @@ -82,9 +82,7 @@ test("Wallet API methods", async ({ page }) => {
await drawer.selectCurrency("bitcoin");
await drawer.selectAccount("bitcoin");

const res = await resPromise;

expect(res).toStrictEqual({
await expect(response).resolves.toMatchObject({
jsonrpc: "2.0",
id,
result: {
Expand All @@ -94,7 +92,7 @@ test("Wallet API methods", async ({ page }) => {
balance: "35688397",
blockHeight: 194870,
currency: "bitcoin",
lastSyncDate: "2020-03-14T13:34:42.000Z",
// lastSyncDate: "2020-03-14T13:34:42.000Z", TODO: make sure the sync date doesn't change
name: "Bitcoin 1 (legacy)",
spendableBalance: "35688397",
},
Expand All @@ -104,7 +102,7 @@ test("Wallet API methods", async ({ page }) => {

await test.step("account.receive", async () => {
const id = randomUUID();
const resPromise = discoverPage.send({
const response = discoverPage.send({
jsonrpc: "2.0",
id,
method: "account.receive",
Expand All @@ -116,9 +114,7 @@ test("Wallet API methods", async ({ page }) => {
await deviceAction.openApp();
await modal.waitForModalToDisappear();

const res = await resPromise;

expect(res).toStrictEqual({
await expect(response).resolves.toStrictEqual({
jsonrpc: "2.0",
id,
result: {
Expand Down
23 changes: 23 additions & 0 deletions apps/ledger-live-mobile/e2e/models/liveApps/liveAppWebview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,31 @@ import { randomUUID } from "crypto";
import { web, by } from "detox";
import { e2eBridgeServer } from "../../bridge/server";
import { first, filter, map } from "rxjs/operators";
import { startDummyServer } from "@ledgerhq/test-utils";

export default class LiveAppWebview {
async startLiveApp(liveAppDirectory: string, liveAppPort = 3000) {
try {
const port = await startDummyServer(`${liveAppDirectory}/build`, liveAppPort);

const url = `http://localhost:${port}`;
const response = await fetch(url);
if (response.ok) {
// eslint-disable-next-line no-console
console.info(
`========> Dummy Wallet API app successfully running on port ${port}! <=========`,
);
return true;
} else {
throw new Error("Ping response != 200, got: " + response.status);
}
} catch (error) {
console.warn(`========> Dummy test app not running! <=========`);
console.error(error);
return false;
}
}

async send(params: Record<string, unknown>) {
const webview = web.element(by.web.id("root"));
const id = randomUUID();
Expand Down
34 changes: 5 additions & 29 deletions apps/ledger-live-mobile/e2e/specs/wallet-api.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import * as detox from "detox"; // this is because we need to use both the jest expect and the detox.expect version, which has some different assertions
import { loadConfig } from "../bridge/server";
import { isAndroid } from "../helpers";
// TODO: move it to common or make it as independent workspace
import * as server from "../../../ledger-live-desktop/tests/utils/serve-dummy-app";
import PortfolioPage from "../models/wallet/portfolioPage";
import DiscoveryPage from "../models/discovery/discoveryPage";
import LiveAppWebview from "../models/liveApps/liveAppWebview";
import CryptoDrawer from "../models/liveApps/cryptoDrawer";
import { stopDummyServer } from "@ledgerhq/test-utils";

let portfolioPage: PortfolioPage;
let discoverPage: DiscoveryPage;
Expand All @@ -15,35 +14,12 @@ let cryptoDrawer: CryptoDrawer;

let continueTest: boolean;

describe.skip("Wallet API methods", () => {
describe("Wallet API methods", () => {
beforeAll(async () => {
// TODO: Move this to LiveAppWebview
await detox.device.reverseTcpPort(52619); // To allow the android emulator to access the dummy app
// Check that dummy app in tests/utils/dummy-app-build has been started successfully
try {
const port = await server.start(
"../../../ledger-live-desktop/tests/utils/dummy-wallet-app/build",
52619,
);

await detox.device.reverseTcpPort(52619); // To allow the android emulator to access the dummy app

const url = `http://localhost:${port}`;
const response = await fetch(url);
if (response.ok) {
continueTest = true;

// eslint-disable-next-line no-console
console.info(
`========> Dummy Wallet API app successfully running on port ${port}! <=========`,
);
} else {
continueTest = false;
throw new Error("Ping response != 200, got: " + response.status);
}
} catch (error) {
console.warn(`========> Dummy test app not running! <=========`);
console.error(error);
}
const continueTest = await liveAppWebview.startLiveApp("dummy-wallet-app", 52619);

if (!continueTest || !isAndroid()) {
console.warn("Stopping Wallet API test setup");
Expand All @@ -69,7 +45,7 @@ describe.skip("Wallet API methods", () => {
});

afterAll(async () => {
await server.stop();
await stopDummyServer();
});

it("account.request", async () => {
Expand Down
1 change: 1 addition & 0 deletions apps/ledger-live-mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"@ledgerhq/react-native-hid": "workspace:^",
"@ledgerhq/react-native-hw-transport-ble": "workspace:^",
"@ledgerhq/react-native-passcode-auth": "^2.1.0",
"@ledgerhq/test-utils": "workspace:^",
"@ledgerhq/types-cryptoassets": "workspace:^",
"@ledgerhq/types-devices": "workspace:^",
"@ledgerhq/types-live": "workspace:^",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
22 changes: 22 additions & 0 deletions libs/test-utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@ledgerhq/test-utils",
"version": "0.0.1",
"description": "Dummy apps and utils for tests",
"keywords": [
"Ledger"
],
"main": "lib/index.js",
"module": "lib-es/index.js",
"types": "lib/index.d.ts",
"scripts": {
"clean": "rimraf lib lib-es",
"build": "tsc && tsc -m ES6 --outDir lib-es",
"install-deps:dummy-apps": "pnpm --filter='dummy-wallet-app' --filter='dummy-live-app' i",
"build:dummy-apps": "pnpm --filter='dummy-wallet-app' --filter='dummy-live-app' build"
},
"dependencies": {
"@ledgerhq/live-common": "workspace:^",
"@types/serve-handler": "^6.1.1",
"serve-handler": "^6.1.3"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,35 @@ import { AppManifest } from "@ledgerhq/live-common/wallet-api/types";

let dummyAppPath: string;

export const server = http.createServer((request, response) => {
// You pass two more arguments for config and middleware
// More details here: https://github.com/vercel/serve-handler#options
handler(request, response, {
public: path.resolve(__dirname, dummyAppPath),
});
});
export const dummyAppServer = http.createServer(
(request: http.IncomingMessage, response: http.ServerResponse) => {
// You pass two more arguments for config and middleware
// More details here: https://github.com/vercel/serve-handler#options

console.log(path.resolve(__dirname, dummyAppPath));
handler(request, response, {
public: path.resolve(__dirname, "..", dummyAppPath),
});
},
);

export function start(appPath: string, port = 0): Promise<number> {
export function startDummyServer(appPath: string, port = 0): Promise<number> {
dummyAppPath = appPath;

return new Promise((resolve, reject) => {
server
dummyAppServer
.listen(port, "localhost")
.once("listening", () => {
resolve((server.address() as any).port as number);
resolve((dummyAppServer.address() as any).port as number);
})
.once("error", error => {
server.close();
dummyAppServer.close();
reject(error);
});
});
}

export function liveAppManifest(params: Partial<AppManifest> & Pick<AppManifest, "url" | "id">) {
export function getLiveAppManifest(params: Partial<AppManifest> & Pick<AppManifest, "url" | "id">) {
const manifest = [
{
name: "Generic Live App",
Expand Down Expand Up @@ -76,10 +80,10 @@ export function liveAppManifest(params: Partial<AppManifest> & Pick<AppManifest,
return manifest;
}

export function stop(): Promise<void> {
server.close();
export function stopDummyServer(): Promise<void> {
dummyAppServer.close();
return new Promise(resolve => {
server.on("close", () => {
dummyAppServer.on("close", () => {
resolve();
});
});
Expand Down
8 changes: 8 additions & 0 deletions libs/test-utils/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.base",
"compilerOptions": {
"declaration": true,
"outDir": "lib"
},
"include": ["src/**/*"]
}
Loading

0 comments on commit be3d133

Please sign in to comment.