Module: Panda::Core::Testing::CupriteHelpers
- Included in:
- SystemTestHelpers
- Defined in:
- lib/panda/core/testing/support/system/cuprite_helpers.rb
Instance Method Summary collapse
-
#browser_debug ⇒ Object
Drop #browser_debug anywhere in a test to open a Chrome inspector and pause the execution Usage: browser_debug(binding).
-
#capybara_artifacts_dir ⇒ Object
Resolve the configured Capybara artifacts directory.
-
#click_on_selectors(*css_selectors) ⇒ Object
Allows sending a list of CSS selectors to be clicked on in the correct order (no delay) Useful where you need to trigger e.g.
-
#ensure_page_loaded ⇒ Object
Ensure page is loaded and stable before interacting.
-
#pause ⇒ Object
Drop #pause anywhere in a test to stop the execution.
-
#record_video!(name = nil, seconds: 3) ⇒ Object
Record a small MP4 video of the test — uses Cuprite’s Chrome DevTools API.
- #safe_click_button(locator) ⇒ Object
-
#safe_fill_in(locator, with:) ⇒ Object
Safe methods that handle Ferrum NodeNotFoundError in CI.
- #safe_select(value, from:) ⇒ Object
- #save_html!(name = nil) ⇒ Object
-
#save_screenshot!(name = nil) ⇒ Object
Save a PNG screenshot for the current page.
-
#wait_for_dom_mutation(timeout: 5) ⇒ Boolean
Waits for JavaScript to modify the DOM.
-
#wait_for_field_value(field_name, value, timeout: 5) ⇒ Object
Wait for a field to have a specific value.
-
#wait_for_javascript(timeout: 5) ⇒ Object
Wait for JavaScript to load (application-specific flag) Override in your application if you have a custom loaded flag.
-
#wait_for_network_idle(timeout: 5) ⇒ Boolean
Waits for network requests to complete.
-
#wait_for_ready_state ⇒ Object
Wait for document ready state.
-
#wait_for_selector(selector, timeout: 5) ⇒ Boolean
Waits for a specific selector to be present and visible on the page.
-
#wait_for_text(text, timeout: 5) ⇒ Boolean
Waits for a specific text to be present on the page.
Instance Method Details
#browser_debug ⇒ Object
Drop #browser_debug anywhere in a test to open a Chrome inspector and pause the execution Usage: browser_debug(binding)
219 220 221 222 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 219 def browser_debug(*) # Cuprite-specific debug method page.driver.debug end |
#capybara_artifacts_dir ⇒ Object
Resolve the configured Capybara artifacts directory
22 23 24 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 22 def Pathname.new(Capybara.save_path || Rails.root.join("tmp/capybara")) end |
#click_on_selectors(*css_selectors) ⇒ Object
Allows sending a list of CSS selectors to be clicked on in the correct order (no delay) Useful where you need to trigger e.g. a blur event on an input field
226 227 228 229 230 231 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 226 def click_on_selectors(*css_selectors) css_selectors.each do |selector| find(selector).click # sleep 0.1 # Add a small delay to allow JavaScript to run end end |
#ensure_page_loaded ⇒ Object
Ensure page is loaded and stable before interacting
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 99 def ensure_page_loaded # Check if we're on about:blank and need to reload current_url = begin page.current_url rescue "unknown" end if current_url.include?("about:blank") puts "[CI] Page is on about:blank, skipping recovery to avoid loops" if ENV["GITHUB_ACTIONS"] # Don't try to recover - let the test handle it return false end # Wait for page to be ready wait_for_ready_state # Wait for JavaScript to load wait_for_javascript true end |
#pause ⇒ Object
Drop #pause anywhere in a test to stop the execution. Useful when you want to checkout the contents of a web page in the middle of a test running in a headful mode.
212 213 214 215 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 212 def pause # Cuprite-specific pause method page.driver.pause end |
#record_video!(name = nil, seconds: 3) ⇒ Object
Record a small MP4 video of the test — uses Cuprite’s Chrome DevTools API
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 42 def record_video!(name = nil, seconds: 3) name ||= example.[:full_description].parameterize path = .join("#{name}.mp4") FileUtils.mkdir_p(File.dirname(path)) session = page.driver.browser client = session.client # Enable screencast client.command("Page.startScreencast", format: "png", quality: 80, maxWidth: 1280, maxHeight: 800) frames = [] start = Time.now while Time.now - start < seconds = client.listen if ["method"] == "Page.screencastFrame" frames << ["params"]["data"] client.command("Page.screencastFrameAck", sessionId: ["params"]["sessionId"]) end end # Stop client.command("Page.stopScreencast") # Convert frames to MP4 using ffmpeg Dir.mktmpdir do |dir| png_dir = File.join(dir, "frames") FileUtils.mkdir_p(png_dir) frames.each_with_index do |data, i| File.binwrite(File.join(png_dir, "frame-%05d.png" % i), Base64.decode64(data)) end system <<~CMD ffmpeg -y -framerate 8 -pattern_type glob -i '#{png_dir}/*.png' -c:v libx264 -pix_fmt yuv420p '#{path}' CMD end puts "🎥 Saved video: #{path}" path rescue => e puts "⚠️ Failed to record video: #{e.}" nil end |
#safe_click_button(locator) ⇒ Object
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 292 def (locator) retries = 0 start_time = Time.now max_duration = 5 # Maximum 5 seconds total begin locator rescue Ferrum::NodeNotFoundError, Capybara::ElementNotFound => e retries += 1 elapsed = Time.now - start_time if retries <= 2 && elapsed < max_duration && ENV["GITHUB_ACTIONS"] puts "[CI] Element not found on click_button '#{locator}', retry #{retries}/2 (#{elapsed.round(1)}s elapsed)" # sleep 0.5 retry else puts "[CI] Giving up on click_button '#{locator}' after #{retries} retries and #{elapsed.round(1)}s" if ENV["GITHUB_ACTIONS"] raise e end end end |
#safe_fill_in(locator, with:) ⇒ Object
Safe methods that handle Ferrum NodeNotFoundError in CI
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 248 def safe_fill_in(locator, with:) retries = 0 start_time = Time.now max_duration = 5 # Maximum 5 seconds total begin fill_in locator, with: with rescue Ferrum::NodeNotFoundError, Capybara::ElementNotFound => e retries += 1 elapsed = Time.now - start_time if retries <= 2 && elapsed < max_duration && ENV["GITHUB_ACTIONS"] puts "[CI] Element not found on fill_in '#{locator}', retry #{retries}/2 (#{elapsed.round(1)}s elapsed)" # sleep 0.5 retry else puts "[CI] Giving up on fill_in '#{locator}' after #{retries} retries and #{elapsed.round(1)}s" if ENV["GITHUB_ACTIONS"] raise e end end end |
#safe_select(value, from:) ⇒ Object
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 270 def safe_select(value, from:) retries = 0 start_time = Time.now max_duration = 5 # Maximum 5 seconds total begin select value, from: from rescue Ferrum::NodeNotFoundError, Capybara::ElementNotFound => e retries += 1 elapsed = Time.now - start_time if retries <= 2 && elapsed < max_duration && ENV["GITHUB_ACTIONS"] puts "[CI] Element not found on select '#{value}' from '#{from}', retry #{retries}/2 (#{elapsed.round(1)}s elapsed)" # sleep 0.5 retry else puts "[CI] Giving up on select '#{value}' from '#{from}' after #{retries} retries and #{elapsed.round(1)}s" if ENV["GITHUB_ACTIONS"] raise e end end end |
#save_html!(name = nil) ⇒ Object
89 90 91 92 93 94 95 96 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 89 def save_html!(name = nil) name ||= example.[:full_description].parameterize path = .join("#{name}.html") FileUtils.mkdir_p(File.dirname(path)) File.write(path, page.html) puts "📝 Saved HTML snapshot: #{path}" path end |
#save_screenshot!(name = nil) ⇒ Object
Save a PNG screenshot for the current page.
28 29 30 31 32 33 34 35 36 37 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 28 def save_screenshot!(name = nil) name ||= example.[:full_description].parameterize path = .join("#{name}.png") FileUtils.mkdir_p(File.dirname(path)) page.save_screenshot(path, full: true) # rubocop:disable Lint/Debugger puts "📸 Saved screenshot: #{path}" path end |
#wait_for_dom_mutation(timeout: 5) ⇒ Boolean
Waits for JavaScript to modify the DOM
198 199 200 201 202 203 204 205 206 207 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 198 def wait_for_dom_mutation(timeout: 5) start_time = Time.now initial_dom = page.html while Time.now - start_time < timeout return true if page.html != initial_dom # sleep 0.1 end false end |
#wait_for_field_value(field_name, value, timeout: 5) ⇒ Object
Wait for a field to have a specific value
237 238 239 240 241 242 243 244 245 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 237 def wait_for_field_value(field_name, value, timeout: 5) start_time = Time.now while Time.now - start_time < timeout return true if page.has_field?(field_name, with: value) # sleep 0.1 end false end |
#wait_for_javascript(timeout: 5) ⇒ Object
Wait for JavaScript to load (application-specific flag) Override in your application if you have a custom loaded flag
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 135 def wait_for_javascript(timeout: 5) Timeout.timeout(timeout) do loop do loaded = begin # Check for common JavaScript loaded indicators page.evaluate_script("document.readyState === 'complete'") rescue false end break if loaded # sleep 0.1 end end true rescue Timeout::Error puts "[CI] Timeout waiting for JavaScript to load" if ENV["GITHUB_ACTIONS"] false end |
#wait_for_network_idle(timeout: 5) ⇒ Boolean
Waits for network requests to complete
186 187 188 189 190 191 192 193 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 186 def wait_for_network_idle(timeout: 5) # Cuprite has direct network idle support page.driver.wait_for_network_idle(timeout: timeout) true rescue => e puts "[CI] Network idle timeout: #{e.}" if ENV["GITHUB_ACTIONS"] false end |
#wait_for_ready_state ⇒ Object
Wait for document ready state
120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 120 def wait_for_ready_state Timeout.timeout(5) do loop do ready = page.evaluate_script("document.readyState") break if ready == "complete" # sleep 0.1 end end rescue Timeout::Error puts "[CI] Timeout waiting for document ready state" if ENV["GITHUB_ACTIONS"] end |
#wait_for_selector(selector, timeout: 5) ⇒ Boolean
Waits for a specific selector to be present and visible on the page
159 160 161 162 163 164 165 166 167 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 159 def wait_for_selector(selector, timeout: 5) start_time = Time.now while Time.now - start_time < timeout return true if page.has_css?(selector, visible: true) # sleep 0.1 end false end |
#wait_for_text(text, timeout: 5) ⇒ Boolean
Waits for a specific text to be present on the page
173 174 175 176 177 178 179 180 181 |
# File 'lib/panda/core/testing/support/system/cuprite_helpers.rb', line 173 def wait_for_text(text, timeout: 5) start_time = Time.now while Time.now - start_time < timeout return true if page.has_text?(text) # sleep 0.1 end false end |