bidi2pdf 0.1.13 → 0.1.14

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.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +53 -0
  3. data/.devcontainer/devcontainer.json +51 -0
  4. data/.devcontainer/docker-compose.yml +33 -0
  5. data/.ruby-gemset +1 -1
  6. data/.ruby-version +1 -1
  7. data/CHANGELOG.md +46 -6
  8. data/README.md +12 -0
  9. data/docker/Dockerfile +1 -1
  10. data/docker/Dockerfile.slim +2 -2
  11. data/docker/chrome-driver/chrome.json +874 -0
  12. data/lib/bidi2pdf/bidi/browser_tab.rb +52 -0
  13. data/lib/bidi2pdf/bidi/commands/capture_screenshot.rb +33 -0
  14. data/lib/bidi2pdf/bidi/commands/set_viewport.rb +32 -0
  15. data/lib/bidi2pdf/bidi/commands.rb +2 -0
  16. data/lib/bidi2pdf/cli.rb +11 -1
  17. data/lib/bidi2pdf/launcher.rb +5 -3
  18. data/lib/bidi2pdf/test_helpers/images.rb +8 -0
  19. data/lib/bidi2pdf/test_helpers/pdf_reader_utils.rb +1 -2
  20. data/lib/bidi2pdf/test_helpers/pdf_text_sanitizer.rb +1 -2
  21. data/lib/bidi2pdf/test_helpers/testcontainers/chromedriver_container.rb +1 -1
  22. data/lib/bidi2pdf/test_helpers/testcontainers/chromedriver_test_helper.rb +18 -0
  23. data/lib/bidi2pdf/test_helpers/testcontainers/container_endpoint.rb +30 -0
  24. data/lib/bidi2pdf/test_helpers/testcontainers/testcontainers_refinement.rb +33 -0
  25. data/lib/bidi2pdf/test_helpers/testcontainers.rb +1 -0
  26. data/lib/bidi2pdf/version.rb +1 -1
  27. data/lib/bidi2pdf.rb +2 -0
  28. data/sig/bidi2pdf/bidi/add_headers_interceptor.rbs +16 -9
  29. data/sig/bidi2pdf/bidi/auth_interceptor.rbs +32 -7
  30. data/sig/bidi2pdf/bidi/browser.rbs +4 -31
  31. data/sig/bidi2pdf/bidi/browser_console_logger.rbs +59 -0
  32. data/sig/bidi2pdf/bidi/browser_tab.rbs +252 -25
  33. data/sig/bidi2pdf/bidi/client.rbs +95 -43
  34. data/sig/bidi2pdf/bidi/command_manager.rbs +5 -17
  35. data/sig/bidi2pdf/bidi/commands/add_intercept.rbs +16 -8
  36. data/sig/bidi2pdf/bidi/commands/base.rbs +49 -9
  37. data/sig/bidi2pdf/bidi/commands/browser_close.rbs +3 -1
  38. data/sig/bidi2pdf/bidi/commands/browser_create_user_context.rbs +2 -0
  39. data/sig/bidi2pdf/bidi/commands/browser_remove_user_context.rbs +19 -0
  40. data/sig/bidi2pdf/bidi/commands/browsing_context_close.rbs +8 -2
  41. data/sig/bidi2pdf/bidi/commands/browsing_context_navigate.rbs +12 -6
  42. data/sig/bidi2pdf/bidi/commands/browsing_context_print.rbs +10 -5
  43. data/sig/bidi2pdf/bidi/commands/cancel_auth.rbs +8 -2
  44. data/sig/bidi2pdf/bidi/commands/capture_screenshot.rbs +31 -0
  45. data/sig/bidi2pdf/bidi/commands/cdp_get_session.rbs +17 -0
  46. data/sig/bidi2pdf/bidi/commands/create_window.rbs +4 -0
  47. data/sig/bidi2pdf/bidi/commands/get_user_contexts.rbs +2 -0
  48. data/sig/bidi2pdf/bidi/commands/network_continue.rbs +4 -0
  49. data/sig/bidi2pdf/bidi/commands/page_print.rbs +28 -0
  50. data/sig/bidi2pdf/bidi/commands/print_parameters_validator.rbs +3 -0
  51. data/sig/bidi2pdf/bidi/commands/provide_credentials.rbs +12 -6
  52. data/sig/bidi2pdf/bidi/commands/script_evaluate.rbs +4 -0
  53. data/sig/bidi2pdf/bidi/commands/session_end.rbs +2 -0
  54. data/sig/bidi2pdf/bidi/commands/session_status.rbs +3 -1
  55. data/sig/bidi2pdf/bidi/commands/session_subscribe.rbs +4 -0
  56. data/sig/bidi2pdf/bidi/commands/set_tab_cookie.rbs +51 -23
  57. data/sig/bidi2pdf/bidi/commands/set_usercontext_cookie.rbs +17 -19
  58. data/sig/bidi2pdf/bidi/commands/set_viewport.rbs +31 -0
  59. data/sig/bidi2pdf/bidi/connection_manager.rbs +2 -1
  60. data/sig/bidi2pdf/bidi/event_manager.rbs +14 -19
  61. data/sig/bidi2pdf/bidi/interceptor.rbs +5 -0
  62. data/sig/bidi2pdf/bidi/js_logger_helper.rbs +9 -0
  63. data/sig/bidi2pdf/bidi/logger_events.rbs +21 -0
  64. data/sig/bidi2pdf/bidi/navigation_failed_events.rbs +17 -0
  65. data/sig/bidi2pdf/bidi/network_event.rbs +54 -46
  66. data/sig/bidi2pdf/bidi/network_event_formatters/network_event_console_formatter.rbs +63 -0
  67. data/sig/bidi2pdf/bidi/network_event_formatters/network_event_formatter_utils.rbs +15 -0
  68. data/sig/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rbs +18 -0
  69. data/sig/bidi2pdf/bidi/network_event_formatters.rbs +6 -0
  70. data/sig/bidi2pdf/bidi/network_events.rbs +14 -39
  71. data/sig/bidi2pdf/bidi/session.rbs +144 -31
  72. data/sig/bidi2pdf/bidi/user_context.rbs +64 -45
  73. data/sig/bidi2pdf/bidi/web_socket_dispatcher.rbs +22 -30
  74. data/sig/bidi2pdf/chromedriver_manager.rbs +57 -22
  75. data/sig/bidi2pdf/cli.rbs +22 -2
  76. data/sig/bidi2pdf/dsl.rbs +34 -0
  77. data/sig/bidi2pdf/launcher.rbs +39 -1
  78. data/sig/bidi2pdf/notifications/event.rbs +40 -0
  79. data/sig/bidi2pdf/notifications/instrumenter.rbs +21 -0
  80. data/sig/bidi2pdf/notifications/logging_subscriber.rbs +41 -0
  81. data/sig/bidi2pdf/notifications.rbs +24 -0
  82. data/sig/bidi2pdf/process_tree.rbs +2 -2
  83. data/sig/bidi2pdf/session_runner.rbs +41 -1
  84. data/sig/bidi2pdf/test_helpers/configuration.rbs +51 -0
  85. data/sig/bidi2pdf/test_helpers/images/extractor.rbs +43 -0
  86. data/sig/bidi2pdf/test_helpers/images/image_similarity_checker.rbs +35 -0
  87. data/sig/bidi2pdf/test_helpers/images/tiff_helper.rbs +70 -0
  88. data/sig/bidi2pdf/test_helpers/images.rbs +0 -0
  89. data/sig/bidi2pdf/test_helpers/matchers/contains_pdf_image.rbs +0 -0
  90. data/sig/bidi2pdf/test_helpers/matchers/contains_pdf_text.rbs +0 -0
  91. data/sig/bidi2pdf/test_helpers/matchers/have_pdf_page_count.rbs +0 -0
  92. data/sig/bidi2pdf/test_helpers/matchers/match_pdf_text.rbs +0 -0
  93. data/sig/bidi2pdf/test_helpers/pdf_file_helper.rbs +23 -0
  94. data/sig/bidi2pdf/test_helpers/pdf_reader_utils.rbs +49 -0
  95. data/sig/bidi2pdf/test_helpers/pdf_text_sanitizer.rbs +38 -0
  96. data/sig/bidi2pdf/test_helpers/spec_paths_helper.rbs +35 -0
  97. data/sig/bidi2pdf/test_helpers/testcontainers/chromedriver_container.rbs +43 -0
  98. data/sig/bidi2pdf/test_helpers/testcontainers/chromedriver_test_helper.rbs +45 -0
  99. data/sig/bidi2pdf/test_helpers/testcontainers/container_endpoint.rbs +25 -0
  100. data/sig/bidi2pdf/test_helpers/testcontainers/shared_docker_network.rbs +3 -0
  101. data/sig/bidi2pdf/test_helpers/testcontainers/testcontainers_refinement.rbs +47 -0
  102. data/sig/bidi2pdf/test_helpers/testcontainers.rbs +6 -0
  103. data/sig/bidi2pdf/test_helpers.rbs +0 -0
  104. data/sig/bidi2pdf/verbose_logger.rbs +37 -0
  105. data/sig/bidi2pdf/version.rbs +3 -0
  106. data/sig/bidi2pdf.rbs +112 -2
  107. data/tasks/generate_rbs.rake +11 -11
  108. metadata +59 -7
  109. data/sig/bidi2pdf/utils.rbs +0 -5
