From 3ce5af20e3dba58050d7793043c60ec3e82d8c20 Mon Sep 17 00:00:00 2001 From: Seb Date: Mon, 29 Jan 2024 17:20:37 +1100 Subject: [PATCH] TINY-10604: LT improvements - Pass tunnel name for webdriver - Use LT project naming - Mark LT test results --- modules/server/src/main/ts/BedrockAuto.ts | 19 +++++++++------ .../server/src/main/ts/bedrock/auto/Driver.ts | 3 +++ .../src/main/ts/bedrock/auto/RemoteDriver.ts | 23 ++++++++++++++++--- .../server/src/main/ts/bedrock/auto/Tunnel.ts | 5 ++++ .../src/main/ts/bedrock/core/Lifecycle.ts | 6 +++++ 5 files changed, 46 insertions(+), 10 deletions(-) diff --git a/modules/server/src/main/ts/BedrockAuto.ts b/modules/server/src/main/ts/BedrockAuto.ts index d66204bd..6be27654 100644 --- a/modules/server/src/main/ts/BedrockAuto.ts +++ b/modules/server/src/main/ts/BedrockAuto.ts @@ -32,8 +32,10 @@ export const go = (bedrockAutoSettings: BedrockAutoSettings): void => { const routes = RunnerRoutes.generate('auto', settings.projectdir, settings.basedir, settings.config, settings.bundler, settings.testfiles, settings.chunk, settings.retries, settings.singleTimeout, settings.stopOnFailure, basePage, settings.coverage, settings.polyfills); + const shutdownServices: (() => Promise)[] = []; + const shutdown = (services: (() => Promise)[]) => () => Promise.allSettled(services.map((fn) => fn())); + routes.then(async (runner) => { - const shutdownServices: (() => Promise)[] = []; // LambdaTest Tunnel must know dev server port, but tunnel must be created before dev server. const servicePort = await portfinder.getPortPromise({ @@ -54,6 +56,7 @@ export const go = (bedrockAutoSettings: BedrockAutoSettings): void => { if (remoteWebdriver == 'aws') { console.log('INFO: Webdriver creation waits for device farm session to activate. Takes 30-45s.'); } + const driver = await Driver.create({ browser: browserName, basedir: settings.basedir, @@ -71,7 +74,9 @@ export const go = (bedrockAutoSettings: BedrockAutoSettings): void => { devicefarmRegion: settings.devicefarmRegion, deviceFarmArn: settings.devicefarmArn, browserVersion: settings.browserVersion, - platformName: settings.platformName + platformName: settings.platformName, + tunnel, + name: settings.name ? settings.name : 'bedrock-auto' }); const webdriver = driver.webdriver; @@ -85,8 +90,6 @@ export const go = (bedrockAutoSettings: BedrockAutoSettings): void => { }); shutdownServices.push(service.shutdown, driver.shutdown); - const shutdown = () => Promise.allSettled(shutdownServices.map((shutdown_fn) => shutdown_fn())); - try { if (!isHeadless) { console.log('bedrock-auto ' + Version.get() + ' available at: ' + location); @@ -106,13 +109,15 @@ export const go = (bedrockAutoSettings: BedrockAutoSettings): void => { return Reporter.writePollExit(settings, data); }); - return Lifecycle.done(result, webdriver, shutdown, settings.gruntDone, settings.delayExit); + return Lifecycle.done(result, webdriver, shutdown(shutdownServices), settings.gruntDone, settings.delayExit); } catch (e) { - return Lifecycle.error(e, webdriver, shutdown, settings.gruntDone, settings.delayExit); + return Lifecycle.error(e, webdriver, shutdown(shutdownServices), settings.gruntDone, settings.delayExit); } - }).catch((err) => { + }).catch(async (err) => { // Chalk does not use a formatter. Using node's built-in to expand Objects, etc. console.error(chalk.red('Error creating webdriver', format(err))); + // Shutdown tunnels in case webdriver fails + await shutdown(shutdownServices)(); Lifecycle.exit(settings.gruntDone, ExitCodes.failures.unexpected); }); }; diff --git a/modules/server/src/main/ts/bedrock/auto/Driver.ts b/modules/server/src/main/ts/bedrock/auto/Driver.ts index 60d584dd..b5665878 100644 --- a/modules/server/src/main/ts/bedrock/auto/Driver.ts +++ b/modules/server/src/main/ts/bedrock/auto/Driver.ts @@ -9,6 +9,7 @@ import * as DriverLoader from './DriverLoader'; import * as RemoteDriver from './RemoteDriver'; import deepmerge = require('deepmerge'); import { RemoteOptions } from 'webdriverio'; +import { Tunnel } from './Tunnel'; export interface DriverSettings { basedir: string; @@ -30,6 +31,8 @@ export interface DriverSettings { deviceFarmArn?: string; platformName?: string; browserVersion: string; + tunnel?: Tunnel; + name?: string; } export interface Driver { diff --git a/modules/server/src/main/ts/bedrock/auto/RemoteDriver.ts b/modules/server/src/main/ts/bedrock/auto/RemoteDriver.ts index e9cf35e2..e80c6501 100644 --- a/modules/server/src/main/ts/bedrock/auto/RemoteDriver.ts +++ b/modules/server/src/main/ts/bedrock/auto/RemoteDriver.ts @@ -52,7 +52,7 @@ const createFarm = async (browserName: string, remoteOpts: WebdriverIO.RemoteOpt webdriver: driver, shutdown: (_: boolean | undefined) => { console.log('Shutting down Device Farm. This currently does nothing.'); - return Promise.resolve(); + return driver.deleteSession(); } }; } catch (e) { @@ -70,7 +70,7 @@ export const getApi = async (settings: DriverSettings, browser: string, opts: We const driver = await WebdriverIO.remote(opts); return { webdriver: driver, - shutdown: () => Promise.resolve() + shutdown: () => driver.deleteSession() }; } return Promise.reject('Unrecognized remote provider: [' + remoteWebdriver + ']'); @@ -78,6 +78,21 @@ export const getApi = async (settings: DriverSettings, browser: string, opts: We const addDriverSpecificOpts = (opts: WebdriverIO.RemoteOptions, settings: DriverSettings): WebdriverIO.RemoteOptions => { if (settings.remoteWebdriver === 'lambdatest') { + // For naming in LT we use PROJECT_BUILD[_NAME] or BUILD + const getProjectNaming = (name: string) => { + const names = name.split('_'); + if (names.length > 1) { + return { + project: names[0], + build: names[1], + ...(names.length > 2 ? { name: names[2] } : {}) + } + } else { + return { build: names[0] } + } + }; + const names = settings.name ? getProjectNaming(settings.name) : {}; + const tunnelName = settings.tunnel?.name ? { tunnelName: settings.tunnel.name } : {}; const platformName = settings.platformName ? { platformName: settings.platformName } : {}; return deepmerge(opts, { user: settings.username, @@ -90,7 +105,9 @@ const addDriverSpecificOpts = (opts: WebdriverIO.RemoteOptions, settings: Driver console: true, w3c: true, plugin: 'node_js-webdriverio', - ...platformName + ...platformName, + ...tunnelName, + ...names } } }); diff --git a/modules/server/src/main/ts/bedrock/auto/Tunnel.ts b/modules/server/src/main/ts/bedrock/auto/Tunnel.ts index 42bb82e6..ee538c17 100644 --- a/modules/server/src/main/ts/bedrock/auto/Tunnel.ts +++ b/modules/server/src/main/ts/bedrock/auto/Tunnel.ts @@ -6,6 +6,7 @@ import { Tunnel as LambdaTunnel } from '@lambdatest/node-tunnel'; export interface Tunnel { url: URL; + name?: string; shutdown: () => Promise; } @@ -85,8 +86,11 @@ const createSSH = async (port: number | string, domain: string): Promise // and no proper typing for it. Excuse the hard type casting const createLambda = async (port: number | string, credentials: LambdaCredentials): Promise => { const tunnel = new LambdaTunnel(); + const suffix = crypto.randomUUID(); + const tunnelName = "bedrock-tunnel-" + suffix; const tunnelArguments = { + tunnelName, user: credentials.user, key: credentials.key, port: port.toString() @@ -99,6 +103,7 @@ const createLambda = async (port: number | string, credentials: LambdaCredential const result: Tunnel = { url: new URL('http://localhost:' + port), + name: tunnelName, shutdown }; diff --git a/modules/server/src/main/ts/bedrock/core/Lifecycle.ts b/modules/server/src/main/ts/bedrock/core/Lifecycle.ts index 0e4037e0..a01d1e18 100644 --- a/modules/server/src/main/ts/bedrock/core/Lifecycle.ts +++ b/modules/server/src/main/ts/bedrock/core/Lifecycle.ts @@ -12,6 +12,10 @@ const exitDelay = (driver: Browser, delayExiting: boolean) => { return delayExiting ? driver.pause(17 * 60 * 1000) : Promise.resolve(); }; +const markLambdaTest = async (driver: Browser, status: 'failed' | 'passed') => { + await driver.executeScript("lambda-status=" + status, []); +} + export const exit = (gruntDone: GruntDoneFn, exitCode: number): void => { if (gruntDone !== undefined) { gruntDone(exitCode === 0); @@ -23,10 +27,12 @@ export const exit = (gruntDone: GruntDoneFn, exitCode: number): void => { export const done = async (result: Attempt, driver: Browser, shutdown: ShutdownFn, gruntDone: GruntDoneFn, delayExiting: boolean): Promise => { // Only delay exiting if tests failed. const exitCode = await Attempt.cata(result, async (errs) => { + await markLambdaTest(driver, 'failed'); await exitDelay(driver, delayExiting); console.log(chalk.red(errs.join('\n'))); return ExitCodes.failures.tests; }, async () => { + await markLambdaTest(driver, 'passed'); console.log(chalk.green('All tests passed.')); return ExitCodes.success; });