From 09bc5f3fbe83152288492e6343479c275d15bb2b Mon Sep 17 00:00:00 2001 From: Duncan Bayne Date: Thu, 20 Mar 2025 14:05:42 +1100 Subject: [PATCH 1/7] Use forked Ferrum library until upstream accepts our patches We need to expose the mobile setting in the Ferrum API as well. Until that's merged, use a fork of Ferrum that exposes it. --- Gemfile | 1 + lib/capybara/cuprite/page.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 73412cd..d0bfe73 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ source "https://rubygems.org" gem "byebug", "~> 11.1", platforms: %i[mri windows] gem "chunky_png", "~> 1.4" +gem "ferrum", github: "radiopaedia/ferrum", branch: "94-expose-mobile" gem "image_size", "~> 3.0" gem "launchy", "~> 2.5" gem "logger" diff --git a/lib/capybara/cuprite/page.rb b/lib/capybara/cuprite/page.rb index 2b21a86..bfda5b7 100644 --- a/lib/capybara/cuprite/page.rb +++ b/lib/capybara/cuprite/page.rb @@ -145,7 +145,7 @@ def prepare_page super width, height = @options.window_size - resize(width: width, height: height) + resize(width: width, height: height, mobile: @options.mobile) if @options.url_blacklist.any? network.blacklist = @options.url_blacklist From 5feb3b7fcbb7ed8255b92c587beed737d685aa74 Mon Sep 17 00:00:00 2001 From: Duncan Bayne Date: Fri, 21 Mar 2025 11:29:26 +1100 Subject: [PATCH 2/7] Test that we can register a driver with mobile emulation This tests for mobile emulation by: * Checking that text controlled by a CSS media query is / isn't visible. * Checking that touch input is enabled / disabled. --- spec/features/driver_spec.rb | 27 ++++++++++++++++++++++++++- spec/spec_helper.rb | 8 ++++++++ spec/support/views/mobile.erb | 26 ++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 spec/support/views/mobile.erb diff --git a/spec/features/driver_spec.rb b/spec/features/driver_spec.rb index 8041d3e..ca5d833 100644 --- a/spec/features/driver_spec.rb +++ b/spec/features/driver_spec.rb @@ -8,11 +8,12 @@ module Capybara module Cuprite describe Driver do let(:device_pixel_ratio) { @driver.device_pixel_ratio } + let(:mobile) { false } include Spec::Support::ExternalBrowser around do |example| - @session = TestSessions::Cuprite + @session = mobile ? TestSessions::CupriteMobile : TestSessions::Cuprite @driver = @session.driver example.run ensure @@ -50,6 +51,30 @@ def session_url(path) driver&.quit end + describe "mobile emulation" do + context "when mobile emulation is enabled" do + let(:mobile) { true } + + it "emulates a mobile browser" do + @session.visit("/cuprite/mobile") + expect(@session).to have_text("I am a mobile.") + expect(@session).to have_no_text("I am a desktop.") + expect(@session.evaluate_script("'ontouchstart' in window || navigator.maxTouchPoints > 0")).to be_truthy + end + end + + context "when mobile emulation is disabled" do + let(:mobile) { false } + + it "does not emulate a mobile browser" do + @session.visit("/cuprite/mobile") + expect(@session).to have_text("I am a desktop.") + expect(@session).to have_no_text("I am a mobile.") + expect(@session.evaluate_script("'ontouchstart' in window || navigator.maxTouchPoints > 0")).to be_falsy + end + end + end + context "output redirection" do let(:logger) { StringIO.new } let(:session) { Capybara::Session.new(:cuprite_with_logger, TestApp) } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b7ebf23..cd2ebb5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -29,6 +29,14 @@ Capybara::Cuprite::Driver.new(app, options) end +Capybara.register_driver(:cuprite_mobile) do |app| + options = { mobile: true } + options.merge!(inspector: true) if ENV["INSPECTOR"] + options.merge!(logger: StringIO.new) if ENV["CI"] + options.merge!(headless: false) if ENV["HEADLESS"] == "false" + Capybara::Cuprite::Driver.new(app, options) +end + module TestSessions Cuprite = Capybara::Session.new(:cuprite, TestApp) end diff --git a/spec/support/views/mobile.erb b/spec/support/views/mobile.erb new file mode 100644 index 0000000..13a1620 --- /dev/null +++ b/spec/support/views/mobile.erb @@ -0,0 +1,26 @@ + + + + Mobile test page + + + +
+ I am a desktop. +
+ +
+ I am a mobile. +
+ + From 88dc3feacd58460ec78033d6a789e00d3bb8611c Mon Sep 17 00:00:00 2001 From: Duncan Bayne Date: Wed, 26 Mar 2025 09:58:29 +1100 Subject: [PATCH 3/7] Fix mobile specs These were (a) trying to use a non-existent session, and (b) trying to set both `mobile: true` and a resolution, which is now disallowed. --- lib/capybara/cuprite/page.rb | 6 +++++- spec/spec_helper.rb | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/capybara/cuprite/page.rb b/lib/capybara/cuprite/page.rb index bfda5b7..5b3f993 100644 --- a/lib/capybara/cuprite/page.rb +++ b/lib/capybara/cuprite/page.rb @@ -145,7 +145,11 @@ def prepare_page super width, height = @options.window_size - resize(width: width, height: height, mobile: @options.mobile) + if @options.mobile + resize(width: 0, height: 0, mobile: @options.mobile) + else + resize(width: width, height: height, mobile: @options.mobile) + end if @options.url_blacklist.any? network.blacklist = @options.url_blacklist diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cd2ebb5..e174ac1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -39,6 +39,7 @@ module TestSessions Cuprite = Capybara::Session.new(:cuprite, TestApp) + CupriteMobile = Capybara::Session.new(:cuprite_mobile, TestApp) end RSpec.configure do |config| From e33324b4b2ec2117ca5bfd35de86b16073713f6f Mon Sep 17 00:00:00 2001 From: Duncan Bayne Date: Thu, 27 Mar 2025 14:51:46 +1100 Subject: [PATCH 4/7] Model devices as simply hashes of options for the driver --- lib/capybara/cuprite/devices.rb | 9 +++++++++ lib/capybara/cuprite/page.rb | 6 +----- spec/spec_helper.rb | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 lib/capybara/cuprite/devices.rb diff --git a/lib/capybara/cuprite/devices.rb b/lib/capybara/cuprite/devices.rb new file mode 100644 index 0000000..98aa1ee --- /dev/null +++ b/lib/capybara/cuprite/devices.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Capybara + module Cuprite + module Devices + IPHONE_14 = { window_size: [390, 844], mobile: true, scale_factor: 3 } + end + end +end diff --git a/lib/capybara/cuprite/page.rb b/lib/capybara/cuprite/page.rb index 5b3f993..bfda5b7 100644 --- a/lib/capybara/cuprite/page.rb +++ b/lib/capybara/cuprite/page.rb @@ -145,11 +145,7 @@ def prepare_page super width, height = @options.window_size - if @options.mobile - resize(width: 0, height: 0, mobile: @options.mobile) - else - resize(width: width, height: height, mobile: @options.mobile) - end + resize(width: width, height: height, mobile: @options.mobile) if @options.url_blacklist.any? network.blacklist = @options.url_blacklist diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e174ac1..e1e61a0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -11,6 +11,7 @@ require "capybara/spec/spec_helper" require "capybara/cuprite" +require "capybara/cuprite/devices" require "support/test_app" require "support/external_browser" @@ -30,7 +31,7 @@ end Capybara.register_driver(:cuprite_mobile) do |app| - options = { mobile: true } + options = Capybara::Cuprite::Devices::IPHONE_14 options.merge!(inspector: true) if ENV["INSPECTOR"] options.merge!(logger: StringIO.new) if ENV["CI"] options.merge!(headless: false) if ENV["HEADLESS"] == "false" From f902d7adcc321fb3ae505635155948c18211bf67 Mon Sep 17 00:00:00 2001 From: Duncan Bayne Date: Thu, 27 Mar 2025 14:54:29 +1100 Subject: [PATCH 5/7] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c6b6e37..3a63c2b 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Capybara.register_driver(:cuprite) do |app| end ``` -if you use `Docker` don't forget to pass `no-sandbox` option: +If you use `Docker` don't forget to pass `no-sandbox` option: ```ruby Capybara::Cuprite::Driver.new(app, browser_options: { 'no-sandbox': nil }) From 7312657b8ee4ace6aed86e5adee4fbefde24ed53 Mon Sep 17 00:00:00 2001 From: Duncan Bayne Date: Thu, 27 Mar 2025 14:57:23 +1100 Subject: [PATCH 6/7] Add documentation about emulation --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 3a63c2b..d606c04 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,14 @@ end * `:url_blacklist` (Array) - array of regexes to match against requested URLs * `:url_whitelist` (Array) - array of regexes to match against requested URLs +You can emulate specific devices at the time you register a driver: + +```ruby +require "capybara/cuprite/devices" +Capybara.register_driver(:cuprite) do |app| + Capybara::Cuprite::Driver.new(app, Capybara::Cuprite::Devices::IPHONE_14) +end +``` ## Debugging From b4e8fef047c27ef137f87bca5c12d8a49dd80f4a Mon Sep 17 00:00:00 2001 From: Duncan Bayne Date: Mon, 4 May 2026 15:45:17 +1000 Subject: [PATCH 7/7] Fixup Rubocop warnings --- lib/capybara/cuprite/devices.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/capybara/cuprite/devices.rb b/lib/capybara/cuprite/devices.rb index 98aa1ee..3a04be2 100644 --- a/lib/capybara/cuprite/devices.rb +++ b/lib/capybara/cuprite/devices.rb @@ -3,7 +3,9 @@ module Capybara module Cuprite module Devices - IPHONE_14 = { window_size: [390, 844], mobile: true, scale_factor: 3 } + # It's idiomatic to merge other options into these, so freezing + # isn't the right thing to do. + IPHONE_14 = { window_size: [390, 844], mobile: true, scale_factor: 3 } # rubocop:disable Style/MutableConstant end end end