Skip to content

Commit

Permalink
Allow usage of the Waldo staging environment
Browse files Browse the repository at this point in the history
  • Loading branch information
vbfox committed Jun 6, 2024
1 parent b9c16b7 commit 8213db8
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 108 deletions.
68 changes: 0 additions & 68 deletions src/config.ts

This file was deleted.

17 changes: 17 additions & 0 deletions src/configuration/configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {
type ProcessEnvironmentConfiguration,
getProcessEnvironmentConfiguration,
} from './processEnvironment.js';
import { loadWaldoProfile } from './waldoProfile.js';

export type Configuration = ProcessEnvironmentConfiguration;

export async function loadConfiguration(): Promise<Configuration> {
const envConfig = getProcessEnvironmentConfiguration();
const profileConfig = await loadWaldoProfile();

return {
...envConfig,
token: envConfig.token ?? profileConfig.user_token,
};
}
32 changes: 32 additions & 0 deletions src/configuration/processEnvironment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as process from 'process';

import type { WaldoEnvironment } from '../types.js';
import { parseEnvironment } from './waldoEnvironment.js';

export type ProcessEnvironmentConfiguration = {
readonly environment: WaldoEnvironment | undefined;
readonly token: string | undefined;
readonly sessionId: string | undefined;
readonly versionId: string | undefined;
readonly showSession: boolean | undefined;
};

function parseBoolean(value: string | undefined): boolean | undefined {
if (value === undefined) {
return undefined;
}

return value === '1' || value.toLowerCase() === 'true';
}

export function getProcessEnvironmentConfiguration(): ProcessEnvironmentConfiguration {
const { env } = process;

return {
environment: parseEnvironment(env.WALDO_ENVIRONMENT),
token: env.WALDO_API_TOKEN ?? env.WALDO_TOKEN ?? process.env.TOKEN,
sessionId: env.WALDO_SESSION_ID ?? env.SESSION_ID,
versionId: env.WALDO_APP_VERSION_ID ?? env.VERSION_ID,
showSession: parseBoolean(env.WALDO_SHOW_SESSION) ?? parseBoolean(env.SHOW_SESSION),
};
}
51 changes: 51 additions & 0 deletions src/configuration/waldoEnvironment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Options } from '@wdio/types';
import type { WaldoEnvironment } from '../types.js';

export type EnvironmentConnection = {
readonly hostname: NonNullable<Options.Connection['hostname']>;
readonly port: NonNullable<Options.Connection['port']>;
readonly protocol: NonNullable<Options.Connection['protocol']>;
readonly path: NonNullable<Options.Connection['path']>;
};

export const DEFAULT_REMOTE_CONFIG: EnvironmentConnection = {
hostname: 'core.waldo.com',
port: 443,
protocol: 'https',
path: '/wd/hub',
};

export const STAGING_REMOTE_CONFIG: EnvironmentConnection = {
...DEFAULT_REMOTE_CONFIG,
hostname: 'core-staging.waldo.io',
};

export const LOCAL_REMOTE_CONFIG: EnvironmentConnection = {
...DEFAULT_REMOTE_CONFIG,
hostname: 'localhost',
port: 3035,
};

export function parseEnvironment(value: string | undefined): WaldoEnvironment | undefined {
switch (value) {
case 'production':
case 'staging':
case 'development':
return value;
default:
return undefined;
}
}

export function getEnvironmentConnectionOptions(
environment: WaldoEnvironment | undefined,
): EnvironmentConnection {
switch (environment) {
case 'staging':
return STAGING_REMOTE_CONFIG;
case 'development':
return LOCAL_REMOTE_CONFIG;
default:
return DEFAULT_REMOTE_CONFIG;
}
}
22 changes: 22 additions & 0 deletions src/configuration/waldoProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as fs from 'fs';
import { homedir } from 'os';
import { parse as parseYaml } from 'yaml';

export type WaldoProfileYaml = {
user_token?: string;
};

