diff --git a/.github/Dockerfile.base b/.github/Dockerfile.base index 5fae1efe..39557d77 100644 --- a/.github/Dockerfile.base +++ b/.github/Dockerfile.base @@ -3,7 +3,7 @@ # call this from rails root: podman build -t mapforge-base -f .github/Dockerfile.base . # Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile -ARG RUBY_VERSION=3.4.5 +ARG RUBY_VERSION=4.0.0 FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base LABEL org.opencontainers.image.source="https://github.com/mapforge-org/mapforge" diff --git a/.rubocop.yml b/.rubocop.yml index 437af5cd..e8f37347 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,7 +5,7 @@ inherit_gem: inherit_from: .rubocop_todo.yml AllCops: - TargetRubyVersion: 3.4 + TargetRubyVersion: 4.0 DisplayCopNames: true DisplayStyleGuide: true ExtraDetails: true diff --git a/.ruby-version b/.ruby-version index 4f5e6973..fcdb2e10 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.5 +4.0.0 diff --git a/Gemfile b/Gemfile index 25ab22ad..d5a4aa29 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,5 @@ source "https://rubygems.org" -ruby "3.4.5" +ruby "4.0.0" gem "rails" @@ -58,7 +58,7 @@ gem "rszr" gem "rgeo" gem "rgeo-geojson" gem "rgeo-proj4" -gem "gpx" +gem "gpx", git: "https://github.com/digitaltom/gpx" # Ruby 4.0 fork # resolving request IP addresses to coordinates gem "maxminddb" @@ -101,6 +101,7 @@ group :test do gem "simplecov" gem "database_cleaner-mongoid" gem "mongoid-rspec" - gem "puffing-billy" + gem "cuprite" + gem "capybara_mock" gem "table_print" end diff --git a/Gemfile.lock b/Gemfile.lock index 4cbd9bf1..42d583ca 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,12 @@ +GIT + remote: https://github.com/digitaltom/gpx + revision: 204842fc2a00dca66adb66d7d8148e7580e197bd + specs: + gpx (1.2.1) + csv + nokogiri (~> 1.7) + rake + GIT remote: https://github.com/ruby/net-pop.git revision: 30d89b359c940610d84ecd1fed0dba4003508fde @@ -117,15 +126,19 @@ GEM capybara-screenshot (1.0.26) capybara (>= 1.0, < 4) launchy + capybara_mock (0.2.0) + rack (>= 2.2.0) childprocess (5.1.0) logger (~> 1.5) coercible (1.0.0) descendants_tracker (~> 0.0.1) concurrent-ruby (1.3.6) connection_pool (3.0.2) - cookiejar (0.3.4) crass (1.0.6) - csv (3.3.3) + csv (3.3.5) + cuprite (0.17) + capybara (~> 3.0) + ferrum (~> 0.17.0) database_cleaner-core (2.0.1) database_cleaner-mongoid (2.0.1) database_cleaner-core (~> 2.0.0) @@ -177,21 +190,8 @@ GEM dry-inflector (~> 1.0) dry-logic (~> 1.4) zeitwerk (~> 2.6) - em-http-request (1.1.7) - addressable (>= 2.3.4) - cookiejar (!= 0.3.1) - em-socksify (>= 0.3) - eventmachine (>= 1.0.3) - http_parser.rb (>= 0.6.0) - em-socksify (0.3.3) - base64 - eventmachine (>= 1.0.0.beta.4) - em-synchrony (1.0.6) - eventmachine (>= 1.0.0.beta.1) erb (6.0.1) erubi (1.13.1) - eventmachine (1.2.7) - eventmachine_httpserver (0.2.1) factory_bot (6.5.5) activesupport (>= 6.1.0) factory_bot_rails (6.5.1) @@ -203,6 +203,12 @@ GEM logger faraday-net_http (3.4.0) net-http (>= 0.5.0) + ferrum (0.17.1) + addressable (~> 2.5) + base64 (~> 0.2) + concurrent-ruby (~> 1.1) + webrick (~> 1.7) + websocket-driver (~> 0.7) ffi (1.17.0) flay (2.13.3) erubi (~> 1.10) @@ -220,10 +226,6 @@ GEM i18n (>= 0.7) multi_json request_store (>= 1.0) - gpx (1.2.1) - csv - nokogiri (~> 1.7) - rake haml (7.1.0) temple (>= 0.8.2) thor @@ -233,7 +235,6 @@ GEM listen rails (>= 7.0.0) zeitwerk - http_parser.rb (0.8.0) i18n (1.14.7) concurrent-ruby (~> 1.0) ice_nine (0.11.2) @@ -319,7 +320,7 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.5) - nokogiri (1.18.10-x86_64-linux-gnu) + nokogiri (1.19.0-x86_64-linux-gnu) racc (~> 1.4) oauth2 (2.0.9) faraday (>= 0.17.3, < 3.0) @@ -362,14 +363,6 @@ GEM date stringio public_suffix (6.0.2) - puffing-billy (4.0.2) - addressable (~> 2.5) - em-http-request (~> 1.1, >= 1.1.0) - em-synchrony - eventmachine (~> 1.2) - eventmachine_httpserver - http_parser.rb (~> 0.8.0) - multi_json puma (7.1.0) nio4r (~> 2.0) puppeteer-ruby (0.45.6) @@ -520,7 +513,7 @@ GEM rubocop-ast (>= 1.44.0, < 2.0) ruby-next-core (1.1.2) ruby-progressbar (1.13.0) - ruby_parser (3.21.1) + ruby_parser (3.22.0) racc (~> 1.5) sexp_processor (~> 4.16) rubycritic (4.11.0) @@ -543,7 +536,7 @@ GEM rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 4.0) websocket (~> 1.0) - sexp_processor (4.17.4) + sexp_processor (4.17.5) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) @@ -592,6 +585,7 @@ GEM activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) + webrick (1.9.2) websocket (1.2.11) websocket-driver (0.8.0) base64 @@ -629,13 +623,15 @@ DEPENDENCIES byebug capybara capybara-screenshot + capybara_mock + cuprite database_cleaner-mongoid debug dotenv-rails dragonfly factory_bot_rails gon - gpx + gpx! haml hotwire-spark importmap-rails @@ -654,7 +650,6 @@ DEPENDENCIES omniauth-google-oauth2 ostruct parallel_tests - puffing-billy puma puppeteer-ruby rack-cache @@ -690,7 +685,7 @@ DEPENDENCIES yabeda-rails RUBY VERSION - ruby 3.4.5 + ruby 4.0.0p0 BUNDLED WITH - 2.7.1 + 4.0.3 diff --git a/spec/features/feature_details_spec.rb b/spec/features/feature_details_spec.rb index 528a1b0c..3d3f83e6 100644 --- a/spec/features/feature_details_spec.rb +++ b/spec/features/feature_details_spec.rb @@ -13,17 +13,18 @@ context 'with selected feature' do before do - click_coord('#maplibre-map', 50, 50) + click_center_of_screen expect(page).to have_css('#feature-details-modal') end it 'can enlarge modal with pull-up button', :mobile do - height = find('#feature-details-modal').native.style('height').sub('px', '').to_i - expect(height).to be < 200 + height = element_offset_height('#feature-details-modal') + # initial height is half the screen height + expect(height).to be < 300 find('.modal-pull-button').click sleep(0.3) - height = find('#feature-details-modal').native.style('height').sub('px', '').to_i - expect(height).to be > 150 + height = element_offset_height('#feature-details-modal') + expect(height).to be > 300 end it 'can download feature export' do diff --git a/spec/features/feature_directions_spec.rb b/spec/features/feature_directions_spec.rb index 3a211147..3553c7d4 100644 --- a/spec/features/feature_directions_spec.rb +++ b/spec/features/feature_directions_spec.rb @@ -12,8 +12,8 @@ it 'can create foot track' do find('.mapbox-gl-draw_line').click find('.mapbox-gl-draw_foot').click - click_coord('#maplibre-map', 50, 50) - click_coord('#maplibre-map', 150, 150) + click_coord('#maplibre-map', 250, 250) + click_coord('#maplibre-map', 450, 450) wait_for { Feature.line_string.count }.to eq(1) end end diff --git a/spec/features/feature_edit_spec.rb b/spec/features/feature_edit_spec.rb index 56dda8ac..bce290ba 100644 --- a/spec/features/feature_edit_spec.rb +++ b/spec/features/feature_edit_spec.rb @@ -57,7 +57,7 @@ context 'with selected polygon feature' do before do - click_coord('#maplibre-map', 50, 50) + click_coord('#maplibre-map', 512, 430) expect(page).to have_css('#edit-button-edit') end @@ -101,7 +101,7 @@ context 'with selected point feature' do before do - click_coord('#maplibre-map', 50, 50) + click_coord('#maplibre-map', 512, 430) find('#edit-button-edit').click end diff --git a/spec/features/map_layers_spec.rb b/spec/features/map_layers_spec.rb index 1586eea2..1624a3af 100644 --- a/spec/features/map_layers_spec.rb +++ b/spec/features/map_layers_spec.rb @@ -80,10 +80,15 @@ context 'overpass layer' do before do - proxy.stub('https://overpass-api.de:443/api/interpreter', method: 'post') - .and_return( - headers: { 'Access-Control-Allow-Origin' => '*' }, - text: File.read(Rails.root.join("spec", "fixtures", "files", "overpass.json"))) + overpass_file = File.read(Rails.root.join("spec", "fixtures", "files", "overpass.json")) + # https://github.com/railsware/capybara_mock + CapybaraMock.stub_request( + :post, 'https://overpass-api.de/api/interpreter' + ).to_return( + headers: { 'Access-Control-Allow-Origin' => '*' }, + status: 200, + body: overpass_file + ) map.layers << layer visit map.private_map_path @@ -95,7 +100,7 @@ let(:layer) { create(:layer, :overpass, name: 'opass') } it 'Shows overpass layer' do - expect(page).to have_text('opass') + expect(page).to have_text('opass(1)') end it 'can add overpass layer' do diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 7d9c8367..297235f0 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -19,6 +19,7 @@ require 'database_cleaner/mongoid' require 'capybara-screenshot/rspec' require 'mongoid-rspec' +require 'capybara_mock/rspec' # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are @@ -40,7 +41,6 @@ config.use_active_record = false config.include Mongoid::Matchers, type: :model - config.include Features::Helpers, type: :feature config.include FactoryBot::Syntax::Methods config.before(:suite) do @@ -56,37 +56,36 @@ end config.around(:each, :mobile) do |spec| - browser = Capybara.current_session.driver.browser - browser.manage.window.resize_to(290, 523) + page.driver.browser.resize(width: 290, height: 523) spec.run - browser.manage.window.resize_to(1024, 576) + page.driver.browser.resize(width: 1024, height: 860) end # raise on js console errors class JavaScriptError< StandardError; end - RSpec.configure do |config| - config.after(:each, type: :feature) do |spec| - unless spec.metadata[:skip_console_errors] - levels = [ "SEVERE" ] - # "maplibre-gl.js TypeError: Failed to fetch" seems to be caused by - # the js file being cached already - exclude = [ /TypeError: Failed to fetch/, - /The user aborted a request/, - /Failed to load resource/ ] - errors = page.driver.browser.logs.get(:browser).to_a - .select { |e| levels.include?(e.level) && e.message.present? } - .reject { |e| exclude.any? { |ex| e.message =~ ex } } - .map(&:message) - if errors.present? - raise JavaScriptError, errors.join("\n\n") - end - end - if spec.metadata[:print_console_logs] - logs = page.driver.browser.logs.get(:browser).to_a.map(&:message) - puts logs.join("\n\n") - end - end - end + # RSpec.configure do |config| + # config.after(:each, type: :feature) do |spec| + # unless spec.metadata[:skip_console_errors] + # levels = [ "SEVERE" ] + # # "maplibre-gl.js TypeError: Failed to fetch" seems to be caused by + # # the js file being cached already + # exclude = [ /TypeError: Failed to fetch/, + # /The user aborted a request/, + # /Failed to load resource/ ] + # errors = page.driver.browser.logs.get(:browser).to_a + # .select { |e| levels.include?(e.level) && e.message.present? } + # .reject { |e| exclude.any? { |ex| e.message =~ ex } } + # .map(&:message) + # if errors.present? + # raise JavaScriptError, errors.join("\n\n") + # end + # end + # if spec.metadata[:print_console_logs] + # logs = page.driver.browser.logs.get(:browser).to_a.map(&:message) + # puts logs.join("\n\n") + # end + # end + # end # If you enable ActiveRecord support you should uncomment these lines, # note if you'd prefer not to run each example within a transaction, you diff --git a/spec/support/billy.rb b/spec/support/billy.rb deleted file mode 100644 index ab787c47..00000000 --- a/spec/support/billy.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'table_print' # Add this dependency to your gemfile - -# https://github.com/oesmith/puffing-billy?tab=readme-ov-file#params -Billy.configure do |c| - # c.record_requests = true # needed for the table output below - c.non_successful_error_level = :error - c.cache = true - c.persist_cache = true - c.cache_path = 'tmp/billy_req_cache/' -end - -# RSpec.configure do |config| -# config.prepend_after(:example, type: :feature) do -# puts "Requests received via Puffing Billy Proxy:" - -# puts TablePrint::Printer.table_print(Billy.proxy.requests, [ -# :status, -# :handler, -# :method, -# { url: { width: 100 } }, -# :headers, -# :body -# ]) -# end -# end - -# RSpec.configure do |config| -# config.prepend_before(:suite) do -# if defined?(Billy) -# local_cache_path = Rails.root.join(Billy.config.cache_path) -# FileUtils.rm_rf(local_cache_path) if File.exist?(local_cache_path) -# end -# end -# end diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index b6784ede..cdf08084 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -1,4 +1,4 @@ -# == Define helpers +require "capybara/cuprite" # Generates the arguments to register chrome as capybara driver # @@ -70,15 +70,19 @@ def chrome_driver_arguments(headless: false) Capybara::Selenium::Driver.new(app, **chrome_driver_arguments(headless: true)) end -require 'billy/capybara/rspec' -Capybara.javascript_driver = :headless_chrome - -# Register our custom driver name. otherwise 'screenshot_failed_tests' would fail -# see https://github.com/mattheworiordan/capybara-screenshot/issues/84#issuecomment-41219326 -Capybara::Screenshot.register_driver(Capybara.javascript_driver) do |driver, path| - driver.browser.save_screenshot(path) +Capybara.register_driver(:cuprite) do |app| + logger = StringIO.new + Capybara::Cuprite::Driver.new(app, window_size: [ 1024, 860 ], + js_errors: true, + logger: logger, + browser_options: { 'no-sandbox': nil }) end +# https://github.com/rubycdp/cuprite +Capybara.javascript_driver = :cuprite + + Capybara.default_driver = Capybara.javascript_driver +Capybara::Screenshot.autosave_on_failure = true # Start Puma silently Capybara.server = :puma, { Silent: true } diff --git a/spec/support/feature_helpers.rb b/spec/support/feature_helpers.rb index 15edabb7..8cad85a8 100644 --- a/spec/support/feature_helpers.rb +++ b/spec/support/feature_helpers.rb @@ -1,9 +1,12 @@ -module Features - module Helpers - def self.take_a_screenshot - filename = Rails.root.join("capybara-#{Time.zone.now.to_i}.png") - puts "\033[36mINFO: Saving screenshot at: #{filename}\033[0m\n\n" - page.save_screenshot(filename, full: true) - end - end +def take_a_screenshot + filename = Rails.root.join("tmp", "capybara", "screen-#{Time.zone.now.to_i}.png") + puts "\033[36mINFO: Saving screenshot at: #{filename}\033[0m\n\n" + browser = page.driver.browser + browser.screenshot(path: filename, full: true) +end + +def element_offset_height(selector) + page.driver.browser.evaluate <<~JS + document.querySelector('#{selector}').offsetHeight; + JS end diff --git a/spec/support/mouse_helpers.rb b/spec/support/mouse_helpers.rb index 40a81729..c137dd44 100644 --- a/spec/support/mouse_helpers.rb +++ b/spec/support/mouse_helpers.rb @@ -1,13 +1,24 @@ -# Selenium Webdriver -# https://rubydoc.info/github/jnicklas/capybara/Capybara/Selenium/Driver +# 0,0 is the left top of the page +def click_coord(_selector, x, y) + browser = page.driver.browser + browser.mouse.click(x: x, y: y) +end + +def click_center_of_screen + viewport = page.driver.browser.evaluate <<~JS + { + width: window.innerWidth, + height: window.innerHeight + } + JS + + center_x = viewport['width'] / 2 + center_y = viewport['height'] / 2 -# 0,0 is the middle of the element -def click_coord(selector, x, y) - element = find(selector) - page.driver.browser.action.move_to(element.native, x, y).click.perform + page.driver.click(center_x, center_y) end -def hover_coord(selector, x, y) - element = find(selector) - page.driver.browser.action.move_to(element.native, x, y).perform +def hover_coord(_selector, x, y) + browser = page.driver.browser + browser.mouse.move(x: x, y: y) end