From 40c34ab3417aade45db2d169fb93fc55ab9d42ae Mon Sep 17 00:00:00 2001
From: Dmitry Vorotilin <d.vorotilin@gmail.com>
Date: Mon, 19 Feb 2024 18:00:09 +0500
Subject: [PATCH] feat: Add Ferrum::Network#wait_for_idle! (#445)

BREAKING CHANGE: `wait_for_idle` now doesn't raise an error. Check your code and replace it with counterpart wait_for_idle!
---
 CHANGELOG.md          |  4 ++++
 README.md             | 16 ++++++++++++---
 lib/ferrum/network.rb | 20 +++++++++++++++----
 spec/network_spec.rb  | 45 ++++++++++++++++++++++++++++++++++++-------
 4 files changed, 71 insertions(+), 14 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c237483f..901dd37c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,8 +2,12 @@
 
 ### Added
 
+- `Ferrum::Network#wait_for_idle!` raises an error if timeout reached.
+
 ### Changed
 
+- `Ferrum::Network#wait_for_idle` now returns true or false. Doesn't raise an error [BREAKING CHANGE].
+
 ### Fixed
 
 ### Removed
diff --git a/README.md b/README.md
index cb65d9c3..350df06e 100644
--- a/README.md
+++ b/README.md
@@ -504,9 +504,9 @@ page.go_to("https://github.com/")
 page.network.status # => 200
 ```
 
-#### wait_for_idle(\*\*options)
+#### wait_for_idle(\*\*options) : `Boolean`
 
-Waits for network idle or raises `Ferrum::TimeoutError` error
+Waits for network idle, returns `true` in case of success and `false` if there are still connections.
 
 * options `Hash`
   * :connections `Integer` how many connections are allowed for network to be
@@ -519,7 +519,17 @@ Waits for network idle or raises `Ferrum::TimeoutError` error
 ```ruby
 page.go_to("https://example.com/")
 page.at_xpath("//a[text() = 'No UI changes button']").click
-page.network.wait_for_idle
+page.network.wait_for_idle # => true
+```
+
+#### wait_for_idle!(\*\*options)
+
+Waits for network idle or raises `Ferrum::TimeoutError` error. Accepts same arguments as `wait_for_idle`.
+
+```ruby
+page.go_to("https://example.com/")
+page.at_xpath("//a[text() = 'No UI changes button']").click
+page.network.wait_for_idle! # might raise an error
 ```
 
 #### clear(type)
diff --git a/lib/ferrum/network.rb b/lib/ferrum/network.rb
index 46845a40..2ede0d41 100644
--- a/lib/ferrum/network.rb
+++ b/lib/ferrum/network.rb
@@ -41,7 +41,7 @@ def initialize(page)
     end
 
     #
-    # Waits for network idle or raises {Ferrum::TimeoutError} error.
+    # Waits for network idle.
     #
     # @param [Integer] connections
     #   how many connections are allowed for network to be idling,
@@ -52,21 +52,33 @@ def initialize(page)
     # @param [Float] timeout
     #   During what time we try to check idle.
     #
-    # @raise [Ferrum::TimeoutError]
+    # @return [Boolean]
     #
     # @example
     #   browser.go_to("https://example.com/")
     #   browser.at_xpath("//a[text() = 'No UI changes button']").click
-    #   browser.network.wait_for_idle
+    #   browser.network.wait_for_idle # => false
     #
     def wait_for_idle(connections: 0, duration: 0.05, timeout: @page.timeout)
       start = Utils::ElapsedTime.monotonic_time
 
       until idle?(connections)
-        raise TimeoutError if Utils::ElapsedTime.timeout?(start, timeout)
+        return false if Utils::ElapsedTime.timeout?(start, timeout)
 
         sleep(duration)
       end
+
+      true
+    end
+
+    #
+    # Waits for network idle or raises {Ferrum::TimeoutError} error.
+    # Accepts same arguments as `wait_for_idle`.
+    #
+    # @raise [Ferrum::TimeoutError]
+    def wait_for_idle!(...)
+      result = wait_for_idle(...)
+      raise TimeoutError unless result
     end
 
     def idle?(connections = 0)
diff --git a/spec/network_spec.rb b/spec/network_spec.rb
index 3934b09e..4d8d16a9 100644
--- a/spec/network_spec.rb
+++ b/spec/network_spec.rb
@@ -36,15 +36,46 @@
     end
   end
 
-  it "#wait_for_idle" do
-    page.go_to("/show_cookies")
-    expect(page.body).not_to include("test_cookie")
+  describe "#wait_for_idle" do
+    it "returns true" do
+      page.go_to("/show_cookies")
+      expect(page.body).not_to include("test_cookie")
 
-    page.at_xpath("//button[text() = 'Set cookie slow']").click
-    network.wait_for_idle
-    page.refresh
+      page.at_xpath("//button[text() = 'Set cookie slow']").click
+      result = network.wait_for_idle
+      page.refresh
+
+      expect(result).to eq(true)
+      expect(page.body).to include("test_cookie")
+    end
+
+    it "returns false" do
+      page.go_to("/show_cookies")
+      expect(page.body).not_to include("test_cookie")
 
-    expect(page.body).to include("test_cookie")
+      page.at_xpath("//button[text() = 'Set cookie slow']").click
+      result = network.wait_for_idle(timeout: 0.2)
+
+      expect(result).to eq(false)
+    end
+  end
+
+  describe "#wait_for_idle!" do
+    it "raises an error" do
+      page.go_to("/show_cookies")
+      expect(page.body).not_to include("test_cookie")
+
+      page.at_xpath("//button[text() = 'Set cookie slow']").click
+      expect { network.wait_for_idle!(timeout: 0.2) }.to raise_error(Ferrum::TimeoutError)
+    end
+
+    it "raises no error" do
+      page.go_to("/show_cookies")
+      expect(page.body).not_to include("test_cookie")
+
+      page.at_xpath("//button[text() = 'Set cookie slow']").click
+      expect { network.wait_for_idle! }.not_to raise_error
+    end
   end
 
   describe "#idle?" do