diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-15 06:09:27 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-15 06:09:27 +0300 |
commit | a7b6d465ae75b497ac34cb43361edd0c8ce45fbd (patch) | |
tree | d1cf9f860c07d082014a895a306a6eca8a8d2691 /qa | |
parent | afd8f58f2d0d42d21496fe4652c1664add9b68b7 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'qa')
-rw-r--r-- | qa/qa/ce/strategy.rb | 5 | ||||
-rw-r--r-- | qa/qa/runtime/browser.rb | 18 | ||||
-rw-r--r-- | qa/qa/runtime/env.rb | 25 | ||||
-rw-r--r-- | qa/qa/service/docker_run/video.rb | 123 | ||||
-rw-r--r-- | qa/qa/specs/spec_helper.rb | 1 | ||||
-rw-r--r-- | qa/spec/service/docker_run/video_spec.rb | 181 |
6 files changed, 334 insertions, 19 deletions
diff --git a/qa/qa/ce/strategy.rb b/qa/qa/ce/strategy.rb index 2587da17739..0e12649a070 100644 --- a/qa/qa/ce/strategy.rb +++ b/qa/qa/ce/strategy.rb @@ -17,7 +17,10 @@ module QA end QA::Runtime::Logger.info("Browser: #{QA::Runtime::Env.browser}") - QA::Runtime::Logger.info("Browser version: #{QA::Runtime::Env.browser_version}") + + if QA::Runtime::Env.use_selenoid? + QA::Runtime::Logger.info("Browser version: #{QA::Runtime::Env.selenoid_browser_version}") + end # The login page could take some time to load the first time it is visited. # We visit the login page and wait for it to properly load only once before the tests. diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index 4297c62e904..7bc4530287c 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -16,8 +16,6 @@ module QA CAPYBARA_MAX_WAIT_TIME = Env.max_capybara_wait_time DEFAULT_WINDOW_SIZE = '1480,2200' - PHONE_VIDEO_SIZE = '500x900' - TABLET_VIDEO_SIZE = '800x1100' def self.blank_page? ['', 'about:blank', 'data:,'].include?(Capybara.current_session.driver.browser.current_url) @@ -174,7 +172,7 @@ module QA if QA::Runtime::Env.remote_grid selenium_options[:browser] = :remote selenium_options[:url] = QA::Runtime::Env.remote_grid - webdriver_options.browser_version = QA::Runtime::Env.browser_version + webdriver_options.browser_version = QA::Runtime::Env.selenoid_browser_version end if QA::Runtime::Env.remote_tunnel_id @@ -185,9 +183,7 @@ module QA if QA::Runtime::Env.record_video? webdriver_options.add_option('selenoid:options', { - enableVideo: true, - videoScreenSize: video_screen_size, - videoName: "#{QA::Runtime::Env.browser}-#{QA::Runtime::Env.browser_version}-#{Time.now}.mp4" + enableVideo: true }) end @@ -242,16 +238,6 @@ module QA ::File.expand_path('../../tmp/qa-profile', __dir__) end - def self.video_screen_size - if QA::Runtime::Env.phone_layout? - PHONE_VIDEO_SIZE - elsif QA::Runtime::Env.tablet_layout? - TABLET_VIDEO_SIZE - else - '' - end - end - class Session include Capybara::DSL diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index 9df33c4d8d3..54f9db65afb 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -207,8 +207,16 @@ module QA ENV['QA_BROWSER'].nil? ? :chrome : ENV['QA_BROWSER'].to_sym end - def browser_version - ENV['QA_BROWSER_VERSION'] || 'latest' + def selenoid_browser_version + ENV['QA_SELENOID_BROWSER_VERSION'] + end + + def selenoid_browser_image + ENV['QA_SELENOID_BROWSER_IMAGE'] + end + + def video_recorder_image + ENV['QA_VIDEO_RECORDER_IMAGE'] end def remote_mobile_device_name @@ -241,6 +249,19 @@ module QA enabled?(ENV['QA_RECORD_VIDEO'], default: false) end + def use_selenoid? + enabled?(ENV['USE_SELENOID'], default: false) + end + + def require_video_variables! + docs_link = 'https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/running_against_remote_grid.md#testing-with-selenoid' + use_selenoid? || (raise ArgumentError, "USE_SELENOID is required! See docs: #{docs_link}") + remote_grid || (raise ArgumentError, "QA_REMOTE_GRID is required! See docs: #{docs_link}") + video_recorder_image || (raise ArgumentError, "QA_VIDEO_RECORDER_IMAGE is required! See docs: #{docs_link}") + selenoid_browser_image || (raise ArgumentError, "QA_SELENOID_BROWSER_IMAGE is required! See docs: #{docs_link}") + selenoid_browser_version || (raise ArgumentError, "QA_SELENOID_BROWSER_VERSION is required! See docs: #{docs_link}") + end + def user_username ENV['GITLAB_USERNAME'] end diff --git a/qa/qa/service/docker_run/video.rb b/qa/qa/service/docker_run/video.rb new file mode 100644 index 00000000000..c75280b1282 --- /dev/null +++ b/qa/qa/service/docker_run/video.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +module QA + module Service + module DockerRun + class Video < Base + class << self + include Service::Shellout + + PHONE_VIDEO_SIZE = '500x900' + TABLET_VIDEO_SIZE = '800x1100' + DESKTOP_VIDEO_SIZE = '1920x1080' + + def configure! + return unless QA::Runtime::Env.record_video? + + begin + QA::Runtime::Env.require_video_variables! + rescue ArgumentError => e + return QA::Runtime::Logger.warn("Aborting video recording setup! Missing variables: #{e}") + end + + @recorder_container_name = get_container_name(QA::Runtime::Env.video_recorder_image) + @browser_image_version = + "#{QA::Runtime::Env.selenoid_browser_image}:#{QA::Runtime::Env.selenoid_browser_version}" + @recorder_container_cmd = "docker exec -d #{@recorder_container_name} sh -c".freeze + set_browser_container_hostname + set_video_screen_size + + if @recorder_container_name.present? && @browser_container_hostname + configure_rspec + + QA::Runtime::Logger.info("Test failure video recording setup complete!") + else + QA::Runtime::Logger.warn("Test failure video recording setup failed!") + end + end + + def start_recording(example) + @current_recording_name = create_recording_name(example) + + ffmpeg_cmd = <<~CMD.tr("\n", ' ') + ffmpeg -y + -f x11grab + -video_size #{@video_size} + -r 15 + -i #{@browser_container_hostname}:99 + -vcodec 'libx264' + -pix_fmt 'yuv420p' + "/data/#{@current_recording_name}.mp4" + CMD + + begin + shell("#{@recorder_container_cmd} '#{ffmpeg_cmd}'") + rescue StandardError => e + QA::Runtime::Logger.warn("Video recording start error: #{e}") + end + end + + def stop_recording + shell("#{@recorder_container_cmd} 'pkill -INT -f ffmpeg'") + rescue StandardError => e + QA::Runtime::Logger.warn("Video recording stop error: #{e}") + end + + def delete_video + shell("#{@recorder_container_cmd} 'rm /data/#{@current_recording_name}.mp4'") + rescue StandardError => e + QA::Runtime::Logger.warn("Video deletion error: #{e}") + end + + private + + def configure_rspec + RSpec.configure do |config| + config.prepend_before do |example| + QA::Service::DockerRun::Video.start_recording(example) + end + + config.append_after do |example| + QA::Service::DockerRun::Video.stop_recording + QA::Service::DockerRun::Video.delete_video unless example.exception + end + end + end + + def create_recording_name(example) + test_name = example.full_description.downcase.parameterize(separator: "_")[0..56] + test_time = Time.now.strftime "%Y-%m-%d-%H-%M-%S-%L" + + "#{test_name}-#{test_time}" + end + + def get_container_name(image) + name = shell("docker ps -f ancestor='#{image}' --format '{{.Names}}'") + + QA::Runtime::Logger.warn("Getting container name for #{image} failed!") if name.empty? + + name + end + + def set_browser_container_hostname + container_name = get_container_name(@browser_image_version) + @browser_container_hostname = shell("docker inspect #{container_name} --format '{{.Config.Hostname}}'") + rescue StandardError => e + QA::Runtime::Logger.warn("Video recording browser container setup error: #{e}") + end + + def set_video_screen_size + @video_size = + if QA::Runtime::Env.phone_layout? + PHONE_VIDEO_SIZE + elsif QA::Runtime::Env.tablet_layout? + TABLET_VIDEO_SIZE + else + DESKTOP_VIDEO_SIZE + end + end + end + end + end + end +end diff --git a/qa/qa/specs/spec_helper.rb b/qa/qa/specs/spec_helper.rb index aa274a4e101..1601dd46c62 100644 --- a/qa/qa/specs/spec_helper.rb +++ b/qa/qa/specs/spec_helper.rb @@ -12,6 +12,7 @@ QA::Runtime::Browser.configure! unless QA::Runtime::Env.dry_run QA::Runtime::AllureReport.configure! QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes) QA::Support::KnapsackReport.configure! +QA::Service::DockerRun::Video.configure! # Enable zero monkey patching mode before loading any other RSpec code. RSpec.configure(&:disable_monkey_patching!) diff --git a/qa/spec/service/docker_run/video_spec.rb b/qa/spec/service/docker_run/video_spec.rb new file mode 100644 index 00000000000..81be5ccffae --- /dev/null +++ b/qa/spec/service/docker_run/video_spec.rb @@ -0,0 +1,181 @@ +# frozen_string_literal: true + +module QA + RSpec.describe Service::DockerRun::Video do + include QA::Support::Helpers::StubEnv + + let(:rspec_config) { instance_double('RSpec::Core::Configuration', append_after: nil, prepend_before: nil) } + let(:video_recorder_image) { 'presidenten/selenoid-manual-video-recorder' } + let(:video_recorder_version) { 'latest' } + let(:selenoid_browser_image) { 'selenoid/chrome' } + let(:selenoid_browser_version) { '111.0' } + let(:remote_grid) { 'selenoid:4444' } + let(:record_video) { 'true' } + let(:use_selenoid) { 'true' } + let(:docs_link) do + 'https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/running_against_remote_grid.md#testing-with-selenoid' + end + + shared_examples 'video set up fails' do + it 'does not perform configuration' do + aggregate_failures do + expect(QA::Runtime::Logger).to have_received(:warn) + .with(/Test failure video recording setup failed!/) + + expect(RSpec).not_to have_received(:configure) + end + end + end + + shared_examples 'video set up missing variable' do |missing_variable| + let(:failure_message) do + <<~FAIL.tr("\n", ' ').strip + Aborting video recording setup! + Missing variables: #{missing_variable} is required! + See docs: #{docs_link} + FAIL + end + + it 'aborts video setup with warning' do + aggregate_failures do + expect(QA::Runtime::Logger).to have_received(:warn) + .with(failure_message) + + expect(described_class).not_to have_received(:get_container_name) + expect(RSpec).not_to have_received(:configure) + end + end + end + + before do + stub_env('QA_VIDEO_RECORDER_IMAGE', video_recorder_image) + stub_env('QA_VIDEO_RECORDER_VERSION', video_recorder_version) + stub_env('QA_SELENOID_BROWSER_IMAGE', selenoid_browser_image) + stub_env('QA_SELENOID_BROWSER_VERSION', selenoid_browser_version) + stub_env('QA_REMOTE_GRID', remote_grid) + stub_env('USE_SELENOID', use_selenoid) + stub_env('QA_RECORD_VIDEO', record_video) + + allow(RSpec).to receive(:configure).and_yield(rspec_config) + allow(described_class).to receive(:get_container_name) + allow(described_class).to receive(:shell) + allow(QA::Runtime::Logger).to receive(:warn) + allow(QA::Runtime::Logger).to receive(:info) + end + + context 'with video disabled' do + let(:record_video) { 'false' } + + before do + described_class.configure! + end + + it 'skips configuration' do + aggregate_failures do + expect(described_class).not_to have_received(:get_container_name) + expect(described_class).not_to have_received(:shell) + expect(RSpec).not_to have_received(:configure) + end + end + end + + context 'with use_selenoid disabled' do + let(:use_selenoid) { 'false' } + + before do + described_class.configure! + end + + it_behaves_like 'video set up missing variable', 'USE_SELENOID' + end + + context 'without video_recorder_image set' do + let(:video_recorder_image) { nil } + + before do + described_class.configure! + end + + it_behaves_like 'video set up missing variable', 'QA_VIDEO_RECORDER_IMAGE' + end + + context 'without selenoid_browser_image set' do + let(:selenoid_browser_image) { nil } + + before do + described_class.configure! + end + + it_behaves_like 'video set up missing variable', 'QA_SELENOID_BROWSER_IMAGE' + end + + context 'without selenoid_browser_version set' do + let(:selenoid_browser_version) { nil } + + before do + described_class.configure! + end + + it_behaves_like 'video set up missing variable', 'QA_SELENOID_BROWSER_VERSION' + end + + context 'without browser_container_hostname' do + before do + allow(described_class).to receive(:get_container_name) + .with(video_recorder_image) + .and_return('recorder_container_name') + allow(described_class).to receive(:get_container_name) + .with("#{selenoid_browser_image}:#{selenoid_browser_version}") + .and_return('browser_image_hostname') + allow(described_class).to receive(:shell) + .and_return(false) + + described_class.configure! + end + + it_behaves_like 'video set up fails' + end + + context 'without recorder_container_name' do + before do + allow(described_class).to receive(:get_container_name) + .with(video_recorder_image) + .and_return('') + allow(described_class).to receive(:get_container_name) + .with("#{selenoid_browser_image}:#{selenoid_browser_version}") + .and_return('browser_image_hostname') + allow(described_class).to receive(:shell) + .and_return('browser_container_hostname') + + described_class.configure! + end + + it_behaves_like 'video set up fails' + end + + context 'with recorder_container_name and browser_container_hostname' do + before do + allow(described_class).to receive(:get_container_name) + .with(video_recorder_image) + .and_return('recorder_container_name') + allow(described_class).to receive(:get_container_name) + .with("#{selenoid_browser_image}:#{selenoid_browser_version}") + .and_return('browser_image_hostname') + allow(described_class).to receive(:shell) + .and_return('browser_container_hostname') + + described_class.configure! + end + + it 'performs configuration' do + aggregate_failures do + expect(QA::Runtime::Logger).to have_received(:info) + .with(/Test failure video recording setup complete!/) + expect(RSpec).to have_received(:configure) + expect(rspec_config).to have_received(:prepend_before) + expect(rspec_config).to have_received(:append_after) + end + end + end + end +end |