capybara-playwright-driver 0.1.2 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e6ff7fd5aaa4b41cc19395605ea19820f140703b1e52d7e2e07aaaa73f0428a
4
- data.tar.gz: d77b98b91bad49e4a115b2cc7c8238611f02389e882a444d065418aeea3c2ae2
3
+ metadata.gz: 2f43dbe09eba1cda75ed608f418120fbe5daebe99aa4bebde377ef120bd88d3f
4
+ data.tar.gz: 825d1aa7230177a37c7f6ce23ce656961084a2f13bfe8e5e36674836866a477e
5
5
  SHA512:
6
- metadata.gz: e3eafd55be3b31d780fe7f4f1c7f5c3afa35ead692a5ce8ea22e755c4cceab37d390bbcf875fad6ae355e5475e047dc4719bbf6ebfd7abaf04732d52e9ed7c60
7
- data.tar.gz: 7cf9a6db8a89ba6437e6790812924682c08bbf55bc182737159aea1883190313a86aeddf86a85219fba048ed884b93a0cfd868e97f74b25e171974f0c56ebabc
6
+ metadata.gz: 9f989b332d2d69bf84e3b270478514cee94a1ed3dc40c6868b8c61565248fa0d597e16f1575a42344bd13cbb4ecce8277956813d67df8b407351db72e684778d
7
+ data.tar.gz: df9dfa30c1f50fb1dc3c037b1b7445308e5301632ec6f8d16ba05f53b071f8a106355ae5d8d59b6885892c70bd7b2b713fe3a6d2f3c763ba528cd37c0c8ec899
@@ -26,7 +26,8 @@ Gem::Specification.new do |spec|
26
26
 
27
27
  spec.required_ruby_version = '>= 2.4'
28
28
  spec.add_dependency 'capybara'
29
- spec.add_dependency 'playwright-ruby-client', '>= 0.5.9'
29
+ spec.add_dependency 'playwright-ruby-client', '>= 0.8.0'
30
+ spec.add_development_dependency 'allure-rspec'
30
31
  spec.add_development_dependency 'bundler', '~> 2.2.3'
31
32
  spec.add_development_dependency 'launchy', '>= 2.0.4'
32
33
  spec.add_development_dependency 'pry-byebug'
@@ -1,16 +1,26 @@
1
+ require_relative './tmpdir_owner'
2
+
1
3
  module Capybara
2
4
  module Playwright
5
+ # Responsibility of this class is:
6
+ # - Handling Capybara::Driver commands.
7
+ # - Managing Playwright browser contexts and pages.
8
+ #
9
+ # Note that this class doesn't manage Playwright::Browser.
10
+ # We should not use Playwright::Browser#close in this class.
3
11
  class Browser
12
+ include TmpdirOwner
4
13
  extend Forwardable
5
14
 
6
15
  class NoSuchWindowError < StandardError ; end
7
16
 
8
- def initialize(playwright:, driver:, browser_type:, browser_options:, page_options:)
17
+ def initialize(driver:, playwright_browser:, page_options:, record_video: false)
9
18
  @driver = driver
10
-
11
- browser_type = playwright.send(browser_type)
12
- @playwright_browser = browser_type.launch(**browser_options)
19
+ @playwright_browser = playwright_browser
13
20
  @page_options = page_options
21
+ if record_video
22
+ @page_options[:record_video_dir] ||= tmpdir
23
+ end
14
24
  @playwright_page = create_page(create_browser_context)
15
25
  end
16
26
 
@@ -34,8 +44,8 @@ module Capybara
34
44
  end
35
45
  end
36
46
 
37
- def quit
38
- @playwright_browser.close
47
+ def clear_browser_contexts
48
+ @playwright_browser.contexts.each(&:close)
39
49
  end
40
50
 
41
51
  def current_url
@@ -155,6 +165,22 @@ module Capybara
155
165
  wrap_node(result)
156
166
  end
157
167
 
168
+ # Not used by Capybara::Session.
169
+ # Intended to be directly called by user.
170
+ def video_path
171
+ return nil if !@playwright_page || @playwright_page.closed?
172
+
173
+ @playwright_page.video&.path
174
+ end
175
+
176
+ # Not used by Capybara::Session.
177
+ # Intended to be directly called by user.
178
+ def raw_screenshot(**options)
179
+ return nil if !@playwright_page || @playwright_page.closed?
180
+
181
+ @playwright_page.screenshot(**options)
182
+ end
183
+
158
184
  def save_screenshot(path, **options)
