Path: blob/trunk/javascript/selenium-webdriver/bidi/logInspector.js
4000 views
// Licensed to the Software Freedom Conservancy (SFC) under one1// or more contributor license agreements. See the NOTICE file2// distributed with this work for additional information3// regarding copyright ownership. The SFC licenses this file4// to you under the Apache License, Version 2.0 (the5// "License"); you may not use this file except in compliance6// with the License. You may obtain a copy of the License at7//8// http://www.apache.org/licenses/LICENSE-2.09//10// Unless required by applicable law or agreed to in writing,11// software distributed under the License is distributed on an12// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13// KIND, either express or implied. See the License for the14// specific language governing permissions and limitations15// under the License.1617const { FilterBy } = require('./filterBy')18const { ConsoleLogEntry, JavascriptLogEntry, GenericLogEntry } = require('./logEntries')1920const LOG = {21TYPE_CONSOLE: 'console',22TYPE_JS_LOGS: 'javascript',23TYPE_JS_EXCEPTION: 'javascriptException',24TYPE_LOGS: 'logs',25TYPE_CONSOLE_FILTER: 'console_filter',26TYPE_JS_LOGS_FILTER: 'javascript_filter',27TYPE_JS_EXCEPTION_FILTER: 'javascriptException_filter',28TYPE_LOGS_FILTER: 'logs_filter',29}3031class LogInspector {32bidi33ws34#callbackId = 03536constructor(driver, browsingContextIds) {37this._driver = driver38this._browsingContextIds = browsingContextIds39this.listener = new Map()40this.listener.set(LOG.TYPE_CONSOLE, new Map())41this.listener.set(LOG.TYPE_JS_LOGS, new Map())42this.listener.set(LOG.TYPE_JS_EXCEPTION, new Map())43this.listener.set(LOG.TYPE_LOGS, new Map())44this.listener.set(LOG.TYPE_CONSOLE_FILTER, new Map())45this.listener.set(LOG.TYPE_JS_LOGS_FILTER, new Map())46this.listener.set(LOG.TYPE_JS_EXCEPTION_FILTER, new Map())47this.listener.set(LOG.TYPE_LOGS_FILTER, new Map())48}4950/**51* Subscribe to log event52* @returns {Promise<void>}53*/54async init() {55this.bidi = await this._driver.getBidi()56await this.bidi.subscribe('log.entryAdded', this._browsingContextIds)57}5859addCallback(eventType, callback) {60const id = ++this.#callbackId6162const eventCallbackMap = this.listener.get(eventType)63eventCallbackMap.set(id, callback)64return id65}6667removeCallback(id) {68let hasId = false69for (const [, callbacks] of this.listener) {70if (callbacks.has(id)) {71callbacks.delete(id)72hasId = true73}74}7576if (!hasId) {77throw Error(`Callback with id ${id} not found`)78}79}8081invokeCallbacks(eventType, data) {82const callbacks = this.listener.get(eventType)83if (callbacks) {84for (const [, callback] of callbacks) {85callback(data)86}87}88}8990invokeCallbacksWithFilter(eventType, data, filterLevel) {91const callbacks = this.listener.get(eventType)92if (callbacks) {93for (const [, value] of callbacks) {94const callback = value.callback95const filter = value.filter96if (filterLevel === filter.getLevel()) {97callback(data)98}99}100}101}102103/**104* Listen to Console logs105* @param callback106* @param filterBy107* @returns {Promise<number>}108*/109async onConsoleEntry(callback, filterBy = undefined) {110if (filterBy !== undefined && !(filterBy instanceof FilterBy)) {111throw Error(`Pass valid FilterBy object. Received: ${filterBy}`)112}113114let id115116if (filterBy !== undefined) {117id = this.addCallback(LOG.TYPE_CONSOLE_FILTER, { callback: callback, filter: filterBy })118} else {119id = this.addCallback(LOG.TYPE_CONSOLE, callback)120}121122this.ws = await this.bidi.socket123124this.ws.on('message', (event) => {125const { params } = JSON.parse(Buffer.from(event.toString()))126127if (params?.type === LOG.TYPE_CONSOLE) {128let consoleEntry = new ConsoleLogEntry(129params.level,130params.source,131params.text,132params.timestamp,133params.type,134params.method,135params.args,136params.stackTrace,137)138139if (filterBy !== undefined) {140if (params?.level === filterBy.getLevel()) {141this.invokeCallbacksWithFilter(LOG.TYPE_CONSOLE_FILTER, consoleEntry, filterBy.getLevel())142}143return144}145146this.invokeCallbacks(LOG.TYPE_CONSOLE, consoleEntry)147}148})149150return id151}152153/**154* Listen to JS logs155* @param callback156* @param filterBy157* @returns {Promise<number>}158*/159async onJavascriptLog(callback, filterBy = undefined) {160if (filterBy !== undefined && !(filterBy instanceof FilterBy)) {161throw Error(`Pass valid FilterBy object. Received: ${filterBy}`)162}163164let id165166if (filterBy !== undefined) {167id = this.addCallback(LOG.TYPE_JS_LOGS_FILTER, { callback: callback, filter: filterBy })168} else {169id = this.addCallback(LOG.TYPE_JS_LOGS, callback)170}171172this.ws = await this.bidi.socket173174this.ws.on('message', (event) => {175const { params } = JSON.parse(Buffer.from(event.toString()))176177if (params?.type === LOG.TYPE_JS_LOGS) {178let jsEntry = new JavascriptLogEntry(179params.level,180params.source,181params.text,182params.timestamp,183params.type,184params.stackTrace,185)186187if (filterBy !== undefined) {188if (params?.level === filterBy.getLevel()) {189this.invokeCallbacksWithFilter(LOG.TYPE_JS_LOGS_FILTER, jsEntry, filterBy.getLevel())190}191return192}193194this.invokeCallbacks(LOG.TYPE_JS_LOGS, jsEntry)195}196})197198return id199}200201/**202* Listen to JS Exceptions203* @param callback204* @returns {Promise<number>}205*/206async onJavascriptException(callback) {207const id = this.addCallback(LOG.TYPE_JS_EXCEPTION, callback)208this.ws = await this.bidi.socket209210this.ws.on('message', (event) => {211const { params } = JSON.parse(Buffer.from(event.toString()))212if (params?.type === 'javascript' && params?.level === 'error') {213let jsErrorEntry = new JavascriptLogEntry(214params.level,215params.source,216params.text,217params.timestamp,218params.type,219params.stackTrace,220)221222this.invokeCallbacks(LOG.TYPE_JS_EXCEPTION, jsErrorEntry)223}224})225226return id227}228229/**230* Listen to any logs231* @param callback232* @param filterBy233* @returns {Promise<number>}234*/235async onLog(callback, filterBy = undefined) {236if (filterBy !== undefined && !(filterBy instanceof FilterBy)) {237throw Error(`Pass valid FilterBy object. Received: ${filterBy}`)238}239240let id241if (filterBy !== undefined) {242id = this.addCallback(LOG.TYPE_LOGS_FILTER, { callback: callback, filter: filterBy })243} else {244id = this.addCallback(LOG.TYPE_LOGS, callback)245}246247this.ws = await this.bidi.socket248249this.ws.on('message', (event) => {250const { params } = JSON.parse(Buffer.from(event.toString()))251if (params?.type === 'javascript') {252let jsEntry = new JavascriptLogEntry(253params.level,254params.source,255params.text,256params.timestamp,257params.type,258params.stackTrace,259)260261if (filterBy !== undefined) {262if (params?.level === filterBy.getLevel()) {263callback(jsEntry)264}265return266}267268if (filterBy !== undefined) {269if (params?.level === filterBy.getLevel()) {270{271this.invokeCallbacksWithFilter(LOG.TYPE_LOGS_FILTER, jsEntry, filterBy.getLevel())272}273return274}275}276277this.invokeCallbacks(LOG.TYPE_LOGS, jsEntry)278return279}280281if (params?.type === 'console') {282let consoleEntry = new ConsoleLogEntry(283params.level,284params.source,285params.text,286params.timestamp,287params.type,288params.method,289params.args,290params.stackTrace,291)292293if (filterBy !== undefined) {294if (params?.level === filterBy.getLevel()) {295this.invokeCallbacksWithFilter(LOG.TYPE_LOGS_FILTER, consoleEntry, filterBy.getLevel())296}297return298}299300this.invokeCallbacks(LOG.TYPE_LOGS, consoleEntry)301return302}303304if (params !== undefined && !['console', 'javascript'].includes(params?.type)) {305let genericEntry = new GenericLogEntry(306params.level,307params.source,308params.text,309params.timestamp,310params.type,311params.stackTrace,312)313314if (filterBy !== undefined) {315if (params?.level === filterBy.getLevel()) {316{317this.invokeCallbacksWithFilter(LOG.TYPE_LOGS_FILTER, genericEntry, filterBy.getLevel())318}319return320}321}322323this.invokeCallbacks(LOG.TYPE_LOGS, genericEntry)324return325}326})327328return id329}330331/**332* Unsubscribe to log event333* @returns {Promise<void>}334*/335async close() {336if (337this._browsingContextIds !== null &&338this._browsingContextIds !== undefined &&339this._browsingContextIds.length > 0340) {341await this.bidi.unsubscribe('log.entryAdded', this._browsingContextIds)342} else {343await this.bidi.unsubscribe('log.entryAdded')344}345}346}347348/**349* initiate inspector instance and return350* @param driver351* @param browsingContextIds352* @returns {Promise<LogInspector>}353*/354async function getLogInspectorInstance(driver, browsingContextIds) {355let instance = new LogInspector(driver, browsingContextIds)356await instance.init()357return instance358}359360/**361* API362* @type {function(*, *): Promise<LogInspector>}363*/364module.exports = getLogInspectorInstance365366367