dommy 0.5.0 → 0.7.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 +31 -13
- data/lib/dommy/animation.rb +288 -0
- data/lib/dommy/attr.rb +23 -11
- data/lib/dommy/backend/nokogiri_adapter.rb +51 -0
- data/lib/dommy/backend/nokolexbor_adapter.rb +80 -0
- data/lib/dommy/backend.rb +129 -0
- data/lib/dommy/blob.rb +2 -2
- data/lib/dommy/compression_streams.rb +147 -0
- data/lib/dommy/cookie_store.rb +128 -0
- data/lib/dommy/crypto.rb +396 -0
- data/lib/dommy/css.rb +7 -7
- data/lib/dommy/custom_elements.rb +6 -6
- data/lib/dommy/document.rb +190 -32
- data/lib/dommy/dom_parser.rb +5 -4
- data/lib/dommy/element.rb +356 -53
- data/lib/dommy/event.rb +431 -25
- data/lib/dommy/event_source.rb +131 -0
- data/lib/dommy/fetch.rb +76 -6
- data/lib/dommy/file_reader.rb +176 -0
- data/lib/dommy/form_data.rb +1 -3
- data/lib/dommy/history.rb +82 -0
- data/lib/dommy/html_collection.rb +4 -4
- data/lib/dommy/html_elements.rb +130 -67
- data/lib/dommy/internal/cookie_jar.rb +2 -0
- data/lib/dommy/internal/css_pseudo_handlers.rb +28 -0
- data/lib/dommy/internal/dom_matching.rb +4 -4
- data/lib/dommy/internal/idna.rb +443 -0
- data/lib/dommy/internal/idna_data.rb +10379 -0
- data/lib/dommy/internal/ipv4_parser.rb +78 -0
- data/lib/dommy/internal/node_traversal.rb +1 -1
- data/lib/dommy/internal/node_wrapper_cache.rb +23 -12
- data/lib/dommy/internal/observable_callback.rb +25 -0
- data/lib/dommy/internal/punycode.rb +202 -0
- data/lib/dommy/internal/range_text_serializer.rb +72 -0
- data/lib/dommy/internal/reflected_attributes.rb +45 -0
- data/lib/dommy/internal/template_content_registry.rb +6 -6
- data/lib/dommy/intersection_observer.rb +82 -0
- data/lib/dommy/{router.rb → location.rb} +8 -142
- data/lib/dommy/media_query_list.rb +118 -0
- data/lib/dommy/message_channel.rb +249 -0
- data/lib/dommy/{observer.rb → mutation_observer.rb} +21 -11
- data/lib/dommy/navigator.rb +365 -5
- data/lib/dommy/node.rb +12 -0
- data/lib/dommy/notification.rb +89 -0
- data/lib/dommy/parser.rb +13 -13
- data/lib/dommy/performance.rb +146 -0
- data/lib/dommy/performance_observer.rb +55 -0
- data/lib/dommy/range.rb +597 -0
- data/lib/dommy/resize_observer.rb +53 -0
- data/lib/dommy/shadow_root.rb +10 -8
- data/lib/dommy/streams.rb +386 -0
- data/lib/dommy/svg_elements.rb +3863 -0
- data/lib/dommy/text_codec.rb +175 -0
- data/lib/dommy/tree_walker.rb +21 -21
- data/lib/dommy/url.rb +274 -29
- data/lib/dommy/url_pattern.rb +144 -0
- data/lib/dommy/version.rb +1 -1
- data/lib/dommy/web_socket.rb +209 -0
- data/lib/dommy/window.rb +369 -0
- data/lib/dommy/worker.rb +143 -0
- data/lib/dommy/xml_http_request.rb +438 -0
- data/lib/dommy.rb +43 -5
- metadata +44 -29
- data/lib/dommy/world.rb +0 -209
metadata
CHANGED
|
@@ -1,38 +1,20 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dommy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- takahashim
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-05-
|
|
11
|
-
dependencies:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- - "~>"
|
|
17
|
-
- !ruby/object:Gem::Version
|
|
18
|
-
version: '1.15'
|
|
19
|
-
type: :runtime
|
|
20
|
-
prerelease: false
|
|
21
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
-
requirements:
|
|
23
|
-
- - "~>"
|
|
24
|
-
- !ruby/object:Gem::Version
|
|
25
|
-
version: '1.15'
|
|
26
|
-
description: |
|
|
27
|
-
A pure-Ruby DOM polyfill on top of Nokogiri::HTML5, a Ruby-side
|
|
28
|
-
analogue to JavaScript DOM libraries like happy-dom and jsdom.
|
|
29
|
-
It exposes browser-like DOM semantics — events, MutationObserver,
|
|
30
|
-
Custom Elements, Shadow DOM, the File API (Blob / File / FormData /
|
|
31
|
-
DataTransfer), URL, Promise, timers, and Storage — without spinning
|
|
32
|
-
up a real browser.
|
|
10
|
+
date: 2026-05-29 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: 'A pure Ruby DOM polyfill built on Nokogiri::HTML5, inspired by happy-dom
|
|
13
|
+
and jsdom. It gives Ruby tests a browser style DOM with events, MutationObserver,
|
|
14
|
+
Custom Elements, Shadow DOM, the File API, timers, and Storage, without requiring
|
|
15
|
+
a real browser.
|
|
33
16
|
|
|
34
|
-
|
|
35
|
-
drop-in RSpec matchers and Minitest assertions.
|
|
17
|
+
'
|
|
36
18
|
email:
|
|
37
19
|
- takahashimm@gmail.com
|
|
38
20
|
executables: []
|
|
@@ -41,9 +23,16 @@ extra_rdoc_files: []
|
|
|
41
23
|
files:
|
|
42
24
|
- README.md
|
|
43
25
|
- lib/dommy.rb
|
|
26
|
+
- lib/dommy/animation.rb
|
|
44
27
|
- lib/dommy/attr.rb
|
|
28
|
+
- lib/dommy/backend.rb
|
|
29
|
+
- lib/dommy/backend/nokogiri_adapter.rb
|
|
30
|
+
- lib/dommy/backend/nokolexbor_adapter.rb
|
|
45
31
|
- lib/dommy/blob.rb
|
|
46
32
|
- lib/dommy/bridge.rb
|
|
33
|
+
- lib/dommy/compression_streams.rb
|
|
34
|
+
- lib/dommy/cookie_store.rb
|
|
35
|
+
- lib/dommy/crypto.rb
|
|
47
36
|
- lib/dommy/css.rb
|
|
48
37
|
- lib/dommy/custom_elements.rb
|
|
49
38
|
- lib/dommy/data_transfer.rb
|
|
@@ -52,39 +41,65 @@ files:
|
|
|
52
41
|
- lib/dommy/dom_parser.rb
|
|
53
42
|
- lib/dommy/element.rb
|
|
54
43
|
- lib/dommy/event.rb
|
|
44
|
+
- lib/dommy/event_source.rb
|
|
55
45
|
- lib/dommy/fetch.rb
|
|
46
|
+
- lib/dommy/file_reader.rb
|
|
56
47
|
- lib/dommy/form_data.rb
|
|
48
|
+
- lib/dommy/history.rb
|
|
57
49
|
- lib/dommy/html_collection.rb
|
|
58
50
|
- lib/dommy/html_elements.rb
|
|
59
51
|
- lib/dommy/internal/cookie_jar.rb
|
|
52
|
+
- lib/dommy/internal/css_pseudo_handlers.rb
|
|
60
53
|
- lib/dommy/internal/dom_matching.rb
|
|
54
|
+
- lib/dommy/internal/idna.rb
|
|
55
|
+
- lib/dommy/internal/idna_data.rb
|
|
56
|
+
- lib/dommy/internal/ipv4_parser.rb
|
|
61
57
|
- lib/dommy/internal/mutation_coordinator.rb
|
|
62
58
|
- lib/dommy/internal/node_traversal.rb
|
|
63
59
|
- lib/dommy/internal/node_wrapper_cache.rb
|
|
60
|
+
- lib/dommy/internal/observable_callback.rb
|
|
64
61
|
- lib/dommy/internal/observer_manager.rb
|
|
65
62
|
- lib/dommy/internal/observer_matcher.rb
|
|
63
|
+
- lib/dommy/internal/punycode.rb
|
|
64
|
+
- lib/dommy/internal/range_text_serializer.rb
|
|
65
|
+
- lib/dommy/internal/reflected_attributes.rb
|
|
66
66
|
- lib/dommy/internal/scope_resolution.rb
|
|
67
67
|
- lib/dommy/internal/shadow_root_registry.rb
|
|
68
68
|
- lib/dommy/internal/template_content_registry.rb
|
|
69
|
+
- lib/dommy/intersection_observer.rb
|
|
70
|
+
- lib/dommy/location.rb
|
|
71
|
+
- lib/dommy/media_query_list.rb
|
|
72
|
+
- lib/dommy/message_channel.rb
|
|
69
73
|
- lib/dommy/minitest.rb
|
|
70
74
|
- lib/dommy/minitest/assertions.rb
|
|
75
|
+
- lib/dommy/mutation_observer.rb
|
|
71
76
|
- lib/dommy/navigator.rb
|
|
72
77
|
- lib/dommy/node.rb
|
|
73
|
-
- lib/dommy/
|
|
78
|
+
- lib/dommy/notification.rb
|
|
74
79
|
- lib/dommy/parser.rb
|
|
80
|
+
- lib/dommy/performance.rb
|
|
81
|
+
- lib/dommy/performance_observer.rb
|
|
75
82
|
- lib/dommy/promise.rb
|
|
76
|
-
- lib/dommy/
|
|
83
|
+
- lib/dommy/range.rb
|
|
84
|
+
- lib/dommy/resize_observer.rb
|
|
77
85
|
- lib/dommy/rspec.rb
|
|
78
86
|
- lib/dommy/rspec/capy_style_matchers.rb
|
|
79
87
|
- lib/dommy/rspec/matchers.rb
|
|
80
88
|
- lib/dommy/scheduler.rb
|
|
81
89
|
- lib/dommy/shadow_root.rb
|
|
82
90
|
- lib/dommy/storage.rb
|
|
91
|
+
- lib/dommy/streams.rb
|
|
92
|
+
- lib/dommy/svg_elements.rb
|
|
83
93
|
- lib/dommy/test_helpers.rb
|
|
94
|
+
- lib/dommy/text_codec.rb
|
|
84
95
|
- lib/dommy/tree_walker.rb
|
|
85
96
|
- lib/dommy/url.rb
|
|
97
|
+
- lib/dommy/url_pattern.rb
|
|
86
98
|
- lib/dommy/version.rb
|
|
87
|
-
- lib/dommy/
|
|
99
|
+
- lib/dommy/web_socket.rb
|
|
100
|
+
- lib/dommy/window.rb
|
|
101
|
+
- lib/dommy/worker.rb
|
|
102
|
+
- lib/dommy/xml_http_request.rb
|
|
88
103
|
homepage: https://github.com/takahashim/dommy
|
|
89
104
|
licenses:
|
|
90
105
|
- MIT
|
data/lib/dommy/world.rb
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "cgi"
|
|
4
|
-
require "erb"
|
|
5
|
-
|
|
6
|
-
# Dommy — a happy-dom-style DOM polyfill in pure Ruby. Backbone is
|
|
7
|
-
# Nokogiri::HTML5 plus a small scheduler/event-loop layer.
|
|
8
|
-
#
|
|
9
|
-
# Two views into the same objects:
|
|
10
|
-
# - Public Ruby API (snake_case methods like `text_content`,
|
|
11
|
-
# `append_child`) for CRuby users writing tests against rendered
|
|
12
|
-
# HTML.
|
|
13
|
-
# - `__js_get__` / `__js_set__` / `__js_call__` / `__js_new__`
|
|
14
|
-
# bridge protocol for JS bridge embedders — dispatches into the
|
|
15
|
-
# same underlying Ruby methods.
|
|
16
|
-
module Dommy
|
|
17
|
-
# The browser global. `JS.global` from inside wasm resolves to this.
|
|
18
|
-
# Property access (`JS.global[:document]`, `JS.global[:console]`) is
|
|
19
|
-
# routed through `#__js_get__`. Method calls (`JS.global.call(:foo)`)
|
|
20
|
-
# are routed through `#__js_call__`.
|
|
21
|
-
class Window
|
|
22
|
-
include EventTarget
|
|
23
|
-
|
|
24
|
-
attr_reader :document, :scheduler, :location, :globals, :custom_elements, :navigator
|
|
25
|
-
|
|
26
|
-
def initialize(host = nil, nokogiri_doc: nil)
|
|
27
|
-
@host = host
|
|
28
|
-
@scheduler = Scheduler.new
|
|
29
|
-
@event_ctor = Bridge::Constructor.new { |args| Event.new(args[0], args[1]) }
|
|
30
|
-
@custom_event_ctor = Bridge::Constructor.new { |args| CustomEvent.new(args[0], args[1]) }
|
|
31
|
-
@mouse_event_ctor = Bridge::Constructor.new { |args| MouseEvent.new(args[0], args[1]) }
|
|
32
|
-
@keyboard_event_ctor = Bridge::Constructor.new { |args| KeyboardEvent.new(args[0], args[1]) }
|
|
33
|
-
@event_target_ctor = Bridge::Constructor.new { |_args| StandaloneEventTarget.new }
|
|
34
|
-
@error_ctor = Bridge::Constructor.new { |args| ErrorValue.new(args[0]) }
|
|
35
|
-
@promise_ctor = Bridge::PromiseConstructor.new(self)
|
|
36
|
-
@mutation_observer_ctor = Bridge::Constructor.new { |args| MutationObserver.new(self, args[0]) }
|
|
37
|
-
@abort_controller_ctor = Bridge::Constructor.new { |_args| AbortController.new }
|
|
38
|
-
@blob_ctor = Bridge::Constructor.new { |args| Blob.new(args[0] || [], args[1] || {}) }
|
|
39
|
-
@file_ctor = Bridge::Constructor.new { |args| File.new(args[0] || [], args[1].to_s, args[2] || {}) }
|
|
40
|
-
@file_list_ctor = Bridge::Constructor.new { |args| FileList.new(args[0] || []) }
|
|
41
|
-
@data_transfer_ctor = Bridge::Constructor.new { |args|
|
|
42
|
-
opts = args[0] || {}
|
|
43
|
-
DataTransfer.new(
|
|
44
|
-
files: opts["files"] || opts[:files] || [],
|
|
45
|
-
data: opts["data"] || opts[:data] || {}
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
@drag_event_ctor = Bridge::Constructor.new { |args| DragEvent.new(args[0], args[1]) }
|
|
49
|
-
@local_storage = Storage.new
|
|
50
|
-
@session_storage = Storage.new
|
|
51
|
-
@location = Location.new(self)
|
|
52
|
-
@history = History.new(self, @location)
|
|
53
|
-
@url_ctor = Bridge::Constructor.new { |args| Url.new(args[0], args[1]) }
|
|
54
|
-
@url_ctor.define_class_method("createObjectURL") { |args| URL.create_object_url(args[0]) }
|
|
55
|
-
@url_ctor.define_class_method("revokeObjectURL") { |args| URL.revoke_object_url(args[0]) }
|
|
56
|
-
# `JS.global[:__some_key__] = ...` from user code lands here.
|
|
57
|
-
# Test code uses this for stub installation (e.g. a custom
|
|
58
|
-
# `__fetch_stub__`); production code stays on the typed
|
|
59
|
-
# accessors above. We keep it last in the read fallback to
|
|
60
|
-
# avoid shadowing intentional getters.
|
|
61
|
-
@globals = {}
|
|
62
|
-
@document = Document.new(host, nokogiri_doc: nokogiri_doc)
|
|
63
|
-
@document.default_view = self
|
|
64
|
-
@custom_elements = CustomElementRegistry.new(self)
|
|
65
|
-
@navigator = Navigator.new(self)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Bridge protocol: respond to a JS-style property read by name.
|
|
69
|
-
# Returns either a Ruby primitive (Integer / String / true / false /
|
|
70
|
-
# nil), a Hash/Array (for JS object/array literals), or a Dom::*
|
|
71
|
-
# instance for live DOM/BOM objects.
|
|
72
|
-
#
|
|
73
|
-
# Anything outside the surface we've explicitly polyfilled returns
|
|
74
|
-
# nil (= JS undefined). Spec failures here are the signal to widen
|
|
75
|
-
# the surface in a future session.
|
|
76
|
-
def __js_get__(key)
|
|
77
|
-
case key
|
|
78
|
-
when "document"
|
|
79
|
-
@document
|
|
80
|
-
when "Event"
|
|
81
|
-
@event_ctor
|
|
82
|
-
when "CustomEvent"
|
|
83
|
-
@custom_event_ctor
|
|
84
|
-
when "MouseEvent"
|
|
85
|
-
@mouse_event_ctor
|
|
86
|
-
when "KeyboardEvent"
|
|
87
|
-
@keyboard_event_ctor
|
|
88
|
-
when "EventTarget"
|
|
89
|
-
@event_target_ctor
|
|
90
|
-
when "Error"
|
|
91
|
-
@error_ctor
|
|
92
|
-
when "Promise"
|
|
93
|
-
@promise_ctor
|
|
94
|
-
when "MutationObserver"
|
|
95
|
-
@mutation_observer_ctor
|
|
96
|
-
when "AbortController"
|
|
97
|
-
@abort_controller_ctor
|
|
98
|
-
when "Blob"
|
|
99
|
-
@blob_ctor
|
|
100
|
-
when "File"
|
|
101
|
-
@file_ctor
|
|
102
|
-
when "FileList"
|
|
103
|
-
@file_list_ctor
|
|
104
|
-
when "DataTransfer"
|
|
105
|
-
@data_transfer_ctor
|
|
106
|
-
when "DragEvent"
|
|
107
|
-
@drag_event_ctor
|
|
108
|
-
# handled by Symbol sentinel
|
|
109
|
-
when "console"
|
|
110
|
-
:console
|
|
111
|
-
# likewise
|
|
112
|
-
when "Object"
|
|
113
|
-
:object_ctor
|
|
114
|
-
when "Array"
|
|
115
|
-
:array_ctor
|
|
116
|
-
when "JSON"
|
|
117
|
-
:json_ctor
|
|
118
|
-
when "performance"
|
|
119
|
-
{"now" => @scheduler.now_ms.to_f}
|
|
120
|
-
when "localStorage"
|
|
121
|
-
@local_storage
|
|
122
|
-
when "sessionStorage"
|
|
123
|
-
@session_storage
|
|
124
|
-
when "location"
|
|
125
|
-
@location
|
|
126
|
-
when "history"
|
|
127
|
-
@history
|
|
128
|
-
when "URL"
|
|
129
|
-
@url_ctor
|
|
130
|
-
when "fetch"
|
|
131
|
-
FetchFn.new(self)
|
|
132
|
-
when "customElements"
|
|
133
|
-
@custom_elements
|
|
134
|
-
when "navigator"
|
|
135
|
-
@navigator
|
|
136
|
-
else
|
|
137
|
-
@globals[key]
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def __js_set__(key, value)
|
|
142
|
-
# Stash arbitrary keys for later reads (e.g.
|
|
143
|
-
# `JS.global[:__fetchy_stub__] = map`).
|
|
144
|
-
@globals[key] = value
|
|
145
|
-
# The Fetchy spec's `install_fetch_stub` resets `__fetch_count__`
|
|
146
|
-
# to 0 inside its JS installer (`globalThis.__fetch_count__ = 0;
|
|
147
|
-
# globalThis.fetch = ...`). Our polyfill ignores raw JS, so we
|
|
148
|
-
# piggy-back on the stub assignment to perform the same reset
|
|
149
|
-
# — without it the count accumulates across tests in one VM run.
|
|
150
|
-
@globals["__fetch_count__"] = 0 if %w[__fetchy_stub__ __resource_fetch_stub__ __inject_fetch_stub__].include?(key)
|
|
151
|
-
nil
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def __js_call__(method, args)
|
|
155
|
-
case method
|
|
156
|
-
when "fetch"
|
|
157
|
-
FetchFn.new(self).__js_call__("call", args)
|
|
158
|
-
when "encodeURIComponent"
|
|
159
|
-
# JS spec encoding: percent-encode anything except
|
|
160
|
-
# `A-Za-z0-9 - _ . ! ~ * ' ( )`. Ruby's `CGI.escape` uses
|
|
161
|
-
# `+` for space; ERB::Util.url_encode matches JS behavior.
|
|
162
|
-
ERB::Util.url_encode(args[0].to_s)
|
|
163
|
-
when "decodeURIComponent"
|
|
164
|
-
CGI.unescape(args[0].to_s)
|
|
165
|
-
when "addEventListener"
|
|
166
|
-
add_event_listener(args[0], args[1], args[2])
|
|
167
|
-
when "removeEventListener"
|
|
168
|
-
remove_event_listener(args[0], args[1])
|
|
169
|
-
when "dispatchEvent"
|
|
170
|
-
dispatch_event(args[0])
|
|
171
|
-
when "setTimeout"
|
|
172
|
-
@scheduler.set_timeout(args[0], args[1] || 0)
|
|
173
|
-
when "clearTimeout"
|
|
174
|
-
@scheduler.clear_timeout(args[0])
|
|
175
|
-
when "setInterval"
|
|
176
|
-
@scheduler.set_interval(args[0], args[1] || 0)
|
|
177
|
-
when "clearInterval"
|
|
178
|
-
@scheduler.clear_interval(args[0])
|
|
179
|
-
when "requestAnimationFrame"
|
|
180
|
-
@scheduler.request_animation_frame(args[0])
|
|
181
|
-
when "cancelAnimationFrame"
|
|
182
|
-
@scheduler.cancel_animation_frame(args[0])
|
|
183
|
-
when "queueMicrotask"
|
|
184
|
-
@scheduler.queue_microtask(args[0])
|
|
185
|
-
else
|
|
186
|
-
# Additional window-level methods (fetch, location, history,
|
|
187
|
-
# Promise, MutationObserver, etc.) arrive in later sessions.
|
|
188
|
-
nil
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def __event_parent__
|
|
193
|
-
nil
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
# Called by History#go and Location.href= to fire popstate /
|
|
197
|
-
# hashchange events. Listeners registered on the Window via
|
|
198
|
-
# `addEventListener("popstate"|"hashchange", cb)` receive them.
|
|
199
|
-
def fire_popstate(state)
|
|
200
|
-
event = CustomEvent.new("popstate", "detail" => state)
|
|
201
|
-
dispatch_event(event)
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
def fire_hashchange(old_hash, new_hash)
|
|
205
|
-
event = CustomEvent.new("hashchange", "detail" => {"oldURL" => old_hash, "newURL" => new_hash})
|
|
206
|
-
dispatch_event(event)
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
end
|