export async function loadWaldoProfile(): Promise<WaldoProfileYaml> {
const waldoProfileFile = `${homedir()}/.waldo/profile.yml`;
try {
const ymlContent = await fs.promises.readFile(waldoProfileFile, 'utf-8');
return parseYaml(ymlContent);
} catch (error: any) {
// File not existing is expected - any other error is not
if (error.code !== 'ENOENT') {
throw error;
}

return {};
}
}
12 changes: 0 additions & 12 deletions src/constants.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { WaldoWdioService } from './service.js';
import { WaldoWdioLauncherService } from './launcher.js';
import { WaldoBrowser, WaldoCapabilities, WaldoServiceOptions } from './types.js';
import type { WaldoBrowser, WaldoCapabilities, WaldoServiceOptions } from './types.js';

export default WaldoWdioService;
export const launcher = WaldoWdioLauncherService;
Expand Down
23 changes: 13 additions & 10 deletions src/launcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
/* eslint-disable max-classes-per-file */
import type { Services, Capabilities, Options } from '@wdio/types';

import { LOCAL_REMOTE_CONFIG, DEFAULT_REMOTE_CONFIG } from './constants.js';
import { type Configuration, loadConfiguration } from './config.js';
import {
LOCAL_REMOTE_CONFIG,
DEFAULT_REMOTE_CONFIG,
getEnvironmentConnectionOptions,
} from './configuration/waldoEnvironment.js';
import { type Configuration, loadConfiguration } from './configuration/configuration.js';
import type { WaldoServiceOptions, CapabilitiesWithWaldo } from './types.js';

export class WaldoWdioLauncherService implements Services.ServiceInstance {
Expand Down Expand Up @@ -32,16 +36,16 @@ export class WaldoWdioLauncherService implements Services.ServiceInstance {
for (const cap of capabilities) {
const alwaysMatch = 'alwaysMatch' in cap ? cap.alwaysMatch : cap;
this.checkCapabilities(config, alwaysMatch, configuration);
await this.overrideRemoteInCapabilities(alwaysMatch, configuration);
await this.overrideRemoteInCapabilities(alwaysMatch);
}
} else if (typeof capabilities === 'object' && capabilities !== null) {
for (const cap of Object.values(capabilities)) {
this.checkCapabilities(config, cap, configuration);
await this.overrideRemoteInCapabilities(cap, configuration);
await this.overrideRemoteInCapabilities(cap);
}
}

await this.overrideRemoteInCapabilities(config, configuration);
await this.overrideRemoteInCapabilities(config);
}

private checkCapabilities(
Expand Down Expand Up @@ -81,11 +85,10 @@ export class WaldoWdioLauncherService implements Services.ServiceInstance {
}
}

