1 / 27

Спецификация и тестирование DOM API

Спецификация и тестирование DOM API. В. Кулямин. План. Стандарт DOM API Что такое DOM Зачем нужен стандарт Основные элементы DOM API Примеры Формализация требований стандарта Как она выглядит Зачем она нужна Разработка тестов DOM API Какие тесты есть и почему их недостаточно

Download Presentation

Спецификация и тестирование DOM API

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Спецификация и тестирование DOMAPI В. Кулямин

  2. План • Стандарт DOM API • Что такое DOM • Зачем нуженстандарт • Основные элементы DOM API • Примеры • Формализация требований стандарта • Как она выглядит • Зачем она нужна • Разработка тестов DOM API • Какие тесты есть и почему их недостаточно • Техническая основа – JsUnit • Разработка тестов на основе формальных требований • Примеры • Приглашение к сотрудничеству

  3. Web приложения Браузер Web-сервер Silverlight Flex Серверные скрипты JavaFX ECMAScript HTTP HTML CSS Клиентские скрипты скрипты SVG Объекты DOM – Document Object Model

  4. Пример использования DOM <HTML> <HEAD> <TITLE>DOM Test</TITLE> </HEAD> <BODY> <script type="text/javascript"> <!-- window.onload = function() { var b = document.getElementById("button"); b.onclick = function() { var num = 0; for(i in document) { num++; } logtxt("Total number of document members", "", num); for(i in document) { try { logtxt(i, typeof(document[i]), document[i]); } catch(e) {} } }; function logtxt( name, kind, value ) { var p = document.createElement("p"); p.appendChild(document.createTextNode(name + " : " + kind + " = " + value)); document.body.appendChild(p); }; }; --> </script> <p> <b>DOM Document - Structure</b> <p> <button id="button">Show document members</button> <hr> </BODY> </HTML>

  5. Стандарт DOM • http://www.w3.org/DOM/DOMTR • Основные документы • Core http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407 • HTML http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109 • Style (StyleSheets + CSS) http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113 • Traversal and Range http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113 • Load-Save http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407 • Validation http://www.w3.org/TR/2004/REC-DOM-Level-3-Val-20040127 • Element Traversal http://www.w3.org/TR/2008/REC-ElementTraversal-20081222 • Xpathhttp://www.w3.org/TR/2004/NOTE-DOM-Level-3-XPath-20040226 • Viewsand Formatting http://www.w3.org/TR/2004/NOTE-DOM-Level-3-Views-20040226 • Events http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908http://www.w3.org/TR/2009/WD-eventsource-20091222/ • Дополнительные части • DOM Events http://www.w3.org/TR/#tr_DOM_events • MathMLhttp://www.w3.org/TR/#tr_MathML • SMIL http://www.w3.org/TR/#tr_SMIL • SVG http://www.w3.org/TR/#tr_SVGhttp://www.w3.org/TR/#tr_SVG_Tiny

  6. Основные модули DOM

  7. Развитие DOM

  8. Поддержка DOM

  9. Переносимость Web-приложений Internet Explorer Клиентские скрипты Mozilla Firefox Web-сервер Интернет Клиентские скрипты Серверные скрипты Opera Клиентские скрипты

  10. Пример проблемы переносимости functionsetBackgroundColor(element, color){ element.setAttribute("bgcolor", color); }; Реализация Element.setAttribute() в IE не удовлетворяет стандарту functionsetBackgroundColor(element, color){ if(element.getAttributeNode("bgcolor")){ for (i = 0; i < element.attributes.length; i++) { if(element.attributes[i].name.toUpperCase() == 'BGCOLOR') element.attributes[i].value = color; } } else element.setAttribute("bgcolor", color); };

  11. Что делать? • Систематизировать требования стандарта • Добиться однозначности и согласованности требований • Сделать тестовый набор, проверяющий соответствие им • Сделать его общедоступным и известным • Постоянно использовать этот набор при разработке, развитии и оценке браузеров

  12. Уже имеющиеся тесты W3C DOM Conformance Test Suite http://www.w3.org/DOM/Test/ http://dev.w3.org/cvsweb/2001/DOM-Test-Suite/ • Последние изменения – май 2004 • Покрыты • Core L1, L2, L3 • HTML L1, L2 • Events L2, L3 (очень слабо) • Load-Save L3 • Validation L3 • Не покрыто все остальное и все изменения с 2004 • Нет детальной прослеживаемости тестов к требованиям • Требования не проанализированы на однозначность, полноту и согласованность

  13. Прослеживаемость к требованиям General, N1 N2 Node insertBefore (Node newChild, Node refChild) Inserts the node newChild before the existing child node refChild. If refChild isnull, insert newChild at the end of the list of children.If newChild is a DocumentFragment [p.40] object, all of its children are inserted, inthe same order, before refChild. If the newChild is already in the tree, it is firstremoved. Note: Inserting a node before itself is implementation dependent. Parameters: newChild of type Node– The node to insert. refChild of type Node – The reference node, i.e., the node before which the new node must be inserted. Return Value: The node being inserted. Exceptions: DOMException HIERARCHY_REQUEST_ERR: Raised if this node is of a typethat does not allow children of the type of the newChildnode,or if the node to insert is one of this node’s ancestors orthis node itself, or if this node is of type Document [p.41] andthe DOM application attempts to insert a secondDocumentType [p.115] or Element [p.85] node. WRONG_DOCUMENT_ERR: Raised if newChild wascreated from a different document than the one that created thisnode. NO_MODIFICATION_ALLOWED_ERR: Raised if this node isreadonly or if the parent of the node being inserted is readonly. NOT_FOUND_ERR: Raised if refChild is not a child of thisnode. NOT_SUPPORTED_ERR: if this node is of type Document[p.41] , this exception might be raised if the DOMimplementation doesn’t support the insertion of aDocumentType [p.115] or Element [p.85] node. N4 N3 N5 N6 E1 E2 E3 E4 E5 E6 E7 E8 E9 E10 E11

  14. Неоднозначности и неполнота • Что будет при вставке null? • При вставке DocumentFragment • Что будет результатом? • Как изменится список «детей» при некорректном «ребенке» в конце? • Можноли добавить пустой DocumentFragment, если добавлять «детей» нельзя? • Как определить, поддерживается ли вставка DocumentType и Element? • Почему нет ограничений на порядок «детей» в Document? • Что имеется в виду под «the node being inserted»? • DocumentFragmentили его элемент?

  15. Формализация стандарта • Обеспечение большей совместимости (основной цели стандартов) • Выявление неоднозначностей • Выявление рассогласований, альтернатив • Выявление неполноты • Возможность строгого контроля соответствия • Для тестов и анализа: правила корректного поведения • Для тестов: классы ситуаций, в которых поведение различимо

  16. Пример Node.insertBefore() General, N1 N2 Node insertBefore (Node newChild, Node refChild) Inserts the node newChild before the existing child node refChild. If refChild isnull, insert newChild at the end of the list of children.If newChild is a DocumentFragment [p.40] object, all of its children are inserted, inthe same order, before refChild. If the newChild is already in the tree, it is firstremoved. Note: Inserting a node before itself is implementation dependent. Parameters: newChild of type Node– The node to insert. refChild of type Node – The reference node, i.e., the node before which the new node must be inserted. Return Value: The node being inserted. Exceptions: DOMException HIERARCHY_REQUEST_ERR: Raised if this node is of a typethat does not allow children of the type of the newChildnode,or if the node to insert is one of this node’s ancestors orthis node itself, or if this node is of type Document [p.41] andthe DOM application attempts to insert a secondDocumentType [p.115] or Element [p.85] node. WRONG_DOCUMENT_ERR: Raised if newChild wascreated from a different document than the one that created thisnode. NO_MODIFICATION_ALLOWED_ERR: Raised if this node isreadonly or if the parent of the node being inserted is readonly. NOT_FOUND_ERR: Raised if refChild is not a child of thisnode. NOT_SUPPORTED_ERR: if this node is of type Document[p.41] , this exception might be raised if the DOMimplementation doesn’t support the insertion of aDocumentType [p.115] or Element [p.85] node. N4 N3 N5 N6 E1 E2 E3 E4 E5 E6 E7 E8 E9 E10 E11

  17. Пример: спецификация исключений Если мы вставляем обычный узел (не DocumentFragment), узлы такого типа должны быть разрешены в качестве «детей» Если мы вставляем DocumentTypeв Document, не должно быть других «детей» типа DocumentType Если мы вставляем DocumentFragment, его «дети» должны быть разрешены в качестве «детей» текущего узла Node Node.InsertBefore(Node newChild, Node refChild) { Contract.Requires(newChild!= null); // What if newChild is null? DOM specification says nothing Contract.EnsuresOnThrow<DomException>( // If this node is of a type that does not allow children of the type of the newChild node !IsAllowedChild(newChild)&& !(newChildis DocumentFragment) // -- in particular, if inserted DocumentFragment has a prohibited child for this node ||newChildis DocumentFragment && newChild.Children.Count> 0 &&Contract.Exists(0, newChild.Children.Count, i=>!IsAllowedChild(newChild.Children[i])) // or this node is of type Document and newChild is a second DocumentType or Element node ||this is Document &&newChildisDocumentType&&HasDifferentChildOfType(typeof(DocumentType), newChild) ||this is Document &&newChildis Element &&HasDifferentChildOfType(typeof(Element), newChild) // -- in part. if inserted DocumentFragment has a DocumetType or an Element child, and this node has one too ||this is Document &&newChildisDocumentFragment&&HasDifferentChildOfType(typeof(DocumentType), null) &&newChild.HasDifferentChildOfType(typeof(DocumentType), null) ||this is Document &&newChildisDocumentFragment&&HasDifferentChildOfType(typeof(Element), null) &&newChild.HasDifferentChildOfType(typeof(Element), null) // if newChild is this node itself or if newChild is one of this node's ancestors ||newChild== this ||Ancestors.Contains(newChild) // if newChild was created from a different document than the one that created this node || !(this is Document) &&OwnerDocument!=newChild.OwnerDocument // -- (refinement) owner document for Document is null, whether newChild owner is other document || this is Document && this !=newChild.OwnerDocument // if this node is readonly or if the parent of the node being inserted is readonly ||IsReadOnly||newChild.Parent!= null && newChild.Parent.IsReadOnly // if refChild is not a child of this node ||refChild!= null && !Children.Contains(refChild) ); E1 E4 E5 E3 E2 E6 Нельзя в качестве «ребенка» вставлять «предка» текущего узла E7 E8 Нельзя вставлять узел, построенный в рамках другого документа E9

  18. Пример: спецификация нормы При формализации приходится выделять разные случаи для одного требования N4 // If the newChild is already in the tree, it is first removed Contract.Ensures(Contract.OldValue<Node>(newChild.Parent) == null|| Contract.OldValue<Node>(newChild.Parent) == this || !Contract.OldValue<Node>(newChild.Parent).Children.Contains(newChild) ); Contract.Ensures(!(newChildisDocumentFragment) ||newChild.Children.Count== 0); // If refChild is null and newChild is not DocumentFragment, insert newChild at the end of the list of children Contract.Ensures(!(refChild== null && !(newChildis DocumentFragment) && !Contract.OldValue<bool>(Children.Contains(newChild))) ||Children.Count==Contract.OldValue<int>(Children.Count) + 1 && Children[Children.Count- 1] == newChild &&Children.GetRange(0, Children.Count- 1).Equals( Contract.OldValue<List<Node>>(Children.GetRange(0, Children.Count))) ); Contract.Ensures( !(refChild== null && !(newChildisDocumentFragment) && Contract.OldValue<bool>(Children.Contains(newChild))) ||Children.Count== Contract.OldValue<int>(Children.Count) && Children[Children.Count- 1] == newChild &&Children.GetRange(0, Contract.OldValue<int>(Children.IndexOf(newChild))).Equals( Contract.OldValue<List<Node>>(Children.GetRange(0, Children.IndexOf(newChild)))) &&Children.GetRange( Contract.OldValue<int>(Children.IndexOf(newChild)) , Children.Count- Contract.OldValue<int>(Children.IndexOf(newChild)) - 1) .Equals(Contract.OldValue<List<Node>>( Children.GetRange(Children.IndexOf(newChild) + 1, Children.Count- Children.IndexOf(newChild) - 1)))); // If refChild isn't null and newChild is not DocumentFragment, insert newChild before the existing child node refChild Contract.Ensures( !(refChild!= null && !(newChildisDocumentFragment) && !Contract.OldValue<bool>(Children.Contains(newChild))) ||Children.Count== Contract.OldValue<int>(Children.Count) + 1 && Children.IndexOf(newChild) == Contract.OldValue<int>(Children.IndexOf(refChild)) && Children.GetRange(0, Children.IndexOf(newChild)).Equals( Contract.OldValue<List<Node>>(Children.GetRange(0, Children.IndexOf(refChild)))) &&Children.GetRange(Children.IndexOf(newChild) + 1, Children.Count-Children.IndexOf(newChild) - 1) .Equals(Contract.OldValue<List<Node>>( Children.GetRange(Children.IndexOf(refChild), Children.Count-Children.IndexOf(refChild)))) ); Contract.Ensures( !( refChild!= null && !(newChildisDocumentFragment) && Contract.OldValue<bool>(Children.Contains(newChild)) && newChild== refChild) ||Children.Count==Contract.OldValue<int>(Children.Count) &&Children.IndexOf(newChild) == Contract.OldValue<int>(Children.IndexOf(refChild)) &&Children.GetRange(0, Children.Count).Equals( Contract.OldValue<List<Node>>(Children.GetRange(0, Children.Count))) ); ... N2 N1

  19. Пример: различные ситуации I Норма • Вставка обычного узла (не DocumentFragment) • Вставка узла, не являющегося «ребенком» текущего • Без своего «родителя» • Со своим «родителем» • Вставка одного из «детей» текущего узла (при этом набор «детей» не меняется) • Совпадающего с refChild • Несовпадающего • Стоящего непосредственно перед refChild • Стоящего перед refChild, но не сразу • Стоящего после refChild • Вставка DocumentFragment • Пустого • Из одного элемента • Из нескольких элементов • Вставка в конец (refChild == null) • Вставка не в конец • В начало • В середину

  20. Пример: различные ситуации II Исключения • Вставляемый узел по типу не может быть «ребенком» текущего • Document --> DocumentType, Element, ProcessingInstruction, Comment • Только один «ребенок» типа DocumentTypeили Element • DocumentFragment, Element, Entity, EntityReference --> Element, ProcessingInstruction, Comment, Text, CDATASection, EntityReference • Attr --> Text, EntityReference • DocumentType, ProcessingInstruction, Comment, Text, CDATASection, Notation --> nothing • Вставляемый узел совпадает с текущим или с его предком • Вставляемый узел из другого документа • Текущий узел неизменяем • «Родитель» вставляемого узла неизменяем • refChildне является «ребенком» текущего узла • Вставка обычного узла • Вставка DocumentFragment • Вставка в конец (refChild == null) • Вставка не в конец • В начало • В середину

  21. Создание тестов для браузеров • JsUnit http://www.jsunit.net/ • Тестовые страницы • Тестовые функции • Инициализация и финализация • Тестирование исключений • Тестовые наборы

  22. Пример тестовой страницы <html><head id="header“> <script language="JavaScript" src="jsunit/app/jsUnitCore.js"></script> <script language="JavaScript"> <!— var mainElement; function setUp() { mainElement = document.documentElement; } function test_Document_ExistingElement_ToEnd() { var l = document.childNodes.length; varres = document.insertBefore(mainElement, null); assertEquals("Result should be inserted node", mainElement, res); assertEquals("Number of children should preserve", l, document.childNodes.length); assertEquals("Inserted node should keep to be the last child", mainElement, document.lastChild); assertEquals("This node should become the inserted node parent", document, mainElement.parentNode); } function tearDown() {} --> </script> </head> <body> Test page for DOM Core Node.insertBefore() method </body> </html>

  23. Оценка корректности • assert([comment], booleanValue) • assertTrue([comment], booleanValue) • assertFalse([comment], booleanValue) • assertEquals([comment], value1, value2) • assertNotEquals([comment], value1, value2) • assertNull([comment], value) • assertNotNull([comment], value) • assertUndefined([comment], value) • assertNotUndefined([comment], value) • assertNaN([comment], value) • assertNotNaN([comment], value) • fail(comment) • Исключения – не поддерживаются прямо

  24. Пример теста на исключение function test_Document_Text_ToEnd() { var l = doc.childNodes.length; var parent = text.parentNode; var exception = true; try { res = doc.insertBefore(text, null); exception = false; } catch(e) { assertEquals("Exception should be HIERARCHY_REQUEST_ERR", 3, e.code); assertEquals("Number of children should preserve", l, doc.childNodes.length); assertEquals("Inserted node parent should preserve", parent, text.parentNode); } if(!exception) fail("Exception should be created"); } function test_Document_Text_ToEnd() { var l = doc.childNodes.length; var parent = text.parentNode; try { res = doc.insertBefore(text, null); fail("Exception should be created"); } catch(e) { assertEquals("Exception should be HIERARCHY_REQUEST_ERR", 3, e.code); assertEquals("Number of children should preserve", l, doc.childNodes.length); assertEquals("Inserted node parent should preserve", parent, text.parentNode); } } 24

  25. <html> <head> <title>DOM Test Suite</title> <link rel="stylesheet" type="text/css" href="jsunit/css/jsUnitStyle.css"> <script language="JavaScript" type="text/javascript" src="jsunit/app/jsUnitCore.js"></script> <script language="JavaScript" type="text/javascript"> <!-- function coreSuite() { var result = new top.jsUnitTestSuite(); result.addTestPage("http://localhost:8080/Node_InsertBefore_Normal.html"); result.addTestPage("http://localhost:8080/Node_InsertBefore_NodeTypes.html"); return result; } function suite() { var newsuite = new top.jsUnitTestSuite(); newsuite.addTestSuite(coreSuite()); return newsuite; } --> </script> </head> <body> <h1>DOM Test Suite</h1> <p>This page contains a suite of tests for testing DOM.</p> </body> </html> Пример тестового набора

  26. Приглашение к сотрудничеству DOM API Testing на CodePlex • http://domapitesting.codeplex.com 26

  27. Спасибо за внимание!

More Related