Animation frames generation on the backend
parent
bc6e1ddc3d
commit
e862acedb7
@ -0,0 +1,9 @@
|
||||
class CellDecorator < ApplicationDecorator
|
||||
|
||||
delegate_all
|
||||
|
||||
def css_class
|
||||
BrushPresenter.new(brush).to_css_class
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,18 @@
|
||||
class Cursor
|
||||
|
||||
attr_reader :x, :y, :visible
|
||||
|
||||
def initialize(x, y, visible)
|
||||
@x, @y, @visible = x, y, visible
|
||||
end
|
||||
|
||||
def diff(other)
|
||||
diff = {}
|
||||
diff[:x] = x if other && x != other.x || other.nil?
|
||||
diff[:y] = y if other && y != other.y || other.nil?
|
||||
diff[:visible] = visible if other && visible != other.visible || other.nil?
|
||||
|
||||
diff
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,24 @@
|
||||
class Frame
|
||||
|
||||
attr_reader :snapshot, :cursor
|
||||
|
||||
def initialize(snapshot, cursor)
|
||||
@snapshot = snapshot
|
||||
@cursor = cursor
|
||||
end
|
||||
|
||||
def diff(other)
|
||||
FrameDiff.new(snapshot_diff(other), cursor_diff(other))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def snapshot_diff(other)
|
||||
snapshot.diff(other && other.snapshot)
|
||||
end
|
||||
|
||||
def cursor_diff(other)
|
||||
cursor.diff(other && other.cursor)
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,28 @@
|
||||
class FrameDiff
|
||||
|
||||
def initialize(line_changes, cursor_changes)
|
||||
@line_changes = line_changes
|
||||
@cursor_changes = cursor_changes
|
||||
end
|
||||
|
||||
def as_json(*)
|
||||
json = {}
|
||||
json[:lines] = optimized_line_changes unless line_changes.blank?
|
||||
json[:cursor] = cursor_changes unless cursor_changes.blank?
|
||||
|
||||
json
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :line_changes, :cursor_changes
|
||||
|
||||
def optimized_line_changes
|
||||
line_optimizer = LineOptimizer.new
|
||||
|
||||
line_changes.each_with_object({}) do |(k, v), h|
|
||||
h[k] = line_optimizer.optimize(v)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,24 @@
|
||||
class FrameDiffList
|
||||
include Enumerable
|
||||
|
||||
delegate :each, :to => :frame_diffs
|
||||
|
||||
def initialize(frames)
|
||||
@frames = frames
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :frames
|
||||
|
||||
def frame_diffs
|
||||
previous_frame = nil
|
||||
|
||||
frames.map { |delay, frame|
|
||||
diff = frame.diff(previous_frame)
|
||||
previous_frame = frame
|
||||
[delay, diff]
|
||||
}
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,26 @@
|
||||
require 'tempfile'
|
||||
|
||||
class AsciicastFramesFileUpdater
|
||||
|
||||
def initialize(file_writer = JsonFileWriter.new)
|
||||
@file_writer = file_writer
|
||||
end
|
||||
|
||||
def update(asciicast)
|
||||
file = Tempfile.new('frames')
|
||||
|
||||
asciicast.with_terminal do |terminal|
|
||||
film = Film.new(asciicast.stdout, terminal)
|
||||
file_writer.write_enumerable(file, film.frames)
|
||||
end
|
||||
|
||||
asciicast.update_attribute(:stdout_frames, file)
|
||||
ensure
|
||||
file.unlink if file
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :file_writer
|
||||
|
||||
end
|
@ -0,0 +1,8 @@
|
||||
class AsciicastProcessor
|
||||
|
||||
def process(asciicast)
|
||||
AsciicastSnapshotUpdater.new.update(asciicast)
|
||||
AsciicastFramesFileUpdater.new.update(asciicast)
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,16 @@
|
||||
class AsciicastSnapshotUpdater
|
||||
|
||||
def update(asciicast, at_seconds = asciicast.duration / 2)
|
||||
snapshot = generate_snapshot(asciicast, at_seconds)
|
||||
asciicast.update_attribute(:snapshot, snapshot)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_snapshot(asciicast, at_seconds)
|
||||
asciicast.with_terminal do |terminal|
|
||||
Film.new(asciicast.stdout, terminal).snapshot_at(at_seconds)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,37 @@
|
||||
class Film
|
||||
|
||||
def initialize(stdout, terminal)
|
||||
@stdout = stdout
|
||||
@terminal = terminal
|
||||
end
|
||||
|
||||
def snapshot_at(time)
|
||||
stdout_each_until(time) do |delay, data|
|
||||
terminal.feed(data)
|
||||
end
|
||||
|
||||
terminal.snapshot
|
||||
end
|
||||
|
||||
def frames
|
||||
frames = stdout.map do |delay, data|
|
||||
terminal.feed(data)
|
||||
[delay, Frame.new(terminal.snapshot, terminal.cursor)]
|
||||
end
|
||||
|
||||
FrameDiffList.new(frames)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def stdout_each_until(seconds)
|
||||
stdout.each do |delay, frame_data|
|
||||
seconds -= delay
|
||||
break if seconds <= 0
|
||||
yield(delay, frame_data)
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :stdout, :terminal
|
||||
|
||||
end
|
@ -0,0 +1,21 @@
|
||||
class JsonFileWriter
|
||||
|
||||
def write_enumerable(file, array)
|
||||
first = true
|
||||
file << '['
|
||||
|
||||
array.each do |item|
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
file << ','
|
||||
end
|
||||
|
||||
file << item.to_json
|
||||
end
|
||||
|
||||
file << ']'
|
||||
file.close
|
||||
end
|
||||
|
||||
end
|
@ -1,17 +0,0 @@
|
||||
class SnapshotCreator
|
||||
|
||||
def create(width, height, stdout, duration)
|
||||
terminal = Terminal.new(width, height)
|
||||
seconds = (duration / 2).to_i
|
||||
|
||||
stdout.each_until(seconds) do |delay, data|
|
||||
terminal.feed(data)
|
||||
end
|
||||
|
||||
terminal.snapshot
|
||||
|
||||
ensure
|
||||
terminal.release
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,13 @@
|
||||
class AsciicastWorker
|
||||
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(asciicast_id)
|
||||
asciicast = Asciicast.find(asciicast_id)
|
||||
AsciicastProcessor.new.process(asciicast)
|
||||
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
# oh well...
|
||||
end
|
||||
|
||||
end
|
@ -1,21 +0,0 @@
|
||||
class SnapshotWorker
|
||||
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(asciicast_id)
|
||||
asciicast = Asciicast.find(asciicast_id)
|
||||
|
||||
snapshot = SnapshotCreator.new.create(
|
||||
asciicast.terminal_columns,
|
||||
asciicast.terminal_lines,
|
||||
asciicast.stdout,
|
||||
asciicast.duration
|
||||
)
|
||||
|
||||
asciicast.update_snapshot(snapshot)
|
||||
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
# oh well...
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,5 @@
|
||||
class AddStdoutFramesToAsciicast < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :asciicasts, :stdout_frames, :string
|
||||
end
|
||||
end
|
@ -0,0 +1,21 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe CellDecorator do
|
||||
|
||||
let(:decorator) { described_class.new(cell) }
|
||||
let(:cell) { double('cell', :brush => brush) }
|
||||
let(:brush) { double('brush') }
|
||||
|
||||
describe '#css_class' do
|
||||
let(:brush_presenter) { double('brush_presenter', :to_css_class => 'kls') }
|
||||
|
||||
subject { decorator.css_class }
|
||||
|
||||
before do
|
||||
allow(BrushPresenter).to receive(:new).with(brush) { brush_presenter }
|
||||
end
|
||||
|
||||
it { should eq('kls') }
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,45 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Cursor do
|
||||
|
||||
let(:cursor) { described_class.new(1, 2, true) }
|
||||
|
||||
describe '#diff' do
|
||||
let(:other) { described_class.new(3, 4, false) }
|
||||
|
||||
subject { cursor.diff(other) }
|
||||
|
||||
it { should eq({ x: 1, y: 2, visible: true }) }
|
||||
|
||||
context "when x is the same" do
|
||||
let(:other) { described_class.new(1, 4, false) }
|
||||
|
||||
it 'skips x from the hash' do
|
||||
expect(subject).not_to have_key(:x)
|
||||
end
|
||||
end
|
||||
|
||||
context "when y is the same" do
|
||||
let(:other) { described_class.new(3, 2, false) }
|
||||
|
||||
it 'skips y from the hash' do
|
||||
expect(subject).not_to have_key(:y)
|
||||
end
|
||||
end
|
||||
|
||||
context "when visible is the same" do
|
||||
let(:other) { described_class.new(3, 4, true) }
|
||||
|
||||
it 'skips visible from the hash' do
|
||||
expect(subject).not_to have_key(:visible)
|
||||
end
|
||||
end
|
||||
|
||||
context "when other is nil" do
|
||||
let(:other) { nil }
|
||||
|
||||
it { should eq({ x: 1, y: 2, visible: true }) }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,32 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe FrameDiffList do
|
||||
|
||||
let(:frame_diff_list) { described_class.new(frames) }
|
||||
let(:frames) { [[1.5, frame_1], [0.5, frame_2]] }
|
||||
let(:frame_1) { double('frame_1', :diff => diff_1) }
|
||||
let(:frame_2) { double('frame_2', :diff => diff_2) }
|
||||
let(:diff_1) { double('diff_1') }
|
||||
let(:diff_2) { double('diff_2') }
|
||||
|
||||
describe '#each' do
|
||||
subject { frame_diff_list.to_a }
|
||||
|
||||
it 'maps each frame to its diff' do
|
||||
expect(subject).to eq([[1.5, diff_1], [0.5, diff_2]])
|
||||
end
|
||||
|
||||
it 'diffs the first frame with nil' do
|
||||
subject
|
||||
|
||||
expect(frame_1).to have_received(:diff).with(nil)
|
||||
end
|
||||
|
||||
it 'diffs the subsequent frames with the previous ones' do
|
||||
subject
|
||||
|
||||
expect(frame_2).to have_received(:diff).with(frame_1)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,48 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe FrameDiff do
|
||||
|
||||
let(:frame_diff) { described_class.new(line_changes, cursor_changes) }
|
||||
let(:line_changes) { { 0 => line_0, 2 => line_2 } }
|
||||
let(:cursor_changes) { { x: 1 } }
|
||||
let(:line_0) { double('line_0') }
|
||||
let(:line_2) { double('line_2') }
|
||||
let(:line_optimizer) { double('line_optimizer') }
|
||||
let(:optimized_line_0) { double('optimized_line_0') }
|
||||
let(:optimized_line_2) { double('optimized_line_2') }
|
||||
|
||||
describe '#as_json' do
|
||||
subject { frame_diff.as_json }
|
||||
|
||||
before do
|
||||
allow(LineOptimizer).to receive(:new) { line_optimizer }
|
||||
allow(line_optimizer).to receive(:optimize).
|
||||
with(line_0) { optimized_line_0 }
|
||||
allow(line_optimizer).to receive(:optimize).
|
||||
with(line_2) { optimized_line_2 }
|
||||
end
|
||||
|
||||
it 'includes line changes and cursor changes' do
|
||||
expect(subject).to eq({ :lines => { 0 => optimized_line_0,
|
||||
2 => optimized_line_2 },
|
||||
:cursor => cursor_changes })
|
||||
end
|
||||
|
||||
context "when there are no line changes" do
|
||||
let(:line_changes) { {} }
|
||||
|
||||
it 'skips the lines hash' do
|
||||
expect(subject).not_to have_key(:lines)
|
||||
end
|
||||
end
|
||||
|
||||
context "when there are no cursor changes" do
|
||||
let(:cursor_changes) { {} }
|
||||
|
||||
it 'skips the cursor hash' do
|
||||
expect(subject).not_to have_key(:cursor)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,46 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Frame do
|
||||
|
||||
let(:frame) { described_class.new(snapshot, cursor) }
|
||||
let(:snapshot) { double('snapshot', :diff => snapshot_diff) }
|
||||
let(:cursor) { double('cursor', :diff => cursor_diff) }
|
||||
let(:snapshot_diff) { double('snapshot_diff') }
|
||||
let(:cursor_diff) { double('cursor_diff') }
|
||||
|
||||
describe '#diff' do
|
||||
let(:other) { double('other', :snapshot => other_snapshot,
|
||||
:cursor => other_cursor) }
|
||||
let(:other_snapshot) { double('other_snapshot') }
|
||||
let(:other_cursor) { double('other_cursor') }
|
||||
let(:frame_diff) { double('frame_diff') }
|
||||
|
||||
subject { frame.diff(other) }
|
||||
|
||||
before do
|
||||
allow(FrameDiff).to receive(:new).
|
||||
with(snapshot_diff, cursor_diff) { frame_diff }
|
||||
end
|
||||
|
||||
it 'returns a FrameDiff instance built from snapshot and cursor diffs' do
|
||||
expect(subject).to be(frame_diff)
|
||||
end
|
||||
|
||||
context "when other is nil" do
|
||||
let(:other) { nil }
|
||||
|
||||
it 'diffs its snapshot with nil' do
|
||||
subject
|
||||
|
||||
expect(snapshot).to have_received(:diff).with(nil)
|
||||
end
|
||||
|
||||
it 'diffs its cursor with nil' do
|
||||
subject
|
||||
|
||||
expect(cursor).to have_received(:diff).with(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,31 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe AsciicastFramesFileUpdater do
|
||||
|
||||
let(:updater) { described_class.new(file_writer) }
|
||||
let(:file_writer) { double('file_writer') }
|
||||
|
||||
describe '#update' do
|
||||
let(:asciicast) { create(:asciicast) }
|
||||
let(:film) { double('film', :frames => frames) }
|
||||
let(:frames) { [1, 2] }
|
||||
|
||||
subject { updater.update(asciicast) }
|
||||
|
||||
before do
|
||||
allow(Film).to receive(:new).with(asciicast.stdout, kind_of(Terminal)) {
|
||||
film
|
||||
}
|
||||
allow(file_writer).to receive(:write_enumerable) do |file, frames|
|
||||
file << frames.to_json
|
||||
end
|
||||
end
|
||||
|
||||
it 'updates stdout_frames file on asciicast' do
|
||||
subject
|
||||
|
||||
expect(asciicast.stdout_frames.read).to eq('[1,2]')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,32 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe AsciicastProcessor do
|
||||
|
||||
let(:processor) { described_class.new }
|
||||
|
||||
describe '#process' do
|
||||
let(:asciicast) { double('asciicast') }
|
||||
let(:snapshot_updater) { double('snapshot_updater', :update => nil) }
|
||||
let(:frames_file_updater) { double('frames_file_updater', :update => nil) }
|
||||
|
||||
subject { processor.process(asciicast) }
|
||||
|
||||
before do
|
||||
allow(AsciicastSnapshotUpdater).to receive(:new) { snapshot_updater }
|
||||
allow(AsciicastFramesFileUpdater).to receive(:new) { frames_file_updater }
|
||||
end
|
||||
|
||||
it 'generates a snapshot' do
|
||||
subject
|
||||
|
||||
expect(snapshot_updater).to have_received(:update).with(asciicast)
|
||||
end
|
||||
|
||||
it 'generates animation frames' do
|
||||
subject
|
||||
|
||||
expect(frames_file_updater).to have_received(:update).with(asciicast)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,41 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe AsciicastSnapshotUpdater do
|
||||
|
||||
let(:updater) { described_class.new }
|
||||
|
||||
describe '#update' do
|
||||
let(:asciicast) { double('asciicast', :duration => 5.0, :stdout => stdout,
|
||||
:update_attribute => nil) }
|
||||
let(:stdout) { double('stdout') }
|
||||
let(:terminal) { double('terminal') }
|
||||
let(:film) { double('film', :snapshot_at => 'foo') }
|
||||
|
||||
subject { updater.update(asciicast) }
|
||||
|
||||
before do
|
||||
allow(asciicast).to receive(:with_terminal).and_yield(terminal)
|
||||
allow(Film).to receive(:new).with(stdout, terminal) { film }
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it "generates the snapshot at half of asciicast's duration" do
|
||||
expect(film).to have_received(:snapshot_at).with(2.5)
|
||||
end
|
||||
|
||||
it "updates asciicast's snapshot to the terminal's snapshot" do
|
||||
expect(asciicast).to have_received(:update_attribute).
|
||||
with(:snapshot, 'foo')
|
||||
end
|
||||
|
||||
context "when snapshot time given" do
|
||||
subject { updater.update(asciicast, 4.3) }
|
||||
|
||||
it "generates the snapshot at the given time" do
|
||||
expect(film).to have_received(:snapshot_at).with(4.3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,38 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Film do
|
||||
|
||||
let(:film) { described_class.new(stdout, terminal) }
|
||||
let(:terminal) { FakeTerminal.new }
|
||||
|
||||
describe '#snapshot_at' do
|
||||
let(:stdout) { [[0.5, 'ab'], [1.0, 'cd'], [2.0, 'ef']] }
|
||||
|
||||
subject { film.snapshot_at(1.7) }
|
||||
|
||||
it "returns the snapshot of the terminal" do
|
||||
expect(subject).to eq('abcd')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#frames' do
|
||||
let(:stdout) { [[0.5, 'ab'], [1.0, 'cd']] }
|
||||
let(:frame_1) { double('frame_1') }
|
||||
let(:frame_2) { double('frame_2') }
|
||||
let(:frame_diff_list) { double('frame_diff_list') }
|
||||
|
||||
subject { film.frames }
|
||||
|
||||
before do
|
||||
allow(Frame).to receive(:new).with('ab', 2) { frame_1 }
|
||||
allow(Frame).to receive(:new).with('abcd', 4) { frame_2 }
|
||||
allow(FrameDiffList).to receive(:new).
|
||||
with([[0.5, frame_1], [1.0, frame_2]]) { frame_diff_list }
|
||||
end
|
||||
|
||||
it 'returns delay and frame tuples wrapped with FrameDiffList' do
|
||||
expect(subject).to be(frame_diff_list)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,28 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe JsonFileWriter do
|
||||
|
||||
let(:writer) { described_class.new }
|
||||
|
||||
describe '#write_enumerable' do
|
||||
let(:file) { StringIO.new }
|
||||
let(:enumerable) { [item_1, item_2] }
|
||||
let(:item_1) { double('item_1', :to_json => 'a') }
|
||||
let(:item_2) { double('item_2', :to_json => 'b') }
|
||||
|
||||
subject { writer.write_enumerable(file, enumerable) }
|
||||
|
||||
before do
|
||||
subject
|
||||
end
|
||||
|
||||
it 'writes the enumerable to the file in json format' do
|
||||
expect(file.string).to eq('[a,b]')
|
||||
end
|
||||
|
||||
it 'closes the file' do
|
||||
expect(file).to be_closed
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,39 +0,0 @@
|
||||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe SnapshotCreator do
|
||||
|
||||
let(:snapshot_creator) { SnapshotCreator.new }
|
||||
|
||||
describe '#create' do
|
||||
let(:stdout) { double('stdout', :each_until => nil) }
|
||||
let(:terminal) { double('terminal', :feed => nil, :snapshot => snapshot,
|
||||
:release => nil) }
|
||||
let(:snapshot) { double('snapshot') }
|
||||
|
||||
subject { snapshot_creator.create(80, 24, stdout, 31.4) }
|
||||
|
||||
before do
|
||||
allow(Terminal).to receive(:new).with(80, 24) { terminal }
|
||||
allow(stdout).to receive(:each_until).and_yield(1.2, "\xBCółć")
|
||||
end
|
||||
|
||||
it 'uses Terminal to generate a snapshot' do
|
||||
subject
|
||||
|
||||
expect(terminal).to have_received(:feed).with("\xBCółć")
|
||||
end
|
||||
|
||||
it 'gets the bytes from stdout for half duration (whole seconds)' do
|
||||
subject
|
||||
|
||||
expect(stdout).to have_received(:each_until).with(15)
|
||||
end
|
||||
|
||||
it 'returns the snapshot from the Terminal' do
|
||||
expect(subject).to be(snapshot)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,19 @@
|
||||
class FakeTerminal
|
||||
|
||||
def initialize
|
||||
@data = ''
|
||||
end
|
||||
|
||||
def feed(data)
|
||||
@data << data
|
||||
end
|
||||
|
||||
def snapshot
|
||||
@data
|
||||
end
|
||||
|
||||
def cursor
|
||||
@data.size
|
||||
end
|
||||
|
||||
end
|
@ -0,0 +1,23 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe AsciicastWorker do
|
||||
|
||||
let(:worker) { described_class.new }
|
||||
|
||||
describe '#perform' do
|
||||
let(:asciicast) { double('asciicast') }
|
||||
let(:asciicast_processor) { double('asciicast_processor', :process => nil) }
|
||||
|
||||
before do
|
||||
allow(Asciicast).to receive(:find).with(123) { asciicast }
|
||||
allow(AsciicastProcessor).to receive(:new).with(no_args) { asciicast_processor }
|
||||
end
|
||||
|
||||
it 'processes given asciicast with AsciicastProcessor' do
|
||||
worker.perform(123)
|
||||
|
||||
expect(asciicast_processor).to have_received(:process).with(asciicast)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
@ -1,35 +0,0 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe SnapshotWorker do
|
||||
|
||||
let(:worker) { SnapshotWorker.new }
|
||||
|
||||
describe '#perform' do
|
||||
let(:snapshot_creator) { double('snapshot_creator', :create => snapshot) }
|
||||
let(:snapshot) { double('snapshot') }
|
||||
let(:asciicast) { double('asciicast', :terminal_columns => 9,
|
||||
:terminal_lines => 5,
|
||||
:duration => 4.3,
|
||||
:stdout => stdout,
|
||||
:update_snapshot => nil) }
|
||||
let(:stdout) { double('stdout') }
|
||||
|
||||
before do
|
||||
allow(Asciicast).to receive(:find).with(123) { asciicast }
|
||||
allow(SnapshotCreator).to receive(:new).with(no_args) { snapshot_creator }
|
||||
end
|
||||
|
||||
it 'uses AsciicastSnapshotCreator to generate a snapshot' do
|
||||
worker.perform(123)
|
||||
|
||||
expect(snapshot_creator).to have_received(:create).with(9, 5, stdout, 4.3)
|
||||
end
|
||||
|
||||
it 'updates the snapshot on the asciicast' do
|
||||
worker.perform(123)
|
||||
|
||||
expect(asciicast).to have_received(:update_snapshot).with(snapshot)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue