Skip to content

Commit

Permalink
feat: Switch babel to typescript (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
mykola-mokhnach authored Aug 18, 2023
1 parent 67a4e75 commit e49d833
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 78 deletions.
18 changes: 15 additions & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
{
"extends": "@appium/eslint-config-appium",
"extends": ["@appium/eslint-config-appium-ts"],
"overrides": [
{
"files": "test/**/*.js",
"rules": {
"func-names": "off"
"func-names": "off",
"@typescript-eslint/no-var-requires": "off"
}
},
{
"files": "scripts/**/*",
"parserOptions": {"sourceType": "script"},
"rules": {
"@typescript-eslint/no-var-requires": "off"
}
}
]}
],
"rules": {
"require-await": "error"
}
}
2 changes: 1 addition & 1 deletion .mocharc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
require: ['@babel/register'],
require: ['ts-node/register'],
forbidOnly: Boolean(process.env.CI)
};
25 changes: 0 additions & 25 deletions babel.config.json

This file was deleted.

50 changes: 29 additions & 21 deletions lib/commands/record-screen.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ const DEFAULT_TIME_LIMIT_MS = 60 * 10 * 1000; // 10 minutes
const PROCESS_SHUTDOWN_TIMEOUT_MS = 10 * 1000;
const DEFAULT_EXT = '.mp4';


/**
*
* @param {string} localFile
* @param {string|null} remotePath
* @param {Object} uploadOptions
* @returns
*/
async function uploadRecordedMedia (localFile, remotePath = null, uploadOptions = {}) {
if (_.isEmpty(remotePath)) {
if (_.isEmpty(remotePath) || !remotePath) {
return (await util.toInMemoryBase64(localFile)).toString();
}

Expand Down Expand Up @@ -198,9 +204,9 @@ async function extractSimulatorUdid (caps) {
/**
* @typedef {Object} StartRecordingOptions
*
* @property {?string} codec [hevc] - Specifies the codec type: "h264" or "hevc"
* @property {?string} display [internal] - Supports "internal" or "external". Default is "internal"
* @property {?string} mask - For non-rectangular displays, handle the mask by policy:
* @property {string} codec [hevc] - Specifies the codec type: "h264" or "hevc"
* @property {string} display [internal] - Supports "internal" or "external". Default is "internal"
* @property {string} mask - For non-rectangular displays, handle the mask by policy:
* - ignored: The mask is ignored and the unmasked framebuffer is saved.
* - alpha: Not supported, but retained for compatibility; the mask is rendered black.
* - black: The mask is rendered black.
Expand All @@ -215,17 +221,18 @@ async function extractSimulatorUdid (caps) {
* This method uses `xcrun simctl io recordVideo` helper under the hood.
* Check the output of `xcrun simctl io` command for more details.
*
* @param {?StartRecordingOptions} options - The available options.
* @param {StartRecordingOptions} options - The available options.
* @this {import('../driver').SafariDriver}
* @throws {Error} If screen recording has failed to start or is not supported for the destination device.
*/
commands.startRecordingScreen = async function startRecordingScreen (options = {}) {
commands.startRecordingScreen = async function startRecordingScreen (options) {
const {
timeLimit,
codec,
display,
mask,
forceRestart = true,
} = options;
} = options ?? {};
if (this._screenRecorder?.isRunning) {
this.log.info('The screen recording is already running');
if (!forceRestart) {
Expand All @@ -248,7 +255,7 @@ commands.startRecordingScreen = async function startRecordingScreen (options = {
suffix: DEFAULT_EXT,
});
this._screenRecorder = new ScreenRecorder(udid, videoPath, this.log, {
timeLimit: parseInt(timeLimit, 10),
timeLimit: parseInt(`${timeLimit}`, 10),
codec,
display,
mask,
Expand All @@ -264,33 +271,34 @@ commands.startRecordingScreen = async function startRecordingScreen (options = {
/**
* @typedef {Object} StopRecordingOptions
*
* @property {?string} remotePath - The path to the remote location, where the resulting video should be uploaded.
* @property {string} remotePath - The path to the remote location, where the resulting video should be uploaded.
* The following protocols are supported: http/https, ftp.
* Null or empty string value (the default setting) means the content of resulting
* file should be encoded as Base64 and passed as the endpoint response value.
* An exception will be thrown if the generated media file is too big to
* fit into the available process memory.
* @property {?string} user - The name of the user for the remote authentication.
* @property {?string} pass - The password for the remote authentication.
* @property {?string} method - The http multipart upload method name. The 'PUT' one is used by default.
* @property {?Object} headers - Additional headers mapping for multipart http(s) uploads
* @property {?string} fileFieldName [file] - The name of the form field, where the file content BLOB should be stored for
* @property {string} user - The name of the user for the remote authentication.
* @property {string} pass - The password for the remote authentication.
* @property {string} method - The http multipart upload method name. The 'PUT' one is used by default.
* @property {Object} headers - Additional headers mapping for multipart http(s) uploads
* @property {string} fileFieldName [file] - The name of the form field, where the file content BLOB should be stored for
* http(s) uploads
* @property {?Object|Array<Pair>} formFields - Additional form fields for multipart http(s) uploads
* @property {Object|[string, string][]} formFields - Additional form fields for multipart http(s) uploads
*/

/**
* Stop recording the screen.
* If no screen recording has been started before then the method returns an empty string.
*
* @param {?StopRecordingOptions} options - The available options.
* @returns {string} Base64-encoded content of the recorded media file if 'remotePath'
* @param {StopRecordingOptions} options - The available options.
* @returns {Promise<string>} Base64-encoded content of the recorded media file if 'remotePath'
* parameter is falsy or an empty string.
* @this {import('../driver').SafariDriver}
* @throws {Error} If there was an error while getting the name of a media file
* or the file content cannot be uploaded to the remote location
* or screen recording is not supported on the device under test.
*/
commands.stopRecordingScreen = async function stopRecordingScreen (options = {}) {
commands.stopRecordingScreen = async function stopRecordingScreen (options) {
if (!this._screenRecorder) {
this.log.info('No screen recording has been started. Doing nothing');
return '';
Expand All @@ -302,11 +310,11 @@ commands.stopRecordingScreen = async function stopRecordingScreen (options = {})
this.log.info('No video data is found. Returning an empty string');
return '';
}
if (_.isEmpty(options.remotePath)) {
if (_.isEmpty(options?.remotePath)) {
const {size} = await fs.stat(videoPath);
this.log.debug(`The size of the resulting screen recording is ${util.toReadableSizeString(size)}`);
}
return await uploadRecordedMedia(videoPath, options.remotePath, options);
return await uploadRecordedMedia(videoPath, options?.remotePath, options);
};

export default commands;
10 changes: 8 additions & 2 deletions lib/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import commands from './commands/index';
import { formatCapsForServer } from './utils';
import { newMethodMap } from './method-map';

/** @type {import('@appium/types').RouteMatcher[]} */
const NO_PROXY = [
['GET', new RegExp('^/session/[^/]+/appium')],
['POST', new RegExp('^/session/[^/]+/appium')],
Expand All @@ -15,11 +16,14 @@ const NO_PROXY = [
['DELETE', new RegExp('^/session/[^/]+/cookie$')],
];

class SafariDriver extends BaseDriver {
export class SafariDriver extends BaseDriver {
/** @type {boolean} */
isProxyActive;

static newMethodMap = newMethodMap;

constructor (opts = {}) {
// @ts-ignore TODO: make args typed
super(opts);
this.desiredCapConstraints = desiredCapConstraints;
this.locatorStrategies = [
Expand Down Expand Up @@ -58,7 +62,9 @@ class SafariDriver extends BaseDriver {
return true;
}

// @ts-ignore TODO: make args typed
async createSession (...args) {
// @ts-ignore TODO: make args typed
const [sessionId, caps] = await super.createSession(...args);
this.safari = new SafariDriverServer(this.log);
try {
Expand All @@ -67,7 +73,7 @@ class SafariDriver extends BaseDriver {
await this.deleteSession();
throw e;
}
this.proxyReqRes = this.safari.proxy.proxyReqRes.bind(this.safari.proxy);
this.proxyReqRes = this.safari.proxy?.proxyReqRes.bind(this.safari.proxy);
this.isProxyActive = true;
return [sessionId, caps];
}
Expand Down
17 changes: 11 additions & 6 deletions lib/safari.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ const SAFARI_SERVER_GUARD = util.getLockFileGuard(


class SafariProxy extends JWProxy {
/** @type {boolean|undefined} */
didProcessExit;

async proxyCommand (url, method, body = null) {
if (this.didProcessExit) {
throw new errors.InvalidContextError(
Expand Down Expand Up @@ -79,7 +82,7 @@ class SafariDriverProcess {
async kill () {
if (this.isRunning) {
try {
await this.proc.stop('SIGKILL');
await this.proc?.stop('SIGKILL');
} catch (ign) {}
}
}
Expand All @@ -90,7 +93,7 @@ const SAFARI_DRIVER_PROCESS = new SafariDriverProcess();
process.once('exit', () => {
if (SAFARI_DRIVER_PROCESS.isRunning) {
try {
execSync(`kill ${SAFARI_DRIVER_PROCESS.proc.pid}`);
execSync(`kill ${SAFARI_DRIVER_PROCESS.proc?.pid}`);
} catch (ign) {}
}
});
Expand All @@ -116,17 +119,19 @@ class SafariDriverServer {
keepAlive: true,
});
this.proxy.didProcessExit = false;
SAFARI_DRIVER_PROCESS.proc.on('exit', () => {
this.proxy.didProcessExit = true;
SAFARI_DRIVER_PROCESS.proc?.on('exit', () => {
if (this.proxy) {
this.proxy.didProcessExit = true;
}
});

try {
await waitForCondition(async () => {
try {
await this.proxy.command('/status', 'GET');
await this.proxy?.command('/status', 'GET');
return true;
} catch (err) {
if (this.proxy.didProcessExit) {
if (this.proxy?.didProcessExit) {
throw new Error(err.message);
}
return false;
Expand Down
1 change: 1 addition & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ function formatCapsForServer (caps) {
for (const [name, value] of _.toPairs(caps)) {
if (SAFARI_CAP_PREFIXES.some((prefix) => name.startsWith(prefix))) {
result[name] = value;
// @ts-ignore This check is OK
} else if (!_.has(result, name) && STANDARD_CAPS.has(name)) {
result[name] = value;
}
Expand Down
43 changes: 27 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
"lib": "lib"
},
"dependencies": {
"@babel/runtime": "^7.0.0",
"asyncbox": "^2.0.2",
"bluebird": "^3.5.1",
"lodash": "^4.17.4",
Expand All @@ -54,7 +53,9 @@
"teen_process": "^2.0.0"
},
"scripts": {
"build": "rimraf build && babel --out-dir=build/lib lib && babel --out-dir=build index.js",
"build": "tsc -b",
"clean": "npm run build -- --clean",
"rebuild": "npm run clean; npm run build",
"dev": "npm run build -- --watch",
"lint": "eslint .",
"lint:fix": "npm run lint -- --fix",
Expand All @@ -69,33 +70,43 @@
"precommit-lint"
],
"peerDependencies": {
"appium": "^2.0.0-beta.40"
"appium": "^2.0.0"
},
"devDependencies": {
"@appium/eslint-config-appium": "^6.0.0",
"@babel/cli": "^7.18.10",
"@babel/core": "^7.18.10",
"@babel/eslint-parser": "^7.18.9",
"@babel/plugin-transform-runtime": "^7.18.10",
"@babel/preset-env": "^7.18.10",
"@babel/register": "^7.18.9",
"@appium/eslint-config-appium": "^8.0.4",
"@appium/eslint-config-appium-ts": "^0.3.1",
"@appium/tsconfig": "^0.3.0",
"@appium/types": "^0.13.2",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/git": "^10.0.1",
"babel-plugin-source-map-support": "^2.2.0",
"@types/bluebird": "^3.5.38",
"@types/chai": "^4.3.5",
"@types/chai-as-promised": "^7.1.5",
"@types/lodash": "^4.14.196",
"@types/mocha": "^10.0.1",
"@types/node": "^20.4.7",
"@types/sinon": "^10.0.16",
"@types/sinon-chai": "^3.2.9",
"@types/teen_process": "2.0.0",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"conventional-changelog-conventionalcommits": "^6.1.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-mocha": "^9.0.0",
"eslint-plugin-promise": "^6.0.0",
"eslint": "^8.46.0",
"eslint-config-prettier": "^8.9.0",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-import": "^2.28.0",
"eslint-plugin-mocha": "^10.1.0",
"eslint-plugin-promise": "^6.1.1",
"lint-staged": "^14.0.0",
"mocha": "^10.0.0",
"pre-commit": "^1.1.3",
"rimraf": "^5.0.0",
"semantic-release": "^20.0.2",
"sinon": "^15.0.0",
"ts-node": "^10.9.1",
"typescript": "^5.1.6",
"webdriverio": "^8.0.5"
}
}
4 changes: 2 additions & 2 deletions test/functional/desktop-driver-e2e-specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const CAPS = {
describe('Desktop SafariDriver', function () {
this.timeout(MOCHA_TIMEOUT);

/** @type {import('webdriverio').Browser} */
let driver;
beforeEach(async function () {
driver = await remote({
Expand All @@ -33,8 +34,7 @@ describe('Desktop SafariDriver', function () {

it('should start and stop a session', async function () {
await driver.url('https://appium.io/');
const button = await driver.$('#downloadLink');
await button.getText().should.eventually.eql('Download Appium');
(await driver.getPageSource()).should.not.be.empty;
});
});

Expand Down
4 changes: 2 additions & 2 deletions test/functional/mobile-driver-e2e-specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const CAPS = {
describe('Mobile SafariDriver', function () {
this.timeout(MOCHA_TIMEOUT);

/** @type {import('webdriverio').Browser} */
let driver;
before(async function () {
if (process.env.CI) {
Expand Down Expand Up @@ -59,8 +60,7 @@ describe('Mobile SafariDriver', function () {

it('should start and stop a session', async function () {
await driver.url('https://appium.io/');
const button = await driver.$('#downloadLink');
await button.getText().should.eventually.eql('Download Appium');
(await driver.getPageSource()).should.not.be.empty;
});
});

Expand Down
Loading

0 comments on commit e49d833

Please sign in to comment.