159
185
  assert_page_alive
160
186
 
@@ -302,7 +328,24 @@ module Capybara
302
328
  when ::Playwright::ElementHandle
303
329
  Node.new(@driver, @playwright_page, arg)
304
330
  when ::Playwright::JSHandle
305
- arg.json_value
331
+ obj_type, is_array = arg.evaluate('obj => [typeof obj, Array.isArray(obj)]')
332
+ if obj_type == 'object'
333
+ if is_array
334
+ # Firefox often include 'toJSON' into properties.
335
+ # https://github.com/microsoft/playwright/issues/7015
336
+ #
337
+ # Get rid of non-numeric entries.
338
+ arg.properties.select { |key, _| key.to_i.to_s == key.to_s }.map do |_, value|
339
+ wrap_node(value)
340
+ end
341
+ else
342
+ arg.properties.map do |key, value|
343
+ [key, wrap_node(value)]
344
+ end.to_h
345
+ end
346
+ else
347
+ arg.json_value
348
+ end
306
349
  else
307
350
  arg
308
351
  end
@@ -310,7 +353,6 @@ module Capybara
310
353
 
311
354
  def with_playwright_page(&block)
312
355
  assert_page_alive
313
- raise ArgumentError.new('block must be given') unless block
314
356
 
315
357
  block.call(@playwright_page)
316
358
  end
@@ -0,0 +1,99 @@
1
+ module Capybara
2
+ module Playwright
3
+ # playwright-ruby-client provides 3 methods to launch/connect browser.
4
+ #
5
+ # Playwright.create do |playwright|
6
+ # playwright.chromium.launch do |browser|
7
+ #
8
+ # Playwright.connect_to_playwright_server do |playwright| ...
9
+ # playwright.chromium.launch do |browser|
10
+ #
11
+ # Playwright.connect_to_browser_server do |browser| ...
12
+ #
13
+ # This class provides start/stop methods for driver.
14
+ # This is responsible for
15
+ # - managing PlaywrightExecution
16
+ # - launching browser with given option if needed
17
+ class BrowserRunner
18
+ class PlaywrightConnectToPlaywrightServer
19
+ def initialize(endpoint_url, options)
20
+ @ws_endpoint = endpoint_url
21
+ @browser_type = options[:browser_type] || :chromium
22
+ unless %i(chromium firefox webkit).include?(@browser_type)
23
+ raise ArgumentError.new("Unknown browser_type: #{@browser_type}")
24
+ end
25
+ @browser_options = BrowserOptions.new(options)
26
+ end
27
+
28
+ def playwright_execution
29
+ @playwright_execution ||= ::Playwright.connect_to_playwright_server(@ws_endpoint)
30
+ end
31
+
32
+ def playwright_browser
33
+ browser_type = playwright_execution.playwright.send(@browser_type)
34
+ browser_options = @browser_options.value
35
+ browser_type.launch(**browser_options)
36
+ end
37
+ end
38
+
39
+ class PlaywrightConnectToBrowserServer
40
+ def initialize(endpoint_url)
41
+ @ws_endpoint = endpoint_url
42
+ end
43
+
44
+ def playwright_execution
45
+ @playwright_execution ||= ::Playwright.connect_to_browser_server(@ws_endpoint)
46
+ end
47
+
48
+ def playwright_browser
49
+ playwright_execution.browser
50
+ end
51
+ end
52
+
53
+ class PlaywrightCreate
54
+ def initialize(options)
55
+ @playwright_cli_executable_path = options[:playwright_cli_executable_path] || 'npx playwright'
56
+ @browser_type = options[:browser_type] || :chromium
57
+ unless %i(chromium firefox webkit).include?(@browser_type)
58
+ raise ArgumentError.new("Unknown browser_type: #{@browser_type}")
59
+ end
60
+ @browser_options = BrowserOptions.new(options)
61
+ end
62
+
63
+ def playwright_execution
64
+ @playwright_execution ||= ::Playwright.create(
65
+ playwright_cli_executable_path: @playwright_cli_executable_path,
66
+ )
67
+ end
68
+
69
+ def playwright_browser
70
+ browser_type = playwright_execution.playwright.send(@browser_type)
71
+ browser_options = @browser_options.value
72
+ browser_type.launch(**browser_options)
73
+ end
74
+ end
75
+
76
+ def initialize(options)
77
+ @runner =
78
+ if options[:playwright_server_endpoint_url]
79
+ PlaywrightConnectToPlaywrightServer.new(options[:playwright_server_endpoint_url], options)
80
+ elsif options[:browser_server_endpoint_url]
81
+ PlaywrightConnectToBrowserServer.new(options[:browser_server_endpoint_url])
82
+ else
83
+ PlaywrightCreate.new(options)
84
+ end
85
+ end
86
+
87
+ # @return [::Playwright::Browser]
88
+ def start
89
+ @playwright_execution = @runner.playwright_execution
90
+ @runner.playwright_browser
91
+ end
92
+
93
+ def stop
94
+ @playwright_execution&.stop
95
+ @playwright_execution = nil
96
+ end
97
+ end
98
+ end
99
+ end
@@ -1,27 +1,34 @@
1
+ require_relative './driver_extension'
2
+
1
3
  module Capybara
