capybara-simulated 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- data/README.md +37 -5
- data/exe/capybara-simulated +143 -0
- data/lib/capybara/simulated/browser.rb +570 -53
- data/lib/capybara/simulated/driver.rb +52 -6
- data/lib/capybara/simulated/js/bridge.bundle.js +11575 -5716
- data/lib/capybara/simulated/js/snapshot_stubs.js +34 -0
- data/lib/capybara/simulated/minitest.rb +65 -0
- data/lib/capybara/simulated/quickjs_runtime.rb +9 -0
- data/lib/capybara/simulated/rspec.rb +32 -0
- data/lib/capybara/simulated/runtime_shared.rb +26 -2
- data/lib/capybara/simulated/trace.rb +35 -2
- data/lib/capybara/simulated/trace_persistence.rb +48 -0
- data/lib/capybara/simulated/trace_viewer.html +408 -0
- data/lib/capybara/simulated/v8_runtime.rb +182 -17
- data/lib/capybara/simulated/version.rb +1 -1
- data/vendor/js/vendor.bundle.js +21 -10
- metadata +23 -3
|
@@ -264,10 +264,10 @@ module Capybara
|
|
|
264
264
|
# matches an existing window navigates that window instead of opening a
|
|
265
265
|
# new one (HTML window-name targeting); `opener_handle` records the
|
|
266
266
|
# opener so the new window's `window.opener` resolves back to it.
|
|
267
|
-
def open_aux_window(url = nil, name: nil, opener_handle: nil)
|
|
267
|
+
def open_aux_window(url = nil, name: nil, opener_handle: nil, source: nil, blob_snapshot: nil)
|
|
268
268
|
name = name.to_s
|
|
269
269
|
if !name.empty? && (existing = @aux_windows.find {|w| w[:name] == name })
|
|
270
|
-
navigate_window(existing[:browser], url)
|
|
270
|
+
navigate_window(existing[:browser], url, source: source)
|
|
271
271
|
return existing[:handle]
|
|
272
272
|
end
|
|
273
273
|
@next_window_seq += 1
|
|
@@ -278,7 +278,15 @@ module Capybara
|
|
|
278
278
|
# `window.opener`, which resolves through this entry — so the entry
|
|
279
279
|
# (with its opener) must exist before `visit` runs those scripts.
|
|
280
280
|
@aux_windows << {handle: handle, browser: aux, name: name, opener: opener_handle}
|
|
281
|
-
|
|
281
|
+
if url && !url.empty?
|
|
282
|
+
# A blob: URL isn't rack-navigable and its bytes live in the OPENER's
|
|
283
|
+
# isolate — load the document directly from a click-time snapshot (a
|
|
284
|
+
# deferred target=_blank nav may revoke the URL first) or, failing that,
|
|
285
|
+
# the opener's blob store.
|
|
286
|
+
unless url.to_s.start_with?('blob:') && load_blob_into_window(aux, url, source, snapshot: blob_snapshot)
|
|
287
|
+
aux.visit(url)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
282
290
|
handle
|
|
283
291
|
rescue StandardError => e
|
|
284
292
|
# Aux window URL-load failure (binary content, network error, …)
|
|
@@ -298,7 +306,23 @@ module Capybara
|
|
|
298
306
|
# against the opener's document and records the opener relationship.
|
|
299
307
|
def open_window_from_js(opener_browser, url, name)
|
|
300
308
|
resolved = url.to_s.empty? ? nil : opener_browser.resolve_document_url(url)
|
|
301
|
-
open_aux_window(resolved, name: name, opener_handle: handle_for(opener_browser))
|
|
309
|
+
open_aux_window(resolved, name: name, opener_handle: handle_for(opener_browser), source: opener_browser)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Load a blob: document into aux window `aux` from `source`'s (the opener's)
|
|
313
|
+
# local blob store — the bytes live in the opener's isolate, not the aux's.
|
|
314
|
+
# Returns false if `source`/bytes are unavailable (caller falls back).
|
|
315
|
+
private def load_blob_into_window(aux, url, source, snapshot: nil)
|
|
316
|
+
data = if snapshot.is_a?(Hash) && snapshot['b64']
|
|
317
|
+
{ bytes: Base64.decode64(snapshot['b64'].to_s), type: snapshot['type'].to_s }
|
|
318
|
+
elsif source.respond_to?(:read_blob_for_window)
|
|
319
|
+
source.read_blob_for_window(url)
|
|
320
|
+
end
|
|
321
|
+
return false unless data
|
|
322
|
+
aux.boot_blob_document(url, data[:bytes], data[:type])
|
|
323
|
+
true
|
|
324
|
+
rescue StandardError
|
|
325
|
+
false
|
|
302
326
|
end
|
|
303
327
|
|
|
304
328
|
# `targetWindow.postMessage(data, origin)` — queue on the target window's
|
|
@@ -308,10 +332,25 @@ module Capybara
|
|
|
308
332
|
target.enqueue_window_message(data, _origin, handle_for(source_browser))
|
|
309
333
|
end
|
|
310
334
|
|
|
335
|
+
# `BroadcastChannel.postMessage` — deliver to every OTHER window's channels
|
|
336
|
+
# with the same name (same-window delivery is handled in-VM by the sender).
|
|
337
|
+
def broadcast_channel(source_browser, name, data)
|
|
338
|
+
window_entries.each do |w|
|
|
339
|
+
next if w[:browser].equal?(source_browser)
|
|
340
|
+
w[:browser].enqueue_broadcast(name, data)
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
311
344
|
def window_location(handle) = (window_browser(handle)&.current_url).to_s
|
|
345
|
+
# A cross-window property read (`win.foo` / `win.document.foo`) — read the
|
|
346
|
+
# primitive off the target window's VM.
|
|
347
|
+
def window_read(handle, prop, doc: false)
|
|
348
|
+
b = window_browser(handle) or return nil
|
|
349
|
+
b.read_property(prop, doc: doc)
|
|
350
|
+
end
|
|
312
351
|
def window_set_location(handle, url)
|
|
313
352
|
b = window_browser(handle) or return
|
|
314
|
-
navigate_window(b, b.resolve_document_url(url))
|
|
353
|
+
navigate_window(b, b.resolve_document_url(url), source: current_browser)
|
|
315
354
|
end
|
|
316
355
|
def window_closed?(handle) = window_browser(handle).nil?
|
|
317
356
|
def opener_handle_of(browser)
|
|
@@ -326,8 +365,15 @@ module Capybara
|
|
|
326
365
|
# navigating it synchronously (`visit` → `rebuild_ctx`) would dispose the
|
|
327
366
|
# V8 context mid-call and abort the running handler. A non-active window
|
|
328
367
|
# can navigate immediately (its VM isn't on the stack).
|
|
329
|
-
private def navigate_window(browser, url)
|
|
368
|
+
private def navigate_window(browser, url, source: nil)
|
|
330
369
|
return if url.nil? || url.to_s.empty?
|
|
370
|
+
# A blob: navigation loads directly from the opener's blob bytes (not
|
|
371
|
+
# rack-navigable, cross-isolate). Only when the target isn't the active
|
|
372
|
+
# window (boot rebuilds its ctx — unsafe mid-call on the running window).
|
|
373
|
+
if url.to_s.start_with?('blob:') && !browser.equal?(current_browser) &&
|
|
374
|
+
load_blob_into_window(browser, url, source)
|
|
375
|
+
return
|
|
376
|
+
end
|
|
331
377
|
if browser.equal?(current_browser)
|
|
332
378
|
browser.location_assign(url)
|
|
333
379
|
else
|