stdweb/webapi/
node.rs

1use std::mem;
2
3use webcore::value::Reference;
4use webcore::try_from::{TryFrom, TryInto};
5use webapi::document::Document;
6use webapi::dom_exception::{HierarchyRequestError, NotFoundError, SyntaxError};
7use webapi::element::Element;
8use webapi::event_target::{IEventTarget, EventTarget};
9use webapi::node_list::NodeList;
10use private::TODO;
11
12/// An enum which determines whenever the DOM [Node](trait.INode.html)'s children will also be cloned or not.
13///
14/// Mainly used in [INode::clone_node](trait.INode.html#method.clone_node).
15/// Also used in [Document::import_node](struct.Document.html#method.import_node).
16#[derive(Copy, Clone, PartialEq, Eq, Debug)]
17pub enum CloneKind {
18    /// Will not clone the children.
19    Shallow,
20    /// Will clone the children.
21    Deep
22}
23
24/// `INode` is an interface from which a number of DOM API object types inherit.
25///
26/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node)
27// https://dom.spec.whatwg.org/#node
28pub trait INode: IEventTarget {
29    /// Casts a reference to this object into a reference to a [Node](struct.Node.html).
30    fn as_node( &self ) -> &Node {
31        let reference: &Reference = self.as_ref();
32        unsafe {
33            mem::transmute( reference )
34        }
35    }
36
37    /// Adds a node to the end of the list of children of a specified parent node.
38    ///
39    /// If the given child is a reference to an existing node in the document then
40    /// it is moved from its current position to the new position (there is no requirement
41    /// to remove the node from its parent node before appending it to some other node).
42    ///
43    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild)
44    // https://dom.spec.whatwg.org/#ref-for-dom-node-appendchild
45    fn append_child< T: INode >( &self, child: &T ) {
46        js! { @(no_return)
47            @{self.as_ref()}.appendChild( @{child.as_ref()} );
48        }
49    }
50
51    /// Removes a child node from the DOM.
52    ///
53    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild)
54    // https://dom.spec.whatwg.org/#ref-for-dom-node-removechild
55    fn remove_child< T: INode >( &self, child: &T ) -> Result< Node, NotFoundError > {
56        js_try! (
57            return @{self.as_ref()}.removeChild( @{child.as_ref()} );
58        ).unwrap()
59    }
60
61    /// Returns a duplicate of the node on which this method was called.
62    ///
63    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode)
64    // https://dom.spec.whatwg.org/#ref-for-dom-node-clonenode
65    fn clone_node( &self, kind: CloneKind ) -> Result< Self, TODO > {
66        let is_deep = match kind {
67            CloneKind::Deep => true,
68            CloneKind::Shallow => false
69        };
70
71        let cloned = js! {
72            return @{self.as_ref()}.cloneNode( @{is_deep} );
73        };
74
75        Ok( cloned.into_reference().unwrap().downcast::< Self >().unwrap() )
76    }
77
78    /// Checks whenever a given node is a descendant of this one or not.
79    ///
80    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/contains)
81    // https://dom.spec.whatwg.org/#ref-for-dom-node-contains
82    fn contains< T: INode >( &self, node: &T ) -> bool {
83        js!(
84            return @{self.as_ref()}.contains( @{node.as_ref()} );
85        ).try_into().unwrap()
86    }
87
88    /// Inserts the specified node before the reference node as a child of the current node.
89    ///
90    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore)
91    // https://dom.spec.whatwg.org/#ref-for-dom-node-insertbefore
92    fn insert_before< T: INode, U: INode >( &self, new_node: &T, reference_node: &U ) -> Result< Node, InsertNodeError > {
93        js_try! (
94            return @{self.as_ref()}.insertBefore( @{new_node.as_ref()}, @{reference_node.as_ref()} );
95        ).unwrap()
96    }
97
98    /// Replaces one hild node of the specified node with another.
99    ///
100    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/replaceChild)
101    // https://dom.spec.whatwg.org/#ref-for-dom-node-replacechild
102    fn replace_child< T: INode, U: INode >( &self, new_child: &T, old_child: &U ) -> Result< Node, InsertNodeError > {
103        js_try! (
104            return @{self.as_ref()}.replaceChild( @{new_child.as_ref()}, @{old_child.as_ref()} );
105        ).unwrap()
106    }
107
108    /// Returns the parent of this node in the DOM tree.
109    ///
110    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/parentNode)
111    // https://dom.spec.whatwg.org/#ref-for-dom-node-parentnode
112    fn parent_node( &self ) -> Option< Node > {
113        js!(
114            return @{self.as_ref()}.parentNode;
115        ).try_into().ok()
116    }
117
118    /// Returns the node's first child in the tree, or `None` if the node is childless.
119    ///
120    /// [(JavaScript docs)](https://developer.mozilla.org/en/docs/Web/API/Node/firstChild)
121    // https://dom.spec.whatwg.org/#ref-for-dom-node-firstchild
122    fn first_child( &self ) -> Option< Node > {
123        js!(
124            return @{self.as_ref()}.firstChild;
125        ).try_into().ok()
126    }
127
128    /// Returns the node's last child in the tree, or `None` if the node is childless.
129    ///
130    /// [(JavaScript docs)](https://developer.mozilla.org/en/docs/Web/API/Node/lastChild)
131    // https://dom.spec.whatwg.org/#ref-for-dom-node-lastchild
132    fn last_child( &self ) -> Option< Node > {
133        js!(
134            return @{self.as_ref()}.lastChild;
135        ).try_into().ok()
136    }
137
138    /// Returns the node's next sibling in the tree, or `None` if there isn't such a node.
139    ///
140    /// [(JavaScript docs)](https://developer.mozilla.org/en/docs/Web/API/Node/nextSibling)
141    // https://dom.spec.whatwg.org/#ref-for-dom-node-nextsibling
142    fn next_sibling( &self ) -> Option< Node > {
143        js!(
144            return @{self.as_ref()}.nextSibling;
145        ).try_into().ok()
146    }
147
148    /// Returns the name of the node.
149    ///
150    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName)
151    // https://dom.spec.whatwg.org/#ref-for-dom-node-nodename
152    fn node_name( &self ) -> String {
153        js!(
154            return @{self.as_ref()}.nodeName;
155        ).try_into().unwrap()
156    }
157
158    /// Returns the type of the node.
159    ///
160    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType)
161    // https://dom.spec.whatwg.org/#ref-for-dom-node-nodetype
162    fn node_type( &self ) -> NodeType {
163        match js!(
164            return @{self.as_ref()}.nodeType;
165        ).try_into().unwrap() {
166            1 => NodeType::Element,
167            2 => NodeType::Attribute,
168            3 => NodeType::Text,
169            4 => NodeType::CDataSection,
170            7 => NodeType::ProcessingInstruction,
171            8 => NodeType::Comment,
172            9 => NodeType::Document,
173            10 => NodeType::DocumentType,
174            11 => NodeType::DocumentFragment,
175            _ => unreachable!("Unexpected nodeType")
176        }
177    }
178
179    /// Returns the value of the node.
180    ///
181    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeValue)
182    // https://dom.spec.whatwg.org/#ref-for-dom-node-nodevalue
183    fn node_value( &self ) -> Option<String> {
184        js!(
185            return @{self.as_ref()}.nodeValue;
186        ).try_into().ok()
187    }
188
189    /// Sets the value of the node.
190    ///
191    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeValue)
192    // https://dom.spec.whatwg.org/#ref-for-dom-node-nodevalue
193    fn set_node_value( &self, value: Option< &str > ) {
194        js! { @(no_return)
195            @{self.as_ref()}.nodeValue = @{value};
196        }
197    }
198
199    /// Returns the `Document` that this node belongs to.
200    ///
201    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/ownerDocument)
202    // https://dom.spec.whatwg.org/#ref-for-dom-node-ownerdocument
203    fn owner_document( &self ) -> Option< Document > {
204        js!(
205            return @{self.as_ref()}.ownerDocument;
206        ).try_into().ok()
207    }
208
209    /// Returns an `Element` that is the parent of this node. Returns `null` if the node
210    /// has no parent or the parent is not an `Element`.
211    ///
212    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/parentElement)
213    // https://dom.spec.whatwg.org/#ref-for-dom-node-parentelement
214    fn parent_element( &self ) -> Option< Element > {
215        js!(
216            return @{self.as_ref()}.parentElement;
217        ).try_into().ok()
218    }
219
220    /// Returns the node's previous sibling in the tree, or `None` if there isn't such a node.
221    ///
222    /// [(JavaScript docs)](https://developer.mozilla.org/en/docs/Web/API/Node/previousSibling)
223    // https://dom.spec.whatwg.org/#ref-for-dom-node-previoussibling
224    fn previous_sibling( &self ) -> Option< Node > {
225        js!(
226            return @{self.as_ref()}.previousSibling;
227        ).try_into().ok()
228    }
229
230    /// A property which represents the text content of a node and its descendants.
231    ///
232    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)
233    // https://dom.spec.whatwg.org/#ref-for-dom-node-textcontent
234    fn text_content( &self ) -> Option< String > {
235        js!(
236            return @{self.as_ref()}.textContent;
237        ).try_into().unwrap()
238    }
239
240    /// Sets the text content of this node; calling thil removes all
241    /// of node's children and replaces them with a single text node
242    /// with the given value.
243    ///
244    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)
245    // https://dom.spec.whatwg.org/#ref-for-dom-node-textcontent
246    fn set_text_content( &self, text: &str ) {
247        js! { @(no_return)
248            @{self.as_ref()}.textContent = @{text};
249        }
250    }
251
252    /// Returns a live collection of child nodes of this node.
253    ///
254    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes)
255    // https://dom.spec.whatwg.org/#ref-for-dom-node-childnodes
256    fn child_nodes( &self ) -> NodeList {
257        unsafe {
258            js!(
259                return @{self.as_ref()}.childNodes;
260            ).into_reference_unchecked().unwrap()
261        }
262    }
263
264    /// Gets the base URL.
265    ///
266    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/baseURI)
267    // https://dom.spec.whatwg.org/#ref-for-dom-node-baseuri
268    fn base_uri( &self ) -> String {
269        js!(
270            return @{self.as_ref()}.baseURI;
271        ).try_into().unwrap()
272    }
273
274    /// Returns whether this node has children nodes.
275    ///
276    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/hasChildNodes)
277    // https://dom.spec.whatwg.org/#ref-for-dom-node-haschildnodes
278    fn has_child_nodes( &self ) -> bool {
279        js!(
280            return @{self.as_ref()}.hasChildNodes();
281        ).try_into().unwrap()
282    }
283
284    /// Determines whether the given namespace is the default namespace of this node.
285    ///
286    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/isDefaultNamespace)
287    // https://dom.spec.whatwg.org/#ref-for-dom-node-isdefaultnamespace
288    fn is_default_namespace( &self, namespace: &str ) -> bool {
289        js!(
290            return @{self.as_ref()}.isDefaultNamespace( @{namespace} );
291        ).try_into().unwrap()
292    }
293
294    /// Tests whether this node is equal to another node. Two nodes are equal if
295    /// they have the same type, defining characteristics, matching attributes,
296    /// and so on.
297    ///
298    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/isEqualNode)
299    // https://dom.spec.whatwg.org/#ref-for-dom-node-isequalnode
300    fn is_equal_node< T: INode >( &self, node: &T ) -> bool {
301        js!(
302            return @{self.as_ref()}.isEqualNode( @{node.as_ref()} );
303        ).try_into().unwrap()
304    }
305
306    /// Test whether two `Node` references are the same.
307    ///
308    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/isSameNode)
309    // https://dom.spec.whatwg.org/#ref-for-dom-node-issamenode
310    fn is_same_node< T: INode >( &self, node: &T ) -> bool {
311        js!(
312            return @{self.as_ref()}.isSameNode( @{node.as_ref()} );
313        ).try_into().unwrap()
314    }
315
316    /// Returns the prefix for the given namespace URI, if present.
317    ///
318    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/lookupPrefix)
319    // https://dom.spec.whatwg.org/#ref-for-dom-node-lookupprefix
320    fn lookup_prefix( &self, namespace: &str ) -> Option<String> {
321        js!(
322            return @{self.as_ref()}.lookupPrefix( @{namespace} );
323        ).try_into().ok()
324    }
325
326    /// Returns the namespace URI for the given prefix.
327    ///
328    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/lookupNamespaceURI)
329    // https://dom.spec.whatwg.org/#ref-for-dom-node-lookupnamespaceuri
330    fn lookup_namespace_uri( &self, prefix: &str ) -> Option<String> {
331        js!(
332            return @{self.as_ref()}.lookupNamespaceURI( @{prefix} );
333        ).try_into().ok()
334    }
335
336    /// Merges any adjacent text nodes and removes empty text nodes under this node.
337    ///
338    /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/normalize)
339    // https://dom.spec.whatwg.org/#ref-for-dom-node-normalize
340    fn normalize( &self ) {
341        js! { @(no_return)
342            @{self.as_ref()}.normalize();
343        }
344    }
345}
346
347/// Errors thrown by `Node` insertion methods.
348error_enum_boilerplate! {
349    InsertNodeError,
350    NotFoundError, HierarchyRequestError
351}
352
353/// A reference to a JavaScript object which implements the [INode](trait.INode.html)
354/// interface.
355///
356/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node)
357// https://dom.spec.whatwg.org/#interface-node
358#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
359#[reference(instance_of = "Node")]
360#[reference(subclass_of(EventTarget))]
361pub struct Node( Reference );
362
363impl IEventTarget for Node {}
364impl INode for Node {}
365
366impl Node {
367    /// Attempt to create the `Node` from raw html. The html string must contain **exactly one**
368    /// root node.
369    ///
370    /// Returns a `SyntaxError` if:
371    ///
372    /// - There is not **exactly one** root node.
373    /// - The html syntax is wrong. However, on most browsers the html parsing algorighm is
374    ///   _unbelievably_ forgiving and will just turn your html into text or maybe even an empty
375    ///   string.
376    ///
377    /// It is recommended to have control over the html being given to this function as not
378    /// having control is a security concern.
379    ///
380    /// For more details, see information about setting `innerHTML`:
381    ///
382    /// <https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML>
383    pub fn from_html(html: &str) -> Result<Node, SyntaxError> {
384        js_try!(
385            var span = document.createElement("span");
386            span.innerHTML = @{html};
387            if( span.childNodes.length != 1 ) {
388                throw new DOMException(
389                    "Node::from_html requires a single root node but has: "
390                    + span.childNodes.length,
391                    "SyntaxError");
392            }
393            return span.childNodes[0];
394        ).unwrap()
395    }
396}
397
398/// Determines the type of a `Node`.
399///
400/// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType)
401#[derive(Clone, Copy, Debug, Eq, PartialEq)]
402pub enum NodeType {
403    /// An `Element` such as `<p>` or `<div>`.
404    Element,
405
406    /// The actual `Text` of `Element` or `Attr`.
407    Text,
408
409    /// A `ProcessingInstruction` of an XML document.
410    ProcessingInstruction,
411
412    /// A `Comment` node.
413    Comment,
414
415    /// A 'Document' node.
416    Document,
417
418    /// A 'DocumentType' node such as `<!DOCTYPE html>`
419    DocumentType,
420
421    /// A 'DocumentFragment' node.
422    DocumentFragment,
423
424    // The following types are deprecated and should not be used.
425
426    /// Deprecated.
427    Attribute,
428
429    /// Deprecated.
430    CDataSection,
431
432    /// Deprecated.
433    XmlEntityReference,
434
435    /// Deprecated.
436    XmlEntity,
437
438    /// Deprecated.
439    XmlNotation,
440}
441
442#[cfg(all(test, feature = "web_test"))]
443mod tests {
444    use super::*;
445    use webapi::document::document;
446    use webcore::value::Value;
447
448    fn div() -> Node {
449        js!(
450            return document.createElement("div");
451        ).try_into().unwrap()
452    }
453
454    fn text(text: &str) -> Node {
455        js!(
456            return new Text(@{text});
457        ).try_into().unwrap()
458    }
459
460    fn comment(text: &str) -> Node {
461        js!(
462            return document.createComment(@{text});
463        ).try_into().unwrap()
464    }
465
466    fn processing_instruction(target: &str, data: &str) -> Node {
467        js!(
468            return document.createProcessingInstruction(@{target}, @{data});
469        ).try_into().unwrap()
470    }
471
472    fn doc_type() -> Node {
473        js!(
474            return document.implementation.createDocumentType(
475                "svg:svg",
476                "-//W3C//DTD SVG 1.1//EN",
477                "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"
478            );
479        ).try_into().unwrap()
480    }
481
482    fn document_fragment() -> Node {
483        js!(
484            return document.createDocumentFragment();
485        ).try_into().unwrap()
486    }
487
488    fn xml(namespace_prefix: &str, namespace_url: &str) -> Node {
489        let xml_text = format!(
490            "<?xml version = \"1.0\"?><foo xmlns:{} = \"{}\" />",
491            namespace_prefix,
492            namespace_url
493        );
494        js!(
495            return new DOMParser().parseFromString(@{xml_text}, "text/xml");
496        ).try_into().unwrap()
497    }
498
499    #[test]
500    fn test_append_child() {
501        let parent = div();
502        let child = div();
503        parent.append_child(&child);
504        assert_eq!(parent.first_child().unwrap().as_ref(), child.as_ref());
505    }
506
507    #[test]
508    fn test_remove_child() {
509        let parent = div();
510        let child1 = div();
511        let child2 = div();
512        parent.append_child(&child1);
513        parent.append_child(&child2);
514
515        let removed = parent.remove_child(&child1).unwrap();
516        assert_eq!(parent.first_child().unwrap().as_ref(), child2.as_ref());
517        assert_eq!(removed.as_ref(), child1.as_ref());
518        match parent.remove_child(&child1) {
519            Err(_) => (),
520            _ => panic!("Expected error")
521        }
522
523        parent.remove_child(&child2).unwrap();
524        assert!(parent.first_child().is_none())
525    }
526
527    #[test]
528    fn test_clone_node() {
529        let node = div();
530        let child = text("test");
531        node.append_child(&child);
532
533        let clone = node.clone_node(CloneKind::Shallow).unwrap();
534        assert_ne!(node.as_ref(), clone.as_ref());
535        assert_eq!(clone.node_name(), "DIV");
536        assert!(clone.first_child().is_none());
537
538        let clone = node.clone_node(CloneKind::Deep).unwrap();
539        assert_ne!(node.as_ref(), clone.as_ref());
540        assert_eq!(clone.node_name(), "DIV");
541        let clone_child = clone.first_child().unwrap();
542        assert_ne!(clone_child.as_ref(), child.as_ref());
543        assert_eq!(&clone_child.node_value().unwrap(), "test");
544    }
545
546    #[test]
547    fn test_contains() {
548        let node = div();
549
550        let child1 = div();
551        node.append_child(&child1);
552
553        let child2 = div();
554        node.append_child(&child2);
555
556        let grandchild = div();
557        child1.append_child(&grandchild);
558
559        assert!(node.contains(&node));
560        assert!(node.contains(&child1));
561        assert!(node.contains(&child2));
562        assert!(node.contains(&grandchild));
563        assert!(child1.contains(&grandchild));
564        assert!(!child1.contains(&child2));
565        assert!(!grandchild.contains(&node));
566    }
567
568    #[test]
569    fn test_insert_before() {
570        let node = div();
571        let child1 = div();
572        let child2 = div();
573        let child3 = div();
574        node.append_child(&child1);
575        node.insert_before(&child2, &child1).unwrap();
576        assert_eq!(node.first_child().unwrap().as_ref(), child2.as_ref());
577
578        match node.insert_before(&child3, &child3) {
579            Err(InsertNodeError::NotFoundError(_)) => (),
580            _ => panic!("Expected NotFoundError")
581        }
582
583        match node.insert_before(&doc_type(), &child1) {
584            Err(InsertNodeError::HierarchyRequestError(_)) => (),
585            _ => panic!("Expected HierarchyRequestError")
586        }
587    }
588
589    #[test]
590    fn test_replace_child() {
591        let node = div();
592        let child1 = div();
593        let child2 = div();
594        node.append_child(&child1);
595        node.replace_child(&child2, &child1).unwrap();
596        assert_eq!(node.first_child().unwrap().as_ref(), child2.as_ref());
597        assert!(child1.parent_node().is_none());
598
599        match node.replace_child(&child2, &child1) {
600            Err(InsertNodeError::NotFoundError(_)) => (),
601            _ => panic!("Expected NotFoundError")
602        }
603
604        match node.replace_child(&doc_type(), &child2) {
605            Err(InsertNodeError::HierarchyRequestError(_)) => (),
606            _ => panic!("Expected HierarchyRequestError")
607        }
608    }
609
610    #[test]
611    fn test_parent_node() {
612        let node = div();
613        let child = div();
614        node.append_child(&child);
615        assert!(node.parent_node().is_none());
616        assert_eq!(child.parent_node().unwrap().as_ref(), node.as_ref());
617    }
618
619    #[test]
620    fn test_first_child() {
621        let node = div();
622        assert!(node.first_child().is_none());
623
624        let child = div();
625        node.append_child(&child);
626        assert_eq!(node.first_child().unwrap().as_ref(), child.as_ref());
627    }
628
629    #[test]
630    fn test_last_child() {
631        let node = div();
632        assert!(node.last_child().is_none());
633
634        let child1 = div();
635        node.append_child(&child1);
636        assert_eq!(node.last_child().unwrap().as_ref(), child1.as_ref());
637
638        let child2 = div();
639        node.append_child(&child2);
640        assert_eq!(node.last_child().unwrap().as_ref(), child2.as_ref());
641    }
642
643    #[test]
644    fn test_next_sibling() {
645        let node = div();
646        let child1 = div();
647        node.append_child(&child1);
648        assert!(child1.next_sibling().is_none());
649
650        let child2 = div();
651        node.append_child(&child2);
652        assert_eq!(child1.next_sibling().unwrap().as_ref(), child2.as_ref());
653    }
654
655    #[test]
656    fn test_previous_sibling() {
657        let node = div();
658        let child1 = div();
659        let child2 = div();
660
661        node.append_child(&child1);
662        assert!(child1.previous_sibling().is_none());
663        node.append_child(&child2);
664        assert_eq!(child2.previous_sibling().unwrap().as_ref(), child1.as_ref());
665    }
666
667    #[test]
668    fn test_node_name() {
669        assert_eq!(div().node_name(), "DIV");
670        assert_eq!(text("x").node_name(), "#text");
671        assert_eq!(document_fragment().node_name(), "#document-fragment");
672        assert_eq!(doc_type().node_name(), "svg:svg");
673        assert_eq!(processing_instruction("foo", "bar").node_name(), "foo");
674    }
675
676    #[test]
677    fn test_node_type() {
678        assert_eq!(div().node_type(), NodeType::Element);
679        assert_eq!(text("x").node_type(), NodeType::Text);
680        assert_eq!(processing_instruction("foo", "bar").node_type(), NodeType::ProcessingInstruction);
681        assert_eq!(comment("foo").node_type(), NodeType::Comment);
682        assert_eq!(document().node_type(), NodeType::Document);
683        assert_eq!(doc_type().node_type(), NodeType::DocumentType);
684        assert_eq!(document_fragment().node_type(), NodeType::DocumentFragment);
685    }
686
687    #[test]
688    fn test_node_value() {
689        let node = text("x");
690        assert_eq!(node.node_value().unwrap(), "x");
691        node.set_node_value(Some("y"));
692        assert_eq!(node.node_value().unwrap(), "y");
693
694        assert_eq!(processing_instruction("foo", "bar").node_value().unwrap(), "bar");
695        assert_eq!(comment("foo").node_value().unwrap(), "foo");
696
697        let node: Node = div();
698        assert!(node.node_value().is_none());
699        node.set_node_value(Some("foo"));
700        assert!(node.node_value().is_none());
701
702        assert!(document().node_value().is_none());
703        assert!(doc_type().node_value().is_none());
704        assert!(document_fragment().node_value().is_none());
705    }
706
707    #[test]
708    fn test_owner_document() {
709        let node = div();
710        assert_eq!(node.owner_document().unwrap().as_ref(), document().as_ref());
711    }
712
713    #[test]
714    fn test_parent_element() {
715        let node = div();
716        let child = div();
717        node.append_child(&child);
718        assert_eq!(child.parent_element().unwrap().as_ref(), node.as_ref());
719    }
720
721    #[test]
722    fn test_text_content() {
723        let node: Node = div();
724        assert_eq!(node.text_content().unwrap(), "");
725        node.append_child(&text("foo "));
726        assert_eq!(node.text_content().unwrap(), "foo ");
727        node.append_child(&text("foo"));
728        assert_eq!(node.text_content().unwrap(), "foo foo");
729        node.set_text_content("bar");
730        assert_eq!(node.text_content().unwrap(), "bar");
731        assert_eq!(node.child_nodes().len(), 1);
732    }
733
734    #[test]
735    fn test_base_uri() {
736        let node = div();
737        assert!(!node.base_uri().is_empty());
738    }
739
740    #[test]
741    fn test_has_child_nodes() {
742        let node = div();
743        assert!(!node.has_child_nodes());
744        node.append_child(&div());
745        assert!(node.has_child_nodes());
746    }
747
748    #[test]
749    fn test_child_nodes() {
750        let node = div();
751        let node_list = node.child_nodes();
752        assert_eq!(node_list.len(), 0);
753        assert!(node_list.iter().next().is_none());
754
755        let child1 = text("foo");
756        node.append_child(&child1);
757        let child2 = text("bar");
758        node.append_child(&child2);
759
760        let node_list = node.child_nodes();
761        assert_eq!(node_list.len(), 2);
762        let mut iter = node_list.iter();
763        assert_eq!(iter.next().unwrap().as_ref(), child1.as_ref());
764        assert_eq!(iter.next().unwrap().as_ref(), child2.as_ref());
765    }
766
767    #[test]
768    fn test_is_default_namespace() {
769        assert!(!div().is_default_namespace("foo"));
770        assert!(div().is_default_namespace("http://www.w3.org/1999/xhtml"));
771    }
772
773    #[test]
774    fn test_is_equal_node() {
775        let node1 = div();
776        let node2 = div();
777        assert!(node1.is_equal_node(&node2));
778
779        let child1 = div();
780        node1.append_child(&child1);
781        assert!(!node1.is_equal_node(&node2));
782
783        let child2 = div();
784        node2.append_child(&child2);
785        assert!(node1.is_equal_node(&node2));
786    }
787
788    #[test]
789    fn test_is_same_node() {
790        let node1 = div();
791        assert!(node1.is_same_node(&node1));
792        assert!(!node1.is_same_node(&div()));
793    }
794
795    #[test]
796    fn test_lookup_prefix() {
797        let xml = xml("x", "http://foo.com");
798        assert!(xml.lookup_prefix("bar").is_none());
799        assert_eq!(xml.lookup_prefix("http://foo.com").unwrap(), "x");
800    }
801
802    #[test]
803    fn test_lookup_namespace_uri() {
804        let xml = xml("x", "http://foo.com");
805        assert!(xml.lookup_namespace_uri("y").is_none());
806        assert_eq!(xml.lookup_namespace_uri("x").unwrap(), "http://foo.com");
807    }
808
809    #[test]
810    fn test_normalize() {
811        let node = div();
812        node.append_child(&text("test "));
813        node.append_child(&text("123"));
814        node.normalize();
815        assert_eq!(node.child_nodes().len(), 1);
816        let child_text = node.first_child().unwrap().text_content().unwrap();
817        assert_eq!(child_text, "test 123");
818    }
819
820    #[test]
821    fn option_node_is_constructible_from_value() {
822        let node: Value = js!( return document.createElement( "div" ) );
823        let opt_node: Option< Node > = node.clone().try_into().unwrap();
824        assert_eq!( opt_node.unwrap().as_ref(), node.as_ref() );
825    }
826
827    #[test]
828    fn empty_option_node_is_constructible_from_null_value() {
829        let empty_opt_node: Option< Node > = Value::Null.try_into().unwrap();
830        assert!( empty_opt_node.is_none() );
831    }
832
833    #[test]
834    fn empty_option_node_is_constructible_from_undefined_value() {
835        let empty_opt_node: Option< Node > = Value::Undefined.try_into().unwrap();
836        assert!( empty_opt_node.is_none() );
837    }
838
839    #[test]
840    fn option_node_from_numeric_value_results_in_an_error() {
841        let value: Value = 123_i32.into();
842        let empty_opt_node: Result< Option< Node >, _ > = value.try_into();
843        assert!( empty_opt_node.is_err() );
844    }
845
846    #[test]
847    fn from_html() {
848        let node = Node::from_html("<div>Some text, horray!</div>").unwrap();
849        let text = node.first_child().unwrap();
850
851        assert_eq!(node.node_name(), "DIV");
852        assert_eq!(node.last_child().unwrap(), text);
853
854        assert_eq!(text.node_name(), "#text");
855        assert_eq!(text.node_value().unwrap(), "Some text, horray!");
856        assert!(text.first_child().is_none());
857
858        let err = Node::from_html("<div>foo</div><div>bar</div>").unwrap_err();
859        assert!(format!("{}", err).contains("requires a single root node"));
860        assert!(Node::from_html("<di").is_err());
861    }
862}