dommy 0.6.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -38
  3. data/lib/dommy/animation.rb +1 -1
  4. data/lib/dommy/attr.rb +23 -11
  5. data/lib/dommy/backend/nokogiri_adapter.rb +51 -0
  6. data/lib/dommy/backend/nokolexbor_adapter.rb +80 -0
  7. data/lib/dommy/backend.rb +129 -0
  8. data/lib/dommy/blob.rb +2 -2
  9. data/lib/dommy/compression_streams.rb +4 -4
  10. data/lib/dommy/cookie_store.rb +1 -1
  11. data/lib/dommy/crypto.rb +9 -8
  12. data/lib/dommy/css.rb +7 -7
  13. data/lib/dommy/custom_elements.rb +6 -6
  14. data/lib/dommy/document.rb +98 -32
  15. data/lib/dommy/dom_parser.rb +5 -4
  16. data/lib/dommy/element.rb +231 -50
  17. data/lib/dommy/event.rb +61 -25
  18. data/lib/dommy/event_source.rb +8 -8
  19. data/lib/dommy/fetch.rb +14 -6
  20. data/lib/dommy/file_reader.rb +3 -3
  21. data/lib/dommy/form_data.rb +1 -3
  22. data/lib/dommy/history.rb +7 -4
  23. data/lib/dommy/html_collection.rb +4 -4
  24. data/lib/dommy/html_elements.rb +110 -42
  25. data/lib/dommy/internal/css_pseudo_handlers.rb +28 -0
  26. data/lib/dommy/internal/dom_matching.rb +3 -3
  27. data/lib/dommy/internal/node_traversal.rb +1 -1
  28. data/lib/dommy/internal/node_wrapper_cache.rb +23 -12
  29. data/lib/dommy/internal/template_content_registry.rb +6 -6
  30. data/lib/dommy/intersection_observer.rb +2 -2
  31. data/lib/dommy/location.rb +8 -4
  32. data/lib/dommy/media_query_list.rb +3 -3
  33. data/lib/dommy/message_channel.rb +9 -9
  34. data/lib/dommy/mutation_observer.rb +21 -11
  35. data/lib/dommy/navigator.rb +12 -12
  36. data/lib/dommy/node.rb +12 -0
  37. data/lib/dommy/notification.rb +3 -3
  38. data/lib/dommy/parser.rb +13 -13
  39. data/lib/dommy/performance_observer.rb +2 -2
  40. data/lib/dommy/range.rb +2 -2
  41. data/lib/dommy/resize_observer.rb +2 -2
  42. data/lib/dommy/shadow_root.rb +10 -8
  43. data/lib/dommy/streams.rb +22 -22
  44. data/lib/dommy/text_codec.rb +4 -4
  45. data/lib/dommy/tree_walker.rb +21 -21
  46. data/lib/dommy/url.rb +25 -8
  47. data/lib/dommy/version.rb +1 -1
  48. data/lib/dommy/web_socket.rb +13 -13
  49. data/lib/dommy/window.rb +14 -1
  50. data/lib/dommy/worker.rb +5 -5
  51. data/lib/dommy/xml_http_request.rb +19 -4
  52. data/lib/dommy.rb +12 -2
  53. metadata +12 -26
@@ -45,9 +45,11 @@ module Dommy
45
45
  # tree rooted at `root` and filter by `whatToShow` + an optional
46
46
  # filter callable (or object with `acceptNode`).
47
47
  module TreeTraversalCore
48
+ private
49
+
48
50
  # Returns FILTER_ACCEPT / FILTER_REJECT / FILTER_SKIP for the
49
51
  # given wrapped node.
50
- def __accept__(node)
52
+ def accept(node)
51
53
  return NodeFilter::FILTER_REJECT unless node
52
54
  return NodeFilter::FILTER_SKIP if (NodeFilter.bitmask_for(node) & @what_to_show) == 0
53
55
 
@@ -55,8 +57,6 @@ module Dommy
55
57
  result || NodeFilter::FILTER_ACCEPT
56
58
  end
57
59
 
58
- private
59
-
60
60
  def invoke_filter(node)
61
61
  return NodeFilter::FILTER_ACCEPT if @filter.nil?
62
62
 
@@ -92,7 +92,7 @@ module Dommy
92
92
  def next_node
