diff --git a/lib/commands/log.ts b/lib/commands/log.ts new file mode 100644 index 0000000..fa469f3 --- /dev/null +++ b/lib/commands/log.ts @@ -0,0 +1,39 @@ +import _ from 'lodash'; +import type { WindowsDriver } from '../driver'; +import { LogDefRecord, StringRecord } from '@appium/types'; + +const COLOR_CODE_PATTERN = /\u001b\[(\d+(;\d+)*)?m/g; // eslint-disable-line no-control-regex +const GET_SERVER_LOGS_FEATURE = 'get_server_logs'; +const DEFAULT_LOG_LEVEL = 'ALL'; + +export const supportedLogTypes: LogDefRecord = { + server: { + description: 'Appium server logs', + getter: (self: WindowsDriver): LogEntry[] => { + self.assertFeatureEnabled(GET_SERVER_LOGS_FEATURE); + return self.log.unwrap().record.map(nativeLogEntryToSeleniumEntry); + }, + }, +}; + +function nativeLogEntryToSeleniumEntry(x: StringRecord): LogEntry { + const msg = _.isEmpty(x.prefix) ? x.message : `[${x.prefix}] ${x.message}`; + return toLogEntry( + _.replace(msg, COLOR_CODE_PATTERN, ''), + x.timestamp ?? Date.now() + ); +} + +function toLogEntry( + message: string, + timestamp: number, + level: string = DEFAULT_LOG_LEVEL +): LogEntry { + return { timestamp, level, message }; +} + +interface LogEntry { + timestamp: number; + level: string; + message: string; +} diff --git a/lib/driver.js b/lib/driver.js index fdfed07..d5c017e 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -14,6 +14,7 @@ import * as powershellCommands from './commands/powershell'; import * as recordScreenCommands from './commands/record-screen'; import * as touchCommands from './commands/touch'; import * as contextCommands from './commands/context'; +import * as logCommands from './commands/log'; import { POWER_SHELL_FEATURE } from './constants'; import { newMethodMap } from './method-map'; import { executeMethodMap } from './execute-method-map'; @@ -32,6 +33,10 @@ const NO_PROXY = [ ['GET', new RegExp('^/session/[^/]+/screenshot')], ['GET', new RegExp('^/session/[^/]+/contexts?')], ['POST', new RegExp('^/session/[^/]+/context')], + ['GET', new RegExp('^/session/[^/]+/log/types')], + ['POST', new RegExp('^/session/[^/]+/log')], + ['GET', new RegExp('^/session/[^/]+/se/log/types')], + ['POST', new RegExp('^/session/[^/]+/se/log')], // Workarounds for // - https://github.com/appium/appium/issues/15923 // - https://github.com/appium/appium/issues/16316 @@ -219,6 +224,8 @@ export class WindowsDriver extends BaseDriver { getContexts = contextCommands.getContexts; getCurrentContext = contextCommands.getCurrentContext; setContext = contextCommands.setContext; + + supportedLogTypes = logCommands.supportedLogTypes; } export default WindowsDriver; diff --git a/test/e2e/commands/log-e2e-specs.js b/test/e2e/commands/log-e2e-specs.js new file mode 100644 index 0000000..350856a --- /dev/null +++ b/test/e2e/commands/log-e2e-specs.js @@ -0,0 +1,40 @@ +import { buildWdIoOptions } from '../helpers'; +import { remote as wdio } from 'webdriverio'; + +describe('log', function () { + let chai; + /** @type {import('webdriverio').Browser} */ + let driver; + + before(async function () { + chai = await import('chai'); + const chaiAsPromised = await import('chai-as-promised'); + + chai.should(); + chai.use(chaiAsPromised.default); + + driver = await wdio(buildWdIoOptions('Root')); + }); + + after(async function () { + try { + if (driver) { + await driver.deleteSession(); + } + } finally { + driver = null; + } + }); + + it('should get the list of available logs', async function () { + (await driver.getLogTypes()).should.eql(['server']); + }); + + it('should throw an error when an invalid type is given', async function () { + await driver.getLogs('INVALID_LOG_TYPE').should.be.rejected; + }); + + it('should get server logs', async function () { + (await driver.getLogs('server')).should.be.an('array'); + }); +});