private async overrideRemoteInCapabilities(
capabilities: Options.Connection,
configuration: Configuration,
): Promise<void> {
const remoteConfig = configuration.localDev ? LOCAL_REMOTE_CONFIG : DEFAULT_REMOTE_CONFIG;
private async overrideRemoteInCapabilities(capabilities: Options.Connection): Promise<void> {
const remoteConfig = getEnvironmentConnectionOptions(
this.serviceOptions.environment ?? this.configuration?.environment,
);

/* eslint-disable no-param-reassign */
capabilities.hostname = remoteConfig.hostname;
Expand Down
4 changes: 2 additions & 2 deletions src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { Services, Frameworks } from '@wdio/types';

import { addDriverCommands } from './commands.js';
import { waitForSessionReady } from './utils.js';
import type { CapabilitiesWithWaldo, WaldoRemoteCapability } from './types.js';
import type { CapabilitiesWithWaldo, WaldoRemoteCapability, WaldoServiceOptions } from './types.js';

const log = logger('@waldoapp/wdio-service');

Expand Down Expand Up @@ -34,7 +34,7 @@ export class WaldoWdioService implements Services.ServiceInstance {
}

if (waldoOptions.waitSessionReady !== false) {
await waitForSessionReady(browser.sessionId);
await waitForSessionReady(browser);
}
}

Expand Down
11 changes: 10 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { Capabilities } from '@wdio/types';

import type { AppiumElement } from './utils.js';

export type WaldoEnvironment = 'production' | 'staging' | 'development';

type WaldoSharedOptions = {
/**
* Security token
Expand Down Expand Up @@ -77,7 +79,14 @@ export type WaldoRemoteCapability = {

export type RemoteCapabilityWithWaldo = Capabilities.RemoteCapability & WaldoRemoteCapability;

export type WaldoServiceOptions = WaldoSharedOptions;
export type WaldoServiceOptions = WaldoSharedOptions & {
/**
* Waldo environment.
*
* Default: `production`
*/
environment?: WaldoEnvironment;
};

export type BoundingBox = { width: number; height: number; top: number; left: number };

Expand Down
16 changes: 8 additions & 8 deletions src/urls.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { getLocalDevFromEnvironment } from './config.js';
import { DEFAULT_REMOTE_CONFIG, LOCAL_REMOTE_CONFIG } from './constants.js';
import { DEFAULT_REMOTE_CONFIG } from './configuration/waldoEnvironment.js';

const remoteConfig = getLocalDevFromEnvironment() ? LOCAL_REMOTE_CONFIG : DEFAULT_REMOTE_CONFIG;

export function getRemoteBaseUrl() {
return `${remoteConfig.protocol}://${remoteConfig.hostname}:${remoteConfig.port}`;
export function getRemoteBaseUrl(browser: WebdriverIO.Browser) {
const protocol = browser.options.protocol ?? 'http';
const hostname = browser.options.hostname ?? DEFAULT_REMOTE_CONFIG.hostname;
const port = browser.options.port ?? DEFAULT_REMOTE_CONFIG.port;
return `${protocol}://${hostname}:${port}`;
}

export function getWdUrl(path: string) {
return `${getRemoteBaseUrl()}${remoteConfig.path ?? '/'}${path}`;
export function getWdUrl(browser: WebdriverIO.Browser, path: string) {
return `${getRemoteBaseUrl(browser)}${browser.options.path ?? '/'}${path}`;
}
17 changes: 11 additions & 6 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { BoundingBox } from './types.js';

const log = logger('@waldoapp/wdio-service');

declare var driver: WebdriverIO.Browser;

export type AppiumElement = ElementReference & { ELEMENT: string };
export type SessionDevice = {
model: string;
Expand All @@ -28,8 +30,11 @@ export async function waitAsPromise(timeMillis: number) {
});
}

async function getSessionInfo(sessionId: string): Promise<SessionInfo> {
const waldoSessionUrl = getWdUrl(`/session/${sessionId}`);
async function getSessionInfo(
driver: WebdriverIO.Browser,
sessionId: string,
): Promise<SessionInfo> {
const waldoSessionUrl = getWdUrl(driver, `/session/${sessionId}`);
const { data } = await axios(waldoSessionUrl);
return data;
}
Expand All @@ -48,12 +53,12 @@ export async function getLatestBuild(token: string) {
return builds[0];
}

export async function waitForSessionReady(sessionId: string) {
const sessionInfo = await getSessionInfo(sessionId);
export async function waitForSessionReady(driver: WebdriverIO.Browser) {
const sessionInfo = await getSessionInfo(driver, driver.sessionId);
log.info(`Preparing device and installing your application...`);
if (sessionInfo.status === 'setup') {
await waitAsPromise(5000);
await waitForSessionReady(sessionId);
await waitForSessionReady(driver);
return;
}
if (sessionInfo.status !== 'ready') {
Expand All @@ -68,7 +73,7 @@ export async function logEvent(
payload: Record<string, string | boolean | number>,
level: 'debug' | 'info' | 'warn' | 'error' = 'info',
) {
const url = `${getRemoteBaseUrl()}/wd/hub/session/${driver.sessionId}/timelineEvent`;
const url = `${getRemoteBaseUrl(driver)}/wd/hub/session/${driver.sessionId}/timelineEvent`;
await axios.post(url, { level, message, payload });
}

Expand Down

0 comments on commit 8213db8

Please sign in to comment.