93
93
  node = first_descendant_or_following(@current_node)
94
94
  while node
95
- verdict = __accept__(node)
95
+ verdict = accept(node)
96
96
  if verdict == NodeFilter::FILTER_ACCEPT
97
97
  @current_node = node
98
98
  return node
@@ -109,7 +109,7 @@ module Dommy
109
109
  def previous_node
110
110
  node = preceding(@current_node)
111
111
  while node && node != @root
112
- verdict = __accept__(node)
112
+ verdict = accept(node)
113
113
  if verdict == NodeFilter::FILTER_ACCEPT
114
114
  @current_node = node
115
115
  return node
@@ -124,7 +124,7 @@ module Dommy
124
124
  def parent_node
125
125
  node = wrapped_parent(@current_node)
126
126
  while node && reachable_from_root?(node)
127
- return @current_node = node if __accept__(node) == NodeFilter::FILTER_ACCEPT
127
+ return @current_node = node if accept(node) == NodeFilter::FILTER_ACCEPT
128
128
 
129
129
  node = wrapped_parent(node)
130
130
  end
@@ -192,7 +192,7 @@ module Dommy
192
192
  def walk_siblings(start, direction)
193
193
  node = start
194
194
  while node
195
- v = __accept__(node)
195
+ v = accept(node)
196
196
  return @current_node = node if v == NodeFilter::FILTER_ACCEPT
197
197
 
198
198
  node = (v == NodeFilter::FILTER_REJECT) ? nil : send(direction, node)
@@ -246,30 +246,30 @@ module Dommy
246
246
  end
247
247
 
248
248
  def wrapped_parent(node)
249
- parent_nk = node.respond_to?(:__node__) ? node.__node__.parent : nil
250
- return nil unless parent_nk && !parent_nk.is_a?(Nokogiri::XML::Document)
249
+ parent_nk = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.parent : nil
250
+ return nil unless parent_nk && !parent_nk.is_a?(Backend.document_class)
251
251
 
252
252
  doc = node.instance_variable_get(:@document) || (@root.respond_to?(:document) ? @root.document : @root)
253
253
  doc.wrap_node(parent_nk)
254
254
  end
255
255
 
256
256
  def first_wrapped_child(node)
257
- child_nk = node.respond_to?(:__node__) ? node.__node__.children.first : nil
257
+ child_nk = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.children.first : nil
258
258
  child_nk && document_for(node).wrap_node(child_nk)
259
259
  end
260
260
 
261
261
  def last_wrapped_child(node)
262
- child_nk = node.respond_to?(:__node__) ? node.__node__.children.last : nil
262
+ child_nk = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.children.last : nil
263
263
  child_nk && document_for(node).wrap_node(child_nk)
264
264
  end
265
265
 
266
266
  def next_sibling_wrapped(node)
267
- n = node.respond_to?(:__node__) ? node.__node__.next : nil
267
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.next : nil
268
268
  n && document_for(node).wrap_node(n)
269
269
  end
270
270
 
271
271
  def previous_sibling_wrapped(node)
272
- n = node.respond_to?(:__node__) ? node.__node__.previous : nil
272
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.previous : nil
273
273
  n && document_for(node).wrap_node(n)
274
274
  end
275
275
 
@@ -306,7 +306,7 @@ module Dommy
306
306
 
307
307
  @reference_node = node
308
308
  @pointer_before_reference = false
309
- return node if __accept__(node) == NodeFilter::FILTER_ACCEPT
309
+ return node if accept(node) == NodeFilter::FILTER_ACCEPT
310
310
  end
311
311
  end
312
312
 
@@ -322,7 +322,7 @@ module Dommy
322
322
 
323
323
  @reference_node = node
324
324
  @pointer_before_reference = true
325
- return node if __accept__(node) == NodeFilter::FILTER_ACCEPT
325
+ return node if accept(node) == NodeFilter::FILTER_ACCEPT
326
326
  end
327
327
  end
328
328
 
@@ -392,28 +392,28 @@ module Dommy
392
392
  end
393
393
 
394
394
  def first_child_node(node)
395
- n = node.respond_to?(:__node__) ? node.__node__.children.first : nil
395
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.children.first : nil
396
396
  n && document_for(node).wrap_node(n)
397
397
  end
398
398
 
399
399
  def last_child_node(node)
400
- n = node.respond_to?(:__node__) ? node.__node__.children.last : nil
400
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.children.last : nil
401
401
  n && document_for(node).wrap_node(n)
402
402
  end
403
403
 
404
404
  def next_sibling_node(node)
405
- n = node.respond_to?(:__node__) ? node.__node__.next : nil
405
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.next : nil
406
406
  n && document_for(node).wrap_node(n)
407
407
  end
408
408
 
409
409
  def previous_sibling_node(node)
410
- n = node.respond_to?(:__node__) ? node.__node__.previous : nil
410
+ n = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.previous : nil
411
411
  n && document_for(node).wrap_node(n)
412
412
  end
413
413
 
414
414
  def parent_node_of(node)
415
- parent_nk = node.respond_to?(:__node__) ? node.__node__.parent : nil
416
- return nil unless parent_nk && !parent_nk.is_a?(Nokogiri::XML::Document)
415
+ parent_nk = node.respond_to?(:__dommy_backend_node__) ? node.__dommy_backend_node__.parent : nil
416
+ return nil unless parent_nk && !parent_nk.is_a?(Backend.document_class)
417
417
 
418
418
  document_for(node).wrap_node(parent_nk)
419
419
  end
data/lib/dommy/url.rb CHANGED
@@ -30,7 +30,7 @@ module Dommy
30
30
 
31
31
  class << self
32
32
  # Create a unique blob: URL that resolves back to `blob` via
33
- # `URL.__resolve_blob_url__(url)`. Returns nil for non-Blob input.
33
+ # `URL.__test_resolve_blob_url__(url)`. Returns nil for non-Blob input.
34
34
  def create_object_url(blob)
35
35
  return nil unless blob.is_a?(Blob)
36
36
 
@@ -53,14 +53,31 @@ module Dommy
53
53
 
54
54
  # Resolve a blob: URL back to its Blob, or nil if revoked / unknown.
55
55
  # Internal — used by fetch / XHR implementations that load blob URLs.
56
- def __resolve_blob_url__(url)
56
+ def __test_resolve_blob_url__(url)
57
57
  @blob_urls[url.to_s]
58
58
  end
59
59
 
60
60
  # Test seam: drop all registered blob URLs.
61
- def __reset_blob_urls__
61
+ def __test_reset_blob_urls__
62
62
  @blob_urls.clear
63
63
  end
64
+
65
+ # WHATWG URL Standard — `URL.parse(input, base)` is the
66
+ # non-throwing static factory. Returns a URL on success, `nil`
67
+ # on parse failure. The constructor (`new URL(...)`) raises
68
+ # `SyntaxError` for the same failure case.
69
+ def parse(input, base = nil)
70
+ new(input, base)
71
+ rescue DOMException::SyntaxError
72
+ nil
73
+ end
74
+
75
+ # WHATWG URL Standard — `URL.canParse(input, base)`. Boolean
76
+ # counterpart to `parse`: lets callers peek at validity
77
+ # without rescuing an exception or holding a URL reference.
78
+ def can_parse(input, base = nil)
79
+ !parse(input, base).nil?
80
+ end
64
81
  end
65
82
 
66
83
  attr_reader :search_params
@@ -78,7 +95,7 @@ module Dommy
78
95
  def href=(value)
79
96
  raw = parse_with_base(value.to_s, nil)
80
97
  @uri = raw
81
- @search_params.__replace__(raw.query.to_s)
98
+ @search_params.__internal_replace__(raw.query.to_s)
82
99
  build_href
83
100
  end
84
101
 
@@ -159,7 +176,7 @@ module Dommy
159
176
  def search=(value)
160
177
  q = value.to_s.sub(/^\?/, "")
161
178
  @uri.query = q.empty? ? nil : q
162
- @search_params.__replace__(q)
179
+ @search_params.__internal_replace__(q)
163
180
  end
164
181
 
165
182
  def hash
@@ -289,7 +306,7 @@ module Dommy
289
306
  # Called by URLSearchParams when it mutates; we need to keep the
290
307
  # underlying URI's query string in sync so subsequent `href` is
291
308
  # accurate.
292
- def __notify_params_changed__
309
+ def __internal_notify_params_changed__
293
310
  sync_uri_query
294
311
  end
295
312
 
@@ -636,7 +653,7 @@ module Dommy
636
653
  @pairs.map { |k, v| "#{encode(k)}=#{encode(v)}" }.join("&")
637
654
  end
638
655
 
639
- def __replace__(query_string)
656
+ def __internal_replace__(query_string)
640
657
  @pairs = parse(query_string)
641
658
  nil
642
659
  end
@@ -701,7 +718,7 @@ module Dommy
701
718
  end
702
719
 
703
720
  def notify
704
- @owner&.__notify_params_changed__
721
+ @owner&.__internal_notify_params_changed__
705
722
  end
706
723
  end
707
724
  end
data/lib/dommy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dommy
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
@@ -5,11 +5,11 @@ module Dommy
5
5
  # connection; dommy exposes an in-memory transport tests drive via
6
6
  # the `__*` seams:
7
7
  #
8
- # ws.__simulate_open__ — fires `open`
9
- # ws.__simulate_message__(data) — fires `message`
10
- # ws.__simulate_close__(code, reason) — fires `close`
11
- # ws.__simulate_error__ — fires `error`
12
- # ws.__sent_messages__ — array of sent payloads
8
+ # ws.__test_simulate_open__ — fires `open`
9
+ # ws.__test_simulate_message__(data) — fires `message`
10
+ # ws.__test_simulate_close__(code, reason) — fires `close`
11
+ # ws.__test_simulate_error__ — fires `error`
12
+ # ws.__test_sent_messages__ — array of sent payloads
13
13
  #
14
14
  # By default a `new WebSocket(url)` auto-opens via microtask so the
15
15
  # common pattern (`ws.onopen = ...; ws.send(...)`) works without
@@ -42,7 +42,7 @@ module Dommy
42
42
 
43
43
  # Auto-open via microtask unless tests disable.
44
44
  auto_open = window.globals["__ws_auto_open__"]
45
- @window.scheduler.queue_microtask(proc { __simulate_open__ }) unless auto_open == false
45
+ @window.scheduler.queue_microtask(proc { __test_simulate_open__ }) unless auto_open == false
46
46
  end
47
47
 
48
48
  def send(data)
@@ -56,30 +56,30 @@ module Dommy
56
56
  return if @ready_state == CLOSED || @ready_state == CLOSING
57
57
 
58
58
  @ready_state = CLOSING
59
- @window.scheduler.queue_microtask(proc { __simulate_close__(code, reason) })
59
+ @window.scheduler.queue_microtask(proc { __test_simulate_close__(code, reason) })
60
60
  nil
61
61
  end
62
62
 
63
63
  # --- Test seams ------------------------------------------------
64
64
 
65
- def __sent_messages__
65
+ def __test_sent_messages__
66
66
  @sent_messages.dup
67
67
  end
68
68
 
69
- def __simulate_open__
69
+ def __test_simulate_open__
70
70
  return if @ready_state != CONNECTING
71
71
 
72
72
  @ready_state = OPEN
73
73
  dispatch_event(Event.new("open"))
74
74
  end
75
75
 
76
- def __simulate_message__(data)
76
+ def __test_simulate_message__(data)
77
77
  return if @ready_state != OPEN
78
78
 
79
79
  dispatch_event(MessageEvent.new("message", "data" => data))
80
80
  end
81
81
 
82
- def __simulate_close__(code = 1000, reason = "", was_clean: true)
82
+ def __test_simulate_close__(code = 1000, reason = "", was_clean: true)
83
83
  @ready_state = CLOSED
84
84
  dispatch_event(
85
85
  CloseEvent.new(
@@ -91,7 +91,7 @@ module Dommy
91
91
  )
92
92
  end
93
93
 
94
- def __simulate_error__
94
+ def __test_simulate_error__
95
95
  dispatch_event(Event.new("error"))
96
96
  end
97
97
 
@@ -151,7 +151,7 @@ module Dommy
151
151
  end
152
152
  end
153
153
 
154
- def __event_parent__
154
+ def __internal_event_parent__
155
155
  nil
156
156
  end
157
157
 
data/lib/dommy/window.rb CHANGED
@@ -101,6 +101,8 @@ module Dommy
101
101
  @url_ctor = Bridge::Constructor.new { |args| URL.new(args[0], args[1]) }
102
102
  @url_ctor.define_class_method("createObjectURL") { |args| URL.create_object_url(args[0]) }
103
103
  @url_ctor.define_class_method("revokeObjectURL") { |args| URL.revoke_object_url(args[0]) }
104
+ @url_ctor.define_class_method("parse") { |args| URL.parse(args[0], args[1]) }
105
+ @url_ctor.define_class_method("canParse") { |args| URL.can_parse(args[0], args[1]) }
104
106
  # `JS.global[:__some_key__] = ...` from user code lands here.
105
107
  # Test code uses this for stub installation (e.g. a custom
106
108
  # `__fetch_stub__`); production code stays on the typed
@@ -273,6 +275,17 @@ module Dommy
273
275
  nil
274
276
  end
275
277
 
278
+ # Methods routed through __js_call__ (keep in sync with its when-arms).
279
+ JS_METHOD_NAMES = %w[
280
+ fetch encodeURIComponent decodeURIComponent addEventListener removeEventListener
281
+ dispatchEvent setTimeout clearTimeout setInterval clearInterval requestAnimationFrame
282
+ cancelAnimationFrame queueMicrotask requestIdleCallback cancelIdleCallback
283
+ structuredClone matchMedia getComputedStyle
284
+ ].freeze
285
+ def __js_method_names__
286
+ JS_METHOD_NAMES
287
+ end
288
+
276
289
  def __js_call__(method, args)
277
290
  case method
278
291
  when "fetch"
@@ -336,7 +349,7 @@ module Dommy
336
349
  end
337
350
  end
338
351
 
339
- def __event_parent__
352
+ def __internal_event_parent__
340
353
  nil
341
354
  end
342
355
 
data/lib/dommy/worker.rb CHANGED
@@ -6,11 +6,11 @@ module Dommy
6
6
  #
7
7
  # - `new Worker("/path/to/worker.js")` records the URL.
8
8
  # - The script body is not executed. Tests install message
9
- # handlers on the worker-side via `worker.__on_message__ { ... }`
9
+ # handlers on the worker-side via `worker.__test_on_message__ { ... }`
10
10
  # to simulate behavior.
11
11
  # - `worker.postMessage(data)` queues a microtask that delivers
12
12
  # to the worker-side handler.
13
- # - The worker-side handler can call `worker.__post_to_main__(data)`
13
+ # - The worker-side handler can call `worker.__test_post_to_main__(data)`
14
14
  # to deliver a message back to the main side's `message` event.
15
15
  #
16
16
  # This is enough surface to test "the app correctly posts/receives
@@ -56,12 +56,12 @@ module Dommy
56
56
 
57
57
  # Register a callback that runs in the "worker side". Multiple
58
58
  # registrations stack.
59
- def __on_message__(&block)
59
+ def __test_on_message__(&block)
60
60
  @worker_side_handlers << block
61
61
  end
62
62
 
63
63
  # Worker-side: deliver a message to the main-side `message` event.
64
- def __post_to_main__(data)
64
+ def __test_post_to_main__(data)
65
65
  cloned = Dommy.structured_clone(data)
66
66
  @window.scheduler.queue_microtask(
67
67
  proc do
@@ -104,7 +104,7 @@ module Dommy
104
104
  end
105
105
  end
106
106
 
107
- def __event_parent__
107
+ def __internal_event_parent__
108
108
  nil
109
109
  end
110
110
 
@@ -117,6 +117,11 @@ module Dommy
117
117
 
118
118
  def abort
119
119
  return if @ready_state == UNSENT || @ready_state == DONE
120
+ # WHATWG: abort() is a no-op when in OPENED with the send()
121
+ # flag unset. Without this guard, `xhr.open(); xhr.abort()`
122
+ # would fire abort + loadend even though no request is
123
+ # in flight.
124
+ return if @ready_state == OPENED && !@sent
120
125
 
121
126
  @aborted = true
122
127
  @generation += 1
@@ -236,7 +241,7 @@ module Dommy
236
241
  end
237
242
  end
238
243
 
239
- def __event_parent__
244
+ def __internal_event_parent__
240
245
  nil
241
246
  end
242
247
 
@@ -360,8 +365,10 @@ module Dommy
360
365
  nil
361
366
  end
362
367
 
363
- when "arraybuffer", "blob"
364
- body
368
+ when "arraybuffer"
369
+ body.bytes
370
+ when "blob"
371
+ Blob.new([body], "type" => response_content_type)
365
372
  when "document"
366
373
  parse_document(body)
367
374
  else
@@ -369,6 +376,14 @@ module Dommy
369
376
  end
370
377
  end
371
378
 
379
+ # Content-Type of the received response, read straight from
380
+ # `@response_headers` (not `get_response_header`, which gates on
381
+ # `readyState` — decode runs before that flag advances).
382
+ def response_content_type
383
+ hit = @response_headers.find { |k, _| k.to_s.downcase == "content-type" }
384
+ hit ? hit.last.to_s : ""
385
+ end
386
+
372
387
  def parse_document(body)
373
388
  DOMParser.new.parse_from_string(body, "text/html")
374
389
  rescue StandardError
@@ -416,7 +431,7 @@ module Dommy
416
431
  end
417
432
  end
418
433
 
419
- def __event_parent__
434
+ def __internal_event_parent__
420
435
  nil
421
436
  end
422
437
  end
data/lib/dommy.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "nokogiri"
4
3
  require "set"
5
4
 
6
5
  require_relative "dommy/version"
6
+ require_relative "dommy/backend"
7
7
  require_relative "dommy/dom_exception"
8
8
  require_relative "dommy/node"
9
9
  require_relative "dommy/html_collection"
@@ -16,6 +16,7 @@ require_relative "dommy/data_transfer"
16
16
  require_relative "dommy/crypto"
17
17
  require_relative "dommy/text_codec"
18
18
  require_relative "dommy/internal/observable_callback"
19
+ require_relative "dommy/internal/css_pseudo_handlers"
19
20
  require_relative "dommy/internal/punycode"
20
21
  require_relative "dommy/internal/idna"
21
22
  require_relative "dommy/internal/ipv4_parser"
@@ -73,7 +74,7 @@ module Dommy
73
74
  def self.parse(html)
74
75
  s = html.to_s
75
76
  if s.match?(/\A\s*(<!doctype|<html\b)/i)
76
- Window.new(nil, nokogiri_doc: Nokogiri::HTML5(s))
77
+ Window.new(nil, nokogiri_doc: Backend.parse(s))
77
78
  else
78
79
  window = Window.new
79
80
  window.document.body.inner_html = s
@@ -81,6 +82,15 @@ module Dommy
81
82
  end
82
83
  end
83
84
 
85
+ # Convenience accessor: `Dommy.backend` / `Dommy.backend=`.
86
+ def self.backend
87
+ Backend.current
88
+ end
89
+
90
+ def self.backend=(new_backend)
91
+ Backend.current = new_backend
92
+ end
93
+
84
94
  # Build a fresh, empty Window (no host). Equivalent to opening a
85
95
  # blank document.
86
96
  def self.new_window
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.6.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-22 00:00:00.000000000 Z
11
- dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: nokogiri
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
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
- Aimed at testing Ruby code that emits or consumes HTML. Includes
35
- drop-in RSpec matchers and Minitest assertions.
17
+ '
36
18
  email:
37
19
  - takahashimm@gmail.com
38
20
  executables: []
@@ -43,6 +25,9 @@ files:
43
25
  - lib/dommy.rb
44
26
  - lib/dommy/animation.rb
45
27
  - lib/dommy/attr.rb
28
+ - lib/dommy/backend.rb
29
+ - lib/dommy/backend/nokogiri_adapter.rb
30
+ - lib/dommy/backend/nokolexbor_adapter.rb
46
31
  - lib/dommy/blob.rb
47
32
  - lib/dommy/bridge.rb
48
33
  - lib/dommy/compression_streams.rb
@@ -64,6 +49,7 @@ files:
64
49
  - lib/dommy/html_collection.rb
65
50
  - lib/dommy/html_elements.rb
66
51
  - lib/dommy/internal/cookie_jar.rb
52
+ - lib/dommy/internal/css_pseudo_handlers.rb
67
53
  - lib/dommy/internal/dom_matching.rb
68
54
  - lib/dommy/internal/idna.rb
69
55
  - lib/dommy/internal/idna_data.rb