Skip to content

Commit

Permalink
Close RUM session on Capybara.reset_session! or WebDriver::Driver.qui…
Browse files Browse the repository at this point in the history
…t; add finishing touches for the integration
  • Loading branch information
anmarchenko committed May 28, 2024
1 parent 767b394 commit 691bf86
Show file tree
Hide file tree
Showing 14 changed files with 236 additions and 19 deletions.
1 change: 1 addition & 0 deletions Steepfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ target :lib do
library "knapsack_pro"
library "bundler"
library "selenium-webdriver"
library "capybara"
end
45 changes: 45 additions & 0 deletions lib/datadog/ci/contrib/selenium/capybara_driver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

require "datadog/tracing/contrib/patcher"

require_relative "ext"
require_relative "rum"
require_relative "../../ext/test"

module Datadog
module CI
module Contrib
module Selenium
# instruments Capybara::Selenium::Driver
module CapybaraDriver
def self.included(base)
base.prepend(InstanceMethods)
end

module InstanceMethods
def reset!
return super unless datadog_configuration[:enabled]

Datadog.logger.debug("[Selenium] Capybara session reset event")

RUM.stop_rum_session(@browser)

Datadog.logger.debug("[Selenium] RUM session stopped, deleting cookie")
@browser.manage.delete_cookie(Ext::COOKIE_TEST_EXECUTION_ID)
rescue ::Selenium::WebDriver::Error::WebDriverError => e
Datadog.logger.debug("[Selenium] Error while resetting Capybara session: #{e.message}")
ensure
super
end

private

def datadog_configuration
Datadog.configuration.ci[:selenium]
end
end
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/datadog/ci/contrib/selenium/configuration/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Settings < Datadog::CI::Contrib::Settings
end

option :rum_flush_wait_millis do |o|
o.type :integer
o.type :int
o.env Ext::ENV_RUM_FLUSH_WAIT_MILLIS
o.default 500
end
Expand Down
45 changes: 45 additions & 0 deletions lib/datadog/ci/contrib/selenium/driver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

require "datadog/tracing/contrib/patcher"

require_relative "ext"
require_relative "rum"
require_relative "../../ext/test"

module Datadog
module CI
module Contrib
module Selenium
# instruments Selenium::WebDriver::Driver
module Driver
def self.included(base)
base.prepend(InstanceMethods)
end

module InstanceMethods
def quit
return super unless datadog_configuration[:enabled]

Datadog.logger.debug("[Selenium] Driver quit event")

RUM.stop_rum_session(@bridge)

Datadog.logger.debug("[Selenium] RUM session stopped, deleting cookie")
@bridge.manage.delete_cookie(Ext::COOKIE_TEST_EXECUTION_ID)
rescue ::Selenium::WebDriver::Error::WebDriverError => e
Datadog.logger.debug("[Selenium] Error while quitting Selenium driver: #{e.message}")
ensure
super
end

private

def datadog_configuration
Datadog.configuration.ci[:selenium]
end
end
end
end
end
end
end
8 changes: 8 additions & 0 deletions lib/datadog/ci/contrib/selenium/ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ module Ext
SCRIPT_IS_RUM_ACTIVE = <<~JS
return !!window.DD_RUM
JS
SCRIPT_STOP_RUM_SESSION = <<~JS
if (window.DD_RUM && window.DD_RUM.stopSession) {
window.DD_RUM.stopSession();
return true;
} else {
return false;
}
JS
end
end
end
Expand Down
18 changes: 10 additions & 8 deletions lib/datadog/ci/contrib/selenium/navigation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,20 @@ def to(url)

return result unless datadog_configuration[:enabled]

Datadog.logger.debug("[Selenium] Navigation to #{url}")

# on session reset Capybara navigates to about:blank
return result if url == "about:blank"

active_test = Datadog::CI.active_test
Datadog.logger.debug("[Selenium] Active test: #{active_test}")

return result unless active_test

# Set the test's trace id as a cookie in browser session
@bridge.manage.add_cookie(name: Ext::COOKIE_TEST_EXECUTION_ID, value: active_test.trace_id.to_s)
cookie_hash = {name: Ext::COOKIE_TEST_EXECUTION_ID, value: active_test.trace_id.to_s}
Datadog.logger.debug { "[Selenium] Setting cookie: #{cookie_hash}" }
@bridge.manage.add_cookie(cookie_hash)

# set the test type to browser
active_test.set_tag(CI::Ext::Test::TAG_TYPE, CI::Ext::Test::Type::BROWSER)
Expand All @@ -48,13 +54,9 @@ def to(url)
@bridge.capabilities.browser_version
)

is_rum_active_result = @bridge.execute_script(Ext::SCRIPT_IS_RUM_ACTIVE)
if is_rum_active_result == "true"
active_test.set_tag(
CI::Ext::Test::TAG_IS_RUM_ACTIVE,
"true"
)
end
result
rescue ::Selenium::WebDriver::Error::WebDriverError => e
Datadog.logger.debug("[Selenium] Error while navigating: #{e.message}")

result
end
Expand Down
8 changes: 8 additions & 0 deletions lib/datadog/ci/contrib/selenium/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

require "datadog/tracing/contrib/patcher"

require_relative "capybara_driver"
require_relative "driver"
require_relative "navigation"

module Datadog
Expand All @@ -19,7 +21,13 @@ def target_version
end

def patch
::Selenium::WebDriver::Driver.include(Driver)
::Selenium::WebDriver::Navigation.include(Navigation)

# capybara calls `reset!` after each test, so we need to patch it as well
if defined?(::Capybara::Selenium::Driver)
::Capybara::Selenium::Driver.include(CapybaraDriver)
end
end
end
end
Expand Down
44 changes: 44 additions & 0 deletions lib/datadog/ci/contrib/selenium/rum.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

require "datadog/tracing/contrib/patcher"

require_relative "ext"
require_relative "../../ext/test"

module Datadog
module CI
module Contrib
module Selenium
# Provides functionality to interact with Datadog Real User Monitoring product
# via executing JavaScript code in the browser.
#
# Relevant docs: https://docs.datadoghq.com/real_user_monitoring/browser/
module RUM
def self.is_rum_active?(script_executor)
is_rum_active_script_result = script_executor.execute_script(Ext::SCRIPT_IS_RUM_ACTIVE)
Datadog.logger.debug { "[Selenium] SCRIPT_IS_RUM_ACTIVE result is #{is_rum_active_script_result.inspect}" }
is_rum_active_script_result == "true" || is_rum_active_script_result == true
end

def self.stop_rum_session(script_executor)
config = Datadog.configuration.ci[:selenium]
if is_rum_active?(script_executor)
Datadog::CI.active_test&.set_tag(
CI::Ext::Test::TAG_IS_RUM_ACTIVE,
"true"
)

result = script_executor.execute_script(Ext::SCRIPT_STOP_RUM_SESSION)
Datadog.logger.debug { "[Selenium] SCRIPT_STOP_RUM_SESSION result is #{result.inspect}" }

# introduce a delay to allow the RUM session to be stopped
delay = config[:rum_flush_wait_millis] / 1000.0
Datadog.logger.debug { "[Selenium] Waiting for #{delay} seconds" }
sleep(delay)
end
end
end
end
end
end
end
19 changes: 19 additions & 0 deletions sig/datadog/ci/contrib/selenium/capybara_driver.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Datadog
module CI
module Contrib
module Selenium
module CapybaraDriver
def self.included: (untyped base) -> untyped

module InstanceMethods : Capybara::Selenium::Driver
def reset!: () -> void

private

def datadog_configuration: () -> untyped
end
end
end
end
end
end
19 changes: 19 additions & 0 deletions sig/datadog/ci/contrib/selenium/driver.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Datadog
module CI
module Contrib
module Selenium
module Driver
def self.included: (untyped base) -> untyped

module InstanceMethods : Selenium::WebDriver::Driver
def quit: () -> void

private

def datadog_configuration: () -> untyped
end
end
end
end
end
end
2 changes: 2 additions & 0 deletions sig/datadog/ci/contrib/selenium/ext.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ module Datadog
COOKIE_TEST_EXECUTION_ID: "datadog-ci-visibility-test-execution-id"

SCRIPT_IS_RUM_ACTIVE: String

SCRIPT_STOP_RUM_SESSION: String
end
end
end
Expand Down
13 changes: 13 additions & 0 deletions sig/datadog/ci/contrib/selenium/rum.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Datadog
module CI
module Contrib
module Selenium
module RUM
def self.is_rum_active?: (untyped script_executor) -> bool

def self.stop_rum_session: (untyped script_executor) -> void
end
end
end
end
end
22 changes: 12 additions & 10 deletions spec/datadog/ci/contrib/selenium/instrumentation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
let(:integration_name) { :cucumber }
end

let(:manager) { spy("manager", add_cookie: nil) }
let(:manager) { spy("manager") }
let(:bridge) do
instance_double(
Selenium::WebDriver::Remote::Bridge,
Expand All @@ -33,10 +33,6 @@
let(:stdout) { StringIO.new }
let(:stderr) { StringIO.new }

# let(:stdin) { $stdin }
# let(:stdout) { $stdout }
# let(:stderr) { $stderr }

let(:kernel) { double(:kernel) }

# Cucumber runtime setup
Expand Down Expand Up @@ -64,29 +60,35 @@
before do
# expect(kernel).to receive(:exit).with(expected_test_run_code)
expect(Selenium::WebDriver::Remote::Bridge).to receive(:new).and_return(bridge)
expect(bridge).to receive(:execute_script) do |script|
allow(bridge).to receive(:execute_script) do |script|
executed_scripts << script
"true"
end
expect(bridge).to receive(:get) do |url|
allow(bridge).to receive(:get) do |url|
visited_urls << url
end

allow(kernel).to receive(:exit)
ClimateControl.modify("DD_CIVISIBILITY_SELENIUM_ENABLED" => "1") do
expect(kernel).to receive(:exit).with(expected_test_run_code)
ClimateControl.modify("DD_CIVISIBILITY_SELENIUM_ENABLED" => "1", "DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS" => "1") do
cli.execute!(existing_runtime)
end
end

it "recognize the test as browser test and adds additional tags" do
expect(visited_urls).to eq(["http://www.example.com"])
expect(executed_scripts).to eq([Datadog::CI::Contrib::Selenium::Ext::SCRIPT_IS_RUM_ACTIVE])
expect(executed_scripts).to eq(
[
Datadog::CI::Contrib::Selenium::Ext::SCRIPT_IS_RUM_ACTIVE,
Datadog::CI::Contrib::Selenium::Ext::SCRIPT_STOP_RUM_SESSION
]
)

expect(test_spans).to have(1).item

expect(manager).to have_received(:add_cookie).with(
{name: "datadog-ci-visibility-test-execution-id", value: first_test_span.trace_id.to_s}
)
expect(manager).to have_received(:delete_cookie).with("datadog-ci-visibility-test-execution-id")

expect(first_test_span).to have_test_tag(:type, "browser")
expect(first_test_span).to have_test_tag(:browser_driver, "selenium")
Expand Down
9 changes: 9 additions & 0 deletions vendor/rbs/capybara/0/capybara.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Capybara
end

module Capybara::Selenium
end

module Capybara::Selenium::Driver
def reset!: () -> void
end

0 comments on commit 691bf86

Please sign in to comment.