diff --git a/AGENTS.md b/AGENTS.md index d958beb4..15abc2f0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,9 +28,10 @@ The suite boots a real Chrome and a Sinatra/Puma test app (`spec/support/applica - **Prefer system/feature specs**: drive a real browser against the test app and assert on observable behavior. They live in `spec/` next to the feature, e.g. `spec/page_spec.rb`, `spec/network/`. -- **Unit specs go in `spec/unit/` and nowhere else.** A spec is a unit spec if it stubs or mocks Ferrum's own - objects (`allow(...).to receive`, `and_raise`, `and_wrap_original`), builds objects with `allocate` or - `instance_variable_set`, or tests one class in isolation. Mirror the `lib/` path: +- **Unit specs go in `spec/unit/` and nowhere else.** A unit spec tests one class in isolation or its internals: + it builds objects with `allocate` or `instance_variable_set`, or asserts on internal calls and state. A spec that + drives the browser and asserts observable behavior stays a feature spec even if it stubs one call, e.g. to + simulate a Chrome failure. Mirror the `lib/` path: `lib/ferrum/contexts.rb` -> `spec/unit/contexts_spec.rb`, `lib/ferrum/client/web_socket.rb` -> `spec/unit/client/web_socket_spec.rb`. Never add them to the feature spec files in `spec/`. - Don't test private methods; cover them through the public API. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e820e75..8f46728d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,9 @@ - Behind a proxy with credentials, `request.abort` in `on(:request)` and `network.blacklist`/`whitelist` were ignored because Ferrum's own proxy handler continued every request first. It now only continues requests nobody else handles [#332] - `mouse.click(modifiers:)` sent its `mousemove` without the modifiers [#581] +- When Chrome can't compute an element's content quads, `node.click` falls back to its bounding rect, but it added the + page scroll offset and missed the element on a scrolled page. It clicks in viewport coordinates now +- `Proxy#rotate` to an upstream proxy failed every HTTPS request with `ERR_TUNNEL_CONNECTION_FAILED` ### Removed - `Browser::Process#default_user_agent`, `#browser_version`, `#protocol_version`, `#v8_version` and `#webkit_version`, diff --git a/lib/ferrum/node.rb b/lib/ferrum/node.rb index 99051a8f..f8ca9b45 100644 --- a/lib/ferrum/node.rb +++ b/lib/ferrum/node.rb @@ -531,8 +531,8 @@ def exists? def bounding_rect_coordinates evaluate <<~JS - [this.getBoundingClientRect().left + window.pageXOffset + (this.offsetWidth / 2), - this.getBoundingClientRect().top + window.pageYOffset + (this.offsetHeight / 2)] + [this.getBoundingClientRect().left + (this.offsetWidth / 2), + this.getBoundingClientRect().top + (this.offsetHeight / 2)] JS end diff --git a/lib/ferrum/proxy.rb b/lib/ferrum/proxy.rb index a7bac649..24df1180 100644 --- a/lib/ferrum/proxy.rb +++ b/lib/ferrum/proxy.rb @@ -134,19 +134,19 @@ def do_CONNECT(req, res) if proxy @logger.debug("CONNECT: sending a Request-Line") - os << proxy_request_line << CRLF + os << proxy_request_line << WEBrick::CRLF @logger.debug("CONNECT: > #{proxy_request_line}") if credentials @logger.debug("CONNECT: sending credentials") - os << "Proxy-Authorization: " << credentials << CRLF + os << "Proxy-Authorization: " << credentials << WEBrick::CRLF end - os << CRLF - proxy_status_line = os.gets(LF) + os << WEBrick::CRLF + proxy_status_line = os.gets(WEBrick::LF) @logger.debug("CONNECT: read Status-Line from the upstream server") @logger.debug("CONNECT: < #{proxy_status_line}") if %r{^HTTP/\d+\.\d+\s+200\s*} =~ proxy_status_line - while line = os.gets(LF) - break if /\A(#{CRLF}|#{LF})\z/om =~ line + while line = os.gets(WEBrick::LF) + break if /\A(#{WEBrick::CRLF}|#{WEBrick::LF})\z/om =~ line end else raise WEBrick::HTTPStatus::BadGateway @@ -170,7 +170,7 @@ def do_CONNECT(req, res) # Should clear request-line not to send the response twice. # see: HTTPServer#run - req.parse(NullReader) rescue nil + req.parse(WEBrick::NullReader) rescue nil end begin diff --git a/sig/ferrum/node.rbs b/sig/ferrum/node.rbs index b47d5a28..a8e7cd17 100644 --- a/sig/ferrum/node.rbs +++ b/sig/ferrum/node.rbs @@ -82,7 +82,7 @@ module Ferrum private - def bounding_rect_coordinates: () -> untyped + def bounding_rect_coordinates: () -> [Numeric, Numeric] def content_quads: () -> untyped diff --git a/spec/browser_spec.rb b/spec/browser_spec.rb index 2ec9237a..a0487437 100644 --- a/spec/browser_spec.rb +++ b/spec/browser_spec.rb @@ -583,24 +583,28 @@ def proxy_tunnel(socket, request, browser_url) end end - context "with rotation", skip: "Think how to make it working on CI" do - it "works after disposing context" do + context "with rotation" do + let(:upstream) { Ferrum::Proxy.start(user: "user", password: "pa$$") } + + after { upstream.stop } + + it "forwards connections to the upstream proxy in a new context", :launch do browser = Ferrum::Browser.new( - proxy: { host: proxy.host, port: proxy.port, **options } + proxy: { host: proxy.host, port: proxy.port } ) - proxy.rotate(host: "host", port: 0, user: "user", password: "password") + proxy.rotate(host: upstream.host, port: upstream.port, user: "user", password: "pa$$") browser.create_page(new_context: true) do |page| - page.go_to("https://api.ipify.org?format=json") + page.go_to("https://example.com") expect(page.network.status).to eq(200) - expect(page.body).to include("x.x.x.x") + expect(page.body).to include("Example Domain") end - proxy.rotate(host: "host", port: 0, user: "user", password: "password") + proxy.rotate(host: upstream.host, port: upstream.port, user: "user", password: "wrong") browser.create_page(new_context: true) do |page| - page.go_to("https://api.ipify.org?format=json") - expect(page.network.status).to eq(200) - expect(page.body).to include("x.x.x.x") + expect { page.go_to("https://example.com") }.to raise_error( + Ferrum::StatusError, /ERR_TUNNEL_CONNECTION_FAILED/ + ) end ensure browser&.quit @@ -631,9 +635,6 @@ def proxy_tunnel(socket, request, browser_url) let(:save_path) { "/tmp/ferrum" } it "saves an attachment" do - # Also https://github.com/puppeteer/puppeteer/issues/10161 - skip "https://bugs.chromium.org/p/chromium/issues/detail?id=1444729" - browser.go_to("/#{filename}") browser.downloads.wait diff --git a/spec/frame_spec.rb b/spec/frame_spec.rb index ff31b6dd..712e3d08 100644 --- a/spec/frame_spec.rb +++ b/spec/frame_spec.rb @@ -152,32 +152,20 @@ end end - it "supports clicking in a frame", skip: true do - page.go_to - page.execute <<-JS - document.body.innerHTML += "