2
4
  module Playwright
3
5
  class Driver < ::Capybara::Driver::Base
4
6
  extend Forwardable
7
+ include DriverExtension
5
8
 
6
9
  def initialize(app, **options)
7
- @playwright_cli_executable_path = options[:playwright_cli_executable_path] || 'npx playwright'
8
- @browser_type = options[:browser_type] || :chromium
9
- unless %i(chromium firefox webkit).include?(@browser_type)
10
- raise ArgumentError.new("Unknown browser_type: #{@browser_type}")
11
- end
12
-
13
- @browser_options = BrowserOptions.new(options)
10
+ @browser_runner = BrowserRunner.new(options)
14
11
  @page_options = PageOptions.new(options)
15
12
  end
16
13
 
17
14
  def wait?; true; end
18
15
  def needs_server?; true; end
19
16
 
20
- def browser
21
- @browser ||= create_browser
17
+ private def browser
18
+ @browser ||= ::Capybara::Playwright::Browser.new(
19
+ driver: self,
20
+ playwright_browser: playwright_browser,
21
+ page_options: @page_options.value,
22
+ record_video: callback_on_save_screenrecord?,
23
+ )
24
+ end
25
+
26
+ private def playwright_browser
27
+ @playwright_browser ||= create_playwright_browser
22
28
  end
23
29
 
24
- private def create_browser
30
+ private def create_playwright_browser
31
+ # clean up @playwright_browser and @playwright_execution on exit.
25
32
  main = Process.pid
26
33
  at_exit do
27
34
  # Store the exit status of the test run since it goes away after calling the at_exit proc...
@@ -30,27 +37,36 @@ module Capybara
30
37
  exit @exit_status if @exit_status # Force exit with stored status
31
38
  end
32
39
 
33
- @execution = execute_playwright
34
- ::Capybara::Playwright::Browser.new(
35
- playwright: @execution.playwright,
36
- driver: self,
37
- browser_type: @browser_type,
38
- browser_options: @browser_options.value,
39
- page_options: @page_options.value,
40
- )
41
- end
42
-
43
- private def execute_playwright
44
- ::Playwright.create(playwright_cli_executable_path: @playwright_cli_executable_path)
40
+ @browser_runner.start
45
41
  end
46
42
 
47
43
  private def quit
48
- @browser&.quit
49
- @execution&.stop
44
+ @playwright_browser&.close
45
+ @playwright_browser = nil
46
+ @browser_runner.stop
50
47
  end
51
48
 
52
49
  def reset!
53
- quit
50
+ # screenshot is available only before closing page.
51
+ if callback_on_save_screenshot?
52
+ raw_screenshot = @browser&.raw_screenshot
53
+ if raw_screenshot
54
+ callback_on_save_screenshot(raw_screenshot)
55
+ end
56
+ end
57
+
58
+ # video path can be aquired only before closing context.
59
+ # video is completedly saved only after closing context.
60
+ video_path = @browser&.video_path
61
+
62
+ # [NOTE] @playwright_browser should keep alive for better performance.
63
+ # Only `Browser` is disposed.
64
+ @browser&.clear_browser_contexts
65
+
66
+ if video_path
67
+ callback_on_save_screenrecord(video_path)
68
+ end
69
+
54
70
  @browser = nil
55
71
  end
56
72
 
@@ -94,9 +110,6 @@ module Capybara
94
110
  def_delegator(:browser, :switch_to_window)
95
111
  def_delegator(:browser, :accept_modal)
96
112
  def_delegator(:browser, :dismiss_modal)
