Skip to content

Instantly share code, notes, and snippets.

@IkumaTadokoro
Created October 23, 2022 00:27
Show Gist options
  • Save IkumaTadokoro/26e08130f849c9aac8c4b18a1adc7561 to your computer and use it in GitHub Desktop.
Save IkumaTadokoro/26e08130f849c9aac8c4b18a1adc7561 to your computer and use it in GitHub Desktop.
Kaigi_on_Rails_2022で使用したOlaywrightです。
# frozen_string_literal: true
require 'json'
require 'net/http'
require 'websocket-client-simple'
class Olaywright
BASE_DOMAINS_KEYS = %i[domain_name experimental deprecated].freeze
PARAMETER_KEYS = %i[name description experimental optional].freeze
COMMAND_KEYS = %i[type name description experimental parameters returns exec].freeze
PRIMARY_KEY_PAIRS = { commands: :name, types: :id, events: :name }.freeze
attr_reader :domains
using Module.new {
refine String do
def underscore = gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
end
}
def self.usage
puts <<~USAGE
○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●
____ __ _ __ __
/ __ \/ /___ ___ ___ _______(_)___ _/ /_ / /_
/ / / / / __ `/ / / / | /| / / ___/ / __ `/ __ \/ __/
/ /_/ / / /_/ / /_/ /| |/ |/ / / / / /_/ / / / / /_
\____/_/\__,_/\__, / |__/|__/_/ /_/\__, /_/ /_/\__/
/____/ /____/
○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●○●
┌─────────────────────────┐
│ What is Olaywright? ├────────────────────────────┐
└┬────────────────────────┘ │
│ "Ole no" Playwright. │
└─────────────────────────────────────────────────────┘
┌─────────────────────────┐
│ Basic Usage ├────────────────────────────┐
└┬────────────────────────┘ │
│ # Setup │
│ browser = Olaywright.new │
│ browser.domains in { page:, target: } │
│ │
│ # Exec Command ex) Target.createTarget │
│ # Notice: Use snake_case. You're in Ruby world. │
│ target.create_target.exec[url: 'https://google.com']│
└─────────────────────────────────────────────────────┘
USAGE
end
def initialize(host = 'localhost', port = 9222)
@id = 0
@ws = WebSocket::Client::Simple.connect(debugger_url(host, port))
@protocol = http_get_body(host, port, '/json/protocol')[:domains]
@domains = build_domains
@ws.on :message do |msg|
puts <<~RESPONSE
#{JSON.pretty_generate(JSON.parse(msg.data))}
RESPONSE
end
end
def send_message(message)
@ws.send(message)
end
private
def debugger_url(host, port)
targets = http_get_body(host, port, '/json/list')
default_target = targets.select { |target| target[:type] == 'page' && target[:webSocketDebuggerUrl] }&.first
default_target[:webSocketDebuggerUrl]
end
def next_id
@id += 1
end
def http_get_body(host, port, path)
JSON.parse(Net::HTTP.new(host, port).get(path).read_body, symbolize_names: true)
end
def build_domains
domains = Struct.new(*@protocol.map { |domain| domain[:domain].underscore.to_sym }, keyword_init: true).new
@protocol.each do |raw_domain|
domain_name = raw_domain[:domain].underscore
domains_keys = BASE_DOMAINS_KEYS + PRIMARY_KEY_PAIRS.flat_map { |type, key| primary_keys(raw_domain, type, key) }
domains.public_send("#{domain_name}=", Struct.new(*domains_keys, keyword_init: true).new)
domain = domains.public_send(domain_name)
add_domain_base(domain, raw_domain)
add_command(domain, raw_domain)
end
domains.each(&:freeze)
domains.freeze
end
def primary_keys(raw_domain, type, key)
raw_domain[type]&.map { |domain| domain[key].underscore.to_sym } || []
end
def add_domain_base(domain, raw_domain)
BASE_DOMAINS_KEYS.each { |key| domain.public_send("#{key.to_s}=", raw_domain[key] || false) }
end
def add_command(domain, raw_domain)
raw_domain[:commands].each do |raw_command|
name = raw_command[:name].underscore
command = Struct.new(*COMMAND_KEYS, keyword_init: true).new(
type: :command,
name:,
description: raw_command[:description],
experimental: raw_command[:experimental] || false,
parameters: raw_command[:parameters]&.map { |parameter| build_parameter(parameter) },
returns: raw_command[:returns],
exec: build_exec(raw_domain[:domain], raw_command[:name])
)
domain.public_send("#{name}=", command)
end
end
def build_parameter(parameter)
Struct.new(*PARAMETER_KEYS, keyword_init: true).new(
name: parameter[:name],
description: parameter[:description],
experimental: parameter[:experimental] || false,
optional: parameter[:optional] || false
)
end
def build_exec(domain_name, command_name)
proc do |**args|
message = { id: next_id, method: "#{domain_name}.#{command_name}", params: args }
send_message(message.to_json)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment