mirror of
https://github.com/appium/appium.git
synced 2026-01-26 12:18:51 -06:00
chore(base-driver,types): implement better types for custom log formats
Previously, the value of any `Driver#supportedLogTypes` prop would ultimately be unaware of what format the log data is actually stored in (and thus, what the `GET /session/:sessionId/log` cmd responds with). Now this can be declared by using the `LogDef<C extends Constraints, LogEntry = string>` type.
Example:
```js
supportedLogTypes = {
debug: /** @type {import('@appium/types').LogDef<RokuDriverCapConstraints, string>} */ ({
description: 'Roku debug logs',
/** @param {RokuDriver} driver */
async getter(driver) {
return await ['foo'];
},
}),
};
```
This is a log named `debug` which stores strings. The `getter` function is always expected to return a type of `LogEntry[]` and receives a type of `Driver<C>`. In this case, it's the default log entry type (`string`) and `RokuDriver`, which implements `Driver<RokuDriverCapConstraints>` where `RokuDriverCapConstraints` extends `Constraints`.
It was kind of tricky since `supportedLogTypes` is an instance field and we don't have access to `this`, so cannot derive the type of the `driver` parameter from it, nor can we use `this` in the `ILogCommands` interface.
Renamed these various mixin interfaces to be prefixed with `I`. All of the mixins themselves (the implementations) are now driver-aware via the `C` type parameter and thus can reference driver-specific caps or options, if needed.
This commit is contained in:
@@ -3,8 +3,9 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
/**
|
||||
* @param {TimeoutBase} Base
|
||||
* @returns {EventBase}
|
||||
* @template {Constraints} C
|
||||
* @param {import('./timeout').TimeoutBase<C>} Base
|
||||
* @returns {EventBase<C>}
|
||||
*/
|
||||
export function EventMixin(Base) {
|
||||
/**
|
||||
@@ -52,7 +53,11 @@ export function EventMixin(Base) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').EventCommands} IEventCommands
|
||||
* @typedef {import('./timeout').TimeoutBase} TimeoutBase
|
||||
* @typedef {import('../driver').BaseDriverBase<import('@appium/types').TimeoutCommands & IEventCommands>} EventBase
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
* @typedef {import('@appium/types').IEventCommands} IEventCommands
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & IEventCommands>} EventBase
|
||||
*/
|
||||
|
||||
@@ -2,8 +2,9 @@ import _ from 'lodash';
|
||||
import {errors, makeArgs, checkParams} from '../../protocol';
|
||||
|
||||
/**
|
||||
* @param {SessionBase} Base
|
||||
* @returns {ExecuteBase}
|
||||
* @template {Constraints} C
|
||||
* @param {import('./session').SessionBase<C>} Base
|
||||
* @returns {ExecuteBase<C>}
|
||||
*/
|
||||
export function ExecuteMixin(Base) {
|
||||
/**
|
||||
@@ -57,9 +58,13 @@ export function ExecuteMixin(Base) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').ExecuteCommands} IExecuteCommands
|
||||
* @typedef {import('@appium/types').IExecuteCommands} IExecuteCommands
|
||||
* @typedef {import('@appium/types').Driver} Driver
|
||||
* @typedef {import('@appium/types').DriverClass} DriverClass
|
||||
* @typedef {import('./session').SessionBase} SessionBase
|
||||
* @typedef {import('../driver').BaseDriverBase<import('@appium/types').TimeoutCommands & import('@appium/types').EventCommands & import('@appium/types').FindCommands & import('@appium/types').LogCommands & import('@appium/types').SettingsCommands & import('@appium/types').SessionCommands & IExecuteCommands>} ExecuteBase
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & import('@appium/types').IEventCommands & import('@appium/types').IFindCommands & import('@appium/types').ILogCommands<C> & import('@appium/types').ISettingsCommands & import('@appium/types').SessionCommands & IExecuteCommands>} ExecuteBase
|
||||
*/
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
import {errors} from '../../protocol';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {EventBase} Base
|
||||
* @returns {FindBase}
|
||||
* @template {Constraints} C
|
||||
* @param {import('./event').EventBase<C>} Base
|
||||
* @returns {FindBase<C>}
|
||||
*/
|
||||
export function FindMixin(Base) {
|
||||
/**
|
||||
@@ -96,7 +96,12 @@ export function FindMixin(Base) {
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').Element} Element
|
||||
* @typedef {import('@appium/types').FindCommands} IFindCommands
|
||||
* @typedef {import('./event').EventBase} EventBase
|
||||
* @typedef {import('../driver').BaseDriverBase<import('@appium/types').TimeoutCommands & import('@appium/types').EventCommands & IFindCommands>} FindBase
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
* @typedef {import('@appium/types').IFindCommands} IFindCommands
|
||||
* @typedef {import('@appium/types').ITimeoutCommands} ITimeoutCommands
|
||||
* @typedef {import('@appium/types').IEventCommands} IEventCommands
|
||||
*/
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, ITimeoutCommands & IEventCommands & IFindCommands>} FindBase
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-check
|
||||
|
||||
import _ from 'lodash';
|
||||
import {EventMixin} from './event';
|
||||
import {FindMixin} from './find';
|
||||
import {LogMixin} from './log';
|
||||
@@ -9,22 +9,27 @@ import {TimeoutMixin} from './timeout';
|
||||
import {ExecuteMixin} from './execute';
|
||||
|
||||
/**
|
||||
* Applies all the mixins to the `BaseDriverBase` class.
|
||||
* Returns a `BaseDriver` class.
|
||||
* @param {BaseDriverBase} Base
|
||||
* Applies all the mixins to the `BaseDriverBase` class; returns a `BaseDriver` class definition.
|
||||
* Each mixin is applied in the order it is listed here, and each type is a union with the previous.
|
||||
*
|
||||
* @template {Constraints} C
|
||||
* @param {BaseDriverBase<C>} Base
|
||||
*/
|
||||
export function createBaseDriverClass(Base) {
|
||||
const WithTimeoutCommands = TimeoutMixin(Base);
|
||||
const WithEventCommands = EventMixin(WithTimeoutCommands);
|
||||
const WithFindCommands = FindMixin(WithEventCommands);
|
||||
const WithLogCommands = LogMixin(WithFindCommands);
|
||||
const WithSettingsCommands = SettingsMixin(WithLogCommands);
|
||||
const WithSessionCommands = SessionMixin(WithSettingsCommands);
|
||||
const WithExecuteCommands = ExecuteMixin(WithSessionCommands);
|
||||
return WithExecuteCommands;
|
||||
}
|
||||
export const createBaseDriverClass = _.flow(
|
||||
TimeoutMixin,
|
||||
EventMixin,
|
||||
FindMixin,
|
||||
LogMixin,
|
||||
SettingsMixin,
|
||||
SessionMixin,
|
||||
ExecuteMixin
|
||||
);
|
||||
|
||||
/**
|
||||
* @template [T={}]
|
||||
* @typedef {import('../driver').BaseDriverBase<T>} BaseDriverBase
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C>} BaseDriverBase
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
*/
|
||||
|
||||
@@ -4,37 +4,40 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {FindBase} Base
|
||||
* @returns {LogBase}
|
||||
* @template {Constraints} C
|
||||
* @param {import('./find').FindBase<C>} Base
|
||||
* @returns {LogBase<C>}
|
||||
*/
|
||||
export function LogMixin(Base) {
|
||||
/**
|
||||
* @implements {ILogCommands}
|
||||
* @implements {ILogCommands<C>}
|
||||
*/
|
||||
class LogCommands extends Base {
|
||||
/** @type {Readonly<import('@appium/types').LogDefRecord<C>>} */
|
||||
supportedLogTypes;
|
||||
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
/** @type {Record<string, LogType<Driver>>} */
|
||||
this.supportedLogTypes = this.supportedLogTypes ?? {};
|
||||
this.supportedLogTypes ??= {};
|
||||
}
|
||||
|
||||
async getLogTypes() {
|
||||
this.log.debug('Retrieving supported log types');
|
||||
return _.keys(this.supportedLogTypes);
|
||||
return Object.keys(this.supportedLogTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {Driver}
|
||||
* @param {string} logType
|
||||
* @this {import('@appium/types').Driver<C>}
|
||||
* @param {keyof typeof this.supportedLogTypes} logType
|
||||
* @returns {Promise<import('type-fest').AsyncReturnType<typeof this.supportedLogTypes[keyof typeof this.supportedLogTypes]['getter']>>}
|
||||
*/
|
||||
async getLog(logType) {
|
||||
this.log.debug(`Retrieving '${logType}' logs`);
|
||||
this.log.debug(`Retrieving '${String(logType)}' logs`);
|
||||
|
||||
if (!(await this.getLogTypes()).includes(logType)) {
|
||||
if (!(logType in this.supportedLogTypes)) {
|
||||
const logsTypesWithDescriptions = _.mapValues(this.supportedLogTypes, 'description');
|
||||
throw new Error(
|
||||
`Unsupported log type '${logType}'. ` +
|
||||
`Unsupported log type '${String(logType)}'. ` +
|
||||
`Supported types: ${JSON.stringify(logsTypesWithDescriptions)}`
|
||||
);
|
||||
}
|
||||
@@ -46,13 +49,16 @@ export function LogMixin(Base) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').LogCommands} ILogCommands
|
||||
* @typedef {import('@appium/types').Driver} Driver
|
||||
* @typedef {import('./find').FindBase} FindBase
|
||||
* @typedef {import('../driver').BaseDriverBase<import('@appium/types').TimeoutCommands & import('@appium/types').EventCommands & import('@appium/types').FindCommands & ILogCommands>} LogBase
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
* @typedef {import('@appium/types').StringRecord} StringRecord
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {import('@appium/types').LogType<T>} LogType
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('@appium/types').ILogCommands<C>} ILogCommands
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & import('@appium/types').IEventCommands & import('@appium/types').IFindCommands & ILogCommands<C>>} LogBase
|
||||
*/
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
/**
|
||||
* @param {SettingsBase} Base
|
||||
* @returns {SessionBase}
|
||||
* @template {Constraints} C
|
||||
* @param {import('./settings').SettingsBase<C>} Base
|
||||
* @returns {SessionBase<C>}
|
||||
*/
|
||||
export function SessionMixin(Base) {
|
||||
/**
|
||||
@@ -46,6 +47,10 @@ export function SessionMixin(Base) {
|
||||
* @typedef {import('@appium/types').SessionCommands} ISessionCommands
|
||||
* @typedef {import('@appium/types').SingularSessionData} SingularSessionData
|
||||
* @typedef {import('@appium/types').MultiSessionData} MultiSessionData
|
||||
* @typedef {import('./settings').SettingsBase} SettingsBase
|
||||
* @typedef {import('../driver').BaseDriverBase<import('@appium/types').TimeoutCommands & import('@appium/types').EventCommands & import('@appium/types').FindCommands & import('@appium/types').LogCommands & import('@appium/types').SettingsCommands & ISessionCommands>} SessionBase
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & import('@appium/types').IEventCommands & import('@appium/types').IFindCommands & import('@appium/types').ILogCommands<C> & import('@appium/types').ISettingsCommands & ISessionCommands>} SessionBase
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ReturnType<import('./log').LogMixin>} Base
|
||||
* @returns {SettingsBase}
|
||||
* @template {Constraints} C
|
||||
* @param {import('./log').LogBase<C>} Base
|
||||
* @returns {SettingsBase<C>}
|
||||
*/
|
||||
export function SettingsMixin(Base) {
|
||||
/**
|
||||
@@ -29,7 +29,10 @@ export function SettingsMixin(Base) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').SettingsCommands} ISettingsCommands
|
||||
* @typedef {import('./log').LogBase} LogBase
|
||||
* @typedef {import('../driver').BaseDriverBase<import('@appium/types').TimeoutCommands & import('@appium/types').EventCommands & import('@appium/types').FindCommands & import('@appium/types').LogCommands & ISettingsCommands>} SettingsBase
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
* @typedef {import('@appium/types').ISettingsCommands} ISettingsCommands
|
||||
*/
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, import('@appium/types').ITimeoutCommands & import('@appium/types').IEventCommands & import('@appium/types').IFindCommands & import('@appium/types').ILogCommands<C> & ISettingsCommands>} SettingsBase
|
||||
*/
|
||||
|
||||
@@ -10,8 +10,9 @@ import {errors} from '../../protocol';
|
||||
const MIN_TIMEOUT = 0;
|
||||
|
||||
/**
|
||||
* @param {import('../driver').BaseDriverBase} Base
|
||||
* @returns {TimeoutBase}
|
||||
* @template {Constraints} C
|
||||
* @param {import('../driver').BaseDriverBase<C>} Base
|
||||
* @returns {TimeoutBase<C>}
|
||||
*/
|
||||
export function TimeoutMixin(Base) {
|
||||
/**
|
||||
@@ -157,6 +158,11 @@ export function TimeoutMixin(Base) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {import('@appium/types').TimeoutCommands} ITimeoutCommands
|
||||
* @typedef {import('../driver').BaseDriverBase<ITimeoutCommands>} TimeoutBase
|
||||
* @typedef {import('@appium/types').ITimeoutCommands} ITimeoutCommands
|
||||
* @typedef {import('@appium/types').Constraints} Constraints
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('../driver').BaseDriverBase<C, ITimeoutCommands>} TimeoutBase
|
||||
*/
|
||||
|
||||
@@ -445,9 +445,10 @@ export default BaseDriver;
|
||||
|
||||
/**
|
||||
* This is used to extend {@linkcode BaseDriverCore} by the mixins and also external drivers.
|
||||
* @template {Constraints} C
|
||||
* @template [Proto={}]
|
||||
* @template [Static={}]
|
||||
* @typedef {import('@appium/types').Class<BaseDriverCore & Proto,import('@appium/types').DriverStatic & Static>} BaseDriverBase
|
||||
* @typedef {import('@appium/types').Class<BaseDriverCore<C> & Proto,import('@appium/types').DriverStatic & Static>} BaseDriverBase
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -468,13 +469,9 @@ export default BaseDriver;
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('@appium/types').ConstraintsToCaps<C>} ConstraintsToCaps
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Constraints} C
|
||||
* @typedef {import('@appium/types').Driver<C>} Driver
|
||||
* @template {Constraints} [C=BaseDriverCapConstraints]
|
||||
* @template {StringRecord} [CArgs=StringRecord]
|
||||
* @typedef {import('@appium/types').Driver<C, CArgs>} Driver
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,8 +14,9 @@ import {
|
||||
Capabilities,
|
||||
} from '.';
|
||||
import {ServerArgs} from './config';
|
||||
import {AsyncReturnType} from 'type-fest';
|
||||
|
||||
export interface TimeoutCommands {
|
||||
export interface ITimeoutCommands {
|
||||
timeouts(
|
||||
type: string,
|
||||
ms: number | string,
|
||||
@@ -38,7 +39,7 @@ export interface TimeoutCommands {
|
||||
parseTimeoutArgument(ms: number | string): number;
|
||||
}
|
||||
|
||||
export interface EventCommands {
|
||||
export interface IEventCommands {
|
||||
logCustomEvent(vendor: string, event: string): Promise<void>;
|
||||
getLogEvents(type?: string | string[]): Promise<EventHistory | Record<string, number>>;
|
||||
}
|
||||
@@ -48,7 +49,7 @@ export interface SessionCommands {
|
||||
getSession(): Promise<SingularSessionData>;
|
||||
}
|
||||
|
||||
export interface ExecuteCommands {
|
||||
export interface IExecuteCommands {
|
||||
executeMethod(script: string, args: [StringRecord] | []): Promise<any>;
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ export type SingularSessionData<
|
||||
Extra extends StringRecord | void = void
|
||||
> = Capabilities<C, Extra> & {events?: EventHistory; error?: string};
|
||||
|
||||
export interface FindCommands {
|
||||
export interface IFindCommands {
|
||||
findElement(strategy: string, selector: string): Promise<Element>;
|
||||
findElements(strategy: string, selector: string): Promise<Element[]>;
|
||||
findElementFromElement(strategy: string, selector: string, elementId: string): Promise<Element>;
|
||||
@@ -101,20 +102,60 @@ export interface FindCommands {
|
||||
getPageSource(): Promise<string>;
|
||||
}
|
||||
|
||||
export interface LogCommands {
|
||||
supportedLogTypes: Record<string, LogType<Driver>>;
|
||||
getLogTypes(): Promise<string[]>;
|
||||
/**
|
||||
* Log-related functionality of a {@linkcode Driver}. To be used as a mixin
|
||||
*/
|
||||
export interface ILogCommands<C extends Constraints> {
|
||||
/**
|
||||
* Gets logs
|
||||
*
|
||||
* TODO: `logType` should be a key in `supportedLogTypes`, and the return value of this function
|
||||
* should be the associated `LogType` object's `LogEntry` parameterized type.
|
||||
* @param logType - Name/key of log type as defined in {@linkcode LogCommands.supportedLogTypes}.
|
||||
* Definition of the available log types
|
||||
*/
|
||||
getLog(logType: string): Promise<any[]>;
|
||||
supportedLogTypes: Readonly<LogDefRecord<C>>;
|
||||
|
||||
/**
|
||||
* Get available log types as a list of strings
|
||||
*/
|
||||
getLogTypes(): Promise<(keyof ILogCommands<C>['supportedLogTypes'])[]>;
|
||||
|
||||
/**
|
||||
* Get the log for a given log type.
|
||||
*
|
||||
* @param logType - Name/key of log type as defined in {@linkcode ILogCommands.supportedLogTypes}.
|
||||
*/
|
||||
getLog(
|
||||
logType: keyof ILogCommands<C>['supportedLogTypes']
|
||||
): Promise<
|
||||
AsyncReturnType<
|
||||
ILogCommands<C>['supportedLogTypes'][keyof ILogCommands<C>['supportedLogTypes']]['getter']
|
||||
>
|
||||
>;
|
||||
}
|
||||
|
||||
export interface SettingsCommands {
|
||||
/**
|
||||
* A record of {@linkcode LogDef} objects, keyed by the log type name.
|
||||
* Used in {@linkcode ILogCommands.supportedLogTypes}
|
||||
*/
|
||||
export type LogDefRecord<C extends Constraints> = Record<string, LogDef<C>>;
|
||||
|
||||
/**
|
||||
* A definition of a log type
|
||||
*/
|
||||
export interface LogDef<C extends Constraints, T = unknown> {
|
||||
/**
|
||||
* Description of the log type.
|
||||
*
|
||||
* The only place this is used is in error messages if the client provides an invalid log type
|
||||
* via {@linkcode ILogCommands.getLog}.
|
||||
*/
|
||||
description: string;
|
||||
/**
|
||||
* Returns all the log data for the given type
|
||||
*
|
||||
* This implementation *should* drain, truncate or otherwise reset the log buffer.
|
||||
*/
|
||||
getter: (driver: Driver<C>) => Promise<T[]>;
|
||||
}
|
||||
|
||||
export interface ISettingsCommands {
|
||||
updateSettings: (settings: StringRecord) => Promise<void>;
|
||||
getSettings(): Promise<StringRecord>;
|
||||
}
|
||||
@@ -185,11 +226,6 @@ export interface DeviceSettings<T = any> {
|
||||
getSettings(): Record<string, T>;
|
||||
}
|
||||
|
||||
export interface LogType<TDriver, LogEntry = string> {
|
||||
description: string;
|
||||
getter: (driver: TDriver) => Promise<LogEntry[]>;
|
||||
}
|
||||
|
||||
// WebDriver
|
||||
|
||||
export interface Rect {
|
||||
@@ -327,17 +363,17 @@ export interface Core<C extends Constraints = BaseDriverCapConstraints> {
|
||||
*/
|
||||
export interface Driver<
|
||||
C extends Constraints = BaseDriverCapConstraints,
|
||||
A extends StringRecord = StringRecord
|
||||
CArgs extends StringRecord = StringRecord
|
||||
> extends SessionCommands,
|
||||
LogCommands,
|
||||
FindCommands,
|
||||
SettingsCommands,
|
||||
TimeoutCommands,
|
||||
EventCommands,
|
||||
ILogCommands<C>,
|
||||
IFindCommands,
|
||||
ISettingsCommands,
|
||||
ITimeoutCommands,
|
||||
IEventCommands,
|
||||
IExecuteCommands,
|
||||
SessionHandler<[string, any], void, C>,
|
||||
ExecuteCommands,
|
||||
Core {
|
||||
cliArgs?: A;
|
||||
cliArgs?: CArgs;
|
||||
// The following methods are implemented by `BaseDriver`.
|
||||
executeCommand(cmd: string, ...args: any[]): Promise<any>;
|
||||
startUnexpectedShutdown(err?: Error): Promise<void>;
|
||||
|
||||
Reference in New Issue
Block a user