bidi2pdf 0.1.4 → 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: f132ccc5d9db5bb621f7e1a0adfa4150b77b4618542ba27873a602cf9c342a40
4
- data.tar.gz: f3df228dc1de555d14baad4347a1b5b8d0bc8b63f88d6adfbcec86f0da571703
3
+ metadata.gz: 541e9193b3285383b2a82065b173af87c68edeb08ec157125761f44285118dff
4
+ data.tar.gz: ab12e33ad21e8377ef964aa12d0d9b0ba757ee941c217febe6d46fb1cc381d5e
5
5
  SHA512:
6
- metadata.gz: d74e6e1b505493d47f66c2f1cb4d13bc07aacfa82dbba536439e3b458395bbcd7213103e5fbed167a65945af28bbfb239f6510e93bed2838fa583b95352df8d6
7
- data.tar.gz: '096bb337a61db6d5392ba64c38bf4632695ca647b2fe95a227f3d56b189241cae1b19cc65e98115dff73997bb3df1cdcc2821058611683aa449acd11db4fe66f'
6
+ metadata.gz: 91be15b3310098c7f16153331fa0a27a8b869dfebc9f3720a7da26d2c59b8444f266e21e93d74891b423e50d8223b01b2198f230753d38ad523b8c846c12150f
7
+ data.tar.gz: 504516ecc1eac40e6c6e159c5f14aafdeb9d23bc587fcf6914ab60d8676a930a7866d6d0c07998398359c402780584f1e5b317c274dd6f3dba0ba50c1a73cbe6
data/CHANGELOG.md CHANGED
@@ -6,10 +6,47 @@ All notable changes to this project will be documented in this file.
6
6
 
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
8
8
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
- [unreleased]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.4..HEAD
9
+
10
+ [unreleased]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.5..HEAD
10
11
 
11
12
  <!-- generated by git-cliff end -->
12
13
 
