Skip to content

Commit

Permalink
[ruby][bidi] Add filtering capability to LogInspector
Browse files Browse the repository at this point in the history
  • Loading branch information
TamsilAmani authored Feb 13, 2023
1 parent 332988c commit e2d160b
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 45 deletions.
1 change: 1 addition & 0 deletions rb/.rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Metrics/ClassLength:
- 'lib/selenium/webdriver/common/driver.rb'
- 'lib/selenium/webdriver/remote/bridge.rb'
- 'lib/selenium/webdriver/remote/capabilities.rb'
- 'spec/integration/selenium/webdriver/bidi/log_inspector_spec.rb'
- 'spec/integration/selenium/webdriver/spec_support/test_environment.rb'

Metrics/CyclomaticComplexity:
Expand Down
40 changes: 40 additions & 0 deletions rb/lib/selenium/webdriver/bidi/log/filter_by.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

module Selenium
module WebDriver
class BiDi
class FilterBy
attr_accessor :level

def initialize(level)
@level = level
end

def self.log_level(level = nil)
unless %w[debug error info warning].include?(level)
raise Error::WebDriverError,
"Valid log levels are 'debug', 'error', 'info' and 'warning'. Received: #{level}"
end
FilterBy.new(level)
end
end # FilterBy
end # BiDi
end # WebDriver
end # Selenium
67 changes: 35 additions & 32 deletions rb/lib/selenium/webdriver/bidi/log_inspector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
require_relative 'log/generic_log_entry'
require_relative 'log/console_log_entry'
require_relative 'log/javascript_log_entry'
require_relative 'log/filter_by'

module Selenium
module WebDriver
Expand All @@ -49,43 +50,41 @@ def initialize(driver, browsing_context_ids = nil)
@bidi.session.subscribe('log.entryAdded', browsing_context_ids)
end

def on_console_entry(&block)
enabled = log_listeners[:console].any?
log_listeners[:console] << block
return if enabled
def on_console_entry(filter_by = nil, &block)
check_valid_filter(filter_by)

on_log do |params|
type = params['type']
console_log_events(params) if type.eql?('console')
console_log_events(params, filter_by, &block) if type.eql?('console')
end
end

def on_javascript_log(&block)
enabled = log_listeners[:javascript].any?
log_listeners[:javascript] << block
return if enabled
def on_javascript_log(filter_by = nil, &block)
check_valid_filter(filter_by)

on_log do |params|
type = params['type']
javascript_log_events(params) if type.eql?('javascript')
javascript_log_events(params, filter_by, &block) if type.eql?('javascript')
end
end

def on_javascript_exception(&block)
enabled = log_listeners[:js_exception].any?
log_listeners[:js_exception] << block
log_listeners[:javascript] << block
return if enabled

on_log do |params|
type = params['type']
level = params['level']

javascript_log_events(params) if type.eql?('javascript') && level.eql?(LOG_LEVEL[:ERROR])
javascript_log_events(params, FilterBy.log_level('error'), &block) if type.eql?('javascript')
end
end

def on_log(&block)
def on_log(filter_by = nil, &block)
unless filter_by.nil?
check_valid_filter(filter_by)

on(:entry_added) do |params|
yield(params) if params['level'] == filter_by.level
end
return
end

on(:entry_added, &block)
end

Expand All @@ -96,11 +95,13 @@ def on(event, &block)
@bidi.callbacks["log.#{event}"] << block
end

def log_listeners
@log_listeners ||= Hash.new { |listeners, kind| listeners[kind] = [] }
def check_valid_filter(filter_by)
return if filter_by.nil? || filter_by.instance_of?(FilterBy)

raise "Pass valid FilterBy object. Received: #{filter_by.inspect}"
end

def console_log_events(params)
def console_log_events(params, filter_by)
event = ConsoleLogEntry.new(
level: params['level'],
text: params['text'],
Expand All @@ -111,28 +112,30 @@ def console_log_events(params)
args: params['args'],
stack_trace: params['stackTrace']
)
log_listeners[:console].each do |listener|
listener.call(event)

