playwright-ruby-client 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/CODE_OF_CONDUCT.md +74 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +49 -0
  7. data/Rakefile +3 -0
  8. data/bin/console +11 -0
  9. data/bin/setup +8 -0
  10. data/lib/playwright.rb +39 -0
  11. data/lib/playwright/channel.rb +28 -0
  12. data/lib/playwright/channel_owner.rb +80 -0
  13. data/lib/playwright/channel_owners/android.rb +3 -0
  14. data/lib/playwright/channel_owners/binding_call.rb +4 -0
  15. data/lib/playwright/channel_owners/browser.rb +80 -0
  16. data/lib/playwright/channel_owners/browser_context.rb +13 -0
  17. data/lib/playwright/channel_owners/browser_type.rb +26 -0
  18. data/lib/playwright/channel_owners/chromium_browser.rb +8 -0
  19. data/lib/playwright/channel_owners/chromium_browser_context.rb +8 -0
  20. data/lib/playwright/channel_owners/electron.rb +3 -0
  21. data/lib/playwright/channel_owners/firefox_browser.rb +8 -0
  22. data/lib/playwright/channel_owners/frame.rb +44 -0
  23. data/lib/playwright/channel_owners/page.rb +53 -0
  24. data/lib/playwright/channel_owners/playwright.rb +57 -0
  25. data/lib/playwright/channel_owners/request.rb +5 -0
  26. data/lib/playwright/channel_owners/response.rb +5 -0
  27. data/lib/playwright/channel_owners/selectors.rb +4 -0
  28. data/lib/playwright/channel_owners/webkit_browser.rb +8 -0
  29. data/lib/playwright/connection.rb +238 -0
  30. data/lib/playwright/errors.rb +35 -0
  31. data/lib/playwright/event_emitter.rb +62 -0
  32. data/lib/playwright/events.rb +86 -0
  33. data/lib/playwright/playwright_api.rb +75 -0
  34. data/lib/playwright/transport.rb +86 -0
  35. data/lib/playwright/version.rb +5 -0
  36. data/lib/playwright_api/accessibility.rb +39 -0
  37. data/lib/playwright_api/binding_call.rb +5 -0
  38. data/lib/playwright_api/browser.rb +123 -0
  39. data/lib/playwright_api/browser_context.rb +285 -0
  40. data/lib/playwright_api/browser_type.rb +144 -0
  41. data/lib/playwright_api/cdp_session.rb +34 -0
  42. data/lib/playwright_api/chromium_browser_context.rb +26 -0
  43. data/lib/playwright_api/console_message.rb +22 -0
  44. data/lib/playwright_api/dialog.rb +46 -0
  45. data/lib/playwright_api/download.rb +54 -0
  46. data/lib/playwright_api/element_handle.rb +361 -0
  47. data/lib/playwright_api/file_chooser.rb +31 -0
  48. data/lib/playwright_api/frame.rb +526 -0
  49. data/lib/playwright_api/js_handle.rb +69 -0
  50. data/lib/playwright_api/keyboard.rb +101 -0
  51. data/lib/playwright_api/mouse.rb +47 -0
  52. data/lib/playwright_api/page.rb +986 -0
  53. data/lib/playwright_api/playwright.rb +35 -0
  54. data/lib/playwright_api/request.rb +119 -0
  55. data/lib/playwright_api/response.rb +61 -0
  56. data/lib/playwright_api/route.rb +53 -0
  57. data/lib/playwright_api/selectors.rb +51 -0
  58. data/lib/playwright_api/touchscreen.rb +10 -0
  59. data/lib/playwright_api/video.rb +14 -0
  60. data/lib/playwright_api/web_socket.rb +21 -0
  61. data/lib/playwright_api/worker.rb +34 -0
  62. data/playwright.gemspec +35 -0
  63. metadata +216 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ac3e5cd3487407baf16ee61f1c08e9a2a20e7463027998218c5466c853c94acf
