diff --git a/lib/commands/bidi/constants.ts b/lib/commands/bidi/constants.ts new file mode 100644 index 00000000..cc35fc15 --- /dev/null +++ b/lib/commands/bidi/constants.ts @@ -0,0 +1,3 @@ +export const BIDI_EVENT_NAME = 'bidiEvent'; +export const CONTEXT_UPDATED_EVENT = 'appium.contextUpdated'; +export const LOG_ENTRY_ADDED_EVENT = 'log.entryAdded'; diff --git a/lib/commands/bidi/models.ts b/lib/commands/bidi/models.ts new file mode 100644 index 00000000..e8c69d5f --- /dev/null +++ b/lib/commands/bidi/models.ts @@ -0,0 +1,30 @@ +import type { LogEntryAddedEvent, ContextUpdatedEvent } from './types'; +import { NATIVE_WIN } from '../context/helpers'; +import { CONTEXT_UPDATED_EVENT, LOG_ENTRY_ADDED_EVENT } from './constants'; +import type { LogcatRecord as LogEntry } from 'appium-adb'; + +export function makeContextUpdatedEvent(contextName: string): ContextUpdatedEvent { + return { + method: CONTEXT_UPDATED_EVENT, + params: { + name: contextName, + type: contextName === NATIVE_WIN ? 'NATIVE' : 'WEB', + }, + }; +} + +export function makeLogEntryAddedEvent(entry: LogEntry, context: string, type: string): LogEntryAddedEvent { + return { + context, + method: LOG_ENTRY_ADDED_EVENT, + params: { + type, + level: entry.level, + source: { + realm: '', + }, + text: entry.message, + timestamp: entry.timestamp, + }, + }; +} diff --git a/lib/commands/bidi/types.ts b/lib/commands/bidi/types.ts new file mode 100644 index 00000000..e19e00f4 --- /dev/null +++ b/lib/commands/bidi/types.ts @@ -0,0 +1,29 @@ +interface BiDiEvent { + method: string; + params: TParams; +}; + +interface LogEntrySource { + realm: string; +} + +interface LogEntryAddedEventParams { + type: string; + level: string; + source: LogEntrySource; + text: string; + timestamp: number; +} + +// https://w3c.github.io/webdriver-bidi/#event-log-entryAdded +export interface LogEntryAddedEvent extends BiDiEvent { + context: string; +} + +interface ContentUpdatedParams { + name: string; + type: 'NATIVE' | 'WEB'; +} + +// https://github.com/appium/appium/issues/20741 +export interface ContextUpdatedEvent extends BiDiEvent {} diff --git a/lib/commands/context/exports.js b/lib/commands/context/exports.js index 086771c6..8a978d92 100644 --- a/lib/commands/context/exports.js +++ b/lib/commands/context/exports.js @@ -17,6 +17,8 @@ import { shouldDismissChromeWelcome, } from './helpers'; import {APP_STATE} from '../app-management'; +import { BIDI_EVENT_NAME } from '../bidi/constants'; +import { makeContextUpdatedEvent } from '../bidi/models'; // https://github.com/appium/appium/issues/20710 const DEFAULT_NATIVE_WINDOW_HANDLE = '1'; @@ -46,26 +48,28 @@ export async function getContexts() { * @returns {Promise} */ export async function setContext(name) { - if (!util.hasValue(name)) { - name = this.defaultContextName(); - } else if (name === WEBVIEW_WIN) { + let newContext = name; + if (!util.hasValue(newContext)) { + newContext = this.defaultContextName(); + } else if (newContext === WEBVIEW_WIN) { // handle setContext "WEBVIEW" - name = this.defaultWebviewName(); + newContext = this.defaultWebviewName(); } // if we're already in the context we want, do nothing - if (name === this.curContext) { + if (newContext === this.curContext) { return; } const webviewsMapping = await getWebViewsMapping.bind(this)(this.opts); const contexts = this.assignContexts(webviewsMapping); // if the context we want doesn't exist, fail - if (!_.includes(contexts, name)) { + if (!_.includes(contexts, newContext)) { throw new errors.NoSuchContextError(); } - await this.switchContext(name, webviewsMapping); - this.curContext = name; + await this.switchContext(newContext, webviewsMapping); + this.curContext = newContext; + await this.notifyBiDiContextChange(); } /** @@ -370,6 +374,17 @@ export function isChromedriverContext(viewName) { return _.includes(viewName, WEBVIEW_WIN) || viewName === CHROMIUM_WIN; } +/** + * https://github.com/appium/appium/issues/20741 + * + * @this {AndroidDriver} + * @returns {Promise} + */ +export async function notifyBiDiContextChange() { + const name = await this.getCurrentContext(); + this.eventEmitter.emit(BIDI_EVENT_NAME, makeContextUpdatedEvent(name)); +} + /** * @this {AndroidDriver} * @returns {Promise} diff --git a/lib/commands/log.js b/lib/commands/log.js index 0aa1fb97..5482a375 100644 --- a/lib/commands/log.js +++ b/lib/commands/log.js @@ -3,12 +3,13 @@ import _ from 'lodash'; import os from 'node:os'; import WebSocket from 'ws'; import { - BIDI_EVENT_NAME, GET_SERVER_LOGS_FEATURE, toLogRecord, nativeLogEntryToSeleniumEntry, } from '../utils'; import { NATIVE_WIN } from './context/helpers'; +import { BIDI_EVENT_NAME } from './bidi/constants'; +import { makeLogEntryAddedEvent } from './bidi/models'; export const supportedLogTypes = { logcat: { @@ -171,19 +172,7 @@ export function assignBiDiLogListener (logEmitter, properties) { } = properties; const listener = (/** @type {import('../utils').LogEntry} */ logEntry) => { const finalEntry = entryTransformer ? entryTransformer(logEntry) : logEntry; - this.eventEmitter.emit(BIDI_EVENT_NAME, { - context, - method: 'log.entryAdded', - params: { - type, - level: finalEntry.level, - source: { - realm: '', - }, - text: finalEntry.message, - timestamp: finalEntry.timestamp, - }, - }); + this.eventEmitter.emit(BIDI_EVENT_NAME, makeLogEntryAddedEvent(finalEntry, context, type)); }; logEmitter.on(srcEventName, listener); return [logEmitter, listener]; diff --git a/lib/driver.ts b/lib/driver.ts index 99259a92..43f7e8db 100644 --- a/lib/driver.ts +++ b/lib/driver.ts @@ -38,6 +38,7 @@ import { getWindowHandle, getWindowHandles, setWindow, + notifyBiDiContextChange, } from './commands/context/exports'; import { getDeviceInfoFromCaps, @@ -371,6 +372,7 @@ class AndroidDriver setWindow = setWindow; getWindowHandle = getWindowHandle; getWindowHandles = getWindowHandles; + notifyBiDiContextChange = notifyBiDiContextChange; getDeviceInfoFromCaps = getDeviceInfoFromCaps; createADB = createADB; diff --git a/lib/index.ts b/lib/index.ts index 00c60ccc..e224b2c8 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -11,6 +11,7 @@ export const utils = { } as const; export type * from './commands/types'; export {ANDROID_DRIVER_CONSTRAINTS as commonCapConstraints} from './constraints'; +export {NATIVE_WIN} from './commands/context/helpers'; export * from './driver'; export * as doctor from './doctor/checks'; diff --git a/lib/utils.js b/lib/utils.js index caee8178..807bd126 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -2,7 +2,6 @@ import _ from 'lodash'; import {errors} from 'appium/driver'; export const ADB_SHELL_FEATURE = 'adb_shell'; -export const BIDI_EVENT_NAME = 'bidiEvent'; export const GET_SERVER_LOGS_FEATURE = 'get_server_logs'; const COLOR_CODE_PATTERN = /\u001b\[(\d+(;\d+)*)?m/g; // eslint-disable-line no-control-regex