14
+ ## [0.1.6]
15
+
16
+ ### ⚠️ Breaking Changes
17
+
18
+ - Rename view_html_page to render_html by @dieter-medium
19
+ - Rename wait_until_all_finished to wait_until_network_idle by @dieter-medium
20
+
21
+ ### 📝 Docs
22
+
23
+ - Update Docker instructions in README by @dieter-medium
24
+
25
+ ### 🔄 Changed
26
+
27
+ - Add details on network logging and console capture by @dieter-medium
28
+
29
+ ### 🚀 Added
30
+
31
+ - Add PDF network log formatting and customizable outputs by @dieter-medium
32
+ - Add option to log network traffic and handle failures within the cli command by @dieter-medium
33
+ - Add structured network traffic logging by @dieter-medium
34
+ - Add slim variant Dockerfile and build matrix for CI by @dieter-medium
35
+ - Add official image at [Docker Hub](https://hub.docker.com/r/dieters877565/bidi2pdf) by @dieter-medium
36
+
37
+ ## [0.1.5] - 2025-04-10
38
+
39
+ ### 📝 Docs
40
+
41
+ - Improve README structure and update content by @dieter-medium
42
+ - Add step-by-step usage guide to README by @dieter-medium
43
+
44
+ ### 🚀 Added
45
+
46
+ - Add support for rendering local HTML files or in memory HTML by @dieter-medium
47
+ - Add DSL and specs for generating PDFs with browser tabs by @dieter-medium
48
+ - Enhance print method with block support for PDF handling by @dieter-medium
49
+
13
50
  ## [0.1.4] - 2025-04-10
14
51
 
15
52
  ### 🎨 Refactored
@@ -79,3 +116,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
79
116
  ## [0.1.0] - 2025-03-26
80
117
 
81
118
  - Initial release
119
+
120
+ [unreleased]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.6..HEAD
121
+
122
+ [0.1.6]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.5..v0.1.6
123
+
124
+ [0.1.5]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.4..v0.1.5
125
+
126
+ [0.1.4]: https://github.com/dieter-medium/bidi2pdf/compare/v0.1.3..v0.1.4
data/README.md CHANGED
@@ -3,52 +3,60 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/bidi2pdf.svg)](https://badge.fury.io/rb/bidi2pdf)
4
4
  [![Test Coverage](https://api.codeclimate.com/v1/badges/6425d9893aa3a9ca243e/test_coverage)](https://codeclimate.com/github/dieter-medium/bidi2pdf/test_coverage)
5
5
 
6
- # Bidi2pdf
6
+ ---
7
7
 
8
- Bidi2pdf is a Ruby gem that generates high-quality PDFs from web pages using Chrome's BiDi (BiDirectional) protocol. It
9
- offers precise control over PDF generation with support for modern web technologies.
8
+ # 📄 Bidi2pdf Bulletproof PDF generation via Chrome's BiDi Protocol
10
9
 
11
- ## Features
10
+ **Bidi2pdf** is a powerful Ruby gem that transforms modern web pages into high-fidelity PDFs using Chrome’s
11
+ **BiDirectional (BiDi)** protocol. Whether you're automating reports, archiving websites, or shipping documentation,
12
+ Bidi2pdf gives you **precision, flexibility, and full control**.
12
13
 
13
- - **Simple CLI** - Generate PDFs with a single command
14
- - **Rich Configuration** - Customize with cookies, headers, and authentication
15
- - **Waiting Conditions** - Wait for window loaded or network idle
16
- - **Headless Support** - Run without a visible browser
17
- - **Docker Ready** - Easy containerization
18
- - **Modern Architecture** - Uses Chrome's BiDi protocol for better control
14
+ ---
19
15
 
20
- ## Installation
16
+ ## ✨ Key Features
21
17
 
22
- Add to your application's Gemfile:
18
+ **One-liner CLI** – From URL to PDF in a single command
19
+ ✅ **Full customization** – Inject cookies, headers, auth credentials
20
+ ✅ **Smart waiting** – Wait for complete page load or network idle
21
+ ✅ **Headless support** – Run quietly in the background
22
+ ✅ **Docker-ready** – Plug and play with containers
23
+ ✅ **Modern architecture** – Built on Chrome's next-gen BiDi protocol
24
+ ✅ **Network logging** – Know which requests fail during rendering
25
+ ✅ **Console log capture** – See what goes wrong inside the browser
26
+
27
+ ---
28
+
29
+ ## 🚀 Installation
30
+
31
+ ### Bundler
23
32
 
24
33
  ```ruby
25
34
  gem 'bidi2pdf'
26
35
  ```
27
36
 
28
- Or install manually:
37
+ ### Standalone
29
38
 
30
39
  ```bash
31
- $ gem install bidi2pdf
40
+ gem install bidi2pdf
32
41
  ```
33
42
 
34
- ### Dependencies
43
+ ### Requirements
35
44
 
36
- - **Ruby**: 3.3 or higher
37
- - **Bidi2pdf** automatically manages ChromeDriver binaries through
38
- the [chromedriver-binary](https://github.com/dieter-medium/chromedriver-binary) gem, which:
39
- Downloads and installs the ChromeDriver version matching your installed Chrome/Chromium browser
40
- Eliminates the need to manually install or update ChromeDriver
41
- Ensures compatibility between Chrome and ChromeDriver versions
45
+ - **Ruby** 3.3
46
+ - **Chrome/Chromium**
47
+ - Automatic ChromeDriver management via [chromedriver-binary](https://github.com/dieter-medium/chromedriver-binary)
42
48
 
43
- ## Usage
49
+ ---
44
50
 
45
- ### Basic Command Line Usage
51
+ ## ⚙️ Basic Usage
52
+
53
+ ### Command-line
46
54
 
47
55
  ```bash
48
56
  bidi2pdf render --url https://example.com --output example.pdf
49
57
  ```
50
58
 
51
- ### Advanced Options
59
+ ### Advanced CLI Options
52
60
 
53
61
  ```bash
54
62
  bidi2pdf render \
@@ -62,14 +70,18 @@ bidi2pdf render \
62
70
  --log-level debug
63
71
  ```
64
72
 
65
- ### Ruby API
73
+ ---
74
+
75
+ ## 🧠 Programmatic API
76
+
77
+ ### Classic Approach
66
78
 
67
79
  ```ruby
68
80
  require 'bidi2pdf'
69
81
 
70
82
  launcher = Bidi2pdf::Launcher.new(
71
83
  url: 'https://example.com',
72
- output: 'example.pdf', # nil for base64 encoded string as result of launcher.launch
84
+ output: 'example.pdf',
73
85
  cookies: { 'session' => 'abc123' },
74
86
  headers: { 'X-API-KEY' => 'token' },
75
87
  auth: { username: 'admin', password: 'password' },
@@ -78,25 +90,111 @@ launcher = Bidi2pdf::Launcher.new(
78
90
  )
79
91
 
80
92
  launcher.launch
93
+ ```
94
+
95
+ ### DSL – Quick & Clean
96
+
97
+ ```ruby
98
+ require "bidi2pdf"
99
+
100
+ Bidi2pdf::DSL.with_tab(headless: true) do |tab|
101
+ tab.navigate_to("https://example.com")
102
+ tab.wait_until_network_idle
103
+ tab.print("example.pdf")
104
+ end
105
+ ```
106
+
107
+ ---
108
+
109
+ ## 🧬 Deep Integration Example
110
+
111
+ Get fine-grained control using Chrome sessions, tabs, and BiDi commands:
112
+
113
+ <details>
114
+ <summary>🔍 Show full example</summary>
115
+
116
+ ```ruby
117
+ require "bidi2pdf"
118
+
119
+ # 1. Remote or local session?
120
+ session = Bidi2pdf::Bidi::Session.new(
121
+ session_url: "http://localhost:9092/session",
122
+ headless: true,
123
+ )
124
+
125
+ # Alternative: local session via ChromeDriver
126
+ # manager = Bidi2pdf::ChromedriverManager.new(headless: false)
127
+ # manager.start
128
+ # session = manager.session
129
+
130
+ session.start
131
+ session.client.on_close { puts "WebSocket session closed" }
132
+
133
+ # 2. Create browser/tab
134
+ browser = session.browser
135
+ context = browser.create_user_context
136
+ window = context.create_browser_window
137
+ tab = window.create_browser_tab
81
138
 
82
- # see Bidi2pdf::SessionRunner for more options
139
+ # 3. Inject configuration
140
+ tab.set_cookie(name: "auth", value: "secret", domain: "example.com", secure: true)
141
+ tab.add_headers(url_patterns: [{ type: "pattern", protocol: "https", hostname: "example.com", port: "443" }],
142
+ headers: [{ name: "X-API-KEY", value: "12345678" }])
143
+ tab.basic_auth(url_patterns: [{ type: "pattern", protocol: "https", hostname: "example.com", port: "443" }],
144
+ username: "username", password: "secret")
145
+
146
+ # 4. Render PDF
147
+ tab.navigate_to "https://example.com"
148
+
149
+ # Alternative: send html code to the browser
150
+ # tab.render_html_content("<html>...</html>")
151
+
152
+ tab.wait_until_network_idle
153
+ tab.print("my.pdf")
154
+
155
+ # 5. Cleanup
156
+ tab.close
157
+ window.close
158
+ session.close
83
159
  ```
84
160
 
85
- ## Docker Support
161
+ </details>
162
+
163
+ ---
164
+
165
+ ## 🐳 Docker Support
86
166
 
87
- Build and run with Docker:
167
+ ### 🛠️ Build & Run Locally
88
168
 
89
169
  ```bash
90
- # Build gem and Docker image
170
+ # Prepare the environment
91
171
  rake build
172
+
173
+ # Build the Docker image
92
174
  docker build -t bidi2pdf -f docker/Dockerfile .
93
175
 
94
- # Generate PDF using Docker
95
- docker run -it --rm -v ./output:/reports bidi2pdf \
176
+ # Run the container and generate a PDF
177
+ docker run -it --rm \
178
+ -v ./output:/reports \
179
+ bidi2pdf \
96
180
  bidi2pdf render --url=https://example.com --output /reports/example.pdf
181
+
97
182
  ```
98
183
 
99
- ### Test it with docker compose
184
+ ### Use the Prebuilt Image (Recommended for Fast Start)
185
+
186
+ Grab it directly from [Docker Hub](https://hub.docker.com/r/dieters877565/bidi2pdf)
187
+
188
+ ```bash
189
+ docker run -it --rm \
190
+ -v ./output:/reports \
191
+ dieters877565/bidi2pdf:main-slim \
192
+ bidi2pdf render --url=https://example.com --output /reports/example.pdf
193
+ ```
194
+
195
+ ✅ Tip: Mount your local directory (e.g. ./output) to /reports in the container to easily access the generated PDFs.
196
+
197
+ ### Docker Compose
100
198
 
101
199
  ```bash
102
200
  rake build
@@ -105,6 +203,10 @@ docker compose -f docker/docker-compose.yml up -d
105
203
  # simple example
106
204
  docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=http://nginx/sample.html --wait_window_loaded --wait_network_idle --output /reports/simple.pdf
107
205
 
206
+ # with a local file
207
+ docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=file:///reports/sample.html--wait_network_idle --output /reports/simple.pdf
208
+
209
+
108
210
  # basic auth example
109
211
  docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=http://nginx/basic/sample.html --auth admin:secret --wait_window_loaded --wait_network_idle --output /reports/basic.pdf
110
212
 
@@ -120,31 +222,42 @@ docker compose -f docker/docker-compose.yml exec app bidi2pdf render --url=http:
120
222
  docker compose -f docker/docker-compose.yml down
121
223
  ```
122
224
 
123
- ## Configuration Options
225
+ ---
226
+
227
+ ## 🧩 Configuration Options
124
228
 
125
- | Option | Description |
126
- |------------------------|---------------------------------------------------------------------------------------------------------------------|
127
- | `--url` | The URL to render (required) |
128
- | `--output` | Output PDF filename (default: output.pdf) |
129
- | `--cookie` | Cookies in name=value format |
130
- | `--header` | HTTP headers in name=value format |
131
- | `--auth` | Basic auth credentials (user:pass) |
132
- | `--headless` | Run Chrome in headless mode (default: true) |
133
- | `--port` | Port for ChromeDriver (0 = auto) |
134
- | `--wait_window_loaded` | Wait for the window to be fully loaded. You need to set a variable `window.loaded`. See ./spec/fixtures/sample.html |
135
- | `--wait_network_idle` | Wait for network to be idle |
136
- | `--log_level` | Log level (debug, info, warn, error, fatal) |
137
- | `--remote_browser_url` | URL of the remote Chrome instance (default: nil) |
138
- | `--default_timeout` | Default timeout for operations (default: 60 seconds) |
229
+ | Flag | Description |
230
+ |------------------------|--------------------------------------------|
231
+ | `--url` | Target URL (required) |
232
+ | `--output` | Output PDF file (default: output.pdf) |
233
+ | `--cookie` | Set cookie in `name=value` format |
234
+ | `--header` | Inject custom header `name=value` |
235
+ | `--auth` | Basic auth as `user:pass` |
236
+ | `--headless` | Run Chrome headless (default: true) |
237
+ | `--port` | ChromeDriver port (0 = auto) |
238
+ | `--wait_window_loaded` | Wait until `window.loaded` is set to true |
239
+ | `--wait_network_idle` | Wait until network is idle |
240
+ | `--log_level` | Log level: debug, info, warn, error, fatal |
241
+ | `--remote_browser_url` | Connect to remote Chrome session |
242
+ | `--default_timeout` | Operation timeout (default: 60s) |
139
243
 
140
- ## Development
244
+ ---
141
245
 
142
- After checking out the repo:
246
+ ## 🛠 Development
247
+
248
+ ```bash
249
+ # Setup
250
+ bin/setup
251
+
252
+ # Run tests
253
+ rake spec
254
+
255
+ # Open interactive console
256
+ bin/console
257
+ ```
143
258
 
144
- 1. Run `bin/setup` to install dependencies
145
- 2. Run `rake spec` to run the tests
146
- 3. Run `bin/console` for an interactive prompt
259
+ ---
147
260
 
148
- ## License
261
+ ## 📜 License
149
262
 
150
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
263
+ This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
data/docker/Dockerfile CHANGED
@@ -1,9 +1,11 @@
1
1
  FROM ruby:3.3
2
2
 
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
3
5
  # Install dependencies
4
- RUN apt-get update && \
5
- apt-get install -y \
6
- chromium \
6
+ RUN apt-get update && apt-get upgrade &&\
7
+ apt-get install -y --no-install-recommends\
8
+ chromium chromium-driver\
7
9
  libglib2.0-0 \
8
10
  libnss3 \
9
11
  libxss1 \
@@ -19,6 +21,12 @@ RUN apt-get update && \
19
21
  # Create a non-root user
20
22
  RUN groupadd -r appuser && useradd -r -g appuser -m -d /home/appuser appuser
21
23
 
24
+ # ARM compatibility workaround:
25
+ # On ARM architectures (such as Apple Silicon), downloading chromedriver via automated scripts may fail or cause ELF binary errors,
26
+ # such as "rosetta error: failed to open elf at /lib64/ld-linux-x86-64.so.2".
27
+ # To avoid these issues, we directly install 'chromium-driver' via the package manager and explicitly create a symlink in the expected location.
28
+
29
+ RUN mkdir -p /home/appuser/.webdrivers && ln -s /usr/bin/chromedriver /home/appuser/.webdrivers/chromedriver
22
30
 
23
31
  # Set working directory
24
32
  WORKDIR /app
@@ -2,9 +2,11 @@ FROM ruby:3.3
2
2
 
3
3
  ARG CHROMEDRIVER_PORT=3000
4
4
 
5
+ ENV DEBIAN_FRONTEND=noninteractive
6
+
5
7
  # Install dependencies
6
- RUN apt-get update && \
7
- apt-get install -y \
8
+ RUN apt-get update && apt-get upgrade && \
9
+ apt-get install -y --no-install-recommends\
8
10
  chromium \
9
11
  libglib2.0-0 \
10
12
  libnss3 \
@@ -0,0 +1,75 @@
1
+ FROM ruby:3.3-slim AS builder
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+
5
+ # Install dependencies
6
+ RUN apt-get update && apt-get upgrade && \
7
+ apt-get install -y --no-install-recommends \
8
+ chromium \
9
+ libglib2.0-0 \
10
+ libnss3 \
11
+ libxss1 \
12
+ libasound2 \
13
+ libatk-bridge2.0-0 \
14
+ libgtk-3-0 \
15
+ libdrm2 \
16
+ curl \
17
+ unzip \
18
+ xvfb \
19
+ build-essential \
20
+ libpq-dev pkg-config \
21
+ && rm -rf /var/lib/apt/lists/*
22
+
23
+ # Set working directory
24
+ WORKDIR /app
25
+
26
+ # Copy your gem into container
27
+ COPY ./pkg/bidi2pdf-*.gem ./
28
+
29
+ RUN gem install ./bidi2pdf-*.gem
30
+
31
+
32
+ # Stage 2
33
+
34
+ FROM ruby:3.3-slim
35
+
36
+ ENV DEBIAN_FRONTEND=noninteractive
37
+
38
+ # Install dependencies
39
+ RUN apt-get update && apt-get upgrade &&\
40
+ apt-get install -y --no-install-recommends\
41
+ chromium chromium-driver\
42
+ libglib2.0-0 \
43
+ libnss3 \
44
+ libxss1 \
45
+ libasound2 \
46
+ libatk-bridge2.0-0 \
47
+ libgtk-3-0 \
48
+ libdrm2 \
49
+ curl \
50
+ unzip \
51
+ xvfb \
52
+ && rm -rf /var/lib/apt/lists/*
53
+
54
+ COPY --from=builder /usr/local/bundle /usr/local/bundle
55
+
56
+ # Create a non-root user
57
+ RUN groupadd -r appuser && useradd -r -g appuser -m -d /home/appuser appuser
58
+
59
+ # ARM compatibility workaround:
60
+ # On ARM architectures (such as Apple Silicon), downloading chromedriver via automated scripts may fail or cause ELF binary errors,
61
+ # such as "rosetta error: failed to open elf at /lib64/ld-linux-x86-64.so.2".
62
+ # To avoid these issues, we directly install 'chromium-driver' via the package manager and explicitly create a symlink in the expected location.
63
+
64
+ RUN mkdir -p /home/appuser/.webdrivers && ln -s /usr/bin/chromedriver /home/appuser/.webdrivers/chromedriver
65
+
66
+ # Set working directory
67
+ WORKDIR /app
68
+
69
+ RUN chown -R appuser:appuser /app
70
+
71
+ # Switch to non-root user
72
+ USER appuser
73
+
74
+ CMD ["/usr/bin/bash"]
75
+
@@ -3,13 +3,14 @@
3
3
  require "base64"
4
4
 
5
5
  require_relative "network_events"
6
+ require_relative "logger_events"
6
7
  require_relative "auth_interceptor"
7
8
  require_relative "add_headers_interceptor"
8
9
 
9
10
  module Bidi2pdf
10
11
  module Bidi
11
12
  class BrowserTab
12
- attr_reader :client, :browsing_context_id, :user_context_id, :tabs, :network_events, :open
13
+ attr_reader :client, :browsing_context_id, :user_context_id, :tabs, :network_events, :open, :logger_events
13
14
 
14
15
  def initialize(client, browsing_context_id, user_context_id)
15
16
  @client = client
@@ -17,6 +18,7 @@ module Bidi2pdf
17
18
  @user_context_id = user_context_id
18
19
  @tabs = []
19
20
  @network_events = NetworkEvents.new browsing_context_id
21
+ @logger_events = LoggerEvents.new browsing_context_id
20
22
  @open = true
21
23
  end
22
24
 
@@ -77,10 +79,13 @@ module Bidi2pdf
77
79
  ).tap { |interceptor| interceptor.register_with_client(client: client) }
78
80
  end
79
81
 
80
- def open_page(url)
81
- client.on_event("network.responseStarted", "network.responseCompleted", "network.fetchError",
82
+ def navigate_to(url)
83
+ client.on_event("network.beforeRequestSent", "network.responseStarted", "network.responseCompleted", "network.fetchError",
82
84
  &network_events.method(:handle_event))
83
85
 
86
+ client.on_event("log.entryAdded",
87
+ &logger_events.method(:handle_event))
88
+
84
89
  cmd = Bidi2pdf::Bidi::Commands::BrowsingContextNavigate.new url: url, context: browsing_context_id
85
90
 
86
91
  client.send_cmd_and_wait(cmd) do |response|
@@ -88,6 +93,13 @@ module Bidi2pdf
88
93
  end
89
94
  end
90
95
 
96
+ def render_html_content(html_content)
97
+ base64_encoded = Base64.strict_encode64(html_content)
98
+ data_url = "data:text/html;charset=utf-8;base64,#{base64_encoded}"
99
+
100
+ navigate_to(data_url)
101
+ end
102
+
91
103
  def execute_script(script)
92
104
  cmd = Bidi2pdf::Bidi::Commands::ScriptEvaluate.new context: browsing_context_id, expression: script
93
105
  client.send_cmd_and_wait(cmd) do |response|
@@ -97,8 +109,29 @@ module Bidi2pdf
97
109
  end
98
110
  end
99
111
 
100
- def wait_until_all_finished(timeout: 10, poll_interval: 0.1)
101
- network_events.wait_until_all_finished(timeout: timeout, poll_interval: poll_interval)
112
+ def wait_until_network_idle(timeout: 10, poll_interval: 0.1)
113
+ network_events.wait_until_network_idle(timeout: timeout, poll_interval: poll_interval)
114
+ end
115
+
116
+ def log_network_traffic(format: :console, output: nil, print_options: { background: true }, &block)
117
+ format = format.to_sym
118
+
119
+ if format == :console
120
+ network_events.log_network_traffic format: :console
121
+ elsif format == :pdf
122
+ html_content = network_events.log_network_traffic format: :html
123
+
124
+ return unless html_content
125
+
126
+ logging_tab = create_browser_tab
127
+
128
+ logging_tab.render_html_content(html_content)
129
+ logging_tab.wait_until_network_idle
130
+
131
+ logging_tab.print(output, print_options: print_options, &block)
132
+
133
+ logging_tab.close
134
+ end
102
135
  end
103
136
 
104
137
  def close
@@ -111,7 +144,8 @@ module Bidi2pdf
111
144
  @open = false
112
145
  end
113
146
 
114
- def print(outputfile, print_options: { background: true })
147
+ # rubocop: disable Metrics/AbcSize, Metrics/PerceivedComplexity
148
+ def print(outputfile = nil, print_options: { background: true }, &block)
115
149
  cmd = Bidi2pdf::Bidi::Commands::BrowsingContextPrint.new context: browsing_context_id, print_options: print_options
116
150
 
117
151
  client.send_cmd_and_wait(cmd) do |response|
@@ -119,19 +153,25 @@ module Bidi2pdf
119
153
  pdf_base64 = response["result"]["data"]
120
154
 
121
155
  if outputfile
156
+ raise PrintError, "Folder does not exist: #{File.dirname(outputfile)}" unless File.directory?(File.dirname(outputfile))
157
+
122
158
  File.binwrite(outputfile, Base64.decode64(pdf_base64))
123
159
  Bidi2pdf.logger.info "PDF saved as '#{outputfile}'."
124
160
  else
125
161
  Bidi2pdf.logger.info "PDF generated successfully."
126
162
  end
127
163
 
128
- return pdf_base64 unless outputfile
164
+ block.call(pdf_base64) if block_given?
165
+
166
+ return pdf_base64 unless outputfile || block_given?
129
167
  else
130
168
  Bidi2pdf.logger.error "Error printing: #{response}"
131
169
  end
132
170
  end
133
171
  end
134
172
 
173
+ # rubocop: enable Metrics/AbcSize, Metrics/PerceivedComplexity
174
+
135
175
  private
136
176
 
137
177
  def close_context
@@ -6,6 +6,14 @@ module Bidi2pdf
6
6
  class SetTabCookie
7
7
  include Base
8
8
 
9
+ class << self
10
+ attr_writer :time_provider
11
+
12
+ def time_provider
13
+ @time_provider ||= -> { Time.now }
14
+ end
15
+ end
16
+
9
17
  attr_reader :name, :value, :domain, :path, :secure, :http_only, :same_site, :ttl, :browsing_context_id
10
18
 
11
19
  def initialize(name:,
@@ -29,7 +37,7 @@ module Bidi2pdf
29
37
  end
30
38
 
31
39
  def expiry
32
- Time.now.to_i + ttl
40
+ self.class.time_provider.call.to_i + ttl
33
41
  end
34
42
 
35
43
  def method_name