venetian 0.2.3-aarch64-linux → 0.3.1-aarch64-linux
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 +4 -4
- data/README.md +46 -6
- data/exe/playwright +1 -1
- data/lib/tasks/venetian.rake +1 -1
- data/lib/venetian/browser_installer.rb +121 -13
- data/lib/venetian/browser_runner_extensions.rb +13 -2
- data/lib/venetian/connection_extensions.rb +28 -0
- data/lib/venetian/core.rb +25 -0
- data/lib/venetian/executable.rb +156 -35
- data/lib/venetian/gemspec.rb +2 -0
- data/lib/venetian/playwright.rb +15 -0
- data/lib/venetian/playwright_create_extensions.rb +4 -1
- data/lib/venetian/system_test_case_extension.rb +23 -8
- data/lib/venetian/upstream.rb +2 -2
- data/lib/venetian/version.rb +1 -1
- data/lib/venetian.rb +4 -19
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 42659ef11b918624297a6f22b27b26bf83c5b7b036e6e3d11b1b08987239a682
|
|
4
|
+
data.tar.gz: 19348cc2e88614446c99c212f999ffa186ce7972564f5579763d11ad664f0577
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bfed6f8650a1af0ebd5609018e789360fa7de20edd5b48359650cfdcc77a8267552775aa8f79999e6cfcd0bb2172c7c98cca4dfe974e4c2b7828c1344b62e3f2
|
|
7
|
+
data.tar.gz: f88f99189c70cdf492117484b33f04e3c65433064ee043a788dcbc650ab9c77444db8d008b39f18fb023864c2d6dce7ac4dd8040beccf59acc9171332cda8514
|
data/README.md
CHANGED
|
@@ -38,9 +38,37 @@ Capybara.register_driver :playwright do |app|
|
|
|
38
38
|
end
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
### Using `playwright-ruby-client` without Capybara
|
|
42
|
+
|
|
43
|
+
Require the Capybara-free entry point instead, either in your Gemfile if you use `Bundler.require`:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
gem "venetian", require: "venetian/playwright"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or directly:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
require "venetian/playwright"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Then use `Venetian.start` in place of `Playwright.create`. It takes the same arguments, with the bundled executable as the default
|
|
56
|
+
`:playwright_cli_executable_path`. Browsers aren't installed automatically here, so install them first, either with
|
|
57
|
+
the [Rake task](#installing-browsers-manually) or `Venetian::BrowserInstaller.install`:
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
Venetian::BrowserInstaller.install(:chromium)
|
|
61
|
+
|
|
62
|
+
Venetian.start do |playwright|
|
|
63
|
+
playwright.chromium.launch do |browser|
|
|
64
|
+
# ...
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
41
69
|
### Automatic browser and dependency installation
|
|
42
70
|
|
|
43
|
-
|
|
71
|
+
With Capybara or Rails system tests, the gem will automatically install a browser and dependencies for you before attempting to use it.
|
|
44
72
|
Browsers are installed to `$XDG_CACHE_HOME/ms-playwright` on Linux, `~/Library/Caches/ms-playwright` on macOS,
|
|
45
73
|
and `%LOCALAPPDATA%\ms-playwright` on Windows. In CI, you will want to
|
|
46
74
|
[cache this directory](https://docs.github.com/en/actions/reference/workflows-and-actions/dependency-caching)
|
|
@@ -52,15 +80,27 @@ due to e.g. differences between package managers and package names across distro
|
|
|
52
80
|
so dependency installation happens only if a compatible package manager is found.
|
|
53
81
|
Depending on what you're doing, you may not need to install dependencies,
|
|
54
82
|
but if you do and your distro is unsupported, you will need to work out what you need and deal with that first.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
83
|
+
On Windows, Firefox also needs the [Microsoft Visual C++ Redistributable](https://learn.microsoft.com/cpp/windows/latest-supported-vc-redist),
|
|
84
|
+
which Playwright doesn't install; you'll get a warning after installation if it's missing.
|
|
85
|
+
|
|
86
|
+
Browsers already installed (e.g. from a restored cache) are left alone. If the browsers directory
|
|
87
|
+
(or `PLAYWRIGHT_BROWSERS_PATH`, if set) is read-only, as with a prepopulated cache, browsers aren't installed at all,
|
|
88
|
+
since Playwright can't take its lock there, though dependencies still are if needed.
|
|
89
|
+
When parallelizing tests, there's also the issue of multiple processes all trying to acquire the package manager lock,
|
|
90
|
+
so installations are serialized across processes. On top of this, if you use Rails system tests with `ActionDispatch::SystemTestCase`,
|
|
91
|
+
all required browsers are installed before disabling auto-installation and then forking.
|
|
92
|
+
|
|
93
|
+
Browsers aren't installed automatically if you pass your own `:playwright_cli_executable_path`, or connect to a server with
|
|
94
|
+
`:playwright_server_endpoint_url` or `:browser_server_endpoint_url`, since the bundled executable won't be the one running them.
|
|
95
|
+
Nor are they if you pass `:executablePath`, or a `:channel` for a system-wide browser like `chrome` or `msedge`
|
|
96
|
+
(install these with e.g. `playwright install chrome`), but other channels are installed instead of the browser type.
|
|
97
|
+
You can also turn installation off with `Venetian.auto_install_browsers = false`, or just dependency installation with
|
|
98
|
+
`Venetian.auto_install_dependencies = false`.
|
|
59
99
|
|
|
60
100
|
### Installing browsers manually
|
|
61
101
|
|
|
62
102
|
If you can't rely on automatic installation, you can use the included Rake task to install browsers.
|
|
63
|
-
With Rails this is included automatically when
|
|
103
|
+
With Rails this is included automatically when `venetian` is required (but not `venetian/playwright`).
|
|
64
104
|
|
|
65
105
|
```bash
|
|
66
106
|
$ RAILS_ENV=test rails venetian:install
|
data/exe/playwright
CHANGED
data/lib/tasks/venetian.rake
CHANGED
|
@@ -5,7 +5,7 @@ namespace :venetian do
|
|
|
5
5
|
Install Playwright browsers. Pass a name to install a specific browser (e.g. venetian:install[chromium]), else installs default browsers.
|
|
6
6
|
USAGE
|
|
7
7
|
task :install, [:browser] do |_task, args|
|
|
8
|
-
require "venetian"
|
|
8
|
+
require "venetian/core"
|
|
9
9
|
Venetian::BrowserInstaller.install(args[:browser])
|
|
10
10
|
end
|
|
11
11
|
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
|
|
3
6
|
module Venetian
|
|
4
7
|
# # Browser Installer
|
|
5
8
|
#
|
|
@@ -8,12 +11,75 @@ module Venetian
|
|
|
8
11
|
class << self
|
|
9
12
|
private
|
|
10
13
|
|
|
11
|
-
def install_dependencies?
|
|
12
|
-
|
|
14
|
+
def install_dependencies?(browser = nil, force: nil)
|
|
15
|
+
return force unless force.nil?
|
|
16
|
+
|
|
17
|
+
Venetian.auto_install_dependencies && dependencies_to_install?(browser)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def install_dry_run(browser = nil)
|
|
21
|
+
Executable.capture("install-deps", *browser&.to_s, "--dry-run", echo: debug?)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def install_into_browsers_path(browser, deps: nil)
|
|
25
|
+
if browsers_to_install.fetch(browser) { browsers_path_writable? }
|
|
26
|
+
run_installer "install", *browser, *("--with-deps" if deps)
|
|
27
|
+
elsif deps
|
|
28
|
+
run_installer "install-deps", *browser
|
|
29
|
+
end
|
|
30
|
+
browsers_to_install[browser] = false
|
|
31
|
+
dependencies_to_install[browser] = false if deps
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def browsers_path_writable?
|
|
35
|
+
[expanded_browsers_path, expanded_browsers_path.join(LINKS_DIRECTORY)].all? do |path|
|
|
36
|
+
Dir.mktmpdir(WRITE_PROBE_PREFIX, path.ascend.find(&:directory?)) { true }
|
|
37
|
+
end
|
|
38
|
+
rescue SystemCallError
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def default_cache_path
|
|
43
|
+
if Gem.win_platform?
|
|
44
|
+
env_path("LOCALAPPDATA") || Pathname(Dir.home).join("AppData", "Local")
|
|
45
|
+
elsif Gem::Platform.local.os == "darwin"
|
|
46
|
+
Pathname(Dir.home).join("Library", "Caches")
|
|
47
|
+
else
|
|
48
|
+
env_path("XDG_CACHE_HOME") || Pathname(Dir.home).join(".cache")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def env_path(name)
|
|
53
|
+
return if (path = ENV.fetch(name, "")).empty?
|
|
54
|
+
|
|
55
|
+
Pathname(path)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def dependencies_to_install
|
|
59
|
+
@dependencies_to_install ||= {}
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def browsers_to_install
|
|
63
|
+
@browsers_to_install ||= {}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def with_install_lock
|
|
67
|
+
File.open(File.join(Dir.tmpdir, "venetian-#{Process.uid}.lock"), File::RDWR | File::CREAT, 0o600) do |file|
|
|
68
|
+
file.flock(File::LOCK_EX)
|
|
69
|
+
yield
|
|
70
|
+
end
|
|
13
71
|
end
|
|
14
72
|
|
|
15
|
-
def
|
|
16
|
-
Venetian.system
|
|
73
|
+
def run_installer(*args)
|
|
74
|
+
return Venetian.system(*args, exception: true, echo: true) if debug?
|
|
75
|
+
|
|
76
|
+
Executable.capture(*args, echo: false, merge_stderr: false).then do |output, status|
|
|
77
|
+
raise InstallError, "exit status #{status.exitstatus}\n#{output}".strip unless status.success?
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def debug?
|
|
82
|
+
ENV.fetch("VENETIAN_DEBUG", nil)
|
|
17
83
|
end
|
|
18
84
|
end
|
|
19
85
|
|
|
@@ -26,21 +92,63 @@ module Venetian
|
|
|
26
92
|
def initialize(message = nil)
|
|
27
93
|
super([INSTALL_FAILED_MESSAGE, *message].join(": "))
|
|
28
94
|
end
|
|
95
|
+
|
|
96
|
+
def self.wrap(error) # :nodoc:
|
|
97
|
+
error.is_a?(self) ? error : new(error.message)
|
|
98
|
+
end
|
|
29
99
|
end
|
|
30
100
|
|
|
31
|
-
# Installs a browser.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
101
|
+
# Installs a browser, unless it and any dependencies are already installed. Playwright skips browsers already in the
|
|
102
|
+
# browsers path, and if that's read-only (e.g. a prepopulated cache), only dependencies are installed. Raises
|
|
103
|
+
# InstallError if installation fails.
|
|
104
|
+
def self.install(browser = nil, install_dependencies: nil)
|
|
105
|
+
with_install_lock do
|
|
106
|
+
install_into_browsers_path(browser&.to_s, deps: install_dependencies?(browser, force: install_dependencies))
|
|
107
|
+
end
|
|
35
108
|
rescue StandardError => e
|
|
36
|
-
raise InstallError
|
|
109
|
+
raise InstallError.wrap(e)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
LINKS_DIRECTORY = ".links" # :nodoc:
|
|
113
|
+
WRITE_PROBE_PREFIX = ".venetian-write-probe" # :nodoc:
|
|
114
|
+
|
|
115
|
+
# Output from a dry run where Playwright couldn't spawn its package manager, so has no way to install dependencies.
|
|
116
|
+
NO_PACKAGE_MANAGER_OUTPUT = "ENOENT" # :nodoc:
|
|
117
|
+
|
|
118
|
+
# Returns true if Playwright's dependency installer has something to install on the current OS for a browser,
|
|
119
|
+
# or for the default browsers if none given.
|
|
120
|
+
#
|
|
121
|
+
# On Linux, the dry run simulates the install with the package manager, exiting non-zero if packages are missing or
|
|
122
|
+
# if it can't simulate (e.g. no package lists yet, which the installer fixes by updating them first), unless there's
|
|
123
|
+
# no package manager it can use. On Windows, the dry run only prints the command it would run, so there is always
|
|
124
|
+
# something to install.
|
|
125
|
+
def self.dependencies_to_install?(browser = nil, force: false)
|
|
126
|
+
return true if Gem.win_platform?
|
|
127
|
+
|
|
128
|
+
dependencies_to_install.delete(browser&.to_s) if force
|
|
129
|
+
dependencies_to_install.fetch(browser&.to_s) do |key|
|
|
130
|
+
dependencies_to_install[key] = install_dry_run(browser).then do |output, status|
|
|
131
|
+
!status.success? && !output.include?(NO_PACKAGE_MANAGER_OUTPUT)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
37
134
|
end
|
|
38
135
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
136
|
+
def self.browsers_path # :nodoc:
|
|
137
|
+
case ENV.fetch("PLAYWRIGHT_BROWSERS_PATH", "")
|
|
138
|
+
when "" then default_cache_path.join("ms-playwright")
|
|
139
|
+
when "0" then Pathname(Executable.base_command.last).dirname.join(".local-browsers")
|
|
140
|
+
else Pathname(ENV.fetch("PLAYWRIGHT_BROWSERS_PATH"))
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def self.expanded_browsers_path # :nodoc:
|
|
145
|
+
browsers_path.expand_path(ENV.fetch("INIT_CWD", nil) || Dir.pwd)
|
|
146
|
+
end
|
|
42
147
|
|
|
43
|
-
|
|
148
|
+
def self.dependencies_supported?(...) # :nodoc:
|
|
149
|
+
warn "Venetian::BrowserInstaller.dependencies_supported? is deprecated; use dependencies_to_install? instead.",
|
|
150
|
+
category: :deprecated, uplevel: 1
|
|
151
|
+
dependencies_to_install?(...)
|
|
44
152
|
end
|
|
45
153
|
end
|
|
46
154
|
end
|
|
@@ -2,13 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
module Venetian
|
|
4
4
|
module BrowserRunnerExtensions # :nodoc:
|
|
5
|
+
ALTERNATIVE_DRIVER_OPTIONS = %i[playwright_cli_executable_path playwright_server_endpoint_url
|
|
6
|
+
browser_server_endpoint_url].freeze
|
|
7
|
+
SYSTEM_CHANNEL_PATTERN = /\A(chrome|msedge)/
|
|
8
|
+
|
|
9
|
+
def self.browser_to_preinstall_from(options)
|
|
10
|
+
return if options.values_at(*ALTERNATIVE_DRIVER_OPTIONS, :executablePath).any?
|
|
11
|
+
return if options[:channel].to_s.match?(SYSTEM_CHANNEL_PATTERN)
|
|
12
|
+
|
|
13
|
+
(options[:channel] || options[:browser_type] || :chromium).to_sym
|
|
14
|
+
end
|
|
15
|
+
|
|
5
16
|
def initialize(options = {}, *args)
|
|
6
|
-
@
|
|
17
|
+
@venetian_browser = BrowserRunnerExtensions.browser_to_preinstall_from(options)
|
|
7
18
|
super
|
|
8
19
|
end
|
|
9
20
|
|
|
10
21
|
def start
|
|
11
|
-
BrowserInstaller.install(@
|
|
22
|
+
BrowserInstaller.install(@venetian_browser) if @venetian_browser && Venetian.auto_install_browsers
|
|
12
23
|
super
|
|
13
24
|
end
|
|
14
25
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Venetian
|
|
4
|
+
# # Forked Connection Error
|
|
5
|
+
#
|
|
6
|
+
# Raised when a Playwright connection is used by a process forked from the one that started it.
|
|
7
|
+
class ForkedConnectionError < Error
|
|
8
|
+
def initialize(pid) # :nodoc:
|
|
9
|
+
super("This Playwright connection belongs to process #{pid}, so can't be used after forking. " \
|
|
10
|
+
"Start Playwright again in this process instead.")
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module ConnectionExtensions # :nodoc:
|
|
15
|
+
def initialize(...)
|
|
16
|
+
@venetian_pid = Process.pid
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def async_send_message_to_server(...)
|
|
21
|
+
raise ForkedConnectionError, @venetian_pid unless Process.pid == @venetian_pid
|
|
22
|
+
|
|
23
|
+
super
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Playwright::Connection.prepend(Venetian::ConnectionExtensions)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "venetian/version"
|
|
4
|
+
require "venetian/upstream"
|
|
5
|
+
require "venetian/executable"
|
|
6
|
+
require "venetian/browser_installer"
|
|
7
|
+
require "venetian/gemspec"
|
|
8
|
+
|
|
9
|
+
module Venetian # :nodoc:
|
|
10
|
+
class << self
|
|
11
|
+
attr_accessor :auto_install_dependencies
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Error < StandardError; end
|
|
15
|
+
|
|
16
|
+
self.auto_install_dependencies = true
|
|
17
|
+
|
|
18
|
+
def self.execute(*, echo: true, **)
|
|
19
|
+
Executable.execute(*, echo: echo, **)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.system(*, echo: true, **)
|
|
23
|
+
Executable.system(*, echo: echo, **)
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/venetian/executable.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "open3"
|
|
4
|
+
require "shellwords"
|
|
5
|
+
|
|
3
6
|
module Venetian
|
|
4
7
|
# # \Executable
|
|
5
8
|
#
|
|
@@ -7,6 +10,14 @@ module Venetian
|
|
|
7
10
|
module Executable
|
|
8
11
|
class Executor # :nodoc:
|
|
9
12
|
public :system, :exec
|
|
13
|
+
|
|
14
|
+
def capture(*command, merge_stderr: true, **)
|
|
15
|
+
merge_stderr ? Open3.capture2e(*command, **) : Open3.capture2(*command, **)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run(*command, **)
|
|
19
|
+
Process.wait2(Process.spawn(*command, **)).last
|
|
20
|
+
end
|
|
10
21
|
end
|
|
11
22
|
|
|
12
23
|
DEFAULT_DIR = File.expand_path(File.join(__dir__, "..", "..", "exe")) # :nodoc:
|
|
@@ -44,11 +55,14 @@ module Venetian
|
|
|
44
55
|
#
|
|
45
56
|
# Raised when the Playwright executable cannot be found for the current platform.
|
|
46
57
|
class ExecutableNotFoundError < StandardError
|
|
47
|
-
def initialize(reason,
|
|
58
|
+
def initialize(reason, exe_dir: nil, exe_path: nil, cli_path: nil, platform: nil) # :nodoc:
|
|
48
59
|
super(case reason
|
|
49
|
-
in :directory_missing then directory_missing_message(
|
|
50
|
-
in :executable_missing then executable_missing_message(
|
|
51
|
-
|
|
60
|
+
in :directory_missing then directory_missing_message(exe_dir)
|
|
61
|
+
in :executable_missing then executable_missing_message(exe_dir)
|
|
62
|
+
in :missing_platform then unsupported_platform_message(platform, exe_dir)
|
|
63
|
+
in :not_a_file then incomplete_message(exe_path, "#{exe_path} is not a file")
|
|
64
|
+
in :not_executable then incomplete_message(exe_path, "#{exe_path} is not executable")
|
|
65
|
+
in :cli_missing then incomplete_message(exe_path, "#{cli_path} is missing")
|
|
52
66
|
end)
|
|
53
67
|
end
|
|
54
68
|
|
|
@@ -62,6 +76,14 @@ module Venetian
|
|
|
62
76
|
"#{INSTALL_DIR_ENV_VAR} is set to #{exe_dir} but playwright was not found there."
|
|
63
77
|
end
|
|
64
78
|
|
|
79
|
+
def incomplete_message(exe_path, problem)
|
|
80
|
+
<<~MSG
|
|
81
|
+
The Playwright driver at #{File.dirname(exe_path)} is incomplete: #{problem}.
|
|
82
|
+
|
|
83
|
+
Reinstall the gem, or check #{INSTALL_DIR_ENV_VAR} points to a complete driver.
|
|
84
|
+
MSG
|
|
85
|
+
end
|
|
86
|
+
|
|
65
87
|
def unsupported_platform_message(platform, exe_dir)
|
|
66
88
|
<<~MSG
|
|
67
89
|
Cannot find the Playwright executable for #{platform} in #{exe_dir}.
|
|
@@ -75,28 +97,103 @@ module Venetian
|
|
|
75
97
|
end
|
|
76
98
|
end
|
|
77
99
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
100
|
+
# # Unsupported Path Error
|
|
101
|
+
#
|
|
102
|
+
# Raised on Windows when the path to the Playwright executable contains an environment variable reference such as
|
|
103
|
+
# +%USERNAME%+, as Ruby would then run the command with +cmd.exe+, which expands it.
|
|
104
|
+
class UnsupportedPathError < StandardError
|
|
105
|
+
def initialize(path) # :nodoc:
|
|
106
|
+
super(<<~MSG)
|
|
107
|
+
Cannot run Playwright from #{path} as Windows would expand the environment variable references in its path.
|
|
108
|
+
|
|
109
|
+
Set #{INSTALL_DIR_ENV_VAR} to a directory without them (e.g. %NAME%) containing your Playwright executable.
|
|
110
|
+
MSG
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
module Command # :nodoc:
|
|
115
|
+
TOKEN_PATTERN = /\\.?|%[A-Za-z_][A-Za-z0-9_]*.?|./m
|
|
116
|
+
VARIABLE_REFERENCE_PATTERN = /\A%[A-Za-z_][A-Za-z0-9_]*%\z/
|
|
117
|
+
|
|
118
|
+
def self.shelljoin(command)
|
|
119
|
+
return command.shelljoin unless Gem.win_platform?
|
|
120
|
+
|
|
121
|
+
command.collect { |arg| %("#{arg}") }
|
|
122
|
+
.join(" ")
|
|
123
|
+
.tap { |string| raise UnsupportedPathError, command.first if run_by_cmd?(string) }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def self.run_by_cmd?(string)
|
|
127
|
+
string.scan(TOKEN_PATTERN).any? { |token| token.match?(VARIABLE_REFERENCE_PATTERN) }
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
module Validations # :nodoc:
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
def validate!
|
|
81
135
|
ensure_exe_dir_exists!
|
|
82
136
|
ensure_gem_platform_supported! unless ENV.key?(INSTALL_DIR_ENV_VAR)
|
|
83
137
|
ensure_executable_exists!
|
|
138
|
+
ensure_exe_file!
|
|
139
|
+
ensure_exe_executable!
|
|
140
|
+
ensure_cli_path_exists!
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def ensure_exe_dir_exists!
|
|
144
|
+
raise ExecutableNotFoundError.new(:directory_missing, exe_dir: exe_dir) unless exe_dir_exists?
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def ensure_gem_platform_supported!
|
|
148
|
+
raise UnsupportedPlatformError, platform if gem_platforms_unsupported?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def ensure_executable_exists!
|
|
152
|
+
return unless exe_path.nil?
|
|
153
|
+
|
|
154
|
+
raise ExecutableNotFoundError.new ENV.key?(INSTALL_DIR_ENV_VAR) ? :executable_missing : :missing_platform,
|
|
155
|
+
exe_dir: exe_dir, platform: platform
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def ensure_exe_file!
|
|
159
|
+
return if exe_file?
|
|
160
|
+
|
|
161
|
+
raise ExecutableNotFoundError.new(:not_a_file, exe_path: exe_path)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def ensure_exe_executable!
|
|
165
|
+
return if exe_executable?
|
|
166
|
+
|
|
167
|
+
raise ExecutableNotFoundError.new(:not_executable, exe_path: exe_path)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def ensure_cli_path_exists!
|
|
171
|
+
return if cli_path_exists?
|
|
172
|
+
|
|
173
|
+
raise ExecutableNotFoundError.new(:cli_missing, exe_path: exe_path, cli_path: cli_path_for(exe_path))
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
extend Validations
|
|
178
|
+
|
|
179
|
+
class << self
|
|
180
|
+
# Returns the path to the Node executable. Raises an error if the executable cannot be found.
|
|
181
|
+
def path
|
|
182
|
+
validate!
|
|
84
183
|
|
|
85
184
|
exe_path
|
|
86
185
|
end
|
|
87
186
|
|
|
88
|
-
# Executes the Playwright executable with the given arguments.
|
|
187
|
+
# Executes the Playwright executable with the given arguments, replacing the current process.
|
|
89
188
|
def execute(*args, echo: true, exception: true, **)
|
|
90
189
|
[*base_command, *args].then do |command|
|
|
91
|
-
# due to mysterious Windows behavior; see equivalent in `tailwindcss-ruby`
|
|
92
|
-
next system(*args, exception:, echo:) if Gem.win_platform?
|
|
93
|
-
|
|
94
190
|
puts command.shelljoin if echo
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
191
|
+
# exec misbehaves on Windows (see equivalent in `tailwindcss-ruby`), so run Playwright and exit likewise
|
|
192
|
+
exit executor.run(*command, **).exitstatus if Gem.win_platform?
|
|
193
|
+
|
|
194
|
+
executor.exec(*command, **)
|
|
195
|
+
rescue StandardError
|
|
196
|
+
exception ? raise : false
|
|
100
197
|
end
|
|
101
198
|
end
|
|
102
199
|
|
|
@@ -104,47 +201,61 @@ module Venetian
|
|
|
104
201
|
def system(*args, echo: true, exception: true, **)
|
|
105
202
|
[*base_command, *args].then do |command|
|
|
106
203
|
puts command.shelljoin if echo
|
|
107
|
-
executor.system(*command,
|
|
108
|
-
|
|
204
|
+
executor.system(*command, exception:, **(echo ? {} : { out: File::NULL, err: File::NULL }), **)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Runs the Playwright executable with the given arguments, returning its combined output and status.
|
|
209
|
+
def capture(*args, echo: true, **)
|
|
210
|
+
[*base_command, *args].then do |command|
|
|
211
|
+
puts command.shelljoin if echo
|
|
212
|
+
executor.capture(*command, **).tap do |output, _|
|
|
213
|
+
puts output if echo
|
|
214
|
+
end
|
|
109
215
|
end
|
|
110
216
|
end
|
|
111
217
|
|
|
112
218
|
# Returns the base command to execute Playwright.
|
|
113
219
|
def base_command
|
|
114
|
-
[path,
|
|
220
|
+
path.then { |path| [path, cli_path_for(path)] }
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Returns the base command as a single string, as expected by +:playwright_cli_executable_path+.
|
|
224
|
+
def cli_command
|
|
225
|
+
Command.shelljoin(base_command)
|
|
115
226
|
end
|
|
116
227
|
|
|
117
228
|
private
|
|
118
229
|
|
|
119
|
-
def
|
|
120
|
-
|
|
230
|
+
def exe_dir_exists?
|
|
231
|
+
File.directory?(exe_dir)
|
|
121
232
|
end
|
|
122
233
|
|
|
123
|
-
def
|
|
124
|
-
|
|
234
|
+
def exe_dir
|
|
235
|
+
ENV[INSTALL_DIR_ENV_VAR] || DEFAULT_DIR
|
|
125
236
|
end
|
|
126
237
|
|
|
127
|
-
def
|
|
128
|
-
|
|
238
|
+
def exe_file?
|
|
239
|
+
File.file?(exe_path.to_s)
|
|
240
|
+
end
|
|
129
241
|
|
|
130
|
-
|
|
131
|
-
|
|
242
|
+
def exe_executable?
|
|
243
|
+
File.executable?(exe_path.to_s)
|
|
132
244
|
end
|
|
133
245
|
|
|
134
|
-
def
|
|
135
|
-
File.
|
|
246
|
+
def cli_path_exists?
|
|
247
|
+
File.file?(cli_path_for(exe_path))
|
|
136
248
|
end
|
|
137
249
|
|
|
138
|
-
def
|
|
139
|
-
|
|
250
|
+
def cli_path_for(exe_path)
|
|
251
|
+
File.join(File.dirname(exe_path), "package", "cli.js")
|
|
140
252
|
end
|
|
141
253
|
|
|
142
254
|
def exe_path
|
|
143
255
|
return custom_exe_path if ENV.key?(INSTALL_DIR_ENV_VAR)
|
|
144
256
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.detect { |candidate| File.exist?(candidate) }
|
|
257
|
+
native_platforms.collect { |platform, info| File.join(exe_dir, platform, info.executable_name) }
|
|
258
|
+
.detect { |candidate| File.exist?(candidate) }
|
|
148
259
|
end
|
|
149
260
|
|
|
150
261
|
def custom_exe_path
|
|
@@ -156,7 +267,17 @@ module Venetian
|
|
|
156
267
|
end
|
|
157
268
|
|
|
158
269
|
def gem_platforms_unsupported?
|
|
159
|
-
|
|
270
|
+
native_platforms.none?
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def native_platforms
|
|
274
|
+
return {} if musl? # musl isn't supported because official Linux Node binaries require glibc
|
|
275
|
+
|
|
276
|
+
Upstream::NATIVE_PLATFORMS.select { |platform, _| Gem::Platform.match_gem?(Gem::Platform.new(platform), gem_name) }
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def musl?
|
|
280
|
+
Gem::Platform.local.then { |local| local.os == "linux" && local.version.to_s.start_with?("musl") }
|
|
160
281
|
end
|
|
161
282
|
|
|
162
283
|
def gem_name
|
data/lib/venetian/gemspec.rb
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "playwright"
|
|
4
|
+
|
|
5
|
+
require "venetian/core"
|
|
6
|
+
require "venetian/connection_extensions"
|
|
7
|
+
|
|
8
|
+
module Venetian # :nodoc:
|
|
9
|
+
# Starts Playwright using the bundled executable. Accepts the same arguments as +Playwright.create+,
|
|
10
|
+
# so +:playwright_cli_executable_path+ can still be overridden.
|
|
11
|
+
def self.start(**options, &)
|
|
12
|
+
options[:playwright_cli_executable_path] ||= Executable.cli_command
|
|
13
|
+
Playwright.create(**options, &)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
module Venetian
|
|
4
4
|
module PlaywrightCreateExtensions # :nodoc:
|
|
5
5
|
def initialize(options = {}, *)
|
|
6
|
-
|
|
6
|
+
unless options[:playwright_cli_executable_path]
|
|
7
|
+
options = options.merge(playwright_cli_executable_path: Executable.cli_command)
|
|
8
|
+
end
|
|
9
|
+
super
|
|
7
10
|
end
|
|
8
11
|
end
|
|
9
12
|
end
|
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Venetian
|
|
4
|
-
module SystemTestCaseExtension # :nodoc:
|
|
4
|
+
module SystemTestCaseExtension # :nodoc: all
|
|
5
5
|
extend ActiveSupport::Concern
|
|
6
6
|
|
|
7
|
+
module ParallelizationExtension
|
|
8
|
+
def start
|
|
9
|
+
ActionDispatch::SystemTestCase.venetian_before_fork
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
7
14
|
prepended do
|
|
8
|
-
|
|
15
|
+
# Set by +driven_by+, so classes using a driver registered under another name are still included.
|
|
16
|
+
class_attribute :venetian_browser_type, default: nil, instance_writer: false
|
|
9
17
|
|
|
10
|
-
parallelize_before_fork
|
|
11
|
-
|
|
18
|
+
if respond_to?(:parallelize_before_fork)
|
|
19
|
+
parallelize_before_fork { venetian_before_fork }
|
|
20
|
+
else
|
|
21
|
+
ActiveSupport::Testing::Parallelization.prepend(ParallelizationExtension)
|
|
12
22
|
end
|
|
13
23
|
end
|
|
14
24
|
|
|
15
|
-
class_methods do
|
|
25
|
+
class_methods do
|
|
16
26
|
def driven_by(driver, options: {}, **)
|
|
27
|
+
self.venetian_browser_type = if driver == :playwright
|
|
28
|
+
BrowserRunnerExtensions.browser_to_preinstall_from(options)
|
|
29
|
+
end
|
|
17
30
|
super
|
|
18
|
-
|
|
19
|
-
self.venetian_browser_type = driver == :playwright ? options.fetch(:browser_type, :chromium).to_sym : nil
|
|
20
31
|
end
|
|
21
32
|
|
|
22
33
|
def install_playwright_browsers
|
|
@@ -26,6 +37,10 @@ module Venetian
|
|
|
26
37
|
end
|
|
27
38
|
end
|
|
28
39
|
|
|
40
|
+
def venetian_before_fork
|
|
41
|
+
install_playwright_browsers if venetian_preinstall_browsers_before_fork?
|
|
42
|
+
end
|
|
43
|
+
|
|
29
44
|
private
|
|
30
45
|
|
|
31
46
|
def venetian_preinstall_browsers_before_fork?
|
|
@@ -34,7 +49,7 @@ module Venetian
|
|
|
34
49
|
|
|
35
50
|
def venetian_browsers
|
|
36
51
|
Minitest::Runnable.runnables
|
|
37
|
-
.select { |klass| klass < ActionDispatch::SystemTestCase
|
|
52
|
+
.select { |klass| klass < ActionDispatch::SystemTestCase }
|
|
38
53
|
.filter_map(&:venetian_browser_type)
|
|
39
54
|
.uniq
|
|
40
55
|
end
|
data/lib/venetian/upstream.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "venetian/version"
|
|
4
4
|
|
|
5
5
|
module Venetian
|
|
6
6
|
# # \Upstream
|
|
@@ -43,7 +43,7 @@ module Venetian
|
|
|
43
43
|
|
|
44
44
|
# Returns the URL to download the playwright-core npm package containing the driver's JS sources.
|
|
45
45
|
def self.playwright_core_url
|
|
46
|
-
"#{NPM_REGISTRY_URL}/playwright-core/-/playwright-core-#{
|
|
46
|
+
"#{NPM_REGISTRY_URL}/playwright-core/-/playwright-core-#{COMPATIBLE_PLAYWRIGHT_VERSION}.tgz"
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
# Returns the gemspec files for the base gem.
|
data/lib/venetian/version.rb
CHANGED
data/lib/venetian.rb
CHANGED
|
@@ -2,33 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
require "capybara/playwright"
|
|
4
4
|
|
|
5
|
-
require "venetian/
|
|
6
|
-
require "venetian/upstream"
|
|
7
|
-
require "venetian/executable"
|
|
5
|
+
require "venetian/playwright"
|
|
8
6
|
require "venetian/playwright_create_extensions"
|
|
9
|
-
require "venetian/browser_installer"
|
|
10
7
|
require "venetian/browser_runner_extensions"
|
|
11
|
-
require "venetian/
|
|
12
|
-
require "venetian/railtie" if defined? Rails
|
|
8
|
+
require "venetian/railtie" if defined?(Rails::Railtie)
|
|
13
9
|
|
|
14
10
|
# # Venetian
|
|
15
11
|
#
|
|
16
|
-
# Native Playwright driver for
|
|
12
|
+
# Native Playwright driver for Ruby.
|
|
17
13
|
module Venetian
|
|
18
14
|
class << self
|
|
19
|
-
attr_accessor :auto_install_browsers
|
|
15
|
+
attr_accessor :auto_install_browsers
|
|
20
16
|
end
|
|
21
17
|
|
|
22
|
-
class Error < StandardError; end
|
|
23
|
-
|
|
24
18
|
self.auto_install_browsers = true
|
|
25
|
-
self.auto_install_dependencies = true
|
|
26
|
-
|
|
27
|
-
def self.execute(*, echo: true, **)
|
|
28
|
-
Executable.execute(*, echo: echo, **)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def self.system(*, echo: true, **)
|
|
32
|
-
Executable.system(*, echo: echo, **)
|
|
33
|
-
end
|
|
34
19
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: venetian
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: aarch64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Malčić
|
|
@@ -27,14 +27,14 @@ dependencies:
|
|
|
27
27
|
name: playwright-ruby-client
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: 1.63.0
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: 1.63.0
|
|
40
40
|
description: Bundles the Playwright driver binary in platform-specific gem variants
|
|
@@ -171,8 +171,11 @@ files:
|
|
|
171
171
|
- lib/venetian.rb
|
|
172
172
|
- lib/venetian/browser_installer.rb
|
|
173
173
|
- lib/venetian/browser_runner_extensions.rb
|
|
174
|
+
- lib/venetian/connection_extensions.rb
|
|
175
|
+
- lib/venetian/core.rb
|
|
174
176
|
- lib/venetian/executable.rb
|
|
175
177
|
- lib/venetian/gemspec.rb
|
|
178
|
+
- lib/venetian/playwright.rb
|
|
176
179
|
- lib/venetian/playwright_create_extensions.rb
|
|
177
180
|
- lib/venetian/railtie.rb
|
|
178
181
|
- lib/venetian/system_test_case_extension.rb
|