4
+ data.tar.gz: add832c083cf0ce9cdf94f044649a815cfee24221385957594f3644264d97327
5
+ SHA512:
6
+ metadata.gz: beb033e38e066d9bf459af794b0112ded024038d7c1903f89902c0d36e9ed0c57191ce72ff2880664719cb333350aa5a85747ab18f5ad113f1b5df2a781c891e
7
+ data.tar.gz: 5c0723a2d4bd529e30eb78adb5b44bbba44df43d1160ce48c1323aba91e064d05e19ee5fb74c1abe6c1f4ecce127fec297e45fd72172a4bde53add9ffc8376eb
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at iwaki@i3-systems.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in playwright.gemspec
8
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 YusukeIwaki
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,49 @@
1
+ [![Gem Version](https://badge.fury.io/rb/playwright-ruby-client.svg)](https://badge.fury.io/rb/playwright-ruby-client)
2
+
3
+ # playwright-ruby-client
4
+
5
+ A Ruby client for Playwright driver.
6
+
7
+ ## Getting Started
8
+
9
+ At this point, playwright-ruby-client doesn't include the downloader of playwright-cli, so **we have to install [playwright-cli](https://github.com/microsoft/playwright-cli) in advance**.
10
+
11
+ via npm: `npm install -g playwright-cli`
12
+
13
+ or
14
+
15
+ direct download: `wget https://playwright.azureedge.net/builds/cli/next/playwright-cli-0.180.0-next.1608746109749-cbc13bd-mac.zip`
16
+
17
+ (`-mac.zip` should be replaced for another OS)
18
+
19
+ ### Capture a site
20
+
21
+ ```ruby
22
+ require 'playwright'
23
+
24
+ Playwright.create(playwright_cli_executable_path: '/path/to/playwright-cli') do |playwright|
25
+ playwright.chromium.launch(headless: false) do |browser|
26
+ page = browser.new_page
27
+ page.goto('https://github.com/YusukeIwaki')
28
+ page.screenshot(path: './YusukeIwaki.png')
29
+ end
30
+ end
31
+ ```
32
+
33
+ ## Development
34
+
35
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
36
+
37
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
38
+
39
+ ## Contributing
40
+
41
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/playwright-ruby-client. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
42
+
43
+ ## License
44
+
45
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
46
+
47
+ ## Code of Conduct
48
+
49
+ Everyone interacting in the Playwright project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/playwright-ruby-client/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'playwright'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require 'pry'
11
+ Pry.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # namespace declaration
4
+ module Playwright; end
5
+
6
+ # concurrent-ruby
7
+ require 'concurrent'
8
+
9
+ # modules & constants
10
+ require 'playwright/errors'
11
+ require 'playwright/events'
12
+ require 'playwright/event_emitter'
13
+
14
+ require 'playwright/channel'
15
+ require 'playwright/channel_owner'
16
+ require 'playwright/connection'
17
+ require 'playwright/transport'
18
+ require 'playwright/version'
19
+
20
+ require 'playwright/playwright_api'
21
+ # load generated files
22
+ Dir[File.join(__dir__, 'playwright_api', '*.rb')].each { |f| require f }
23
+
24
+ module Playwright
25
+ module_function def create(playwright_cli_executable_path:, &block)
26
+ raise ArgumentError.new("block must be provided") unless block
27
+
28
+ connection = Connection.new(playwright_cli_executable_path: playwright_cli_executable_path)
29
+
30
+ playwright_promise = connection.async_wait_for_object_with_known_name('Playwright')
31
+ Thread.new { connection.run }
32
+ playwright = PlaywrightApi.from_channel_owner(playwright_promise.value!)
33
+ begin
34
+ block.call(playwright)
35
+ ensure
36
+ connection.stop
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,28 @@
1
+ module Playwright
2
+ class Channel
3
+ include EventEmitter
4
+
5
+ # @param connection [Playwright::Connection]
6
+ # @param guid [String]
7
+ # @param object [Playwright::ChannelOwner]
8
+ def initialize(connection, guid, object:)
9
+ @connection = connection
10
+ @guid = guid
11
+ @object = object
12
+ end
13
+
14
+ attr_reader :guid, :object
15
+
16
+ # @param method [String]
17
+ # @param params [Hash]
18
+ def send_message_to_server(method, params = {})
19
+ result = @connection.send_message_to_server(@guid, method, params)
20
+ if result.is_a?(Hash)
21
+ _type, channel_owner = result.first
22
+ channel_owner
23
+ else
24
+ nil
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,80 @@
1
+ module Playwright
2
+ class ChannelOwner
3
+ include Playwright::EventEmitter
4
+
5
+ def self.from(channel)
6
+ channel.object
7
+ end
8
+
9
+ # @param parent [Playwright::ChannelOwner|Playwright::Connection]
10
+ # @param type [String]
11
+ # @param guid [String]
12
+ # @param initializer [Hash]
13
+ def initialize(parent, type, guid, initializer)
14
+ @objects = {}
15
+
16
+ if parent.is_a?(ChannelOwner)
17
+ @connection = parent.instance_variable_get(:@connection)
18
+ @connection.send(:update_object_from_channel_owner, guid, self)
19
+ @parent = parent
20
+ @parent.send(:update_object_from_child, guid, self)
21
+ elsif parent.is_a?(Connection)
22
+ @connection = parent
23
+ @connection.send(:update_object_from_channel_owner, guid, self)
24
+ else
25
+ raise ArgumentError.new('parent must be an instance of Playwright::ChannelOwner or Playwright::Connection')
26
+ end
27
+
28
+ @channel = Channel.new(@connection, guid, object: self)
29
+ @type = type
30
+ @guid = guid
31
+ @initializer = initializer
32
+
33
+ after_initialize
34
+ end
35
+
36
+ attr_reader :channel
37
+
38
+ def dispose
39
+ # Clean up from parent and connection.
40
+ @parent&.send(:delete_object_from_child, @guid)
41
+ @connection.send(:delete_object_from_channel_owner, @guid)
42
+
43
+ # Dispose all children.
44
+ @objects.each_value(&:dispose)
45
+ @objects.clear
46
+ end
47
+
48
+ private
49
+
50
+ def after_initialize
51
+ end
52
+
53
+ def update_object_from_child(guid, child)
54
+ @objects[guid] = child
55
+ end
56
+
57
+ def delete_object_from_child(guid)
58
+ @objects.delete(guid)
59
+ end
60
+ end
61
+
62
+ class RootChannelOwner < ChannelOwner
63
+ # @param connection [Playwright::Connection]
64
+ def initialize(connection)
65
+ super(connection, '', '', {})
66
+ end
67
+ end
68
+
69
+ # namespace declaration
70
+ module ChannelOwners ; end
71
+
72
+ def self.define_channel_owner(class_name, &block)
73
+ klass = Class.new(ChannelOwner)
74
+ klass.class_eval(&block) if block
75
+ ChannelOwners.const_set(class_name, klass)
76
+ end
77
+ end
78
+
79
+ # load subclasses
80
+ Dir[File.join(__dir__, 'channel_owners', '*.rb')].each { |f| require f }
@@ -0,0 +1,3 @@
1
+ module Playwright
2
+ define_channel_owner :Android
3
+ end
@@ -0,0 +1,4 @@
1
+ module Playwright
2
+ define_channel_owner :BindingCall do
3
+ end
4
+ end
@@ -0,0 +1,80 @@
1
+ module Playwright
2
+ # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_browser.py
3
+ define_channel_owner :Browser do
4
+ def after_initialize
5
+ @contexts = Set.new
6
+ @channel.on('close', method(:handle_close))
7
+ end
8
+
9
+ def contexts
10
+ @contexts.to_a
11
+ end
12
+
13
+ def connected?
14
+ @connected
15
+ end
16
+
17
+ def new_context(**options)
18
+ params = options.dup
19
+ # @see https://github.com/microsoft/playwright/blob/5a2cfdbd47ed3c3deff77bb73e5fac34241f649d/src/client/browserContext.ts#L265
20
+ if params[:viewport] == 0
21
+ params.delete(:viewport)
22
+ params[:noDefaultViewport] = true
23
+ end
24
+ if params[:extraHTTPHeaders]
25
+ # TODO
26
+ end
27
+ if params[:storageState].is_a?(String)
28
+ params[:storageState] = JSON.parse(File.read(params[:storageState]))
29
+ end
30
+
31
+ resp = @channel.send_message_to_server('newContext', params.compact)
32
+ context = ChannelOwners::BrowserContext.from(resp)
33
+ @contexts << context
34
+ context.browser = self
35
+ context.options = params
36
+ context
37
+ end
38
+
39
+ def new_page(*args)
40
+ context = new_context(*args)
41
+ page = context.new_page
42
+ page.owned_context = context
43
+ context.owner_page = page
44
+ page
45
+ end
46
+
47
+ def close
48
+ return if @closed_or_closing
49
+ @closed_or_closing = true
50
+ @channel.send_message_to_server('close')
51
+ nil
52
+ rescue => err
53
+ raise unless safe_close_error?(err)
54
+ end
55
+
56
+ def version
57
+ @initializer['version']
58
+ end
59
+
60
+ private
61
+
62
+ def handle_close(_ = {})
63
+ @connected = false
64
+ emit(Events::Browser::Disconnected)
65
+ @closed_or_closing = false
66
+ end
67
+
68
+ # @param err [Exception]
69
+ def safe_close_error?(err)
70
+ return true if err.is_a?(Transport::AlreadyDisconnectedError)
71
+
72
+ [
73
+ 'Browser has been closed',
74
+ 'Target page, context or browser has been closed',
75
+ ].any? do |closed_message|
76
+ err.message.end_with?(closed_message)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,13 @@
1
+ module Playwright
2
+ # @ref https://github.com/microsoft/playwright-python/blob/master/playwright/_impl/_browser_context.py
3
+ define_channel_owner :BrowserContext do
4
+ attr_writer :browser, :owner_page, :options
5
+
6
+ # @returns [Playwright::Page]
7
+ def new_page
8
+ raise 'Please use browser.new_context' if @owner_page
9
+ resp = @channel.send_message_to_server('newPage')
10
+ ChannelOwners::Page.from(resp)
11
+ end
12
+ end
13
+ end