From 25da084c15df7914ae4a7070eff6acedd0362d67 Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Wed, 3 Jan 2024 16:45:00 +0300 Subject: [PATCH 01/11] ref: Compatibility with latest Ferrum --- .rspec | 2 +- CHANGELOG.md | 2 +- Gemfile | 4 +-- lib/capybara/cuprite.rb | 1 + lib/capybara/cuprite/browser.rb | 44 +++++++++++++++++++-------------- lib/capybara/cuprite/driver.rb | 2 +- lib/capybara/cuprite/options.rb | 14 +++++++++++ lib/capybara/cuprite/page.rb | 16 ++++++------ 8 files changed, 53 insertions(+), 32 deletions(-) create mode 100644 lib/capybara/cuprite/options.rb diff --git a/.rspec b/.rspec index 221fc808..311a4e79 100644 --- a/.rspec +++ b/.rspec @@ -1,3 +1,3 @@ --color ---format=doc +--format=progress --require spec_helper diff --git a/CHANGELOG.md b/CHANGELOG.md index 972a6029..36fe1277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ### Added ### Changed -- `@window_size` attribute is moved from Ferrum viewport size is still inherited [#253] +- `@window_size` attribute is moved from Ferrum, viewport size is still inherited [#253] ### Fixed - Detect whether element is in the viewport and clickable before click [#251] diff --git a/Gemfile b/Gemfile index 0a6a692f..b936ae2f 100644 --- a/Gemfile +++ b/Gemfile @@ -6,12 +6,12 @@ gem "byebug", "~> 11.1", platforms: %i[mri mingw x64_mingw] gem "chunky_png", "~> 1.4" gem "image_size", "~> 3.0" gem "launchy", "~> 2.5" -gem "pdf-reader", "~> 2.5" +gem "pdf-reader", "~> 2.12" gem "puma", ">= 5.6.7" gem "rake", "~> 13.0" gem "rspec", "~> 3.10" gem "rubocop", "~> 1.22" gem "rubocop-rake", require: false -gem "sinatra", "~> 2.1" +gem "sinatra", "~> 3.2" gemspec diff --git a/lib/capybara/cuprite.rb b/lib/capybara/cuprite.rb index 15469f5b..aa1f23dc 100644 --- a/lib/capybara/cuprite.rb +++ b/lib/capybara/cuprite.rb @@ -5,6 +5,7 @@ require "capybara/cuprite/driver" require "capybara/cuprite/browser" require "capybara/cuprite/page" +require "capybara/cuprite/options" require "capybara/cuprite/node" require "capybara/cuprite/errors" diff --git a/lib/capybara/cuprite/browser.rb b/lib/capybara/cuprite/browser.rb index d817a204..25c13dd5 100644 --- a/lib/capybara/cuprite/browser.rb +++ b/lib/capybara/cuprite/browser.rb @@ -11,24 +11,20 @@ class Browser < Ferrum::Browser find_modal accept_confirm dismiss_confirm accept_prompt dismiss_prompt reset_modals] => :page - attr_reader :url_blacklist, :url_whitelist, :window_size - alias url_blocklist url_blacklist - alias url_allowlist url_whitelist - def initialize(options = nil) - options ||= {} - @client = nil - self.url_blacklist = options[:url_blacklist] - self.url_whitelist = options[:url_whitelist] - super - @window_size = @options.window_size + + @options.url_blacklist = prepare_wildcards(options&.dig(:url_blacklist)) + @options.url_whitelist = prepare_wildcards(options&.dig(:url_whitelist)) + @page = false end - def timeout=(value) + def command(...) super - @page.timeout = value unless @page.nil? + rescue Ferrum::DeadBrowserError + restart + raise end def page @@ -39,7 +35,7 @@ def page def reset super - @window_size = options.window_size + @options.reset_window_size @page = attach_page end @@ -49,19 +45,29 @@ def quit end def resize(**options) - @window_size = [options[:width], options[:height]] + @options.window_size = [options[:width], options[:height]] super end + def url_whitelist + @options.url_whitelist + end + alias url_allowlist url_whitelist + def url_whitelist=(patterns) - @url_whitelist = prepare_wildcards(patterns) - page.network.whitelist = @url_whitelist if @client && @url_whitelist.any? + @options.url_whitelist = prepare_wildcards(patterns) + page.network.whitelist = @options.url_whitelist if @client && @options.url_whitelist.any? end alias url_allowlist= url_whitelist= + def url_blacklist + @options.url_blacklist + end + alias url_blocklist url_blacklist + def url_blacklist=(patterns) - @url_blacklist = prepare_wildcards(patterns) - page.network.blacklist = @url_blacklist if @client && @url_blacklist.any? + @options.url_blacklist = prepare_wildcards(patterns) + page.network.blacklist = @options.url_blacklist if @client && @options.url_blacklist.any? end alias url_blocklist= url_blacklist= @@ -234,7 +240,7 @@ def attach_page(target_id = nil) return target.page if target.attached? target.maybe_sleep_if_new_window - target.page = Page.new(target.id, self) + target.page = Page.new(client, context_id: target.context_id, target_id: target.id) target.page end end diff --git a/lib/capybara/cuprite/driver.rb b/lib/capybara/cuprite/driver.rb index be253547..4e52c443 100644 --- a/lib/capybara/cuprite/driver.rb +++ b/lib/capybara/cuprite/driver.rb @@ -115,7 +115,7 @@ def switch_to_frame(locator) def open_new_window target = browser.default_context.create_target target.maybe_sleep_if_new_window - target.page = Page.new(target.id, browser) + target.page = Page.new(browser.client, context_id: target.context_id, target_id: target.id) target.page end diff --git a/lib/capybara/cuprite/options.rb b/lib/capybara/cuprite/options.rb new file mode 100644 index 00000000..1f863dee --- /dev/null +++ b/lib/capybara/cuprite/options.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Ferrum + class Browser + class Options + attr_writer :window_size + attr_accessor :url_blacklist, :url_whitelist + + def reset_window_size + @window_size = @options[:window_size] + end + end + end +end diff --git a/lib/capybara/cuprite/page.rb b/lib/capybara/cuprite/page.rb index 8f2977ee..7d5b1f71 100644 --- a/lib/capybara/cuprite/page.rb +++ b/lib/capybara/cuprite/page.rb @@ -13,7 +13,7 @@ class Page < Ferrum::Page current_url current_title body execution_id execution_id! evaluate evaluate_on evaluate_async execute] => :active_frame - def initialize(*args) + def initialize(...) @frame_stack = [] @accept_modal = [] @modal_messages = [] @@ -70,17 +70,17 @@ def dismiss_prompt def find_modal(options) start = Ferrum::Utils::ElapsedTime.monotonic_time - timeout = options.fetch(:wait, browser.timeout) expect_text = options[:text] expect_regexp = expect_text.is_a?(Regexp) ? expect_text : Regexp.escape(expect_text.to_s) not_found_msg = "Unable to find modal dialog" not_found_msg += " with #{expect_text}" if expect_text + wait = options.fetch(:wait, timeout) begin modal_text = @modal_messages.shift raise Capybara::ModalNotFound if modal_text.nil? || (expect_text && !modal_text.match(expect_regexp)) rescue Capybara::ModalNotFound => e - raise e, not_found_msg if Ferrum::Utils::ElapsedTime.timeout?(start, timeout) + raise e, not_found_msg if Ferrum::Utils::ElapsedTime.timeout?(start, wait) sleep(MODAL_WAIT) retry @@ -134,13 +134,13 @@ def title def prepare_page super - width, height = @browser.window_size + width, height = @options.window_size resize(width: width, height: height) - if @browser.url_blacklist.any? - network.blacklist = @browser.url_blacklist - elsif @browser.url_whitelist.any? - network.whitelist = @browser.url_whitelist + if @options.url_blacklist.any? + network.blacklist = @options.url_blacklist + elsif @options.url_whitelist.any? + network.whitelist = @options.url_whitelist end on("Page.javascriptDialogOpening") do |params| From 3464a23a10e548f8af30416f4affeb7ea00e4297 Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Wed, 3 Jan 2024 19:47:31 +0300 Subject: [PATCH 02/11] chore: add CHANGELOG entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36fe1277..b365c3fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ### Changed - `@window_size` attribute is moved from Ferrum, viewport size is still inherited [#253] +- Compatibility with latest Ferrum. Browser instance is not passed everywhere now. +`window_size`, `url_blacklist`, `url_whitelist`, `timeout` are moved to options. Page arguments are changed to +`Page.new(client, context_id:, target_id:)`. These changes are changes to internal API. [#254] ### Fixed - Detect whether element is in the viewport and clickable before click [#251] From dca1bdfb4f49eebf621b6bf3f10f550d3ededbde Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Sat, 17 Feb 2024 14:43:03 +0300 Subject: [PATCH 03/11] chore: update ferrum --- cuprite.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cuprite.gemspec b/cuprite.gemspec index 5aaf6171..235d0b69 100644 --- a/cuprite.gemspec +++ b/cuprite.gemspec @@ -25,5 +25,5 @@ Gem::Specification.new do |s| s.required_ruby_version = ">= 2.7.0" s.add_runtime_dependency "capybara", "~> 3.0" - s.add_runtime_dependency "ferrum", "~> 0.14.0" + s.add_runtime_dependency "ferrum", "~> 0.15.0" end From 60d5328b8448f3946beef5a84734491f743e469e Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Sat, 17 Feb 2024 14:52:37 +0300 Subject: [PATCH 04/11] chore: get rid of websocket-driver-0.6.x --- .github/gemfiles/websocket-driver-0.6.x.gemfile | 11 ----------- .github/gemfiles/websocket-driver-0.7.x.gemfile | 11 ----------- .github/workflows/tests.yml | 2 -- 3 files changed, 24 deletions(-) delete mode 100644 .github/gemfiles/websocket-driver-0.6.x.gemfile delete mode 100644 .github/gemfiles/websocket-driver-0.7.x.gemfile diff --git a/.github/gemfiles/websocket-driver-0.6.x.gemfile b/.github/gemfiles/websocket-driver-0.6.x.gemfile deleted file mode 100644 index 5ddc6303..00000000 --- a/.github/gemfiles/websocket-driver-0.6.x.gemfile +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -gem "websocket-driver", ">= 0.6", "< 0.7" -gem "capybara", "~> 3.0" -gem "matrix" - -eval_gemfile "../../Gemfile" - -gemspec path: "../../" diff --git a/.github/gemfiles/websocket-driver-0.7.x.gemfile b/.github/gemfiles/websocket-driver-0.7.x.gemfile deleted file mode 100644 index 0da8211d..00000000 --- a/.github/gemfiles/websocket-driver-0.7.x.gemfile +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -gem "websocket-driver", ">= 0.7", "< 0.8" -gem "capybara", "~> 3.0" -gem "matrix" - -eval_gemfile "../../Gemfile" - -gemspec path: "../../" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d97467ea..1a4c3fd8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,13 +11,11 @@ jobs: strategy: fail-fast: false matrix: - gemfile: [websocket-driver-0.6.x, websocket-driver-0.7.x] ruby: [2.7, "3.0", 3.1, 3.2, 3.3] runs-on: ubuntu-latest env: FERRUM_PROCESS_TIMEOUT: 25 FERRUM_DEFAULT_TIMEOUT: 15 - BUNDLE_GEMFILE: .github/gemfiles/${{ matrix.gemfile }}.gemfile steps: - name: Checkout code uses: actions/checkout@v4 From dbe79f2b8a571a01c9e6829332c11ed0d1433107 Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Sun, 18 Feb 2024 14:58:17 +0300 Subject: [PATCH 05/11] chore: fix client --- lib/capybara/cuprite/browser.rb | 5 +++-- lib/capybara/cuprite/driver.rb | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/capybara/cuprite/browser.rb b/lib/capybara/cuprite/browser.rb index 25c13dd5..1b6da63e 100644 --- a/lib/capybara/cuprite/browser.rb +++ b/lib/capybara/cuprite/browser.rb @@ -124,6 +124,7 @@ def close_window(target_id) @page = nil if @page.target_id == target.id target.page.close + targets.delete(target_id) # page.close is async, delete target asap end def browser_error @@ -237,10 +238,10 @@ def prepare_wildcards(patterns) def attach_page(target_id = nil) target = targets[target_id] if target_id target ||= default_context.default_target - return target.page if target.attached? + return target.page if target.connected? target.maybe_sleep_if_new_window - target.page = Page.new(client, context_id: target.context_id, target_id: target.id) + target.page = Page.new(target.client, context_id: target.context_id, target_id: target.id) target.page end end diff --git a/lib/capybara/cuprite/driver.rb b/lib/capybara/cuprite/driver.rb index 4e52c443..eff76899 100644 --- a/lib/capybara/cuprite/driver.rb +++ b/lib/capybara/cuprite/driver.rb @@ -115,7 +115,7 @@ def switch_to_frame(locator) def open_new_window target = browser.default_context.create_target target.maybe_sleep_if_new_window - target.page = Page.new(browser.client, context_id: target.context_id, target_id: target.id) + target.page = Page.new(target.client, context_id: target.context_id, target_id: target.id) target.page end From ef47a1d9184df34c3b31ee5b3dfabdea7bf8cd0f Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Sun, 18 Feb 2024 15:16:49 +0300 Subject: [PATCH 06/11] fix: CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run --- spec/spec_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 91e4577f..33be5a18 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -119,7 +119,7 @@ def save_exception_artifacts(browser, meta) end def save_exception_screenshot(browser, filename, line_number, timestamp) - screenshot_name = "screenshot-#{filename}-#{line_number}-#{timestamp}.png" + screenshot_name = "screenshot-#{filename}-#{line_number}-#{timestamp}-#{rand(1000)}.png" screenshot_path = "/tmp/cuprite/#{screenshot_name}" browser.screenshot(path: screenshot_path, full: true) rescue StandardError => e @@ -127,7 +127,7 @@ def save_exception_screenshot(browser, filename, line_number, timestamp) end def save_exception_log(browser, filename, line_number, timestamp) - log_name = "logfile-#{filename}-#{line_number}-#{timestamp}.txt" + log_name = "logfile-#{filename}-#{line_number}-#{timestamp}-#{rand(1000)}.txt" File.binwrite("/tmp/cuprite/#{log_name}", browser.options.logger.string) rescue StandardError => e puts "#{e.class}: #{e.message}" From c8d0768126d68385741d13583775eb4f3ec0bcc2 Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Sun, 18 Feb 2024 15:34:00 +0300 Subject: [PATCH 07/11] chore: skip broken capybara test --- .github/workflows/tests.yml | 1 + spec/spec_helper.rb | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1a4c3fd8..61a1bfd3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -42,3 +42,4 @@ jobs: with: name: footprints path: /tmp/cuprite/ + overwrite: true diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 33be5a18..c8f8246f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -84,6 +84,7 @@ module TestSessions #has_css? with spatial requirements accepts spatial options #has_css? with spatial requirements supports spatial sugar #fill_in should fill in a textarea in a reasonable time by default + #has_element? should be true if the given element is on the page REGEXP metadata[:skip] = true if metadata[:full_description].match(/#{regexes}/) @@ -119,7 +120,7 @@ def save_exception_artifacts(browser, meta) end def save_exception_screenshot(browser, filename, line_number, timestamp) - screenshot_name = "screenshot-#{filename}-#{line_number}-#{timestamp}-#{rand(1000)}.png" + screenshot_name = "screenshot-#{filename}-#{line_number}-#{timestamp}.png" screenshot_path = "/tmp/cuprite/#{screenshot_name}" browser.screenshot(path: screenshot_path, full: true) rescue StandardError => e @@ -127,7 +128,7 @@ def save_exception_screenshot(browser, filename, line_number, timestamp) end def save_exception_log(browser, filename, line_number, timestamp) - log_name = "logfile-#{filename}-#{line_number}-#{timestamp}-#{rand(1000)}.txt" + log_name = "logfile-#{filename}-#{line_number}-#{timestamp}.txt" File.binwrite("/tmp/cuprite/#{log_name}", browser.options.logger.string) rescue StandardError => e puts "#{e.class}: #{e.message}" From 68658cdf34952e3ae1b7ad86607d7c2e62f99feb Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Sun, 18 Feb 2024 15:51:44 +0300 Subject: [PATCH 08/11] chore: check artifact --- spec/spec_helper.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c8f8246f..91e4577f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -84,7 +84,6 @@ module TestSessions #has_css? with spatial requirements accepts spatial options #has_css? with spatial requirements supports spatial sugar #fill_in should fill in a textarea in a reasonable time by default - #has_element? should be true if the given element is on the page REGEXP metadata[:skip] = true if metadata[:full_description].match(/#{regexes}/) From 229c79084e47783a0aeb0a656325a76eaeaf262f Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Sun, 18 Feb 2024 16:17:18 +0300 Subject: [PATCH 09/11] fix: artifacts --- .github/workflows/tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 61a1bfd3..d3141659 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,6 +40,5 @@ jobs: uses: actions/upload-artifact@v4 if: ${{ failure() }} with: - name: footprints + name: artifacts-ruby-v${{ matrix.ruby }} path: /tmp/cuprite/ - overwrite: true From b0c4d8226b6b86b1e054fea8fc203e662583d3f3 Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Sun, 18 Feb 2024 17:01:49 +0300 Subject: [PATCH 10/11] fix: build --- spec/spec_helper.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 91e4577f..c8f8246f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -84,6 +84,7 @@ module TestSessions #has_css? with spatial requirements accepts spatial options #has_css? with spatial requirements supports spatial sugar #fill_in should fill in a textarea in a reasonable time by default + #has_element? should be true if the given element is on the page REGEXP metadata[:skip] = true if metadata[:full_description].match(/#{regexes}/) From 9ba7e6597108a57bdef94e9a3174e8b0f285ed60 Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Mon, 19 Feb 2024 08:57:08 +0300 Subject: [PATCH 11/11] chore: update CHANGELOG --- CHANGELOG.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b365c3fe..a9b50e89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,16 @@ ### Changed - `@window_size` attribute is moved from Ferrum, viewport size is still inherited [#253] -- Compatibility with latest Ferrum. Browser instance is not passed everywhere now. -`window_size`, `url_blacklist`, `url_whitelist`, `timeout` are moved to options. Page arguments are changed to -`Page.new(client, context_id:, target_id:)`. These changes are changes to internal API. [#254] +- Compatibility with latest Ferrum. Browser instance is not passed everywhere now [#254] + - `Cuprite::Browser` methods are located in `Options`. + - `#window_size` + - `#url_blacklist` + - `#url_whitelist` + - `#timeout` + - `Page#new` arguments are changed to `client, context_id:, target_id:` + - `Target#attached?` renamed to `Target#connected?` + - Ferrum doesn't restart browser automatically, Cuprite does + - `Browser#close_window` removes target id asap from the target list ### Fixed - Detect whether element is in the viewport and clickable before click [#251]