mattheworiordan / capybara-screenshot

Automatically save screen shots when a Capybara scenario fails
MIT License
1.02k stars 168 forks source link

would it be possible to create capybara-video? #167

Open tansaku opened 8 years ago

tansaku commented 8 years ago

I can take multiple screenshots, but I wonder would it be possible to do something like tagging a cucumber scenario so that a complete video of the operation in the browser would be produced on error or possibly on completion?

mattheworiordan commented 8 years ago

He he, nice idea, feel free to do a PR if you can make it happen :)

aclima93 commented 6 years ago

using the headless gem, you can achieve this :)

mattheworiordan commented 6 years ago

@aclima93 fancy trying?

aclima93 commented 6 years ago

I'll see what I can cook up :)

ishields commented 1 year ago

Just curious if anyone ever found a solution for this?

aclima93 commented 1 year ago

Apologies for never returning to this thread with my results but maybe it can still serve as a starting point to someone. Here's a bunch of snippets which i worked on back in 2018-2019, I ended up using the headless gem and capybara to capture both screenshots and videos of our app during tests.

(Disclaimer: I haven't actively used this tech-stack for over 4 years, so probably won't be of much help in troubleshooting)

/app/controllers/concerns/recording_videos_concern.rb

require 'headless'

module RecordingVideosConcern
  extend ActiveSupport::Concern

  def recording_filename
    "TODO.mov" # TODO:
  end

  def screenshot_filename(counter)
    "TODO.png" # TODO:
  end

  def setup_recording
    @image_counter = 0
    @headless = Headless.new(video: { provider: :ffmpeg, log_file_path: STDERR })
    @headless.start
  end

  def start_recording
    @headless.video.start_capture
  end

  def stop_recording
    # headless stores a local copy of the recording
    @headless.video.stop_and_save recording_filename

    upload_local_recording if File.exist? recording_filename
  end

  def upload_local_recording
    # TODO:
  end

  def take_screenshot
    @session.save_screenshot screenshot_filename(@image_counter)
    @image_counter += 1
  end

  def save_screenshots
    return unless @image_counter.positive?

    # capybara session stores a local copy of the recording
    filenames = (0..@image_counter - 1).map do |number|
      screenshot_filename number
    end
    filenames = filenames.select do |filename|
      File.exist? filename
    end

    upload_local_screenshots filenames
  end

  def upload_local_screenshots(filenames)
    # TODO:
  end
end

/app/controllers/concerns/capybara_videos_concern.rb

module CapybaraVideosConcern
  extend ActiveSupport::Concern

  def setup_capybara_session
    configure_chrome_driver
    configure_capybara_with_chrome_driver
    Capybara::Session.new(:headless_chrome)
  end

  def configure_chrome_driver
    # define our chrome browser's driver
    capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
      chromeOptions: { args: %w[headless disable-gpu] }
    )

    options = Selenium::WebDriver::Chrome::Options.new
    options.add_argument('--disable-infobars')
    Capybara.register_driver :headless_chrome do |app|
      Capybara::Selenium::Driver.new(
        app,
        browser: :chrome,
        options: options,
        desired_capabilities: capabilities
      )
    end
  end

  def configure_capybara_with_chrome_driver
    Capybara.configure do |config|
      # load assets
      config.asset_host = ENV.fetch('RAILS_HOST')

      # don't auto-fill a port into requests
      config.always_include_port = false

      config.run_server = false
      config.app_host = ENV.fetch('RAILS_HOST')
      config.default_driver = :headless_chrome
      config.javascript_driver = :headless_chrome
    end
  end
end

/spec/spec_helper.rb

require 'capybara/rspec'
require 'capybara/dsl'
require 'capybara-screenshot/rspec'
require 'capybara_extensions'
require 'headless'

RSpec.configure do |config|
  config.include Capybara::DSL
end

# Customized webdriver for Capybara
# https://drivy.engineering/running-capybara-headless-chrome/
# https://peter.sh/experiments/chromium-command-line-switches/
Capybara.register_driver :custom_chrome do |app|
  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    loggingPrefs:{
      browser: 'ALL'
    }
  )
  # remove the `headless` option to watch the specs in action
  options = Selenium::WebDriver::Chrome::Options.new(
    args: %w[
      disable-dev-shm-usage
      disable-gpu
      disable-logging
      disable-notifications
      headless
      disable-web-security
      incognito
      lang=en-us
      log-level=3
      no-sandbox
      silent
      start-maximized
      window-size=1920,1080
    ]
  )
  Capybara::Selenium::Driver.new(
    app,
    browser: :chrome,
    options: options,
    desired_capabilities: capabilities
  )
end

# Capybara configurations
Capybara.configure do |config|
  config.always_include_port = false
  server_port = # TODO:
  Capybara.server_port = server_port
  Ngrok::Rspec.tunnel = {
    port: server_port
  }

  config.javascript_driver = :custom_chrome
  config.default_driver = :custom_chrome
  config.default_max_wait_time = 10 # seconds
end

# use this to disable screenshot uploading
Capybara::Screenshot.autosave_on_failure = true

# Capybara screenshots
save_path = 'capybara/failures/'
Capybara::Screenshot.register_driver(:custom_chrome) do |driver, path|
  driver.browser.save_screenshot(path)
end

Capybara.save_path = save_path
Capybara::Screenshot::RSpec::REPORTERS["RSpec::Core::Formatters::HtmlFormatter"] = Capybara::Screenshot::RSpec::HtmlEmbedReporter

usage

    # setup
    @session = setup_capybara_session
    setup_recording
    start_recording

    # TODO: navigate through the app / perform actions

    # cleanup
    stop_recording