playwright-on-rails 0.7.1
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 +7 -0
- data/.github/workflows/main.yml +21 -0
- data/.gitignore +8 -0
- data/.standard.yml +5 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +115 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +155 -0
- data/LICENSE.txt +25 -0
- data/README.md +241 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/playwright-on-rails +17 -0
- data/lib/playwright-on-rails/config.rb +41 -0
- data/lib/playwright-on-rails/env.rb +14 -0
- data/lib/playwright-on-rails/finds_bin.rb +16 -0
- data/lib/playwright-on-rails/init.rb +93 -0
- data/lib/playwright-on-rails/initializer_hooks.rb +53 -0
- data/lib/playwright-on-rails/launches_playwright.rb +63 -0
- data/lib/playwright-on-rails/manages_transactions.rb +78 -0
- data/lib/playwright-on-rails/open.rb +14 -0
- data/lib/playwright-on-rails/railtie.rb +12 -0
- data/lib/playwright-on-rails/rake.rb +18 -0
- data/lib/playwright-on-rails/resets_state.rb +20 -0
- data/lib/playwright-on-rails/run.rb +14 -0
- data/lib/playwright-on-rails/server/checker.rb +42 -0
- data/lib/playwright-on-rails/server/middleware.rb +67 -0
- data/lib/playwright-on-rails/server/puma.rb +31 -0
- data/lib/playwright-on-rails/server/timer.rb +24 -0
- data/lib/playwright-on-rails/server.rb +121 -0
- data/lib/playwright-on-rails/starts_rails_server.rb +34 -0
- data/lib/playwright-on-rails/tracks_resets.rb +26 -0
- data/lib/playwright-on-rails/version.rb +3 -0
- data/lib/playwright-on-rails.rb +11 -0
- data/playwright_rails.gemspec +32 -0
- data/script/test +21 -0
- data/script/test_example_app +21 -0
- metadata +166 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative "env"
|
2
|
+
|
3
|
+
module PlaywrightOnRails
|
4
|
+
class Config
|
5
|
+
attr_accessor :rails_dir, :playwright_dir, :host, :port, :base_path, :transactional_server, :playwright_cli_opts
|
6
|
+
|
7
|
+
def initialize(
|
8
|
+
rails_dir: Env.fetch("PLAYWRIGHT_RAILS_DIR", default: Dir.pwd),
|
9
|
+
playwright_dir: Env.fetch("PLAYWRIGHT_RAILS_PLAYWRIGHT_DIR", default: rails_dir),
|
10
|
+
host: Env.fetch("PLAYWRIGHT_RAILS_HOST", default: "127.0.0.1"),
|
11
|
+
port: Env.fetch("PLAYWRIGHT_RAILS_PORT"),
|
12
|
+
base_path: Env.fetch("PLAYWRIGHT_RAILS_BASE_PATH", default: "/"),
|
13
|
+
transactional_server: Env.fetch("PLAYWRIGHT_RAILS_TRANSACTIONAL_SERVER", type: :boolean, default: true),
|
14
|
+
playwright_cli_opts: Env.fetch("PLAYWRIGHT_RAILS_PLAYWRIGHT_OPTS", default: "")
|
15
|
+
)
|
16
|
+
@rails_dir = rails_dir
|
17
|
+
@playwright_dir = playwright_dir
|
18
|
+
@host = host
|
19
|
+
@port = port
|
20
|
+
@base_path = base_path
|
21
|
+
@transactional_server = transactional_server
|
22
|
+
@playwright_cli_opts = playwright_cli_opts
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
<<~DESC
|
27
|
+
|
28
|
+
playwright-on-rails configuration:
|
29
|
+
============================
|
30
|
+
PLAYWRIGHT_RAILS_DIR.....................#{rails_dir.inspect}
|
31
|
+
PLAYWRIGHT_RAILS_PLAYWRIGHT_DIR.............#{playwright_dir.inspect}
|
32
|
+
PLAYWRIGHT_RAILS_HOST....................#{host.inspect}
|
33
|
+
PLAYWRIGHT_RAILS_PORT....................#{port.inspect}
|
34
|
+
PLAYWRIGHT_RAILS_BASE_PATH...............#{base_path.inspect}
|
35
|
+
PLAYWRIGHT_RAILS_TRANSACTIONAL_SERVER....#{transactional_server.inspect}
|
36
|
+
PLAYWRIGHT_RAILS_PLAYWRIGHT_OPTS............#{playwright_cli_opts.inspect}
|
37
|
+
|
38
|
+
DESC
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module PlaywrightOnRails
|
2
|
+
module Env
|
3
|
+
def self.fetch(name, type: :string, default: nil)
|
4
|
+
return default unless ENV.key?(name)
|
5
|
+
|
6
|
+
if type == :boolean
|
7
|
+
no_like_flag = ["", "0", "n", "no", "false"].include?(ENV.fetch(name))
|
8
|
+
!no_like_flag
|
9
|
+
else
|
10
|
+
ENV.fetch(name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module PlaywrightOnRails
|
4
|
+
class FindsBin
|
5
|
+
LOCAL_PATH = "node_modules/.bin/playwright"
|
6
|
+
|
7
|
+
def call(playwright_dir = Dir.pwd)
|
8
|
+
local_path = Pathname.new(playwright_dir).join(LOCAL_PATH)
|
9
|
+
if File.exist?(local_path)
|
10
|
+
local_path
|
11
|
+
else
|
12
|
+
"playwright"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module PlaywrightOnRails
|
2
|
+
class Init
|
3
|
+
DEFAULT_CONFIG = <<~JS
|
4
|
+
import { defineConfig, devices } from '@playwright/test';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Read environment variables from file.
|
8
|
+
* https://github.com/motdotla/dotenv
|
9
|
+
*/
|
10
|
+
// require('dotenv').config();
|
11
|
+
|
12
|
+
/**
|
13
|
+
* See https://playwright.dev/docs/test-configuration.
|
14
|
+
*/
|
15
|
+
export default defineConfig({
|
16
|
+
testDir: './tests',
|
17
|
+
/* Run tests in files in parallel */
|
18
|
+
fullyParallel: true,
|
19
|
+
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
20
|
+
forbidOnly: !!process.env.CI,
|
21
|
+
/* Retry on CI only */
|
22
|
+
retries: process.env.CI ? 2 : 0,
|
23
|
+
/* Opt out of parallel tests on CI. */
|
24
|
+
workers: process.env.CI ? 1 : undefined,
|
25
|
+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
26
|
+
reporter: 'html',
|
27
|
+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
28
|
+
use: {
|
29
|
+
/* Base URL to use in actions like `await page.goto('/')`. */
|
30
|
+
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://127.0.0.1:3000',
|
31
|
+
|
32
|
+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
33
|
+
trace: 'on-first-retry',
|
34
|
+
},
|
35
|
+
|
36
|
+
/* Configure projects for major browsers */
|
37
|
+
projects: [
|
38
|
+
{
|
39
|
+
name: 'chromium',
|
40
|
+
use: { ...devices['Desktop Chrome'] },
|
41
|
+
},
|
42
|
+
|
43
|
+
{
|
44
|
+
name: 'firefox',
|
45
|
+
use: { ...devices['Desktop Firefox'] },
|
46
|
+
},
|
47
|
+
|
48
|
+
{
|
49
|
+
name: 'webkit',
|
50
|
+
use: { ...devices['Desktop Safari'] },
|
51
|
+
},
|
52
|
+
|
53
|
+
/* Test against mobile viewports. */
|
54
|
+
// {
|
55
|
+
// name: 'Mobile Chrome',
|
56
|
+
// use: { ...devices['Pixel 5'] },
|
57
|
+
// },
|
58
|
+
// {
|
59
|
+
// name: 'Mobile Safari',
|
60
|
+
// use: { ...devices['iPhone 12'] },
|
61
|
+
// },
|
62
|
+
|
63
|
+
/* Test against branded browsers. */
|
64
|
+
// {
|
65
|
+
// name: 'Microsoft Edge',
|
66
|
+
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
67
|
+
// },
|
68
|
+
// {
|
69
|
+
// name: 'Google Chrome',
|
70
|
+
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
71
|
+
// },
|
72
|
+
],
|
73
|
+
|
74
|
+
/* Run your local dev server before starting the tests */
|
75
|
+
// webServer: {
|
76
|
+
// command: 'npm run start',
|
77
|
+
// url: 'http://127.0.0.1:3000',
|
78
|
+
// reuseExistingServer: !process.env.CI,
|
79
|
+
// },
|
80
|
+
});
|
81
|
+
JS
|
82
|
+
|
83
|
+
def call(playwright_dir = Config.new.playwright_dir)
|
84
|
+
config_path = File.join(playwright_dir, "playwright.config.js")
|
85
|
+
if !File.exist?(config_path)
|
86
|
+
File.write(config_path, DEFAULT_CONFIG)
|
87
|
+
puts "Playwright config initialized in `#{config_path}'"
|
88
|
+
else
|
89
|
+
warn "Playwright config already exists in `#{config_path}'. Skipping."
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module PlaywrightOnRails
|
2
|
+
def self.hooks
|
3
|
+
InitializerHooks.instance
|
4
|
+
end
|
5
|
+
|
6
|
+
class InitializerHooks
|
7
|
+
def self.instance
|
8
|
+
@instance ||= new
|
9
|
+
end
|
10
|
+
|
11
|
+
def before_server_start(&blk)
|
12
|
+
register(:before_server_start, blk)
|
13
|
+
end
|
14
|
+
|
15
|
+
def after_server_start(&blk)
|
16
|
+
register(:after_server_start, blk)
|
17
|
+
end
|
18
|
+
|
19
|
+
def after_transaction_start(&blk)
|
20
|
+
register(:after_transaction_start, blk)
|
21
|
+
end
|
22
|
+
|
23
|
+
def after_state_reset(&blk)
|
24
|
+
register(:after_state_reset, blk)
|
25
|
+
end
|
26
|
+
|
27
|
+
def before_server_stop(&blk)
|
28
|
+
register(:before_server_stop, blk)
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset!
|
32
|
+
@hooks = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def run(name)
|
36
|
+
return unless @hooks[name]
|
37
|
+
@hooks[name].each do |blk|
|
38
|
+
blk.call
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def register(name, blk)
|
45
|
+
@hooks[name] ||= []
|
46
|
+
@hooks[name] << blk
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
reset!
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative "finds_bin"
|
2
|
+
require_relative "config"
|
3
|
+
require_relative "initializer_hooks"
|
4
|
+
require_relative "manages_transactions"
|
5
|
+
require_relative "starts_rails_server"
|
6
|
+
|
7
|
+
module PlaywrightOnRails
|
8
|
+
class LaunchesPlaywright
|
9
|
+
def initialize
|
10
|
+
@initializer_hooks = InitializerHooks.instance
|
11
|
+
@manages_transactions = ManagesTransactions.instance
|
12
|
+
@starts_rails_server = StartsRailsServer.new
|
13
|
+
@finds_bin = FindsBin.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(command, config)
|
17
|
+
puts config
|
18
|
+
@initializer_hooks.run(:before_server_start)
|
19
|
+
if config.transactional_server
|
20
|
+
@manages_transactions.begin_transaction
|
21
|
+
end
|
22
|
+
server = @starts_rails_server.call(
|
23
|
+
host: config.host,
|
24
|
+
port: config.port,
|
25
|
+
transactional_server: config.transactional_server
|
26
|
+
)
|
27
|
+
bin = @finds_bin.call(config.playwright_dir)
|
28
|
+
|
29
|
+
set_exit_hooks!(config)
|
30
|
+
|
31
|
+
command = <<~EXEC
|
32
|
+
PLAYWRIGHT_BASE_URL="http://#{server.host}:#{server.port}#{config.base_path}" "#{bin}" #{command} --config "#{config.playwright_dir}/playwright.config.js" #{config.playwright_cli_opts}
|
33
|
+
EXEC
|
34
|
+
|
35
|
+
puts "\nLaunching Playwright…\n$ #{command}\n"
|
36
|
+
system command
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def set_exit_hooks!(config)
|
42
|
+
at_exit do
|
43
|
+
run_exit_hooks_if_necessary!(config)
|
44
|
+
end
|
45
|
+
Signal.trap("INT") do
|
46
|
+
puts "Exiting playwright-on-rails…"
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def run_exit_hooks_if_necessary!(config)
|
52
|
+
@at_exit_hooks_have_fired ||= false # avoid warning
|
53
|
+
return if @at_exit_hooks_have_fired
|
54
|
+
|
55
|
+
if config.transactional_server
|
56
|
+
@manages_transactions.rollback_transaction
|
57
|
+
end
|
58
|
+
@initializer_hooks.run(:before_server_stop)
|
59
|
+
|
60
|
+
@at_exit_hooks_have_fired = true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative "initializer_hooks"
|
2
|
+
|
3
|
+
module PlaywrightOnRails
|
4
|
+
class ManagesTransactions
|
5
|
+
def self.instance
|
6
|
+
@instance ||= new
|
7
|
+
end
|
8
|
+
|
9
|
+
def begin_transaction
|
10
|
+
@connections = gather_connections
|
11
|
+
@connections.each do |connection|
|
12
|
+
connection.begin_transaction joinable: false, _lazy: false
|
13
|
+
connection.pool.lock_thread = true
|
14
|
+
end
|
15
|
+
|
16
|
+
# When connections are established in the future, begin a transaction too
|
17
|
+
@connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") { |_, _, _, _, payload|
|
18
|
+
if payload.key?(:spec_name) && (spec_name = payload[:spec_name])
|
19
|
+
setup_shared_connection_pool
|
20
|
+
|
21
|
+
begin
|
22
|
+
connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name)
|
23
|
+
rescue ActiveRecord::ConnectionNotEstablished
|
24
|
+
connection = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
if connection && !@connections.include?(connection)
|
28
|
+
connection.begin_transaction joinable: false, _lazy: false
|
29
|
+
connection.pool.lock_thread = true
|
30
|
+
@connections << connection
|
31
|
+
end
|
32
|
+
end
|
33
|
+
}
|
34
|
+
|
35
|
+
@initializer_hooks.run(:after_transaction_start)
|
36
|
+
end
|
37
|
+
|
38
|
+
def rollback_transaction
|
39
|
+
return unless @connections.present?
|
40
|
+
|
41
|
+
ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
|
42
|
+
|
43
|
+
@connections.each do |connection|
|
44
|
+
connection.rollback_transaction if connection.transaction_open?
|
45
|
+
connection.pool.lock_thread = false
|
46
|
+
end
|
47
|
+
@connections.clear
|
48
|
+
|
49
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def initialize
|
55
|
+
@initializer_hooks = InitializerHooks.instance
|
56
|
+
end
|
57
|
+
|
58
|
+
def gather_connections
|
59
|
+
setup_shared_connection_pool
|
60
|
+
|
61
|
+
ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Shares the writing connection pool with connections on
|
65
|
+
# other handlers.
|
66
|
+
#
|
67
|
+
# In an application with a primary and replica the test fixtures
|
68
|
+
# need to share a connection pool so that the reading connection
|
69
|
+
# can see data in the open transaction on the writing connection.
|
70
|
+
def setup_shared_connection_pool
|
71
|
+
return unless ActiveRecord::TestFixtures.respond_to?(:setup_shared_connection_pool)
|
72
|
+
@legacy_saved_pool_configs ||= Hash.new { |hash, key| hash[key] = {} }
|
73
|
+
@saved_pool_configs ||= Hash.new { |hash, key| hash[key] = {} }
|
74
|
+
|
75
|
+
ActiveRecord::TestFixtures.instance_method(:setup_shared_connection_pool).bind(self).call
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative "launches_playwright"
|
2
|
+
require_relative "config"
|
3
|
+
|
4
|
+
module PlaywrightOnRails
|
5
|
+
class Open
|
6
|
+
def initialize
|
7
|
+
@launches_playwright = LaunchesPlaywright.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(config = Config.new)
|
11
|
+
@launches_playwright.call("test --ui", config)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "pathname"
|
2
|
+
CLI = Pathname.new(File.dirname(__FILE__)).join("../../exe/playwright-on-rails")
|
3
|
+
|
4
|
+
desc "Initialize playwright.config.js"
|
5
|
+
task :"playwright:init" do
|
6
|
+
system "#{CLI} init"
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Open interactive Playwright app for developing tests"
|
10
|
+
task :"playwright:open" do
|
11
|
+
trap("SIGINT") {} # avoid traceback
|
12
|
+
system "#{CLI} open"
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Run Playwright tests headlessly"
|
16
|
+
task :"playwright:run" do
|
17
|
+
abort unless system "#{CLI} run"
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative "config"
|
2
|
+
require_relative "manages_transactions"
|
3
|
+
require_relative "initializer_hooks"
|
4
|
+
|
5
|
+
module PlaywrightOnRails
|
6
|
+
class ResetsState
|
7
|
+
def initialize
|
8
|
+
@manages_transactions = ManagesTransactions.instance
|
9
|
+
@initializer_hooks = InitializerHooks.instance
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(transactional_server:)
|
13
|
+
if transactional_server
|
14
|
+
@manages_transactions.rollback_transaction
|
15
|
+
@manages_transactions.begin_transaction
|
16
|
+
end
|
17
|
+
@initializer_hooks.run(:after_state_reset)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative "launches_playwright"
|
2
|
+
require_relative "config"
|
3
|
+
|
4
|
+
module PlaywrightOnRails
|
5
|
+
class Run
|
6
|
+
def initialize
|
7
|
+
@launches_playwright = LaunchesPlaywright.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(config = Config.new)
|
11
|
+
@launches_playwright.call("test", config)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module PlaywrightOnRails
|
2
|
+
class Server
|
3
|
+
class Checker
|
4
|
+
TRY_HTTPS_ERRORS = [EOFError, Net::ReadTimeout, Errno::ECONNRESET].freeze
|
5
|
+
|
6
|
+
def initialize(host, port)
|
7
|
+
@host, @port = host, port
|
8
|
+
@ssl = false
|
9
|
+
end
|
10
|
+
|
11
|
+
def request(&block)
|
12
|
+
ssl? ? https_request(&block) : http_request(&block)
|
13
|
+
rescue *TRY_HTTPS_ERRORS
|
14
|
+
res = https_request(&block)
|
15
|
+
@ssl = true
|
16
|
+
res
|
17
|
+
end
|
18
|
+
|
19
|
+
def ssl?
|
20
|
+
@ssl
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def http_request(&block)
|
26
|
+
make_request(read_timeout: 2, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def https_request(&block)
|
30
|
+
make_request(**ssl_options, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def make_request(**options, &block)
|
34
|
+
Net::HTTP.start(@host, @port, options.merge(max_retries: 0), &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def ssl_options
|
38
|
+
{use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module PlaywrightOnRails
|
2
|
+
class Server
|
3
|
+
class Middleware
|
4
|
+
class Counter
|
5
|
+
def initialize
|
6
|
+
@value = []
|
7
|
+
@mutex = Mutex.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def increment(uri)
|
11
|
+
@mutex.synchronize { @value.push(uri) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def decrement(uri)
|
15
|
+
@mutex.synchronize { @value.delete_at(@value.index(uri) || @value.length) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def positive?
|
19
|
+
@mutex.synchronize { @value.length.positive? }
|
20
|
+
end
|
21
|
+
|
22
|
+
def value
|
23
|
+
@mutex.synchronize { @value.dup }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :error
|
28
|
+
|
29
|
+
def initialize(app, server_errors, extra_middleware = [])
|
30
|
+
@app = app
|
31
|
+
@extended_app = extra_middleware.inject(@app) { |ex_app, klass|
|
32
|
+
klass.new(ex_app)
|
33
|
+
}
|
34
|
+
@counter = Counter.new
|
35
|
+
@server_errors = server_errors
|
36
|
+
end
|
37
|
+
|
38
|
+
def pending_requests
|
39
|
+
@counter.value
|
40
|
+
end
|
41
|
+
|
42
|
+
def pending_requests?
|
43
|
+
@counter.positive?
|
44
|
+
end
|
45
|
+
|
46
|
+
def clear_error
|
47
|
+
@error = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def call(env)
|
51
|
+
if env["PATH_INFO"] == "/__identify__"
|
52
|
+
[200, {}, [@app.object_id.to_s]]
|
53
|
+
else
|
54
|
+
@counter.increment(env["REQUEST_URI"])
|
55
|
+
begin
|
56
|
+
@extended_app.call(env)
|
57
|
+
rescue *@server_errors => e
|
58
|
+
@error ||= e
|
59
|
+
raise e
|
60
|
+
ensure
|
61
|
+
@counter.decrement(env["REQUEST_URI"])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module PlaywrightOnRails
|
2
|
+
class Server
|
3
|
+
module Puma
|
4
|
+
def self.create(app, port, host)
|
5
|
+
require "rack/handler/puma"
|
6
|
+
|
7
|
+
# If we just run the Puma Rack handler it installs signal handlers which prevent us from being able to interrupt tests.
|
8
|
+
# Therefore construct and run the Server instance ourselves.
|
9
|
+
# Rack::Handler::Puma.run(app, { Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false }.merge(options))
|
10
|
+
default_options = {Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false}
|
11
|
+
options = default_options # .merge(options)
|
12
|
+
|
13
|
+
conf = Rack::Handler::Puma.config(app, options)
|
14
|
+
conf.clamp
|
15
|
+
logger = (defined?(::Puma::LogWriter) ? ::Puma::LogWriter : ::Puma::Events).stdio
|
16
|
+
|
17
|
+
puma_ver = Gem::Version.new(::Puma::Const::PUMA_VERSION)
|
18
|
+
require_relative "patches/puma_ssl" if (Gem::Version.new("4.0.0")...Gem::Version.new("4.1.0")).cover? puma_ver
|
19
|
+
|
20
|
+
logger.log "Starting Puma..."
|
21
|
+
logger.log "* Version #{::Puma::Const::PUMA_VERSION} , codename: #{::Puma::Const::CODE_NAME}"
|
22
|
+
logger.log "* Min threads: #{conf.options[:min_threads]}, max threads: #{conf.options[:max_threads]}"
|
23
|
+
|
24
|
+
::Puma::Server.new(conf.app, defined?(::Puma::LogWriter) ? nil : logger, conf.options).tap do |s|
|
25
|
+
s.binder.parse conf.options[:binds], s.respond_to?(:log_writer) ? s.log_writer : s.events
|
26
|
+
s.min_threads, s.max_threads = conf.options[:min_threads], conf.options[:max_threads] if s.respond_to?(:min_threads=)
|
27
|
+
end.run.join
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module PlaywrightOnRails
|
2
|
+
class Server
|
3
|
+
class Timer
|
4
|
+
def initialize(expire_in)
|
5
|
+
@start = current
|
6
|
+
@expire_in = expire_in
|
7
|
+
end
|
8
|
+
|
9
|
+
def expired?
|
10
|
+
current - @start >= @expire_in
|
11
|
+
end
|
12
|
+
|
13
|
+
def stalled?
|
14
|
+
@start == current
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def current
|
20
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|