diff --git a/packages/base-driver/lib/basedriver/commands/event.js b/packages/base-driver/lib/basedriver/commands/event.js index d871a3ccb..21e1bd14a 100644 --- a/packages/base-driver/lib/basedriver/commands/event.js +++ b/packages/base-driver/lib/basedriver/commands/event.js @@ -3,8 +3,9 @@ import _ from 'lodash'; /** - * @param {TimeoutBase} Base - * @returns {EventBase} + * @template {Constraints} C + * @param {import('./timeout').TimeoutBase} Base + * @returns {EventBase} */ 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} EventBase + * @typedef {import('@appium/types').Constraints} Constraints + * @typedef {import('@appium/types').IEventCommands} IEventCommands + */ + +/** + * @template {Constraints} C + * @typedef {import('../driver').BaseDriverBase} EventBase */ diff --git a/packages/base-driver/lib/basedriver/commands/execute.js b/packages/base-driver/lib/basedriver/commands/execute.js index 7bbf3c2b8..ea594b9f7 100644 --- a/packages/base-driver/lib/basedriver/commands/execute.js +++ b/packages/base-driver/lib/basedriver/commands/execute.js @@ -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} Base + * @returns {ExecuteBase} */ 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} ExecuteBase + * @typedef {import('@appium/types').Constraints} Constraints + */ + +/** + * @template {Constraints} C + * @typedef {import('../driver').BaseDriverBase & import('@appium/types').ISettingsCommands & import('@appium/types').SessionCommands & IExecuteCommands>} ExecuteBase */ diff --git a/packages/base-driver/lib/basedriver/commands/find.js b/packages/base-driver/lib/basedriver/commands/find.js index 18fd74af3..b78306adc 100644 --- a/packages/base-driver/lib/basedriver/commands/find.js +++ b/packages/base-driver/lib/basedriver/commands/find.js @@ -4,9 +4,9 @@ import {errors} from '../../protocol'; /** - * - * @param {EventBase} Base - * @returns {FindBase} + * @template {Constraints} C + * @param {import('./event').EventBase} Base + * @returns {FindBase} */ 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} 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} FindBase */ diff --git a/packages/base-driver/lib/basedriver/commands/index.js b/packages/base-driver/lib/basedriver/commands/index.js index 1dae4b232..50b300093 100644 --- a/packages/base-driver/lib/basedriver/commands/index.js +++ b/packages/base-driver/lib/basedriver/commands/index.js @@ -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} 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} BaseDriverBase + * @template {Constraints} C + * @typedef {import('../driver').BaseDriverBase} BaseDriverBase + */ + +/** + * @typedef {import('@appium/types').Constraints} Constraints */ diff --git a/packages/base-driver/lib/basedriver/commands/log.js b/packages/base-driver/lib/basedriver/commands/log.js index 86a19101b..2a948772b 100644 --- a/packages/base-driver/lib/basedriver/commands/log.js +++ b/packages/base-driver/lib/basedriver/commands/log.js @@ -4,37 +4,40 @@ import _ from 'lodash'; /** - * - * @param {FindBase} Base - * @returns {LogBase} + * @template {Constraints} C + * @param {import('./find').FindBase} Base + * @returns {LogBase} */ export function LogMixin(Base) { /** - * @implements {ILogCommands} + * @implements {ILogCommands} */ class LogCommands extends Base { + /** @type {Readonly>} */ + supportedLogTypes; + constructor(...args) { super(...args); - /** @type {Record>} */ - 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} + * @param {keyof typeof this.supportedLogTypes} logType + * @returns {Promise>} */ 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} LogBase + * @typedef {import('@appium/types').Constraints} Constraints + * @typedef {import('@appium/types').StringRecord} StringRecord */ /** - * @template T - * @typedef {import('@appium/types').LogType} LogType + * @template {Constraints} C + * @typedef {import('@appium/types').ILogCommands} ILogCommands + */ + +/** + * @template {Constraints} C + * @typedef {import('../driver').BaseDriverBase>} LogBase */ diff --git a/packages/base-driver/lib/basedriver/commands/session.js b/packages/base-driver/lib/basedriver/commands/session.js index e9b20d199..b13f61a43 100644 --- a/packages/base-driver/lib/basedriver/commands/session.js +++ b/packages/base-driver/lib/basedriver/commands/session.js @@ -4,8 +4,9 @@ import _ from 'lodash'; /** - * @param {SettingsBase} Base - * @returns {SessionBase} + * @template {Constraints} C + * @param {import('./settings').SettingsBase} Base + * @returns {SessionBase} */ 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} SessionBase + * @typedef {import('@appium/types').Constraints} Constraints + */ + +/** + * @template {Constraints} C + * @typedef {import('../driver').BaseDriverBase & import('@appium/types').ISettingsCommands & ISessionCommands>} SessionBase */ diff --git a/packages/base-driver/lib/basedriver/commands/settings.js b/packages/base-driver/lib/basedriver/commands/settings.js index b3638b862..b14ce95da 100644 --- a/packages/base-driver/lib/basedriver/commands/settings.js +++ b/packages/base-driver/lib/basedriver/commands/settings.js @@ -1,9 +1,9 @@ // @ts-check /** - * - * @param {ReturnType} Base - * @returns {SettingsBase} + * @template {Constraints} C + * @param {import('./log').LogBase} Base + * @returns {SettingsBase} */ 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} SettingsBase + * @typedef {import('@appium/types').Constraints} Constraints + * @typedef {import('@appium/types').ISettingsCommands} ISettingsCommands + */ +/** + * @template {Constraints} C + * @typedef {import('../driver').BaseDriverBase & ISettingsCommands>} SettingsBase */ diff --git a/packages/base-driver/lib/basedriver/commands/timeout.js b/packages/base-driver/lib/basedriver/commands/timeout.js index a9b107021..cc3ad57cd 100644 --- a/packages/base-driver/lib/basedriver/commands/timeout.js +++ b/packages/base-driver/lib/basedriver/commands/timeout.js @@ -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} Base + * @returns {TimeoutBase} */ export function TimeoutMixin(Base) { /** @@ -157,6 +158,11 @@ export function TimeoutMixin(Base) { } /** - * @typedef {import('@appium/types').TimeoutCommands} ITimeoutCommands - * @typedef {import('../driver').BaseDriverBase} TimeoutBase + * @typedef {import('@appium/types').ITimeoutCommands} ITimeoutCommands + * @typedef {import('@appium/types').Constraints} Constraints + */ + +/** + * @template {Constraints} C + * @typedef {import('../driver').BaseDriverBase} TimeoutBase */ diff --git a/packages/base-driver/lib/basedriver/driver.js b/packages/base-driver/lib/basedriver/driver.js index ed2e84e98..8c294d7ee 100644 --- a/packages/base-driver/lib/basedriver/driver.js +++ b/packages/base-driver/lib/basedriver/driver.js @@ -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} BaseDriverBase + * @typedef {import('@appium/types').Class & Proto,import('@appium/types').DriverStatic & Static>} BaseDriverBase */ /** @@ -468,13 +469,9 @@ export default BaseDriver; */ /** - * @template {Constraints} C - * @typedef {import('@appium/types').ConstraintsToCaps} ConstraintsToCaps - */ - -/** - * @template {Constraints} C - * @typedef {import('@appium/types').Driver} Driver + * @template {Constraints} [C=BaseDriverCapConstraints] + * @template {StringRecord} [CArgs=StringRecord] + * @typedef {import('@appium/types').Driver} Driver */ /** diff --git a/packages/types/lib/driver.ts b/packages/types/lib/driver.ts index f0ab421f1..a57520cdc 100644 --- a/packages/types/lib/driver.ts +++ b/packages/types/lib/driver.ts @@ -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; getLogEvents(type?: string | string[]): Promise>; } @@ -48,7 +49,7 @@ export interface SessionCommands { getSession(): Promise; } -export interface ExecuteCommands { +export interface IExecuteCommands { executeMethod(script: string, args: [StringRecord] | []): Promise; } @@ -74,7 +75,7 @@ export type SingularSessionData< Extra extends StringRecord | void = void > = Capabilities & {events?: EventHistory; error?: string}; -export interface FindCommands { +export interface IFindCommands { findElement(strategy: string, selector: string): Promise; findElements(strategy: string, selector: string): Promise; findElementFromElement(strategy: string, selector: string, elementId: string): Promise; @@ -101,20 +102,60 @@ export interface FindCommands { getPageSource(): Promise; } -export interface LogCommands { - supportedLogTypes: Record>; - getLogTypes(): Promise; +/** + * Log-related functionality of a {@linkcode Driver}. To be used as a mixin + */ +export interface ILogCommands { /** - * 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; + supportedLogTypes: Readonly>; + + /** + * Get available log types as a list of strings + */ + getLogTypes(): Promise<(keyof ILogCommands['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['supportedLogTypes'] + ): Promise< + AsyncReturnType< + ILogCommands['supportedLogTypes'][keyof ILogCommands['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 = Record>; + +/** + * A definition of a log type + */ +export interface LogDef { + /** + * 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) => Promise; +} + +export interface ISettingsCommands { updateSettings: (settings: StringRecord) => Promise; getSettings(): Promise; } @@ -185,11 +226,6 @@ export interface DeviceSettings { getSettings(): Record; } -export interface LogType { - description: string; - getter: (driver: TDriver) => Promise; -} - // WebDriver export interface Rect { @@ -327,17 +363,17 @@ export interface Core { */ export interface Driver< C extends Constraints = BaseDriverCapConstraints, - A extends StringRecord = StringRecord + CArgs extends StringRecord = StringRecord > extends SessionCommands, - LogCommands, - FindCommands, - SettingsCommands, - TimeoutCommands, - EventCommands, + ILogCommands, + 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; startUnexpectedShutdown(err?: Error): Promise;