97
-
98
- # capybara-playwright-driver specific methods
99
- def_delegator(:browser, :with_playwright_page)
100
113
  end
101
114
  end
102
115
  end
@@ -0,0 +1,45 @@
1
+ module Capybara
2
+ module Playwright
3
+ module DriverExtension
4
+ # Register screenshot save process.
5
+ # The callback is called just before page is closed.
6
+ # (just before #reset_session!)
7
+ #
8
+ # The **binary** (String) of the page screenshot is called back into the given block
9
+ def on_save_raw_screenshot_before_reset(&block)
10
+ @callback_on_save_screenshot = block
11
+ end
12
+
13
+ private def callback_on_save_screenshot?
14
+ !!@callback_on_save_screenshot
15
+ end
16
+
17
+ private def callback_on_save_screenshot(raw_screenshot)
18
+ @callback_on_save_screenshot&.call(raw_screenshot)
19
+ end
20
+
21
+ # Register screenrecord save process.
22
+ # The callback is called just after page is closed.
23
+ # (just after #reset_session!)
24
+ #
25
+ # The video path (String) is called back into the given block
26
+ def on_save_screenrecord(&block)
27
+ @callback_on_save_screenrecord = block
28
+ end
29
+
30
+ private def callback_on_save_screenrecord?
31
+ !!@callback_on_save_screenrecord
32
+ end
33
+
34
+ private def callback_on_save_screenrecord(video_path)
35
+ @callback_on_save_screenrecord&.call(video_path)
36
+ end
37
+
38
+ def with_playwright_page(&block)
39
+ raise ArgumentError.new('block must be given') unless block
40
+
41
+ @browser&.with_playwright_page(&block)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,40 @@
1
+ module Capybara
2
+ module Playwright
3
+ module TmpdirOwner
4
+ require 'tmpdir'
5
+
6
+ def tmpdir
7
+ return @tmpdir if @tmpdir
8
+
9
+ dir = Dir.mktmpdir
10
+ ObjectSpace.define_finalizer(self, TmpdirRemover.new(dir))
11
+ @tmpdir = dir
12
+ end
13
+
14
+ def remove_tmpdir
15
+ if @tmpdir
16
+ FileUtils.remove_entry(@tmpdir, true)
17
+ ObjectSpace.undefine_finalizer(self)
18
+ @tmpdir = nil
19
+ end
20
+ end
21
+
22
+ class TmpdirRemover
23
+ def initialize(tmpdir)
24
+ @pid = Process.pid
25
+ @tmpdir = tmpdir
26
+ end
27
+
28
+ def call(*args)
29
+ return if @pid != Process.pid
30
+
31
+ begin
32
+ FileUtils.remove_entry(@tmpdir, true)
33
+ rescue => err
34
+ $stderr.puts err
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capybara
4
4
  module Playwright
5
- VERSION = '0.1.2'
5
+ VERSION = '0.1.6'
6
6
  end
7
7
  end
@@ -4,6 +4,7 @@ require 'capybara'
4
4
  require 'playwright'
5
5
 
6
6
  require 'capybara/playwright/browser'
7
+ require 'capybara/playwright/browser_runner'
7
8
  require 'capybara/playwright/browser_options'
8
9
  require 'capybara/playwright/dialog_event_handler'
9
10
  require 'capybara/playwright/driver'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capybara-playwright-driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-02 00:00:00.000000000 Z
11
+ date: 2021-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -30,14 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.5.9
33
+ version: 0.8.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.5.9
40
+ version: 0.8.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: allure-rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -170,11 +184,14 @@ files:
170
184
  - lib/capybara/playwright.rb
171
185
  - lib/capybara/playwright/browser.rb
172
186
  - lib/capybara/playwright/browser_options.rb
187
+ - lib/capybara/playwright/browser_runner.rb
173
188
  - lib/capybara/playwright/dialog_event_handler.rb
174
189
  - lib/capybara/playwright/driver.rb
190
+ - lib/capybara/playwright/driver_extension.rb
175
191
  - lib/capybara/playwright/node.rb
176
192
  - lib/capybara/playwright/page.rb
177
193
  - lib/capybara/playwright/page_options.rb
194
+ - lib/capybara/playwright/tmpdir_owner.rb
178
195
  - lib/capybara/playwright/version.rb
179
196
  homepage: https://github.com/YusukeIwaki/capybara-playwright-driver
180
197
  licenses: