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

Download Report

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

Спецификация и тестирование
DOM API
В. Кулямин
План
• Стандарт DOM API
–
–
–
–
Что такое DOM
Зачем нужен стандарт
Основные элементы DOM API
Примеры
• Формализация требований стандарта
– Как она выглядит
– Зачем она нужна
• Разработка тестов DOM API
–
–
–
–
–
Какие тесты есть и почему их недостаточно
Техническая основа – JsUnit
Разработка тестов на основе формальных требований
Примеры
Приглашение к сотрудничеству
2
Web приложения
Браузер
Silverlight
Flex
JavaFX
ECMAScript
Web-сервер
Серверные
скрипты
HTTP
HTML
CSS
скрипты
SVGКлиентские
скрипты
Объекты DOM – Document Object Model
3
Пример использования 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 + " : "
document.body.appendChild(p);
};
+ kind + " = " + value));
};
-->
</script>
<p> <b>DOM Document - Structure</b>
<p> <button id="button">Show document members</button>
<hr>
</BODY>
</HTML>
4
Стандарт DOM
•
•
http://www.w3.org/DOM/DOMTR
Основные документы
–
–
–
–
–
–
–
–
–
–
•
Core
HTML
Style (StyleSheets + CSS)
Traversal and Range
Load-Save
Validation
Element Traversal
Xpath
Views and Formatting
Events
http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407
http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109
http://www.w3.org/TR/2000/REC-DOM-Level-2-Style-20001113
http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113
http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407
http://www.w3.org/TR/2004/REC-DOM-Level-3-Val-20040127
http://www.w3.org/TR/2008/REC-ElementTraversal-20081222
http://www.w3.org/TR/2004/NOTE-DOM-Level-3-XPath-20040226
http://www.w3.org/TR/2004/NOTE-DOM-Level-3-Views-20040226
http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908
http://www.w3.org/TR/2009/WD-eventsource-20091222/
Дополнительные части
–
–
–
–
DOM Events
MathML
SMIL
SVG
http://www.w3.org/TR/#tr_DOM_events
http://www.w3.org/TR/#tr_MathML
http://www.w3.org/TR/#tr_SMIL
http://www.w3.org/TR/#tr_SVG
http://www.w3.org/TR/#tr_SVG_Tiny
5
Основные модули DOM
Модуль
Типы
Константы
Core
34
HTML
56
Events
15
Views
25
Style Sheets
CSS
64
Методы
Атрибуты
R/W Атрибуты
89
75
10
36
293
255
29
35
45
13
32
67
21
4
12
2
5
22
37
22
158
132
Traversal
4
16
13
9
1
Range
3
8
19
7
Load-Save
15
15
14
27
15
Validation
5
14
22
13
1
XPath
6
15
7
9
190
211
283
715
Всего
437
6
Развитие DOM
Level
Core
1
(2000)
2
Нов
(20002003)
Мод
3
Нов
(20042010)
Мод
HTML
Events
Views
StSh
CSS
Trav
Rng
L-S
Val
XPath
Всего
Тип
22
55
77
Мет
35
34
69
Атрб
33
289
322
RWA
4
254
258
Тип
2
1
8
Мет
22
2
11
Атрб
8
4
25
RWA
1
1
Тип
7
13
20
Мет
1
6
7
Атрб
1
21
22
RWA
1
16
17
Тип
10
7
23
15
5
6
66
Мет
32
24
32
14
22
7
131
Атрб
35
20
65
27
13
9
169
RWA
5
21
15
1
Тип
7
6
13
Мет
5
1
6
Атрб
1
RWA
2
2
5
22
4
3
47
4
22
13
19
93
12
158
9
7
225
2
132
1
137
42
1
7
Поддержка DOM
8
Переносимость Web-приложений
Internet Explorer
Клиентские
скрипты
Mozilla Firefox
Клиентские
скрипты
Web-сервер
Интернет
Серверные
скрипты
Opera
Клиентские
скрипты
9
Пример проблемы переносимости
function setBackgroundColor(element, color) {
element.setAttribute("bgcolor", color);
};
Реализация Element.setAttribute() в IE не удовлетворяет стандарту
function setBackgroundColor(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);
};
10
Что делать?
• Систематизировать требования стандарта
• Добиться однозначности и согласованности
требований
• Сделать тестовый набор, проверяющий
соответствие им
• Сделать его общедоступным и известным
• Постоянно использовать этот набор при
разработке, развитии и оценке браузеров
11
Уже имеющиеся тесты
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
• Нет детальной прослеживаемости тестов к требованиям
• Требования не проанализированы на однозначность, полноту и
согласованность
12
Прослеживаемость к требованиям
General, N1
N2
Node insertBefore (Node newChild, Node refChild)
Inserts the node newChild before the existing child node refChild. If refChild is null, insert newChild at the end
of the list of children. If newChild is a DocumentFragment [p.40] object, all of its children are inserted, in the same
order, before refChild. If the newChild is already in the tree, it is first removed.
Note: Inserting a node before itself is implementation dependent.
N4
N3
N5
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.
N6
E1
E2
E3
E4
E5
E6
Exceptions: DOMException
HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the newChild
node, or if the node to insert is one of this node’s ancestors or this node itself, or if this node is of type Document [p.41]
and the DOM application attempts to insert a second DocumentType [p.115] or Element [p.85] node.
WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that created this
node.
NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly or if the parent of the node being inserted is
readonly.
NOT_FOUND_ERR: Raised if refChild is not a child of this node.
NOT_SUPPORTED_ERR: if this node is of type Document [p.41] , this exception might be raised if the DOM
implementation doesn’t support the insertion of a DocumentType [p.115] or Element [p.85] node.
E7
E8
E9
E10
E11
13
Неоднозначности и неполнота
Что имеется в виду под «the node being inserted»?
DocumentFragment или его элемент?
• Что будет при вставке null?
• При вставке DocumentFragment
– Что будет результатом?
– Как изменится список «детей» при некорректном «ребенке» в конце?
– Можно ли добавить пустой DocumentFragment, если добавлять «детей»
нельзя?
• Как определить, поддерживается ли вставка DocumentType и Element?
• Почему нет ограничений на порядок «детей» в Document?
14
Формализация стандарта
• Обеспечение большей совместимости
(основной цели стандартов)
– Выявление неоднозначностей
– Выявление рассогласований, альтернатив
– Выявление неполноты
• Возможность строгого контроля соответствия
– Для тестов и анализа:
правила корректного поведения
– Для тестов:
классы ситуаций, в которых поведение различимо
15
Пример Node.insertBefore()
General, N1
N2
Node insertBefore (Node newChild, Node refChild)
Inserts the node newChild before the existing child node refChild. If refChild is null, insert newChild at the
end of the list of children. If newChild is a DocumentFragment [p.40] object, all of its children are inserted, in the
same order, before refChild. If the newChild is already in the tree, it is first removed.
Note: Inserting a node before itself is implementation dependent.
N4
N3
N5
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.
N6
E1
E2
E3
E4
E5
E6
Exceptions: DOMException
HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the newChild
node, or if the node to insert is one of this node’s ancestors or this node itself, or if this node is of type Document [p.41]
and the DOM application attempts to insert a second DocumentType [p.115] or Element [p.85] node.
WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that created this
node.
NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly or if the parent of the node being inserted is
readonly.
NOT_FOUND_ERR: Raised if refChild is not a child of this node.
NOT_SUPPORTED_ERR: if this node is of type Document [p.41] , this exception might be raised if the DOM
implementation doesn’t support the insertion of a DocumentType [p.115] or Element [p.85] node.
E7
E8
E9
E10
E11
16
Пример: спецификация исключений
Если мы вставляем обычный узел (не
Если
Если
мымы
вставляем
вставляем
DocumentType
DocumentFragment,
в
DocumentFragment), узлы такого типа должны Document,
его «дети»
недолжны
должно быть разрешены
других
в
Node Node.InsertBefore(Node newChild, Node refChild)
быть
разрешены в качестве «детей»
«детей»
качестве
типа
«детей»
DocumentType
текущего узла
{
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) && !(newChild is DocumentFragment)
// -- in particular, if inserted DocumentFragment has a prohibited child for this node
||
newChild is 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 && newChild is DocumentType && HasDifferentChildOfType(typeof(DocumentType), newChild)
|| this is Document && newChild is 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 && newChild is DocumentFragment && HasDifferentChildOfType(typeof(DocumentType), null)
&& newChild.HasDifferentChildOfType(typeof(DocumentType), null)
||
this is Document && newChild is DocumentFragment && 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
E7
Нельзя вE6
качестве «ребенка»
вставлять «предка» текущего узла
E8 узел, построенный в
Нельзя вставлять
E9
рамках
другого документа
17
Пример:Приспецификация
нормы
формализации приходится выделять
разные случаи для одного требования
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( !(newChild is DocumentFragment) || 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 && !(newChild is 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 && !(newChild is DocumentFragment) && 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 && !(newChild is DocumentFragment) && !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 && !(newChild is DocumentFragment)
&& 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
18
Пример: различные ситуации I
Норма
o Вставка обычного узла (не DocumentFragment)
– Вставка узла, не являющегося «ребенком» текущего
•
•
Без своего «родителя»
Со своим «родителем»
– Вставка одного из «детей» текущего узла (при этом набор «детей» не меняется)
•
•
Совпадающего с refChild
Несовпадающего



Стоящего непосредственно перед refChild
Стоящего перед refChild, но не сразу
Стоящего после refChild
o Вставка DocumentFragment
– Пустого
– Из одного элемента
– Из нескольких элементов
 Вставка в конец (refChild == null)
 Вставка не в конец
– В начало
– В середину
19
Пример: различные ситуации 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)
Вставка не в конец
– В начало
– В середину
20
Создание тестов для браузеров
•
•
•
•
•
•
JsUnit http://www.jsunit.net/
Тестовые страницы
Тестовые функции
Инициализация и финализация
Тестирование исключений
Тестовые наборы
21
Пример тестовой страницы
<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;
var res = 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>
22
Оценка корректности
•
•
•
•
•
•
•
•
•
•
•
•
•
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)
Исключения – не поддерживаются прямо
23
Пример теста на исключение
function test_Document_Text_ToEnd()
{
var l = doc.childNodes.length;
var parent = text.parentNode;
var exception = true;
try
try
{
{ res = doc.insertBefore(text, null);
res
fail("Exception
= doc.insertBefore(text,
should be created");
null);
} exception = false;
}
catch(e)
catch(e)
{
{ assertEquals("Exception should be HIERARCHY_REQUEST_ERR", 3, e.code);
assertEquals("Exception
assertEquals("Number of should
children
beshould
HIERARCHY_REQUEST_ERR",
preserve", l, doc.childNodes.length);
3, e.code);
assertEquals("Inserted
assertEquals("Number ofnode
children
parentshould
shouldpreserve",
preserve",l,parent,
doc.childNodes.length);
text.parentNode);
} assertEquals("Inserted node parent should preserve", parent, text.parentNode);
} }
if(!exception) fail("Exception should be created");
}
24
Пример тестового набора
<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>
25
Приглашение к сотрудничеству
DOM API Testing на CodePlex
• http://domapitesting.codeplex.com
26
Спасибо за внимание!