dockerselenium-webdriverdocker-composecapybara

Accessing file downloads from containerized RSpec/Capybara and Selenium Chrome


I would like to run an RSpec/Capybara test suite in Docker. This test suite performs a file download.

If I run the test suite with rspec, I am able to access the downloaded file.

If I run both rspec and selenium chrome as containers, I cannot figure out how to access the downloaded file.

.ruby-version

2.7.0

Gemfile

source "https://rubygems.org"
gem 'rspec'
gem 'capybara'
gem 'capybara-webmock'
gem 'colorize'
gem 'webdrivers'
#gem 'chromedriver-helper'
gem 'selenium-webdriver'
gem 'byebug'

spec/spec_helper.rb

require 'colorize'
require 'capybara/dsl'
require 'capybara/rspec'
require 'byebug'

RSpec.configure do |config|
  config.color = true
  config.tty = true
  config.formatter = :documentation
  config.include Capybara::DSL
end

def create_web_session
  Capybara.app_host = 'https://github.com'
  Capybara.run_server = false # don't start Rack

  if ENV['CHROME_URL']
    Capybara.register_driver :selenium_chrome_headless do |app|
      args = [
        '--no-default-browser-check',
        '--start-maximized',
        '--headless',
        '--disable-dev-shm-usage',
        '--whitelisted-ips'
      ]
      caps = Selenium::WebDriver::Remote::Capabilities.chrome("chromeOptions" => {"args" => args})

      Capybara::Selenium::Driver.new(
        app,
        browser: :remote,
        desired_capabilities: caps,
        url: "http://chrome:4444/wd/hub"
      )
    end
  end
  @session = Capybara::Session.new(:selenium_chrome_headless)
  #@session = Capybara::Session.new(:selenium_chrome)
end

spec/test/demo_spec.rb

require 'spec_helper.rb'
require 'webdrivers/chromedriver'

sleep 1

RSpec.describe 'basic_tests', type: :feature do
  before(:each) do
    @session = create_web_session
  end

  it 'Load page' do
    @session.visit '/docker/compose/releases/tag/1.27.0'
    @session.find_link('Source code (zip)')
    @session.click_link('Source code (zip)')
    sleep 3
    f = File.join('compose-1.27.0.zip')
    expect(File.exists?(f)).to be true
    File.delete(f)
  end

end

Dockerfile

FROM ruby:2.7

RUN gem install bundler

COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock

RUN bundle install

COPY . .

RUN chmod 777 .

CMD ["bundle", "exec", "rspec", "spec"]

docker-compose.yml

version: '3.7'
networks:
  mynet:
services:
  rspec-chrome:
    container_name: rspec-chrome
    image: rspec-chrome
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      CHROME_URL: http://chrome:4444/wd/hub
    stdin_open: true
    tty: true
    networks:
      mynet:
    depends_on:
    - chrome
  chrome:
    container_name: chrome
    image: selenium/standalone-chrome
    networks:
      mynet:
    volumes:
    - /dev/shm:/dev/shm

Output when running rspec

basic_tests
  Load page

Finished in 8.45 seconds (files took 6.79 seconds to load)
1 example, 0 failures

Output when running docker-compose up -d --build

docker logs -f rspec-chrome

basic_tests
  Load page (FAILED - 1)

Failures:

  1) basic_tests Load page
     Failure/Error: expect(File.exists?(f)).to be true
     
       expected true
            got false
     # /spec/test/demo_spec.rb:17:in `block (2 levels) in <top (required)>'

Solution

  • The following modifications resolved my issue.

    spec/spec_helper.rb

    Pass the following prefs in chromeOptions

            "prefs" => {
              'download.default_directory' => '/tmp',
              'download.directory_upgrade' => true,
              'download.prompt_for_download' => false
            }
    
    

    Here is the complete file

    require 'colorize'
    require 'capybara/dsl'
    require 'capybara/rspec'
    require 'byebug'
    
    RSpec.configure do |config|
      config.color = true
      config.tty = true
      config.formatter = :documentation
      config.include Capybara::DSL
    end
    
    def create_web_session
      Capybara.app_host = 'https://github.com'
      Capybara.run_server = false # don't start Rack
    
      if ENV['CHROME_URL']
        Capybara.register_driver :selenium_chrome_headless do |app|
          args = [
            '--no-default-browser-check',
            '--start-maximized',
            '--headless',
            '--disable-dev-shm-usage',
            '--whitelisted-ips'
          ]
          caps = Selenium::WebDriver::Remote::Capabilities.chrome("chromeOptions" => {
            "args" => args,
            "prefs" => {
              'download.default_directory' => '/tmp',
              'download.directory_upgrade' => true,
              'download.prompt_for_download' => false
            }
          })
    
          Capybara::Selenium::Driver.new(
            app,
            browser: :remote,
            desired_capabilities: caps,
            url: ENV['CHROME_URL']
          )
        end
      end
      @session = Capybara::Session.new(:selenium_chrome_headless)
      #@session = Capybara::Session.new(:selenium_chrome)
    end
    
    

    spec/test/demo_spec.rb

    Change directory to /tmp and look for the download in /tmp

    require 'spec_helper.rb'
    require 'webdrivers/chromedriver'
    
    sleep 1
    
    RSpec.describe 'basic_tests', type: :feature do
      before(:each) do
        @session = create_web_session
        Dir.chdir "/tmp"
      end
    
      it 'Load page' do
        @session.visit '/docker/compose/releases/tag/1.27.0'
        @session.find_link('Source code (zip)')
        @session.click_link('Source code (zip)')
        sleep 3
        f = File.join('/tmp','compose-1.27.0.zip')
        expect(File.exists?(f)).to be true
        File.delete(f)
      end
    
    end
    
    

    docker-compose.yml

    Share /tmp as a docker volume between the rspec and chrome containers

    version: '3.7'
    networks:
      mynet:
    volumes:
      downloads:
    services:
      rspec-chrome:
        container_name: rspec-chrome
        image: rspec-chrome
        build:
          context: .
          dockerfile: Dockerfile
        environment:
          CHROME_URL: http://chrome:4444/wd/hub
        stdin_open: true
        tty: true
        networks:
          mynet:
        depends_on:
        - chrome
        volumes:
        - downloads:/tmp
      chrome:
        container_name: chrome
        image: selenium/standalone-chrome
        networks:
          mynet:
        volumes:
        - /dev/shm:/dev/shm
        - downloads:/tmp