Class: TestRecorder::CdpRecorder
- Inherits:
-
Object
- Object
- TestRecorder::CdpRecorder
- Defined in:
- lib/test_recorder/cdp_recorder.rb
Defined Under Namespace
Classes: Record
Constant Summary collapse
- FFMPEG_ENCODE_OPTIONS =
%w[-y -an -r 25 -c:v vp8 -qmin 0 -qmax 50 -crf 8 -deadline realtime -speed 8 -b:v 1M -threads 1].freeze
- FFMPEG_DURATION_SLACK_S =
ffmpeg's -t treats the given duration as an exclusive cutoff, dropping the very frame that marks the video's real length if it lands exactly on it. This nudges -t just past that frame so it is kept, without adding a duration long enough to be noticeable.
0.01
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(enabled:) ⇒ CdpRecorder
constructor
A new instance of CdpRecorder.
- #start(page:, enabled: nil) ⇒ Object
- #stop_and_discard ⇒ Object
- #stop_and_save(filename) ⇒ Object
Constructor Details
#initialize(enabled:) ⇒ CdpRecorder
Returns a new instance of CdpRecorder.
73 74 75 76 |
# File 'lib/test_recorder/cdp_recorder.rb', line 73 def initialize(enabled:) @enabled = enabled @started = nil end |
Class Method Details
.record(devtools) ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/test_recorder/cdp_recorder.rb', line 19 def record(devtools) records[devtools] ||= begin page = devtools.page page.enable record = Record.new(page) page.on(:screencast_frame) do |event| write_frame(record, event) record.page.screencast_frame_ack(session_id: event["sessionId"]) end record end end |
Instance Method Details
#start(page:, enabled: nil) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/test_recorder/cdp_recorder.rb', line 78 def start(page:, enabled: nil) enabled = @enabled if enabled.nil? @started = enabled return unless @started @tmp_video = Tempfile.new(["testrecorder", ".mkv"]) @tmp_video.binmode @record = self.class.record(page.driver.browser.devtools) @frame_writer = FrameWriter.new(@tmp_video) @record.io = @frame_writer @record.page.start_screencast(format: "jpeg", quality: TestRecorder.jpeg_quality, max_width: TestRecorder.max_dimension, max_height: TestRecorder.max_dimension, every_nth_frame: TestRecorder.every_nth_frame) end |
#stop_and_discard ⇒ Object
93 94 95 96 97 98 99 |
# File 'lib/test_recorder/cdp_recorder.rb', line 93 def stop_and_discard return unless @started @record.io = nil @record.page.stop_screencast @tmp_video.close! end |
#stop_and_save(filename) ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/test_recorder/cdp_recorder.rb', line 101 def stop_and_save(filename) return "" unless @started @record.io = nil @record.page.stop_screencast @frame_writer.finish @tmp_video.flush # Chrome sends a frame as soon as the screencast starts, so a test that fails # before it draws anything still leaves behind the one frame of the blank page # it started on. if @frame_writer.frame_count < 2 warn "[TestRecorder] The screencast captured no page updates, so no video was saved." @tmp_video.close! return "" end video_dir = ::Rails.root.join("tmp", "videos") FileUtils.mkdir_p(video_dir) video_path = video_dir.join(filename).to_s # Each frame carries its own timestamp in the Matroska stream, so ffmpeg knows how long to # hold it and duplicates frames to reach the constant output frame rate on its own. Without a # known end, though, ffmpeg pads however long it likes past the last real timestamp, so -t # caps the output at the video's real duration explicitly. duration = @frame_writer.duration + FFMPEG_DURATION_SLACK_S result = system("ffmpeg", "-loglevel", "error", "-f", "matroska", "-i", @tmp_video.path, "-t", duration.to_s, *FFMPEG_ENCODE_OPTIONS, video_path) @tmp_video.close! if result.nil? warn "[TestRecorder] Failed to execute ffmpeg. Please make sure that FFmpeg is installed." return "" elsif !result warn "[TestRecorder] ffmpeg failed to encode #{video_path}." return "" end video_path end |