unless filter_by.nil?
yield(event) if params['level'] == filter_by.level
return
end

yield(event)
end

def javascript_log_events(params)
def javascript_log_events(params, filter_by)
event = JavascriptLogEntry.new(
level: params['level'],
text: params['text'],
timestamp: params['timestamp'],
type: params['type'],
stack_trace: params['stackTrace']
)
log_listeners[:javascript].each do |listener|
listener.call(event)
end

return unless params['level'].eql?(LOG_LEVEL[:ERROR])

log_listeners[:js_exception].each do |listener|
listener.call(event)
unless filter_by.nil?
yield(event) if params['level'] == filter_by.level
return
end

yield(event)
end
end # LogInspector
end # Bidi
Expand Down
151 changes: 138 additions & 13 deletions rb/spec/integration/selenium/webdriver/bidi/log_inspector_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,40 @@
module Selenium
module WebDriver
class BiDi
describe LogInspector, exclusive: {browser: %i[chrome firefox]} do
before do
@page = '/bidi/logEntryAdded.html'
describe LogInspector, only: {browser: %i[chrome edge firefox]} do
let(:page) { '/bidi/logEntryAdded.html' }

it 'can listen to console log' do
reset_driver!(web_socket_url: true) do |driver|
log_entry = nil
log_inspector = described_class.new(driver)
log_inspector.on_console_entry { |log| log_entry = log }

driver.navigate.to url_for(page)
driver.find_element(id: 'consoleLog').click
wait.until { !log_entry.nil? }

expect(log_entry).to have_attributes(
text: 'Hello, world!',
realm: nil,
type: 'console',
level: LogInspector::LOG_LEVEL[:INFO],
method: 'log'
)
expect(log_entry.args.size).to eq(1)
end
end

it 'can listen to console log', except: {browser: :chrome} do
it 'can listen to console log with different consumers' do
reset_driver!(web_socket_url: true) do |driver|
log_entry = nil
log_inspector = described_class.new(driver)
log_inspector.on_console_entry { |log| log_entry = log }

driver.navigate.to url_for(@page)
log_entry_text = nil
log_inspector.on_console_entry { |log| log_entry_text = log.text }

driver.navigate.to url_for(page)
driver.find_element(id: 'consoleLog').click
wait.until { !log_entry.nil? }

Expand All @@ -42,10 +64,46 @@ class BiDi
realm: nil,
type: 'console',
level: LogInspector::LOG_LEVEL[:INFO],
method: 'log',
stack_trace: nil
method: 'log'
)
expect(log_entry.args.size).to eq(1)
expect(log_entry_text).to eq('Hello, world!')
end
end

it 'can filter console info level log' do
reset_driver!(web_socket_url: true) do |driver|
log_entry = nil
log_inspector = described_class.new(driver)
log_inspector.on_console_entry(FilterBy.log_level('info')) { |log| log_entry = log }

driver.navigate.to url_for(page)
driver.find_element(id: 'consoleLog').click
wait.until { !log_entry.nil? }

expect(log_entry).to have_attributes(
text: 'Hello, world!',
realm: nil,
type: 'console',
level: LogInspector::LOG_LEVEL[:INFO],
method: 'log'
)
expect(log_entry.args.size).to eq(1)
end
end

it 'can filter console log' do
reset_driver!(web_socket_url: true) do |driver|
log_entry = nil
log_inspector = described_class.new(driver)
log_inspector.on_console_entry(FilterBy.log_level('error')) { |log| log_entry = log }

driver.navigate.to url_for(page)
# Generating info level log but we are filtering by error level
wait.until { driver.find_element(id: 'consoleLog').displayed? }
driver.find_element(id: 'consoleLog').click

expect(log_entry).to be_nil
end
end

Expand All @@ -55,7 +113,7 @@ class BiDi
log_inspector = described_class.new(driver)
log_inspector.on_javascript_log { |log| log_entry = log }

driver.navigate.to url_for(@page)
driver.navigate.to url_for(page)
driver.find_element(id: 'jsException').click
wait.until { !log_entry.nil? }

Expand All @@ -67,13 +125,46 @@ class BiDi
end
end

it 'can filter javascript log at error level' do
reset_driver!(web_socket_url: true) do |driver|
log_entry = nil
log_inspector = described_class.new(driver)
log_inspector.on_javascript_log(FilterBy.log_level('error')) { |log| log_entry = log }

driver.navigate.to url_for(page)
driver.find_element(id: 'jsException').click
wait.until { !log_entry.nil? }

expect(log_entry).to have_attributes(
text: 'Error: Not working',
type: 'javascript',
level: LogInspector::LOG_LEVEL[:ERROR]
)
end
end

it 'can filter javascript log' do
reset_driver!(web_socket_url: true) do |driver|
log_entry = nil
log_inspector = described_class.new(driver)
log_inspector.on_javascript_log(FilterBy.log_level('info')) { |log| log_entry = log }

driver.navigate.to url_for(page)
# Generating js error level log but we are filtering by info level
wait.until { driver.find_element(id: 'jsException').displayed? }
driver.find_element(id: 'jsException').click

expect(log_entry).to be_nil
end
end

it 'can listen to javascript error log' do
reset_driver!(web_socket_url: true) do |driver|
log_entry = nil
log_inspector = described_class.new(driver)
log_inspector.on_javascript_exception { |log| log_entry = log }

driver.navigate.to url_for(@page)
driver.navigate.to url_for(page)
driver.find_element(id: 'jsException').click
wait.until { !log_entry.nil? }

Expand All @@ -91,7 +182,7 @@ class BiDi
log_inspector = described_class.new(driver)
log_inspector.on_log { |log| log_entry = log }

driver.navigate.to url_for(@page)
driver.navigate.to url_for(page)
driver.find_element(id: 'consoleError').click
wait.until { !log_entry.nil? }

Expand All @@ -101,20 +192,54 @@ class BiDi
end
end

it 'can retrieve stack trace for a log', except: {browser: :chrome} do
it 'can filter any log' do
reset_driver!(web_socket_url: true) do |driver|
log_entry = nil
log_inspector = described_class.new(driver)
log_inspector.on_log(FilterBy.log_level('info')) { |log| log_entry = log }

driver.navigate.to url_for(page)
driver.find_element(id: 'consoleLog').click
wait.until { !log_entry.nil? }

expect(log_entry['text']).to eq('Hello, world!')
expect(log_entry['realm']).to be_nil
expect(log_entry['type']).to eq('console')
expect(log_entry['level']).to eq('info')
expect(log_entry['method']).to eq('log')
expect(log_entry['args'].size).to eq(1)
end
end

it 'can filter any log at error level' do
reset_driver!(web_socket_url: true) do |driver|
log_entry = nil
log_inspector = described_class.new(driver)
log_inspector.on_log(FilterBy.log_level('error')) { |log| log_entry = log }

driver.navigate.to url_for(page)
driver.find_element(id: 'jsException').click
wait.until { !log_entry.nil? }

expect(log_entry['text']).to eq('Error: Not working')
expect(log_entry['type']).to eq('javascript')
expect(log_entry['level']).to eq('error')
end
end

it 'can retrieve stack trace for a log' do
reset_driver!(web_socket_url: true) do |driver|
log_entry = nil
log_inspector = described_class.new(driver)
log_inspector.on_javascript_log { |log| log_entry = log }

driver.navigate.to url_for(@page)
driver.navigate.to url_for(page)
driver.find_element(id: 'jsException').click
wait.until { !log_entry.nil? }

stack_trace = log_entry.stack_trace

expect(stack_trace).not_to be_nil
expect(stack_trace['callFrames'].size).to eq(3)
end
end
end
Expand Down

0 comments on commit e2d160b

Please sign in to comment.