Class: Browserctl::Recording

Inherits:
Object
  • Object
show all
Defined in:
lib/browserctl/recording.rb

Constant Summary collapse

RECORDINGS_DIR =
File.join(Dir.tmpdir, "browserctl-recordings")
STATE_FILE =
File.expand_path("~/.browserctl/active_recording")
RECORDABLE =
%w[open_page goto fill click screenshot evaluate].freeze
SENSITIVE_PARAM_PATTERN =
/\A(token|key|secret|auth|code|access_token|api_key|client_secret|state)\z/ix

Class Method Summary collapse

Class Method Details

.activeObject



36
37
38
# File 'lib/browserctl/recording.rb', line 36

def self.active
  File.exist?(STATE_FILE) ? File.read(STATE_FILE).strip : nil
end

.append(cmd, **attrs) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/browserctl/recording.rb', line 40

def self.append(cmd, **attrs)
  name = active
  return unless name
  return unless RECORDABLE.include?(cmd.to_s)

  if %w[click fill].include?(cmd.to_s) && attrs[:selector].nil?
    record_ref_interaction(name, cmd.to_s, attrs)
    return
  end

  attrs = prepare_attrs(cmd.to_s, attrs)

  File.open(log_path(name), "a") do |f|
    f.puts JSON.generate({ cmd: cmd.to_s }.merge(attrs.transform_keys(&:to_s)))
  end
end

.generate_workflow(name, output_path: nil) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/browserctl/recording.rb', line 57

def self.generate_workflow(name, output_path: nil)
  log = log_path(name)
  raise "no recording found for '#{name}'" unless File.exist?(log)

  lines = File.readlines(log).map { |l| JSON.parse(l, symbolize_names: true) }
  ruby  = build_workflow_ruby(name, lines)
  File.write(output_path, ruby) if output_path

  ref_count = lines.count { |l| l[:cmd] == "_ref_interaction" }
  if ref_count.positive?
    warn "Warning: #{ref_count} ref-based interaction(s) were captured but cannot be replayed by ref."
    warn "Search the generated workflow for 'TODO: ref-based' and replace with stable CSS selectors."
  end

  ruby
ensure
  FileUtils.rm_f(log) if log
end

.start(name) ⇒ Object



18
19
20
21
22
23
24
25
26
# File 'lib/browserctl/recording.rb', line 18

def self.start(name)
  FileUtils.mkdir_p(RECORDINGS_DIR, mode: 0o700)
  FileUtils.mkdir_p(File.dirname(STATE_FILE))
  File.write(STATE_FILE, name)
  FileUtils.rm_f(log_path(name))
  FileUtils.touch(log_path(name))
  File.chmod(0o600, log_path(name))
  name
end

.stopObject



28
29
30
31
32
33
34
# File 'lib/browserctl/recording.rb', line 28

def self.stop
  name = active
  raise "no active recording — run: browserctl record start <name>" unless name

  File.unlink(STATE_FILE)
  name
end