mirror of
https://github.com/appium/appium.git
synced 2026-02-20 10:20:05 -06:00
feat(driver-test-support): add startStoppableAppium()
This adds a function `startStoppableAppium()` which starts an Appium server (via its `main()` entry point), but resolves with an object having a `stop() => Promise<boolean>` method. This method force-quits the server, even if there are active connections.
This commit is contained in:
19
package-lock.json
generated
19
package-lock.json
generated
@@ -2405,6 +2405,14 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/stoppable": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/stoppable/-/stoppable-1.1.1.tgz",
|
||||
"integrity": "sha512-b8N+fCADRIYYrGZOcmOR8ZNBOqhktWTB/bMUl5LvGtT201QKJZOOH5UsFyI3qtteM6ZAJbJqZoBcLqqxKIwjhw==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/supports-color": {
|
||||
"version": "8.1.1",
|
||||
"license": "MIT"
|
||||
@@ -14379,6 +14387,15 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/stoppable": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
|
||||
"integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==",
|
||||
"engines": {
|
||||
"node": ">=4",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-browserify": {
|
||||
"version": "2.0.2",
|
||||
"dev": true,
|
||||
@@ -16582,6 +16599,7 @@
|
||||
"dependencies": {
|
||||
"@appium/types": "^0.9.1",
|
||||
"@types/lodash": "4.14.191",
|
||||
"@types/stoppable": "1.1.1",
|
||||
"axios": "1.3.3",
|
||||
"bluebird": "3.7.2",
|
||||
"chai": "4.3.7",
|
||||
@@ -16589,6 +16607,7 @@
|
||||
"lodash": "4.17.21",
|
||||
"sinon": "15.0.1",
|
||||
"source-map-support": "0.5.21",
|
||||
"stoppable": "1.1.0",
|
||||
"type-fest": "3.5.7"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -2,6 +2,9 @@ export {createSessionHelpers, driverE2ETestSuite} from './e2e-suite';
|
||||
export * from './unit-suite';
|
||||
export * from './helpers';
|
||||
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
export * from './stoppable';
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').DriverClass} DriverClass
|
||||
* @typedef {import('@appium/types').BaseNSCapabilities} BaseNSCapabilities
|
||||
|
||||
57
packages/driver-test-support/lib/stoppable.ts
Normal file
57
packages/driver-test-support/lib/stoppable.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import type {AppiumServer} from '@appium/types';
|
||||
import {main as startAppium} from 'appium';
|
||||
import type {Args, CliCommandServer} from 'appium/types';
|
||||
import B from 'bluebird';
|
||||
import type {Server} from 'node:http';
|
||||
import stoppable from 'stoppable';
|
||||
import type {Asyncify} from 'type-fest';
|
||||
|
||||
/**
|
||||
* Options for {@linkcode startStoppableAppium}
|
||||
*/
|
||||
export type AppiumServerOpts = Args<CliCommandServer>;
|
||||
|
||||
/**
|
||||
* An {@linkcode AppiumServer} with a method `stop() => Promise<void>`, which closes all sockets and fully stops the server.
|
||||
*
|
||||
* Returned by {@linkcode startStoppableAppium}
|
||||
*/
|
||||
export type TestAppiumServer = Omit<NormativeAppiumServer, 'close'> & {
|
||||
stop: Asyncify<stoppable.WithStop['stop']>;
|
||||
close: (callback: (err?: Error) => void) => Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@linkcode AppiumServer} type, but with the `close` method normalized to a callback-style function.
|
||||
*/
|
||||
export type NormativeAppiumServer = Omit<AppiumServer, 'close'> & {
|
||||
close: Server['close'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Coerces {@linkcode AppiumServer} into a {@linkcode TestAppiumServer}.
|
||||
* @param opts Options for {@linkcode startAppium}
|
||||
* @todo This should be moved into `@appium/driver-test-support` or something
|
||||
* @returns A stoppable Appium server
|
||||
*/
|
||||
export async function startStoppableAppium(opts: AppiumServerOpts): Promise<TestAppiumServer> {
|
||||
const appiumServer = (await startAppium(opts)) as AppiumServer;
|
||||
const stoppableServer = stoppable(appiumServer as unknown as NormativeAppiumServer, 0);
|
||||
const originalAsyncClose = appiumServer.close;
|
||||
(stoppableServer as unknown as TestAppiumServer).close = async function (
|
||||
callback?: (err?: Error) => void
|
||||
) {
|
||||
if (callback) {
|
||||
try {
|
||||
await originalAsyncClose.call(this);
|
||||
callback();
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
} else {
|
||||
await originalAsyncClose.call(this);
|
||||
}
|
||||
};
|
||||
stoppableServer.stop = B.promisify(stoppableServer.stop, {context: stoppableServer});
|
||||
return stoppableServer as unknown as TestAppiumServer;
|
||||
}
|
||||
@@ -43,6 +43,7 @@
|
||||
"dependencies": {
|
||||
"@appium/types": "^0.9.1",
|
||||
"@types/lodash": "4.14.191",
|
||||
"@types/stoppable": "1.1.1",
|
||||
"axios": "1.3.3",
|
||||
"bluebird": "3.7.2",
|
||||
"chai": "4.3.7",
|
||||
@@ -50,6 +51,7 @@
|
||||
"lodash": "4.17.21",
|
||||
"sinon": "15.0.1",
|
||||
"source-map-support": "0.5.21",
|
||||
"stoppable": "1.1.0",
|
||||
"type-fest": "3.5.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
37
packages/driver-test-support/test/e2e/stoppable.e2e.spec.ts
Normal file
37
packages/driver-test-support/test/e2e/stoppable.e2e.spec.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import axios from 'axios';
|
||||
import B from 'bluebird';
|
||||
import {Agent} from 'node:http';
|
||||
import {startStoppableAppium, TestAppiumServer} from '../../lib';
|
||||
import getPort from 'get-port';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('startStoppableAppium()', function () {
|
||||
it('should start an Appium server', async function () {
|
||||
let server: TestAppiumServer | undefined;
|
||||
try {
|
||||
server = await startStoppableAppium({port: await getPort()});
|
||||
expect(server, 'to be an object');
|
||||
} finally {
|
||||
if (server) {
|
||||
await expect(server.stop()).to.be.fulfilled;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('when the server has connections', function () {
|
||||
it('should stop the server and resolve with a boolean', async function () {
|
||||
const port = await getPort();
|
||||
const server = await startStoppableAppium({port});
|
||||
const getConnections = B.promisify(server.getConnections, {context: server});
|
||||
await axios.get(`http://127.0.0.1:${port}/status`, {
|
||||
httpAgent: new Agent({keepAlive: true}),
|
||||
});
|
||||
try {
|
||||
await expect(getConnections()).to.eventually.be.greaterThan(0);
|
||||
} finally {
|
||||
await expect(server.stop()).to.eventually.be.a('boolean');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -10,6 +10,6 @@
|
||||
},
|
||||
"types": ["mocha", "chai", "chai-as-promised"]
|
||||
},
|
||||
"include": ["./lib"],
|
||||
"include": ["lib", "test"],
|
||||
"references": [{"path": "../types"}, {"path": "../base-driver"}]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user