@@ -444,6 +444,58 @@ module Bidi2pdf
444
444
 
445
445
  # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
446
446
 
447
+ # Captures a screenshot of the browser tab, mirroring #print's own
448
+ # file/block/base64-return trichotomy.
449
+ #
450
+ # @param [String, nil] outputfile The output file for the PNG. Defaults to nil.
451
+ # @param [String] origin "document" (default) or "viewport" - see browsingContext.captureScreenshot.
452
+ # @param [String, nil] format Image format override, e.g. "image/png". Defaults to the browser's own default.
453
+ # @param [Hash, nil] clip A clip rectangle/element, passed through verbatim. Defaults to nil (full capture).
454
+ # @yield [png_base64] A block to handle the PNG content.
455
+ # @return [String, nil] The base64-encoded PNG content, or nil if outputfile or block is provided.
456
+ # rubocop:disable Metrics/AbcSize
457
+ def screenshot(outputfile = nil, origin: "document", format: nil, clip: nil, &block)
458
+ Bidi2pdf.notification_service.instrument("screenshot.bidi2pdf") do |instrumentation_payload|
459
+ cmd = Commands::CaptureScreenshot.new(context: browsing_context_id, origin: origin, format: format, clip: clip)
460
+
461
+ instrumentation_payload[:cmd] = cmd
462
+
463
+ client.send_cmd_and_wait(cmd) do |response|
464
+ if response["result"]
465
+ png_base64 = response.dig("result", "data")
466
+
467
+ instrumentation_payload[:png_base64] = png_base64
468
+
469
+ if outputfile
470
+ raise ScreenshotError, "Folder does not exist: #{File.dirname(outputfile)}" unless File.directory?(File.dirname(outputfile))
471
+
472
+ File.binwrite(outputfile, Base64.decode64(png_base64))
473
+ Bidi2pdf.logger.info "Screenshot saved as '#{outputfile}'."
474
+ end
475
+
476
+ block.call(png_base64) if block_given?
477
+
478
+ return png_base64 unless outputfile || block_given?
479
+ else
480
+ Bidi2pdf.logger.error "Error capturing screenshot: #{response}"
481
+ end
482
+ end
483
+ end
484
+ end
485
+
486
+ # rubocop:enable Metrics/AbcSize
487
+
488
+ # Sets the browsing context's viewport - makes a headless render deterministic across
489
+ # machines, unlike leaving it at the browser's own default.
490
+ #
491
+ # @param [Integer] width
492
+ # @param [Integer] height
493
+ # @param [Float, nil] device_pixel_ratio Defaults to nil (browser default).
494
+ def set_viewport(width:, height:, device_pixel_ratio: nil)
495
+ cmd = Commands::SetViewport.new(context: browsing_context_id, width: width, height: height, device_pixel_ratio: device_pixel_ratio)
496
+ client.send_cmd_and_wait(cmd) { |response| response }
497
+ end
498
+
447
499
  private
448
500
 
449
501
  def build_command_and_extractor(print_options)
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class CaptureScreenshot
7
+ include Base
8
+
9
+ attr_reader :context, :origin, :format, :clip
10
+
11
+ def initialize(context:, origin: "document", format: nil, clip: nil)
12
+ @context = context
13
+ @origin = origin
14
+ @format = format
15
+ @clip = clip
16
+ end
17
+
18
+ def method_name
19
+ "browsingContext.captureScreenshot"
20
+ end
21
+
22
+ def params
23
+ {
24
+ context: context,
25
+ origin: origin,
26
+ format: format,
27
+ clip: clip
28
+ }.compact
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class SetViewport
7
+ include Base
8
+
9
+ attr_reader :context, :width, :height, :device_pixel_ratio
10
+
11
+ def initialize(context:, width:, height:, device_pixel_ratio: nil)
12
+ @context = context
13
+ @width = width
14
+ @height = height
15
+ @device_pixel_ratio = device_pixel_ratio
16
+ end
17
+
18
+ def method_name
19
+ "browsingContext.setViewport"
20
+ end
21
+
22
+ def params
23
+ {
24
+ context: context,
25
+ viewport: { width: width, height: height },
26
+ devicePixelRatio: device_pixel_ratio
27
+ }.compact
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -18,6 +18,8 @@ module Bidi2pdf
18
18
  require_relative "commands/browsing_context_close"
19
19
  require_relative "commands/browsing_context_navigate"
20
20
  require_relative "commands/browsing_context_print"
21
+ require_relative "commands/capture_screenshot"
22
+ require_relative "commands/set_viewport"
21
23
  require_relative "commands/cdp_get_session"
22
24
  require_relative "commands/page_print"
23
25
  require_relative "commands/session_subscribe"
data/lib/bidi2pdf/cli.rb CHANGED
@@ -40,6 +40,8 @@ module Bidi2pdf
40
40
  option :header, type: :array, default: [], banner: "name=value", desc: "One or more custom headers", aliases: "-H"
41
41
  option :auth, type: :string, banner: "user:pass", desc: "Basic auth credentials", aliases: "-a"
42
42
  option :headless, type: :boolean, default: true, desc: "Run Chrome in headless mode"
43
+ option :no_sandbox, type: :boolean, default: false, desc: "Disable Chrome sandbox (needed inside containers)"
44
+ option :chrome_flag, type: :array, default: [], banner: "--flag[=value]", desc: "Extra Chrome flags appended to defaults", aliases: "--cf"
43
45
  option :port, type: :numeric, default: 0, desc: "Port to run ChromeDriver on (0 = auto)"
44
46
  option :wait_window_loaded,
45
47
  type: :boolean,
@@ -114,6 +116,8 @@ module Bidi2pdf
114
116
  "url" => "https://example.com",
115
117
  "output" => "output.pdf",
116
118
  "headless" => true,
119
+ "no_sandbox" => false,
120
+ "chrome_flag" => [],
117
121
  "print_options" => {
118
122
  "background" => true,
119
123
  "orientation" => "portrait",
@@ -210,6 +214,11 @@ module Bidi2pdf
210
214
  @launcher ||= begin
211
215
  username, password = parse_auth(merged_options[:auth]) if merged_options[:auth]
212
216
 
217
+ chrome_args = Bidi2pdf::Bidi::Session::DEFAULT_CHROME_ARGS.dup
218
+ no_sandbox = merged_options[:no_sandbox] || ENV["DISABLE_CHROME_SANDBOX"] == "true"
219
+ chrome_args << "--no-sandbox" if no_sandbox
220
+ chrome_args.concat(Array(merged_options[:chrome_flag]))
221
+
213
222
  Bidi2pdf::Launcher.new(
214
223
  url: merged_options[:url],
215
224
  inputfile: merged_options[:hmtl_file],
@@ -223,7 +232,8 @@ module Bidi2pdf
223
232
  wait_window_loaded: merged_options[:wait_window_loaded],
224
233
  wait_network_idle: merged_options[:wait_network_idle],
225
234
  print_options: print_options,
226
- network_log_format: merged_options[:network_log_format]
235
+ network_log_format: merged_options[:network_log_format],
236
+ chrome_args: chrome_args
227
237
  )
228
238
  end
229
239
  end
@@ -38,7 +38,8 @@ module Bidi2pdf
38
38
  class Launcher
39
39
  # rubocop:disable Metrics/ParameterLists
40
40
  def initialize(url:, inputfile:, output:, cookies:, headers:, auth:, headless: true, port: 0, wait_window_loaded: false,
41
- wait_network_idle: false, print_options: {}, remote_browser_url: nil, network_log_format: :console)
41
+ wait_network_idle: false, print_options: {}, remote_browser_url: nil, network_log_format: :console,
42
+ chrome_args: Bidi::Session::DEFAULT_CHROME_ARGS)
42
43
  @url = url
43
44
  @inputfile = inputfile
44
45
  @port = port
@@ -54,6 +55,7 @@ module Bidi2pdf
54
55
  @remote_browser_url = remote_browser_url
55
56
  @custom_session = nil
56
57
  @network_log_format = network_log_format
58
+ @chrome_args = chrome_args
57
59
  end
58
60
 
59
61
  # rubocop:enable Metrics/ParameterLists
@@ -84,9 +86,9 @@ module Bidi2pdf
84
86
 
85
87
  def session
86
88
  if @remote_browser_url
87
- @custom_session = Bidi::Session.new(session_url: @remote_browser_url, headless: @headless)
89
+ @custom_session = Bidi::Session.new(session_url: @remote_browser_url, headless: @headless, chrome_args: @chrome_args)
88
90
  else
89
- @manager = ChromedriverManager.new(port: @port, headless: @headless)
91
+ @manager = ChromedriverManager.new(port: @port, headless: @headless, chrome_args: @chrome_args)
90
92
  @manager.start
91
93
  @manager.session
92
94
  end
@@ -6,6 +6,14 @@ rescue LoadError
6
6
  warn "Missing #{dep}. Add it to your Gemfile if you're using Bidi2pdf image test helpers."
7
7
  end
8
8
 
9
+ # CVE-2026-66066 (Rails Active Storage, 2026-07-29): libvips ships loaders/savers that were never
10
+ # fuzz-tested (SVG, JPEG XL, JPEG 2000, BMP, ICO, PSD, anything delegated to ImageMagick, ...) -
11
+ # left reachable, a crafted file can trigger arbitrary file read or RCE. Applied once, here, for
12
+ # every consumer of this opt-in module (Extractor, ImageSimilarityChecker, contains_pdf_image),
13
+ # rather than left to each call site to remember - PNG/JPEG (what this gem's own screenshot/PDF
14
+ # rendering actually produces) load through already-fuzzed loaders and are unaffected.
15
+ Vips.block_untrusted(true) if defined?(Vips)
16
+
9
17
  require_relative "images/tiff_helper"
10
18
  require_relative "images/extractor"
11
19
  require_relative "images/image_similarity_checker"
@@ -49,7 +49,7 @@ module Bidi2pdf
49
49
  # @param pdf_data [String, StringIO, File] The PDF data to be converted.
50
50
  # @return [IO] An IO object containing the PDF data.
51
51
  def convert_data_to_io(pdf_data)
52
- # rubocop:disable Lint/DuplicateBranch
52
+ # rubocop:disable-next Lint/DuplicateBranch
53
53
  if pdf_data.is_a?(String) && (pdf_data.start_with?("JVBERi") || pdf_data.start_with?("JVBER"))
54
54
  StringIO.new(Base64.decode64(pdf_data))
55
55
  elsif pdf_data.start_with?("%PDF-")
@@ -61,7 +61,6 @@ module Bidi2pdf
61
61
  else
62
62
  StringIO.new(pdf_data)
63
63
  end
64
- # rubocop:enable Lint/DuplicateBranch
65
64
  end
66
65
  end
67
66
 
@@ -6,7 +6,7 @@ require "diff/lcs/hunk"
6
6
 
7
7
  module Bidi2pdf
8
8
  module TestHelpers
9
- # rubocop: disable Metrics/ModuleLength
9
+ # rubocop: disable-next Metrics/ModuleLength
10
10
  module PDFTextSanitizer
11
11
  class << self
12
12
  def clean(text)
@@ -148,6 +148,5 @@ module Bidi2pdf
148
148
  end
149
149
  end
150
150
  end
151
- # rubocop:enable Metrics/ModuleLength
152
151
  end
153
152
  end
@@ -73,7 +73,7 @@ module Bidi2pdf
73
73
  # rubocop: enable Metrics/AbcSize
74
74
 
75
75
  def session_url(protocol: "http")
76
- "#{protocol}://#{host}:#{mapped_port(port)}/session"
76
+ ContainerEndpoint.new(self, port).url(scheme: protocol, path: "session")
77
77
  end
78
78
  end
79
79
  end
@@ -36,6 +36,24 @@ module Bidi2pdf
36
36
  def create_session(session_url)
37
37
  Bidi2pdf::Bidi::Session.new(session_url: session_url, headless: true, chrome_args: chrome_args)
38
38
  end
39
+
40
+ # Full session/tab lifecycle in one call - create_session above only builds the session
41
+ # object, it never starts or closes it, and there is no after(:each) teardown for a spec
42
+ # that uses it directly. Mirrors Bidi2pdf::DSL.with_tab's own ensure-based teardown, but
43
+ # against a session_url this module already knows about (a long-lived remote-chrome
44
+ # reached over `session:` metadata, not a freshly spawned local chromedriver per call).
45
+ def with_tab(session_url)
46
+ session = create_session(session_url)
47
+ session.start
48
+ user_context = session.browser.create_user_context
49
+ tab = user_context.create_browser_window
50
+
51
+ yield tab
52
+ ensure
53
+ tab&.close
54
+ user_context&.close
55
+ session&.close
56
+ end
39
57
  end
40
58
  end
41
59
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module TestHelpers
5
+ module Testcontainers
6
+ # Builds a URL for reaching a container's published port from the test
7
+ # process, using the container's own host/port resolution.
8
+ class ContainerEndpoint
9
+ def initialize(container, port)
10
+ @container = container
11
+ @port = port
12
+ end
13
+
14
+ def url(scheme: "http", path: "")
15
+ "#{scheme}://#{@container.accessible_host}:#{@container.mapped_port(@port)}/#{path}"
16
+ end
17
+
18
+ # Address reachable from another container on the same Docker network (e.g. remote-chrome
19
+ # reaching nginx), by alias and internal port - as opposed to #url's host-accessible,
20
+ # externally-mapped port. Uses the same @port this instance was built with, not a
21
+ # container_ports.first guess: the two methods must agree on which port they're both
22
+ # describing for the same container, or a caller comparing #url and #alias_url for what's
23
+ # meant to be the same endpoint could silently get two different services.
24
+ def alias_url(scheme: "http", path: "")
25
+ "#{scheme}://#{@container.aliases.first}:#{@port}/#{path}"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -29,6 +29,39 @@ module Bidi2pdf
29
29
  self
30
30
  end
31
31
 
32
+ def container_json
33
+ @_container&.json
34
+ end
35
+
36
+ # Address of the Docker host at which this container's *mapped* ports
37
+ # are reachable from the test process.
38
+ #
39
+ # Resolution belongs to testcontainers: it honours TC_HOST, a tcp:/ssh:
40
+ # DOCKER_HOST, a native local daemon, and a sibling container reaching
41
+ # the daemon over the bridge gateway — and it derives #mapped_port from
42
+ # the same decision, so the two must stay on one path. We only add a
43
+ # fallback for the cases 0.2.0 leaves unresolved.
44
+ #
45
+ # This is not the address containers use to reach each other; for that
46
+ # the helpers join a shared network and address containers by alias.
47
+ def accessible_host
48
+ resolved_host || "localhost"
49
+ end
50
+
51
+ # testcontainers-core 0.2.0 cannot resolve a host when the test process
52
+ # runs inside a container and the container under test is on a custom
53
+ # network: #container_gateway_ip hardcodes the "bridge" network key
54
+ # (docker_container.rb:1074), so #host returns nil. On the default
55
+ # bridge it can instead reach docker_container.rb:703, which calls an
56
+ # undefined `bridge_ip`. Both are fixed on testcontainers-ruby main;
57
+ # treat either as "unresolved" and fall back.
58
+ def resolved_host
59
+ value = host
60
+ value unless value.nil? || value.empty?
61
+ rescue NoMethodError
62
+ nil
63
+ end
64
+
32
65
  def _container_create_options
33
66
  opts = super
34
67
  network_name = network&.info&.[]("Name")
@@ -10,6 +10,7 @@ module Bidi2pdf
10
10
  module TestHelpers
11
11
  module Testcontainers
12
12
  require_relative "testcontainers/testcontainers_refinement"
13
+ require_relative "testcontainers/container_endpoint"
13
14
  require_relative "testcontainers/chromedriver_container"
14
15
  require_relative "testcontainers/chromedriver_test_helper"
15
16
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bidi2pdf
4
- VERSION = "0.1.13"
4
+ VERSION = "0.1.14"
5
5
  end
data/lib/bidi2pdf.rb CHANGED
@@ -51,6 +51,8 @@ module Bidi2pdf
51
51
 
52
52
  class PrintError < Error; end
53
53
 
54
+ class ScreenshotError < Error; end
55
+
54
56
  class ScriptInjectionError < Error; end
55
57
 
56
58
  class StyleInjectionError < Error; end
@@ -1,20 +1,27 @@
1
1
  module Bidi2pdf
2
2
  module Bidi
3
3
  class AddHeadersInterceptor
4
- @id: String
5
- @client: untyped
6
- @headers: Hash[String, String]
4
+ @headers: untyped
7
5
 
8
- attr_reader id: String
9
- attr_reader headers: Hash[String, String]
6
+ @url_patterns: untyped
10
7
 
11
- def initialize: (String id, Hash[String, String] headers, untyped client) -> void
8
+ @context: untyped
12
9
 
13
- def handle_event: (Hash[String, untyped] response) -> (nil | untyped)
10
+ include Interceptor
14
11
 
15
- private
12
+ def self.phases: () -> ::Array[untyped]
16
13
 
17
- attr_reader client: untyped
14
+ def self.events: () -> ::Array["network.beforeRequestSent"]
15
+
16
+ attr_reader headers: untyped
17
+
18
+ attr_reader url_patterns: untyped
19
+
20
+ attr_reader context: untyped
21
+
22
+ def initialize: (headers: untyped, url_patterns: untyped, context: untyped) -> void
23
+
24
+ def process_interception: (untyped _event_response, untyped _navigation_id, untyped network_id, untyped _url) -> untyped
18
25
  end
19
26
  end
20
27
  end
@@ -1,17 +1,42 @@
1
1
  module Bidi2pdf
2
2
  module Bidi
3
3
  class AuthInterceptor
4
- @credentials: Hash[Symbol, String]?
4
+ @url_patterns: untyped
5
5
 
6
- def initialize: (?credentials: Hash[Symbol, String]?) -> void
6
+ @context: untyped
7
7
 
8
- def intercept: (untyped request) -> untyped
8
+ @username: untyped
9
9
 
10
- private
10
+ @password: untyped
11
+
12
+ @network_ids: untyped
13
+
14
+ include Interceptor
15
+
16
+ def self.phases: () -> ::Array[untyped]
17
+
18
+ def self.events: () -> ::Array["network.authRequired"]
19
+
20
+ attr_reader headers: untyped
21
+
22
+ attr_reader url_patterns: untyped
11
23
 
12
- def add_auth_header: (untyped request) -> void
24
+ attr_reader context: untyped
25
+
26
+ attr_reader username: untyped
27
+
28
+ attr_reader password: untyped
29
+
30
+ attr_reader network_ids: untyped
31
+
32
+ def initialize: (username: untyped, password: untyped, url_patterns: untyped, context: untyped) -> void
33
+
34
+ def process_interception: (untyped _event_response, untyped navigation_id, untyped network_id, untyped url) -> untyped
35
+
36
+ private
13
37
 
14
- def auth_header_value: () -> String?
38
+ # rubocop:disable Naming/PredicateMethod
39
+ def handled_bad_credentials: (untyped navigation_id, untyped network_id, untyped url) -> (false | true)
15
40
  end
16
41
  end
17
- end
42
+ end
@@ -1,38 +1,11 @@
1
1
  module Bidi2pdf
2
2
  module Bidi
3
3
  class Browser
4
- @url: String
5
- @chromedriver_manager: Bidi2pdf::ChromedriverManager
6
- @browser: untyped
7
- @timeout: Integer
8
- @debug: bool
4
+ @client: untyped
9
5
 
10
- attr_reader browser: untyped
11
- attr_reader timeout: Integer
6
+ def initialize: (untyped client) -> void
12
7
 
13
- def initialize: (
14
- url: String,
15
- chromedriver_manager: Bidi2pdf::ChromedriverManager,
16
- ?timeout: Integer,
17
- ?debug: bool
18
- ) -> void
19
-
20
- def navigate: (?reload: bool) -> void
21
-
22
- def current_url: -> String
23
-
24
- def wait_until: [T] (
25
- ?timeout: Integer,
26
- ?message: String
27
- ) { () -> T? } -> T
28
-
29
- def close: () -> void
30
-
31
- private
32
-
33
- def connect: () -> untyped
34
-
35
- def setup_browser: () -> untyped
8
+ def create_user_context: () -> untyped
36
9
  end
37
10
  end
38
- end
11
+ end
@@ -0,0 +1,59 @@
1
+ module Bidi2pdf
2
+ module Bidi
3
+ class BrowserConsoleLoggerSuggar
4
+ @browser_console_logger: untyped
5
+
6
+ @level: untyped
7
+
8
+ @prefix: untyped
9
+
10
+ @timestamp: untyped
11
+
12
+ @text: untyped
13
+
14
+ @args: untyped
15
+
16
+ @stack_trace: untyped
17
+
18
+ attr_reader browser_console_logger: untyped
19
+
20
+ def initialize: (untyped browser_console_logger) -> void
21
+
22
+ def with_level: (untyped level) -> self
23
+
24
+ def with_prefix: (untyped prefix) -> self
25
+
26
+ def with_timestamp: (untyped timestamp) -> self
27
+
28
+ def with_text: (untyped text) -> self
29
+
30
+ def with_args: (untyped args) -> self
31
+
32
+ def with_stack_trace: (untyped stack_trace) -> self
33
+
34
+ def log_event: () -> untyped
35
+
36
+ def prefix: () -> untyped
37
+ end
38
+
39
+ class BrowserConsoleLogger
40
+ @logger: untyped
41
+
42
+ include JsLoggerHelper
43
+
44
+ attr_accessor logger: untyped
45
+
46
+ def initialize: (untyped logger) -> void
47
+
48
+ def builder: () -> untyped
49
+
50
+ def log_message: (untyped level, untyped prefix, untyped text) -> (nil | untyped)
51
+
52
+ def log_args: (untyped prefix, untyped args) -> (nil | untyped)
53
+
54
+ def log_stack_trace: (untyped prefix, untyped trace) -> untyped
55
+
56
+ def self.format_timestamp: (untyped timestamp) -> ("N/A" | untyped)
57
+ end
58
+ end
59
+ end