|
1 | 1 | # frozen_string_literal: true |
2 | 2 |
|
3 | | -require "concurrent-ruby" |
| 3 | +require "ferrum/utils/platform" |
| 4 | +require "ferrum/utils/elapsed_time" |
| 5 | +require "ferrum/utils/attempt" |
| 6 | +require "ferrum/errors" |
4 | 7 | require "ferrum/browser" |
5 | 8 | require "ferrum/node" |
6 | 9 |
|
7 | 10 | module Ferrum |
8 | | - class Error < StandardError; end |
9 | | - |
10 | | - class NoSuchPageError < Error; end |
11 | | - |
12 | | - class NoSuchTargetError < Error; end |
13 | | - |
14 | | - class NotImplementedError < Error; end |
15 | | - |
16 | | - class StatusError < Error |
17 | | - def initialize(url, message = nil) |
18 | | - super(message || "Request to #{url} failed to reach server, check DNS and server status") |
19 | | - end |
20 | | - end |
21 | | - |
22 | | - class PendingConnectionsError < StatusError |
23 | | - attr_reader :pendings |
24 | | - |
25 | | - def initialize(url, pendings = []) |
26 | | - @pendings = pendings |
27 | | - |
28 | | - message = "Request to #{url} reached server, but there are still pending connections: #{pendings.join(', ')}" |
29 | | - |
30 | | - super(url, message) |
31 | | - end |
32 | | - end |
33 | | - |
34 | | - class TimeoutError < Error |
35 | | - def message |
36 | | - "Timed out waiting for response. It's possible that this happened " \ |
37 | | - "because something took a very long time (for example a page load " \ |
38 | | - "was slow). If so, setting the :timeout option to a higher value might " \ |
39 | | - "help." |
40 | | - end |
41 | | - end |
42 | | - |
43 | | - class ScriptTimeoutError < Error |
44 | | - def message |
45 | | - "Timed out waiting for evaluated script to return a value" |
46 | | - end |
47 | | - end |
48 | | - |
49 | | - class ProcessTimeoutError < Error |
50 | | - attr_reader :output |
51 | | - |
52 | | - def initialize(timeout, output) |
53 | | - @output = output |
54 | | - super("Browser did not produce websocket url within #{timeout} seconds, try to increase `:process_timeout`. See https://github.com/rubycdp/ferrum#customization") |
55 | | - end |
56 | | - end |
57 | | - |
58 | | - class DeadBrowserError < Error |
59 | | - def initialize(message = "Browser is dead or given window is closed") |
60 | | - super |
61 | | - end |
62 | | - end |
63 | | - |
64 | | - class NodeMovingError < Error |
65 | | - def initialize(node, prev, current) |
66 | | - @node = node |
67 | | - @prev = prev |
68 | | - @current = current |
69 | | - super(message) |
70 | | - end |
71 | | - |
72 | | - def message |
73 | | - "#{@node.inspect} that you're trying to click is moving, hence " \ |
74 | | - "we cannot. Previously it was at #{@prev.inspect} but now at " \ |
75 | | - "#{@current.inspect}." |
76 | | - end |
77 | | - end |
78 | | - |
79 | | - class CoordinatesNotFoundError < Error |
80 | | - def initialize(message = "Could not compute content quads") |
81 | | - super |
82 | | - end |
83 | | - end |
84 | | - |
85 | | - class BrowserError < Error |
86 | | - attr_reader :response |
87 | | - |
88 | | - def initialize(response) |
89 | | - @response = response |
90 | | - super(response["message"]) |
91 | | - end |
92 | | - |
93 | | - def code |
94 | | - response["code"] |
95 | | - end |
96 | | - |
97 | | - def data |
98 | | - response["data"] |
99 | | - end |
100 | | - end |
101 | | - |
102 | | - class NodeNotFoundError < BrowserError; end |
103 | | - |
104 | | - class NoExecutionContextError < BrowserError |
105 | | - def initialize(response = nil) |
106 | | - response ||= { "message" => "There's no context available" } |
107 | | - super(response) |
108 | | - end |
109 | | - end |
110 | | - |
111 | | - class JavaScriptError < BrowserError |
112 | | - attr_reader :class_name, :message, :stack_trace |
113 | | - |
114 | | - def initialize(response, stack_trace = nil) |
115 | | - @class_name, @message = response.values_at("className", "description") |
116 | | - @stack_trace = stack_trace |
117 | | - super(response.merge("message" => @message)) |
118 | | - end |
119 | | - end |
120 | | - |
121 | | - class << self |
122 | | - def windows? |
123 | | - RbConfig::CONFIG["host_os"] =~ /mingw|mswin|cygwin/ |
124 | | - end |
125 | | - |
126 | | - def mac? |
127 | | - RbConfig::CONFIG["host_os"] =~ /darwin/ |
128 | | - end |
129 | | - |
130 | | - def mri? |
131 | | - defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" |
132 | | - end |
133 | | - |
134 | | - def started |
135 | | - @started ||= monotonic_time |
136 | | - end |
137 | | - |
138 | | - def elapsed_time(start = nil) |
139 | | - monotonic_time - (start || @started) |
140 | | - end |
141 | | - |
142 | | - def monotonic_time |
143 | | - Concurrent.monotonic_time |
144 | | - end |
145 | | - |
146 | | - def timeout?(start, timeout) |
147 | | - elapsed_time(start) > timeout |
148 | | - end |
149 | | - |
150 | | - def with_attempts(errors:, max:, wait:) |
151 | | - attempts ||= 1 |
152 | | - yield |
153 | | - rescue *Array(errors) |
154 | | - raise if attempts >= max |
155 | | - |
156 | | - attempts += 1 |
157 | | - sleep(wait) |
158 | | - retry |
159 | | - end |
160 | | - end |
161 | 11 | end |
0 commit comments