Skip to content

Commit

Permalink
feat: Support simulator/emulator test (#9)
Browse files Browse the repository at this point in the history
* feat: Support simulator/emulator test

* rename file

* extract sauce opts from caps

* let testcafe handle exception
  • Loading branch information
tianfeng92 authored Jul 29, 2024
1 parent c086dc1 commit 58fa94b
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 10 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
lib
node_modules
.vscode
19 changes: 19 additions & 0 deletions src/device.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Checks if the provided name indicates a device (simulator or emulator).
*
* @param name - The name to check.
* @returns `true` if the name includes 'Simulator' or 'Emulator', otherwise `false`.
*/
export function isDevice(name: string): boolean {
return name.includes('Simulator') || name.includes('Emulator');
}

/**
* Checks if the provided name indicates a simulator.
*
* @param name - The name to check.
* @returns `true` if the name includes 'Simulator', otherwise `false`.
*/
export function isSimulator(name: string): boolean {
return name.includes('Simulator');
}
52 changes: 42 additions & 10 deletions src/driver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import wd, { Client } from 'webdriver';
import { isDevice, isSimulator } from './device';
import { CreateSessionError } from './errors';

export class SauceDriver {
private readonly username: string;
Expand All @@ -12,6 +14,39 @@ export class SauceDriver {
this.tunnelName = tunnelName;
}

createCapabilities(
browserName: string,
browserVersion: string,
platformName: string,
): WebDriver.Capabilities {
const sauceOpts = {
name: 'testcafe sauce provider job', // TODO make this configurable
build: 'TCPRVDR', // TODO make this configurable
tunnelIdentifier: this.tunnelName,
idleTimeout: 3600, // 1 hour
enableTestReport: true,
};

if (!isDevice(browserName)) {
return {
browserName,
browserVersion,
platformName,
'sauce:options': sauceOpts,
};
}

const isSim = isSimulator(browserName);
return {
browserName: isSim ? 'Safari' : 'Chrome',
platformName: isSim ? 'iOS' : 'Android',
'appium:deviceName': browserName,
'appium:platformVersion': browserVersion,
'appium:automationName': isSim ? 'XCUITest' : 'UiAutomator2',
'sauce:options': sauceOpts,
};
}

async openBrowser(
browserId: string,
url: string,
Expand All @@ -21,27 +56,24 @@ export class SauceDriver {
) {
const webDriver = await wd.newSession({
protocol: 'https',
hostname: `ondemand.saucelabs.com`, // TODO multi region support
hostname: `ondemand.us-west-1.saucelabs.com`, // TODO multi region support
port: 443,
user: this.username,
key: this.accessKey,
capabilities: {
capabilities: this.createCapabilities(
browserName,
browserVersion,
platformName,
'sauce:options': {
name: 'testcafe sauce provider job', // TODO make this configurable
build: 'TCPRVDR', // TODO make this configurable
tunnelIdentifier: this.tunnelName,
idleTimeout: 3600, // 1 hour
enableTestReport: true,
} as WebDriver.DesiredCapabilities,
},
),
logLevel: 'error',
connectionRetryTimeout: 9 * 60 * 1000, // 9 minutes
connectionRetryCount: 3,
path: '/wd/hub',
});
if (!webDriver.sessionId) {
throw new CreateSessionError();
}

this.sessions.set(browserId, webDriver);

// TODO do we need a keep-alive?
Expand Down
7 changes: 7 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@ export class TunnelNameError extends Error {
this.name = 'TunnelNameError';
}
}

export class CreateSessionError extends Error {
constructor() {
super('Failed to run test on Sauce Labs: no session id returned');
this.name = 'CreateSessionError';
}
}
22 changes: 22 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SauceDriver } from './driver.js';
import { AuthError, TunnelNameError } from './errors';
import { getPlatforms } from './api';
import { rcompareOses, rcompareVersions } from './sort';
import { isDevice } from './device.js';

type Browser = string;
type Version = string;
Expand Down Expand Up @@ -89,6 +90,27 @@ module.exports = {
});
});
});

const devices: string[] = [];
browserMap.forEach((versionMap, name) => {
if (!isDevice(name)) {
return;
}
[...versionMap.keys()]
.sort(rcompareVersions)
.slice(0, 2)
.forEach((v) => {
const oses = versionMap.get(v);
if (!oses) {
return;
}
oses.forEach((os) => {
devices.push(`${name}@${v}:${os}`);
});
});
});
devices.sort().reverse();
platforms.push(...devices);
},

/**
Expand Down

0 comments on commit 58fa94b

Please sign in to comment.