capybara-simulated 0.1.1 → 0.3.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 +91 -9
- data/lib/capybara/simulated/browser.rb +730 -73
- data/lib/capybara/simulated/driver.rb +92 -13
- data/lib/capybara/simulated/errors.rb +7 -0
- data/lib/capybara/simulated/js/bridge.bundle.js +608 -67
- data/lib/capybara/simulated/node.rb +19 -3
- data/lib/capybara/simulated/runtime_shared.rb +15 -0
- data/lib/capybara/simulated/v8_runtime.rb +64 -9
- data/lib/capybara/simulated/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 02f56bfd3215e67c4a8faa799352e0ae3c399557f928926cafcdc9dde7dd2b9c
|
|
4
|
+
data.tar.gz: 12e88e441d93de921d0727bfae9e3728cc56d100f8e32e525e37e21a28f0e273
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a1f22b4a5d7ffec1b33080982aa169b5b3356c014d49a399de4cf5b5a430021eec7fb1ba4d624b00769640ffdcd314e55df765afdd44ac90575c88cb137519c9
|
|
7
|
+
data.tar.gz: 04d95fd0e7215800124d4d81de11247b814e352c0b511d550bb9bb1557a0af069c1261b52a570e82f6100cab90e07d627b18b7a02267f193dd92c4c98c92e2cd
|
data/README.md
CHANGED
|
@@ -14,6 +14,59 @@ resolve through css-select (CSS) and xpathway (XPath) running in the
|
|
|
14
14
|
same context as the page's JS, so `find` / `has_css?` / `within` see
|
|
15
15
|
exactly the tree the app sees.
|
|
16
16
|
|
|
17
|
+
## Is it a fit?
|
|
18
|
+
|
|
19
|
+
**A good fit when** your tests are JavaScript-driven but don't depend on
|
|
20
|
+
visual layout:
|
|
21
|
+
|
|
22
|
+
- **Fast, in-process** — no Chrome to boot, no WebDriver, no Node
|
|
23
|
+
toolchain. About **1.9× faster** than a headless browser on
|
|
24
|
+
server-rendered / Hotwire apps, and roughly at parity on JS-heavy SPAs
|
|
25
|
+
(with rusty_racer).
|
|
26
|
+
- **Deterministic** — a virtual clock and synchronous in-process execution
|
|
27
|
+
remove the wall-clock timing, network, and rendering races that make
|
|
28
|
+
headless-browser suites flaky.
|
|
29
|
+
- **Real front-end JS runs**: inline `<script>` + event handlers,
|
|
30
|
+
MutationObserver, custom elements, `<template>`, Shadow DOM, ES modules
|
|
31
|
+
+ importmap, **Hotwire (Stimulus + Turbo)**, Trix.
|
|
32
|
+
- **Drop-in**: the Capybara DSL is unchanged — register `:simulated` and
|
|
33
|
+
go. Just this gem plus one JS-engine gem.
|
|
34
|
+
- **Held to spec**: a vendored
|
|
35
|
+
[web-platform-tests](https://github.com/web-platform-tests/wpt)
|
|
36
|
+
conformance gate plus five real app suites (see [Status](#status)).
|
|
37
|
+
|
|
38
|
+
**Reach for a real browser** (Selenium / Cuprite) **when** your tests need
|
|
39
|
+
what this driver doesn't simulate **by design** — there's no rendering
|
|
40
|
+
engine or real network stack, the same ground Selenium covers via a real
|
|
41
|
+
browser:
|
|
42
|
+
|
|
43
|
+
- **Pixel layout** — `getBoundingClientRect()` returns zeros and
|
|
44
|
+
`elementFromPoint()` isn't implemented, so visual hit-testing,
|
|
45
|
+
coordinate drag-and-drop, and sticky-scroll math don't work.
|
|
46
|
+
- **Real networking** — `fetch` / XHR are synchronous through Rack: no
|
|
47
|
+
streaming, no HTTP concurrency. (`EventSource` and `WebSocket` *do*
|
|
48
|
+
work — they ride real reader threads / the in-process `rack.hijack`
|
|
49
|
+
socket; see below.)
|
|
50
|
+
- **Screenshots**.
|
|
51
|
+
|
|
52
|
+
**`within_frame` / `switch_to_frame`** work on the V8 (rusty_racer) engine:
|
|
53
|
+
each `<iframe>` runs its own scripts in its own per-frame realm, and the DSL
|
|
54
|
+
routes finds, reads, interactions, `evaluate_script`, and self-targeted
|
|
55
|
+
navigation (a link or form submit inside the frame) into the active frame —
|
|
56
|
+
nested frames included. (QuickJS keeps a same-realm fallback, so
|
|
57
|
+
`within_frame` is V8-only.)
|
|
58
|
+
|
|
59
|
+
**Multiple windows / tabs** work on both engines: each window is its own
|
|
60
|
+
Browser + JS VM (own DOM, sessionStorage, history; cookies + localStorage
|
|
61
|
+
shared). `open_new_window` / `within_window` / `switch_to_window` /
|
|
62
|
+
`window_opened_by` drive them, and JS `window.open` opens a real window,
|
|
63
|
+
`window.opener` points back to the opener, and `postMessage` is delivered
|
|
64
|
+
across windows. (`target="_blank"` defaults to no-opener, matching modern
|
|
65
|
+
browsers; cross-window `postMessage` data is JSON-shaped, not a full
|
|
66
|
+
structured clone.)
|
|
67
|
+
|
|
68
|
+
See [Known limits](#known-limits) for the full picture.
|
|
69
|
+
|
|
17
70
|
## Status
|
|
18
71
|
|
|
19
72
|
The architecture and behaviour are stable. Correctness is held to two
|
|
@@ -305,15 +358,44 @@ referenced page-specific DOM.
|
|
|
305
358
|
but there's no real network, no streaming, no `Request#body`
|
|
306
359
|
ReadableStream, and no concurrent requests. XHR is implemented
|
|
307
360
|
with the same Rack pass-through.
|
|
308
|
-
- **
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
361
|
+
- **WebSocket** works in-process: `new WebSocket(url)` rides the
|
|
362
|
+
`rack.hijack` socket the Rack app hijacks, with a hand-rolled RFC6455
|
|
363
|
+
client (handshake + subprotocol negotiation, masked client frames,
|
|
364
|
+
ping/pong, close handshake). Frames deliver as `message` events when
|
|
365
|
+
the page next settles, like SSE. **Action Cable** works end-to-end on
|
|
366
|
+
this: the real `@rails/actioncable` consumer connects, subscribes, and
|
|
367
|
+
receives server broadcasts (so `turbo_stream_from` live updates are
|
|
368
|
+
reachable) — Action Cable hijacks the connection just as csim drives
|
|
369
|
+
it. Caveats: server pushes land at settle (not instant); the app must
|
|
370
|
+
use the **async / in-process** Cable adapter (a real Redis adapter
|
|
371
|
+
would need real Redis); binary frames are V8-only (QuickJS corrupts
|
|
372
|
+
raw bytes across the host boundary — text, hence Action Cable, is fine
|
|
373
|
+
on both). `EventSource` and Web Workers are likewise implemented.
|
|
374
|
+
- **Screenshots and drag pixel coordinates** are out of scope by
|
|
375
|
+
design — use Selenium / Cuprite.
|
|
376
|
+
- **`within_frame` / `switch_to_frame`** work on the V8 engine: each
|
|
377
|
+
`<iframe>` runs in its own per-frame realm and the DSL routes finds,
|
|
378
|
+
reads, interactions, `evaluate_script`, and self-targeted navigation
|
|
379
|
+
(link / form submit) into the active frame (nested frames included) — the
|
|
380
|
+
frame's realm is rebuilt from the fetched document, leaving the top page
|
|
381
|
+
untouched. `_top` navigates the main page; a `_parent` target from a
|
|
382
|
+
frame nested ≥2 levels falls back to navigating the main page, and
|
|
383
|
+
cross-origin frame locality resolves against the main origin. QuickJS has
|
|
384
|
+
no nested browsing context, so `within_frame` raises there.
|
|
385
|
+
- **Multiple windows / tabs** work on both engines: each window is its own
|
|
386
|
+
Browser + JS VM (own DOM, sessionStorage, history; cookies + localStorage
|
|
387
|
+
shared across windows). `open_new_window` / `within_window` /
|
|
388
|
+
`switch_to_window` / `window_opened_by` drive them; JS `window.open` opens
|
|
389
|
+
a real window, `window.opener` links back, and `postMessage` is routed
|
|
390
|
+
across windows (delivered as a `message` event when the target window next
|
|
391
|
+
settles). Caveats: `target="_blank"` opens with no opener (modern-browser
|
|
392
|
+
no-opener default); cross-window `postMessage` data is JSON-shaped, not a
|
|
393
|
+
full structured clone (no `DataCloneError`, `undefined`→`null`) — but a
|
|
394
|
+
buffer in the `transfer` list moves **zero-copy** (its backing store crosses
|
|
395
|
+
isolates by token and the source is detached); and only the active window's
|
|
396
|
+
event loop runs, so a message is delivered when you switch to its window.
|
|
397
|
+
Window viewport APIs (`maximize` / `fullscreen` /
|
|
398
|
+
pixel-exact `resize_to`) are no-ops — no layout engine.
|
|
317
399
|
|
|
318
400
|
## Architecture
|
|
319
401
|
|