mirror of
https://github.com/appium/appium.git
synced 2026-10-19 14:32:18 +08:00
228 lines
6.6 KiB
TypeScript
228 lines
6.6 KiB
TypeScript
import type {
|
|
AppiumServer,
|
|
BidiModuleMap,
|
|
ExecuteMethodMap,
|
|
ExternalDriver,
|
|
IIpcSubscription,
|
|
IpcMessage,
|
|
MethodMap,
|
|
} from '@appium/types';
|
|
import {BasePlugin} from 'appium/plugin';
|
|
import {sleep} from 'asyncbox';
|
|
import type {Application, Request, Response} from 'express';
|
|
|
|
/** Driver as seen by this plugin; may include plugin-specific session data */
|
|
export type DriverLike = ExternalDriver & {fakeSessionData?: unknown};
|
|
|
|
type ClockStatus = {running: boolean};
|
|
|
|
export class FakePlugin extends BasePlugin {
|
|
static newMethodMap: MethodMap<FakePlugin> = {
|
|
'/session/:sessionId/fake_data': {
|
|
GET: {command: 'getFakeSessionData', neverProxy: true},
|
|
POST: {
|
|
command: 'setFakeSessionData',
|
|
payloadParams: {required: ['data']},
|
|
neverProxy: true,
|
|
},
|
|
},
|
|
'/session/:sessionId/fakepluginargs': {
|
|
GET: {command: 'getFakePluginArgs', neverProxy: true},
|
|
},
|
|
};
|
|
|
|
static newBidiCommands: BidiModuleMap = {
|
|
'appium:fake': {
|
|
getPluginThing: {
|
|
command: 'getPluginThing',
|
|
},
|
|
setPluginThing: {
|
|
command: 'setPluginThing',
|
|
params: {
|
|
required: ['thing'],
|
|
},
|
|
},
|
|
doSomeMath: {
|
|
command: 'doSomeMath',
|
|
params: {
|
|
required: ['num1', 'num2'],
|
|
},
|
|
},
|
|
doSomeMath2: {
|
|
command: 'doSomeMath2',
|
|
params: {
|
|
required: ['num1', 'num2'],
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
static executeMethodMap: ExecuteMethodMap<FakePlugin> = {
|
|
'fake: getThing': {
|
|
command: 'getFakeThing',
|
|
},
|
|
'fake: plugMeIn': {
|
|
command: 'plugMeIn',
|
|
params: {required: ['socket']},
|
|
},
|
|
'fake: getFakeDriverClockStatus': {
|
|
command: 'getFakeDriverClockStatus',
|
|
},
|
|
};
|
|
|
|
private static _unexpectedData: string | null = null;
|
|
protected _clockRunning: boolean = true;
|
|
private readonly fakeThing: string;
|
|
private pluginThing: unknown = null;
|
|
private fakeDriverClockIsRunning: boolean = false;
|
|
private ipcPluginMath?: IIpcSubscription<number>;
|
|
|
|
constructor(name: string, cliArgs: Record<string, unknown> = {}) {
|
|
super(name, cliArgs);
|
|
this.fakeThing = 'PLUGIN_FAKE_THING';
|
|
void this.startClock();
|
|
}
|
|
|
|
static fakeRoute(_req: Request, res: Response): void {
|
|
res.send(JSON.stringify({fake: 'fakeResponse'}));
|
|
}
|
|
|
|
static unexpectedData(_req: Request, res: Response): void {
|
|
res.send(JSON.stringify(FakePlugin._unexpectedData));
|
|
FakePlugin._unexpectedData = null;
|
|
}
|
|
|
|
static async updateServer(
|
|
expressApp: Application,
|
|
httpServer: AppiumServer,
|
|
cliArgs: Record<string, unknown>,
|
|
): Promise<void> {
|
|
expressApp.all('/fake', FakePlugin.fakeRoute);
|
|
expressApp.all('/unexpected', FakePlugin.unexpectedData);
|
|
expressApp.all('/cliArgs', (req, res) => {
|
|
res.send(JSON.stringify(cliArgs));
|
|
});
|
|
// global middleware, exercised via httpServer.frontRouter
|
|
httpServer.frontRouter.use((_req, res, next) => {
|
|
res.set('x-fake-plugin-pre-server', 'true');
|
|
next();
|
|
});
|
|
}
|
|
|
|
async onIpcInit() {
|
|
const clockSub = this.ipcSubscribe<ClockStatus>('clockLifecycle');
|
|
clockSub.on('message', (message: IpcMessage<ClockStatus>) => {
|
|
this.fakeDriverClockIsRunning = message.data.running;
|
|
});
|
|
// subscribing to clockLifecycle doesn't tell us the current status if it started before this
|
|
// constructor was called, so retrieve it
|
|
const message = clockSub.getMessage();
|
|
if (message) {
|
|
this.fakeDriverClockIsRunning = message.data.running;
|
|
}
|
|
this.ipcPluginMath = this.ipcSubscribe<number>('pluginMath');
|
|
}
|
|
|
|
async startClock(): Promise<void> {
|
|
while (this._clockRunning) {
|
|
await sleep(250);
|
|
this.eventEmitter.emit('bidiEvent', {
|
|
method: 'appium:clock.currentTime',
|
|
params: {time: Date.now()},
|
|
});
|
|
}
|
|
}
|
|
|
|
async doSomeMath(next: () => Promise<number>, _driver: DriverLike, num1: number, num2: number): Promise<number> {
|
|
const sum = await next();
|
|
return num1 * num2 + sum;
|
|
}
|
|
|
|
async doSomeMath2(_next: () => Promise<unknown>, _driver: DriverLike, num1: number, num2: number): Promise<number> {
|
|
await sleep(1);
|
|
const result = num1 * num2;
|
|
if (this.ipcPluginMath) {
|
|
await this.ipcPluginMath.publish(result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
async getFakeThing(): Promise<string> {
|
|
await sleep(1);
|
|
return this.fakeThing;
|
|
}
|
|
|
|
async getPluginThing(): Promise<unknown> {
|
|
this.eventEmitter.emit('bidiEvent', {
|
|
method: 'appium:fake.pluginThingRetrieved',
|
|
params: {},
|
|
});
|
|
return this.pluginThing;
|
|
}
|
|
|
|
async setPluginThing(_next: () => Promise<unknown>, _driver: DriverLike, thing: unknown): Promise<void> {
|
|
this.pluginThing = thing;
|
|
}
|
|
|
|
async plugMeIn(_next: () => Promise<unknown>, _driver: DriverLike, socket: string): Promise<string> {
|
|
await sleep(1);
|
|
return `Plugged in to ${socket}`;
|
|
}
|
|
|
|
async getFakePluginArgs(): Promise<Record<string, unknown>> {
|
|
await sleep(1);
|
|
return this.cliArgs;
|
|
}
|
|
|
|
async getFakeDriverClockStatus(): Promise<boolean> {
|
|
return this.fakeDriverClockIsRunning;
|
|
}
|
|
|
|
async getPageSource(_next: () => Promise<string>, _driver: DriverLike, ...args: unknown[]): Promise<string> {
|
|
await sleep(10);
|
|
return `<Fake>${JSON.stringify(args)}</Fake>`;
|
|
}
|
|
|
|
async findElement(
|
|
next: () => Promise<{fake?: boolean} & Record<string, unknown>>,
|
|
_driver: DriverLike,
|
|
...args: unknown[]
|
|
): Promise<{fake?: boolean} & Record<string, unknown>> {
|
|
this.log.info(`Before findElement is run with args ${JSON.stringify(args)}`);
|
|
const originalRes = await next();
|
|
this.log.info('After findElement is run');
|
|
originalRes.fake = true;
|
|
return originalRes;
|
|
}
|
|
|
|
async getFakeSessionData(_next: () => Promise<unknown>, driver: DriverLike): Promise<unknown> {
|
|
await sleep(1);
|
|
return driver.fakeSessionData ?? null;
|
|
}
|
|
|
|
async setFakeSessionData(_next: () => Promise<unknown>, driver: DriverLike, ...args: unknown[]): Promise<null> {
|
|
await sleep(1);
|
|
driver.fakeSessionData = args[0];
|
|
return null;
|
|
}
|
|
|
|
async getWindowHandle(next: () => Promise<string>): Promise<string> {
|
|
const handle = await next();
|
|
return `<<${handle}>>`;
|
|
}
|
|
|
|
async onUnexpectedShutdown(_driver: DriverLike, cause: Error | string): Promise<void> {
|
|
this._clockRunning = false;
|
|
FakePlugin._unexpectedData = `Session ended because ${cause}`;
|
|
}
|
|
|
|
async execute(next: () => Promise<unknown>, driver: DriverLike, script: string, args: unknown[]): Promise<unknown> {
|
|
return await this.executeMethod(next, driver as any, script, args);
|
|
}
|
|
|
|
async deleteSession(next: () => Promise<unknown>): Promise<unknown> {
|
|
this._clockRunning = false;
|
|
return await next();
|
|
}
|
|
}
|