capybara-simulated 0.0.6 → 0.0.7
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/lib/capybara/simulated/version.rb +1 -1
- data/vendor/js/prelude.js +4 -0
- data/vendor/js/runtime.js +43 -9
- 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: 3d7b5f908d1c75a41fabf90b90c779ae21813de6a4620a7b574e4dfca69134cf
|
|
4
|
+
data.tar.gz: 57ddffb46aca509cd981106c4ac0ad421d38956c52fba5d439add59ae2e10aab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 576d88b9d8fe7d0c480e8ad7309e7861d9be563a95ab992f6d870f66c06a12d0255f779622ac25e7bab19aa7c6868bf929593e31fe146b4b3fe4d01e3447a37b
|
|
7
|
+
data.tar.gz: 8cfe9a00eff04e4df4d8aa3b1938f122001a00bc7e15f0f3c5b92975fd51c82b2d115aa9b5031ead063a236e4b4c81159ec9d947ea7e5df948e165d5047f3055
|
data/vendor/js/prelude.js
CHANGED
|
@@ -131,6 +131,7 @@
|
|
|
131
131
|
globalThis.__csim_runTimers = function (ms) {
|
|
132
132
|
const advance = (typeof ms === 'number') ? Math.max(0, ms) : Infinity;
|
|
133
133
|
_virtualClock += advance;
|
|
134
|
+
let everFired = false;
|
|
134
135
|
while (true) {
|
|
135
136
|
let fired = false;
|
|
136
137
|
const due = [];
|
|
@@ -141,10 +142,13 @@
|
|
|
141
142
|
_timers.delete(id);
|
|
142
143
|
try { t.fn(); } catch (_) {}
|
|
143
144
|
fired = true;
|
|
145
|
+
everFired = true;
|
|
144
146
|
}
|
|
145
147
|
if (!fired) break;
|
|
146
148
|
}
|
|
149
|
+
return everFired;
|
|
147
150
|
};
|
|
151
|
+
globalThis.__csim_pendingTimerCount = function () { return _timers.size; };
|
|
148
152
|
globalThis.__csim_clearTimers = function () {
|
|
149
153
|
_timers.clear();
|
|
150
154
|
_virtualClock = 0;
|
data/vendor/js/runtime.js
CHANGED
|
@@ -11,6 +11,19 @@
|
|
|
11
11
|
|
|
12
12
|
const handles = new Map();
|
|
13
13
|
let nextHandleId = 0;
|
|
14
|
+
|
|
15
|
+
// Capybara's `have_text` matchers and several internal checks call into
|
|
16
|
+
// `visibleText` repeatedly against the same handle while polling. The
|
|
17
|
+
// text-collection walks happy-dom's textContent / styles each call, so
|
|
18
|
+
// re-running it 1000+ times per spec adds up. Cache by handle and drop
|
|
19
|
+
// the whole cache the moment any mutating runtime method runs (or
|
|
20
|
+
// `drainTimers` advances the virtual clock — Stimulus / Turbo timers
|
|
21
|
+
// may have rewritten the page). The cache is intentionally narrow:
|
|
22
|
+
// we measured a broader MutationObserver-driven cache as a wash, since
|
|
23
|
+
// the dispatch overhead ate the cache wins; pinning a bare `Map.clear()`
|
|
24
|
+
// to known-mutating call sites is much cheaper.
|
|
25
|
+
const visibleTextCache = new Map();
|
|
26
|
+
function clearReadCache() { if (visibleTextCache.size) visibleTextCache.clear(); }
|
|
14
27
|
let currentWindow = null;
|
|
15
28
|
let currentDocument = null;
|
|
16
29
|
let activeHandleId = null;
|
|
@@ -47,6 +60,7 @@
|
|
|
47
60
|
catch (_) {}
|
|
48
61
|
}
|
|
49
62
|
handles.clear();
|
|
63
|
+
visibleTextCache.clear();
|
|
50
64
|
nextHandleId = 0;
|
|
51
65
|
activeHandleId = null;
|
|
52
66
|
modalQueue.length = 0;
|
|
@@ -890,8 +904,9 @@
|
|
|
890
904
|
|
|
891
905
|
function drainTimers(ms) {
|
|
892
906
|
if (typeof globalThis.__csim_runTimers === 'function') {
|
|
893
|
-
try { globalThis.__csim_runTimers(ms); } catch (_) {}
|
|
907
|
+
try { return !!globalThis.__csim_runTimers(ms); } catch (_) {}
|
|
894
908
|
}
|
|
909
|
+
return false;
|
|
895
910
|
}
|
|
896
911
|
|
|
897
912
|
// happy-dom has no layout engine, so getBoundingClientRect always returns
|
|
@@ -1308,10 +1323,13 @@
|
|
|
1308
1323
|
},
|
|
1309
1324
|
allText(id) { return String(lookup(id).textContent || ''); },
|
|
1310
1325
|
visibleText(id) {
|
|
1326
|
+
const cached = visibleTextCache.get(id);
|
|
1327
|
+
if (cached !== undefined) return cached;
|
|
1311
1328
|
const el = lookup(id);
|
|
1312
1329
|
// If any ancestor of `el` is hidden, the element renders nothing.
|
|
1313
|
-
|
|
1314
|
-
|
|
1330
|
+
const txt = visibleAncestorChainOk(el) ? String(visibleTextOf(el)) : '';
|
|
1331
|
+
visibleTextCache.set(id, txt);
|
|
1332
|
+
return txt;
|
|
1315
1333
|
},
|
|
1316
1334
|
path(id) { return String(buildXPath(lookup(id))); },
|
|
1317
1335
|
rect(id) {
|
|
@@ -1350,6 +1368,7 @@
|
|
|
1350
1368
|
},
|
|
1351
1369
|
|
|
1352
1370
|
setValue(id, value) {
|
|
1371
|
+
clearReadCache();
|
|
1353
1372
|
const el = lookup(id);
|
|
1354
1373
|
const tag = (el.tagName || '').toUpperCase();
|
|
1355
1374
|
const type = (el.type || (el.getAttribute && el.getAttribute('type')) || '').toLowerCase();
|
|
@@ -1457,6 +1476,7 @@
|
|
|
1457
1476
|
},
|
|
1458
1477
|
|
|
1459
1478
|
selectOption(id) {
|
|
1479
|
+
clearReadCache();
|
|
1460
1480
|
const opt = lookup(id);
|
|
1461
1481
|
const select = opt.parentNode && (opt.parentNode.tagName === 'SELECT'
|
|
1462
1482
|
? opt.parentNode
|
|
@@ -1475,6 +1495,7 @@
|
|
|
1475
1495
|
return true;
|
|
1476
1496
|
},
|
|
1477
1497
|
unselectOption(id) {
|
|
1498
|
+
clearReadCache();
|
|
1478
1499
|
const opt = lookup(id);
|
|
1479
1500
|
const select = opt.closest && opt.closest('select');
|
|
1480
1501
|
if (!select || !isMultipleSelect(select)) return false;
|
|
@@ -1521,8 +1542,8 @@
|
|
|
1521
1542
|
boolPropOrAttr(lookup(id), 'readonly'); },
|
|
1522
1543
|
multiple(id) { return boolPropOrAttr(lookup(id), 'multiple'); },
|
|
1523
1544
|
|
|
1524
|
-
focus(id) { activeHandleId = id; lookup(id).dispatchEvent(makeEvent('focus', {bubbles: false})); return true; },
|
|
1525
|
-
blur(id) { lookup(id).dispatchEvent(makeEvent('blur', {bubbles: false})); if (activeHandleId === id) activeHandleId = null; return true; },
|
|
1545
|
+
focus(id) { clearReadCache(); activeHandleId = id; lookup(id).dispatchEvent(makeEvent('focus', {bubbles: false})); return true; },
|
|
1546
|
+
blur(id) { clearReadCache(); lookup(id).dispatchEvent(makeEvent('blur', {bubbles: false})); if (activeHandleId === id) activeHandleId = null; return true; },
|
|
1526
1547
|
activeElement() {
|
|
1527
1548
|
// Honour the JS-side `document.activeElement` first — happy-dom
|
|
1528
1549
|
// updates it when scripts call `focus()` directly, which the
|
|
@@ -1534,14 +1555,22 @@
|
|
|
1534
1555
|
},
|
|
1535
1556
|
|
|
1536
1557
|
hover(id) {
|
|
1558
|
+
clearReadCache();
|
|
1537
1559
|
const el = lookup(id);
|
|
1538
1560
|
el.dispatchEvent(makeEvent('mouseover'));
|
|
1539
1561
|
el.dispatchEvent(makeEvent('mouseenter', {bubbles: false}));
|
|
1540
1562
|
return true;
|
|
1541
1563
|
},
|
|
1542
|
-
trigger(id, name) { lookup(id).dispatchEvent(makeEvent(name)); return true; },
|
|
1543
|
-
|
|
1544
|
-
drainTimers(ms) {
|
|
1564
|
+
trigger(id, name) { clearReadCache(); lookup(id).dispatchEvent(makeEvent(name)); return true; },
|
|
1565
|
+
|
|
1566
|
+
drainTimers(ms) {
|
|
1567
|
+
// Only clear the read cache if a timer actually fired — most
|
|
1568
|
+
// `advance_virtual_clock` calls from Ruby have nothing to do but
|
|
1569
|
+
// tick the wall clock. Without this the visibleText cache is
|
|
1570
|
+
// invalidated on every cross-bridge call and never hits.
|
|
1571
|
+
if (drainTimers(ms)) clearReadCache();
|
|
1572
|
+
return true;
|
|
1573
|
+
},
|
|
1545
1574
|
|
|
1546
1575
|
consumeHistoryPushed() {
|
|
1547
1576
|
const v = _historyPushed;
|
|
@@ -1564,6 +1593,7 @@
|
|
|
1564
1593
|
},
|
|
1565
1594
|
|
|
1566
1595
|
click(id, button, modifiers, skipDown) {
|
|
1596
|
+
clearReadCache();
|
|
1567
1597
|
const el = lookup(id);
|
|
1568
1598
|
activeHandleId = id;
|
|
1569
1599
|
const m = Object.assign({button: button || 0}, modifiers || {});
|
|
@@ -1710,6 +1740,7 @@
|
|
|
1710
1740
|
},
|
|
1711
1741
|
|
|
1712
1742
|
doubleClick(id, modifiers) {
|
|
1743
|
+
clearReadCache();
|
|
1713
1744
|
const el = lookup(id);
|
|
1714
1745
|
const m = Object.assign({button: 0}, modifiers || {});
|
|
1715
1746
|
applyClickOffset(el, m);
|
|
@@ -1723,6 +1754,7 @@
|
|
|
1723
1754
|
return {action: 'none'};
|
|
1724
1755
|
},
|
|
1725
1756
|
rightClick(id, modifiers, skipDown) {
|
|
1757
|
+
clearReadCache();
|
|
1726
1758
|
const el = lookup(id);
|
|
1727
1759
|
const m = Object.assign({button: 2}, modifiers || {});
|
|
1728
1760
|
applyClickOffset(el, m);
|
|
@@ -1738,6 +1770,7 @@
|
|
|
1738
1770
|
// exposes `DragEvent` as a plain `Event`, so we paste `dataTransfer`
|
|
1739
1771
|
// onto each event before dispatch.
|
|
1740
1772
|
drop(id, items) {
|
|
1773
|
+
clearReadCache();
|
|
1741
1774
|
const el = lookup(id);
|
|
1742
1775
|
const win = currentWindow;
|
|
1743
1776
|
const dt = new win.DataTransfer();
|
|
@@ -1764,9 +1797,10 @@
|
|
|
1764
1797
|
return true;
|
|
1765
1798
|
},
|
|
1766
1799
|
|
|
1767
|
-
submit(id) { return submitDescriptor(lookup(id), null); },
|
|
1800
|
+
submit(id) { clearReadCache(); return submitDescriptor(lookup(id), null); },
|
|
1768
1801
|
|
|
1769
1802
|
sendKeys(id, keys) {
|
|
1803
|
+
clearReadCache();
|
|
1770
1804
|
const el = lookup(id);
|
|
1771
1805
|
const editable = (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA');
|
|
1772
1806
|
let formToSubmit = null;
|