capybara-simulated 0.8.0 → 0.9.0

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.
@@ -52,7 +52,10 @@ module Capybara
52
52
  @@live.select!(&:weakref_alive?)
53
53
  @@live.filter_map {|ref| ref.__getobj__ rescue nil }
54
54
  }
55
- drivers.each {|d| yield d if d.owner_thread == thread }
55
+ # A DISPOSED driver is not live: its runtime context is gone, so calling into it raises
56
+ # (`undefined method 'call' for nil` out of `run_loop_step`). `dispose` deregisters, but the
57
+ # predicate is the belt — a WeakRef stays in the list until GC actually collects.
58
+ drivers.each {|d| yield d if d.owner_thread == thread && !d.disposed? }
56
59
  end
57
60
 
58
61
  # `viewport: [w, h]` and `user_agent:` (typically supplied via
@@ -310,9 +313,18 @@ module Capybara
310
313
  # isolate per cross-origin file (hundreds over the suite); disposing here
311
314
  # incrementally is what reset_windows! already does for aux windows.
312
315
  def dispose
316
+ return if @disposed
317
+ @disposed = true
318
+ # Drop out of the live registry FIRST: everything below tears down the runtime this driver
319
+ # would be asked to step if `each_live_on_thread` still yielded it.
320
+ @@live_lock.synchronize { @@live.reject! {|ref| (ref.__getobj__ rescue nil).equal?(self) } }
313
321
  reset_windows!
314
322
  @browser.dispose rescue nil
315
323
  end
324
+
325
+ # Has this driver been permanently dropped? (A `reset!` between examples does NOT set this —
326
+ # that rebuilds the page on a live runtime.)
327
+ def disposed? = @disposed == true
316
328
  def go_back = current_browser.go_back
317
329
  def go_forward = current_browser.go_forward
318
330
  def reset_history! = current_browser.reset_history!
@@ -362,6 +374,12 @@ module Capybara
362
374
  window_entries.find {|w| w[:handle] == handle }&.fetch(:browser)
363
375
  end
364
376
 
377
+ # Same, but for the operations that ADDRESS a window rather than probe for one: a closed or
378
+ # unknown handle is Capybara's `WindowError`, never a silent fall back to the current window.
379
+ def window_browser!(handle)
380
+ window_browser(handle) or raise Capybara::WindowError, "Unknown window handle: #{handle}"
381
+ end
382
+
365
383
  # Open (or, by `name`, reuse) an auxiliary window. `target="_blank"`
366
384
  # clicks and `window.open` both land here. A non-empty `name` that
367
385
  # matches an existing window navigates that window instead of opening a
@@ -425,7 +443,7 @@ module Capybara
425
443
 
426
444
  # `window.open(url, name)` from the `opener` window's JS. Resolves the URL
427
445
  # against the opener's document and records the opener relationship.
428
- def open_window_from_js(opener_browser, url, name, opener_realm_id = 0)
446
+ def open_window_from_js(opener_browser, url, name, opener_realm_id = 0, about_base = nil, about_origin = nil)
429
447
  resolved = url.to_s.empty? ? nil : opener_browser.resolve_document_url(url)
430
448
  # Opening a blob: URL whose storage partition differs from the opener's
431
449
  # top-level site is forced NOOPENER (cross-partition-navigation): the new
@@ -441,7 +459,7 @@ module Capybara
441
459
  # cross-window scripting/adoption need no cross-isolate RPC. The opener's realm
442
460
  # id wires the popup's window.opener. Falls through to the separate-VM aux path
443
461
  # (cross-origin, or a URL we don't yet realm-load).
444
- if (rid = opener_browser.open_window_realm(resolved, name: name, opener_realm_id: opener_realm_id))
462
+ if (rid = opener_browser.open_window_realm(resolved, name: name, opener_realm_id: opener_realm_id, about_base: about_base, about_origin: about_origin))
445
463
  return rid
446
464
  end
447
465
  open_aux_window(resolved, name: name, opener_handle: handle_for(opener_browser), source: opener_browser)
@@ -634,7 +652,14 @@ module Capybara
634
652
  def open_new_window(_kind = :tab)
635
653
  open_aux_window('about:blank')
636
654
  end
637
- def window_size(_) = [current_browser.viewport_width, current_browser.viewport_height]
655
+ # Every window has its own viewport, so these address the window the
656
+ # handle names — not the active one. `Capybara::Window#resize_to` on a
657
+ # background window must resize THAT window and leave the current one
658
+ # (and Capybara's idea of which window is current) alone.
659
+ def window_size(handle)
660
+ b = window_browser!(handle)
661
+ [b.viewport_width, b.viewport_height]
662
+ end
638
663
  def close_window(h)
639
664
  return if h == PRIMARY_HANDLE
640
665
  @aux_windows.reject! {|w|
@@ -655,19 +680,23 @@ module Capybara
655
680
  end
656
681
  end
657
682
  def switch_to_window(h)
658
- if h == PRIMARY_HANDLE
659
- @active_handle = nil
660
- elsif @aux_windows.any? {|w| w[:handle] == h }
661
- @active_handle = h
662
- else
663
- raise Capybara::WindowError, "Unknown window handle: #{h}"
664
- end
683
+ window_browser!(h) # unknown / already-closed handle → WindowError
684
+ @active_handle = (h == PRIMARY_HANDLE ? nil : h)
665
685
  end
666
- def resize_window_to(_, w, h) = current_browser.set_viewport(w, h)
686
+ def resize_window_to(handle, w, h) = window_browser!(handle).set_viewport(w, h)
667
687
  # Forem's ahoy-tracking spec calls `driver.resize(w, h)` directly
668
688
  # rather than through `current_window.resize_to`.
669
689
  def resize(w, h) = current_browser.set_viewport(w, h)
670
- def maximize_window(_) ; nil ; end
690
+ # Both restore the window to the display it lives on (`Browser#screen_size`), which is where
691
+ # it started — so they undo a `resize_to` rather than doing nothing. Coarse: we model no
692
+ # window chrome, so a maximized window and a fullscreen one end up the same size (a real
693
+ # browser's fullscreen is taller by the chrome it hides).
694
+ def maximize_window(handle) = restore_window_size(handle)
695
+ def fullscreen_window(handle) = restore_window_size(handle)
696
+ private def restore_window_size(handle)
697
+ b = window_browser!(handle)
698
+ b.set_viewport(*b.screen_size)
699
+ end
671
700
 
672
701
  def evaluate_script(script, *args)
673
702
  unwrap(current_browser.evaluate_script(script, args))