UNPKG

node-webodf

Version:

WebODF - JavaScript Document Engine http://webodf.org/

507 lines (433 loc) 25.9 kB
/** * Copyright (C) 2012 KO GmbH <copyright@kogmbh.com> * * @licstart * This file is part of WebODF. * * WebODF is free software: you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License (GNU AGPL) * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * WebODF is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with WebODF. If not, see <http://www.gnu.org/licenses/>. * @licend * * @source: http://www.webodf.org/ * @source: https://github.com/kogmbh/WebODF/ */ /*global runtime, core, odf, NodeFilter*/ /** * @constructor * @param {core.UnitTestRunner} runner * @implements {core.UnitTest} */ odf.FormattingTests = function FormattingTests(runner) { "use strict"; var self = this, t, r = runner, namespace = odf.Namespaces.namespaceMap, cssUnits = new core.CSSUnits(); this.setUp = function () { t = { formatting : new odf.Formatting(), body : core.UnitTest.provideTestAreaDiv(), ns : namespace }; }; this.tearDown = function () { t = {}; core.UnitTest.cleanupTestAreaDiv(); }; /** * @param {!string} dom * @param {string=} pageLayoutStyle * @return {!Node} */ function createDocument(dom, pageLayoutStyle) { var xml, container, fragment; xml = "<office:styles>"; xml += " <style:style style:name='P1' style:display-name='P1 Display' style:family='paragraph' style:master-page-name='Index'>"; xml += " <style:text-properties fo:font-name='P1 Font' />"; xml += " </style:style>"; xml += " <style:style style:name='PEmpty' style:display-name='PEmpty Display' style:family='paragraph' style:master-page-name='Index'>"; xml += " </style:style>"; xml += " <style:style style:name='PMissingDefinition' style:display-name='Missing Def' style:family='paragraph' style:master-page-name='Missing'>"; xml += " <style:text-properties fo:font-name='PMissingDefinition Font' />"; xml += " </style:style>"; xml += " <style:style style:name='P2' style:display-name='P1 Display' style:family='paragraph'>"; xml += " <style:text-properties fo:font-name='P2 Font' />"; xml += " </style:style>"; xml += " <style:style style:name='S1' style:display-name='S1 Display' style:family='text'>"; xml += " <style:text-properties fo:font-name='S1 Font' />"; xml += " </style:style>"; xml += " <style:style style:name='S2' style:display-name='S2 Display' style:family='text'>"; xml += " <style:text-properties fo:font-weight='bold'/>"; xml += " </style:style>"; xml += " <style:style style:name='SBold' style:display-name='SBold Display' style:family='text'>"; xml += " <style:text-properties fo:font-weight='bold'/>"; xml += " </style:style>"; xml += " <style:style style:name='SItalic' style:family='text'>"; xml += " <style:text-properties fo:font-style='italic' />"; xml += " </style:style>"; xml += " <style:style style:name='SUnderline' style:family='text'>"; xml += " <style:text-properties style:text-underline-style='solid' />"; xml += " </style:style>"; xml += " <style:style style:name='SStrikeThrough' style:family='text'>"; xml += " <style:text-properties style:text-line-through-style='solid' />"; xml += " </style:style>"; xml += "</office:styles>"; xml += "<office:automatic-styles>"; xml += pageLayoutStyle; xml += " <text:list-style style:name='L1' style:display-name='L1 Display'>"; xml += " <text:list-level-style-bullet text:level='1' text:bullet-char='*' />"; xml += " <text:list-level-style-bullet text:level='2' text:bullet-char='@' />"; xml += " </text:list-style>"; xml += " <text:list-style style:name='L2' style:display-name='L2 Display'>"; xml += " <text:list-level-style-number text:level='1' text:num-format='1' />"; xml += " </text:list-style>"; xml += " <style:style style:name='autoWithInherit' style:parent-style-name='S1' style:family='text'>"; xml += " <style:text-properties fo:font-weight='bold'/>"; xml += " </style:style>"; xml += " <style:style style:name='autoWithoutInherit' style:family='text'>"; xml += " <style:text-properties fo:font-weight='bold'/>"; xml += " </style:style>"; xml += "</office:automatic-styles>"; xml += "<office:text>" + dom + "</office:text>"; xml += "<office:master-styles>"; xml += " <style:master-page style:name='Standard' style:page-layout-name='pm1'/>"; xml += " <style:master-page style:name='Index' style:page-layout-name='pm2'/>"; xml += "</office:master-styles>"; fragment = core.UnitTest.createOdtDocument(xml, namespace); t.body.appendChild(fragment.documentElement); container = { rootElement : { styles : t.body.firstChild.childNodes[0], automaticStyles: t.body.firstChild.childNodes[1], masterStyles : t.body.firstChild.childNodes[3] }}; t.formatting.setOdfContainer(container); t.range = t.body.ownerDocument.createRange(); return t.body.firstChild.childNodes[2].firstChild; } /** * Return an array containing all attribute names defined for the node * @param {!Node} node * @return {!Array.<!string>} */ function getAttributeNames(node) { return Array.prototype.slice.call(node.attributes).map(function(attr) { return attr.name; }); } /** * Get all text nodes in the test div * @return {!Array.<!Node>} */ function getTextNodes() { var document = t.body.ownerDocument, walker = document.createTreeWalker(t.body, NodeFilter.SHOW_TEXT, null, false), nodes = [], n = walker.nextNode(); while (n) { nodes.push(n); n = walker.nextNode(); } return nodes; } function getStyleElement_ParagraphStyle() { createDocument("<text:p/>"); t.element = t.formatting.getStyleElement("P1", "paragraph"); r.shouldBeNonNull(t, "t.element"); r.shouldBe(t, "t.element.getAttributeNS(t.ns.style, 'name')", "'P1'"); } function getStyleElement_TextStyle() { createDocument("<text:p/>"); t.element = t.formatting.getStyleElement("S1", "text"); r.shouldBeNonNull(t, "t.element"); r.shouldBe(t, "t.element.getAttributeNS(t.ns.style, 'name')", "'S1'"); } function getStyleElement_ListStyle() { createDocument("<text:p/>"); t.element = t.formatting.getStyleElement("L2", "list-style"); r.shouldBeNonNull(t, "t.element"); r.shouldBe(t, "t.element.getAttributeNS(t.ns.style, 'name')", "'L2'"); } function getStyleElement_MismatchedFamily_ReturnsNull() { createDocument("<text:p/>"); t.element = t.formatting.getStyleElement("L2", "paragraph"); r.shouldBeNull(t, "t.element"); } function updateStyle_UpdatesStyleElement() { createDocument("<text:p/>"); t.element = t.formatting.getStyleElement("P1", "paragraph"); t.formatting.updateStyle(t.element, { "style:family" : "frog", "style:paragraph-properties": { "fo:background-color" : "red" }, "style:text-properties": { "fo:font-size" : "12pt" } }); r.shouldBe(t, "t.element.getAttributeNS(t.ns.style, 'family')", "'frog'"); t.paragraphProperties = t.element.getElementsByTagNameNS(t.ns.style, 'paragraph-properties')[0]; t.textProperties = t.element.getElementsByTagNameNS(t.ns.style, 'text-properties')[0]; r.shouldBe(t, "t.paragraphProperties.getAttributeNS(t.ns.fo, 'background-color')", "'red'"); r.shouldBe(t, "t.textProperties.getAttributeNS(t.ns.fo, 'font-size')", "'12pt'"); r.shouldBe(t, "t.textProperties.getAttributeNS(t.ns.fo, 'font-name')", "'P1 Font'"); } function updateStyle_UpdatesStyleElement_EmptyObjectOnSource() { createDocument("<text:p/>"); t.element = t.formatting.getStyleElement("PEmpty", "paragraph"); t.formatting.updateStyle(t.element, { "style:family" : "frog", "style:paragraph-properties": { "fo:background-color" : "red" }, "style:text-properties": {} }); t.attributeNames = getAttributeNames(t.element); r.shouldBe(t, "t.attributeNames", "['style:name', 'style:display-name', 'style:family', 'style:master-page-name']"); r.shouldBe(t, "t.element.getAttributeNS(t.ns.style, 'family')", "'frog'"); t.paragraphProperties = t.element.getElementsByTagNameNS(t.ns.style, 'paragraph-properties')[0]; t.textProperties = t.element.getElementsByTagNameNS(t.ns.style, 'text-properties')[0]; r.shouldBe(t, "t.paragraphProperties.getAttributeNS(t.ns.fo, 'background-color')", "'red'"); r.shouldBe(t, "t.textProperties", "undefined"); } function updateStyle_IgnoresUnknownElementPrefixes() { var i; createDocument("<text:p/>"); t.element = t.formatting.getStyleElement("P1", "paragraph"); t.formatting.updateStyle(t.element, { "custom:item" : "custom-value", "style:family" : "frog" }); r.shouldBe(t, "t.element.getAttributeNS(t.ns.style, 'family')", "'frog'"); for (i = 0; i < t.element.attributes.length; i += 1) { t.attribute = t.element.attributes[i]; r.shouldBe(t, "t.attribute.value === 'custom-value'", "false"); } } function createDerivedStyleObject_Style() { createDocument("<text:p/>"); t.newStyle = t.formatting.createDerivedStyleObject("P1", "paragraph", {"derived" : true}); r.shouldBe(t, "t.newStyle['style:parent-style-name']", "'P1'"); r.shouldBe(t, "t.newStyle['style:family']", "'paragraph'"); r.shouldBe(t, "t.newStyle['derived']", "true"); } function createDerivedStyleObject_AutomaticStyle_Inherited() { createDocument("<text:p/>"); t.newStyle = t.formatting.createDerivedStyleObject("autoWithInherit", "text", {"derived" : true}); r.shouldBe(t, "t.newStyle['style:parent-style-name']", "'S1'"); r.shouldBe(t, "t.newStyle['style:family']", "'text'"); r.shouldBe(t, "t.newStyle['style:text-properties']", "({ 'fo:font-weight' : 'bold' })"); } function createDerivedStyleObject_AutomaticStyle_NonInherited() { createDocument("<text:p/>"); t.newStyle = t.formatting.createDerivedStyleObject("autoWithoutInherit", "text", {"derived" : true}); r.shouldBe(t, "t.newStyle.hasOwnProperty('style:parent-style-name')", "false"); r.shouldBe(t, "t.newStyle['style:family']", "'text'"); r.shouldBe(t, "t.newStyle['style:text-properties']", "({ 'fo:font-weight' : 'bold' })"); } function getStyleAttributes_ReturnsAllStyleAttributes() { createDocument("<text:p/>"); t.styleElement = t.formatting.getStyleElement('P1', 'paragraph'); t.styleAttributes = t.formatting.getStyleAttributes(t.styleElement); r.shouldBe(t, "t.styleAttributes['style:name']", "'P1'"); r.shouldBe(t, "t.styleAttributes['style:display-name']", "'P1 Display'"); r.shouldBe(t, "t.styleAttributes['style:family']", "'paragraph'"); r.shouldBe(t, "t.styleAttributes['style:text-properties']", "({'fo:font-name':'P1 Font'})"); } function getContentSize_PageSizePaddingAndMarginSpecified() { createDocument("<text:p style:name='P1'/>", "<style:page-layout style:name='pm2' scope='document-styles'><style:page-layout-properties fo:page-width='10px' fo:page-height='20px' fo:margin-top='1px' fo:margin-bottom='0px' fo:margin-left='1.5px' fo:margin-right='1.5px' fo:padding='3px' /></style:page-layout>"); t.contentSize = t.formatting.getContentSize("P1", "paragraph"); r.shouldBe(t, "t.contentSize", "({'width':1,'height':13})"); } function getContentSize_PageSizePaddingAndMarginNotSpecified() { var heightPx = cssUnits.convertMeasure("21.001cm", "px"), widthPx = cssUnits.convertMeasure("29.7cm", "px"), marginPx = cssUnits.convertMeasure("2cm", "px"); createDocument("<text:p style:name='P2'/>", "<style:page-layout style:name='pm1' scope='document-styles'><style:page-layout-properties style:print-orientation='landscape' /></style:page-layout>"); t.contentSize = t.formatting.getContentSize("P2", "paragraph"); t.expectedSize = { height: heightPx - marginPx - marginPx, width: widthPx - marginPx - marginPx }; r.shouldBe(t, "t.contentSize", "t.expectedSize"); } function getContentSize_MissingPageDefinition_DocumentHasStandard() { var heightPx = cssUnits.convertMeasure("21.001cm", "px"), widthPx = cssUnits.convertMeasure("29.7cm", "px"), marginPx = cssUnits.convertMeasure("2cm", "px"); createDocument("<text:p style:name='PMissingDefinition'/>", "<style:page-layout style:name='pm1' scope='document-styles'><style:page-layout-properties style:print-orientation='landscape' /></style:page-layout>"); t.contentSize = t.formatting.getContentSize("PMissingDefinition", "paragraph"); t.expectedSize = { height: heightPx - marginPx - marginPx, width: widthPx - marginPx - marginPx }; r.shouldBe(t, "t.contentSize", "t.expectedSize"); } function getContentSize_NoMasterPageDefined() { var doc = createDocument("<text:p style:name='P2'/>"), pageDefinitions = doc.getElementsByTagNameNS(namespace.style, "master-page"), heightPx = cssUnits.convertMeasure("29.7cm", "px"), widthPx = cssUnits.convertMeasure("21.001cm", "px"), marginPx = cssUnits.convertMeasure("2cm", "px"); while (pageDefinitions[0]) { pageDefinitions[0].parentNode.removeChild(pageDefinitions[0]); } t.contentSize = t.formatting.getContentSize("P2", "paragraph"); t.expectedSize = { height: heightPx - marginPx - marginPx, width: widthPx - marginPx - marginPx }; r.shouldBe(t, "t.contentSize", "t.expectedSize"); } function getAppliedStyles_SimpleHierarchy() { t.doc = createDocument("<text:p text:style-name='P1'><text:span text:style-name='S1'>A</text:span></text:p>"); t.appliedStyles = t.formatting.getAppliedStyles(getTextNodes()); r.shouldBe(t, "t.appliedStyles.length", "1"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.length", "2"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'S1', displayName: 'S1 Display', family: 'text', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'P1', displayName: 'P1 Display', family: 'paragraph', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].styleProperties['style:text-properties']", "({'fo:font-name': 'S1 Font'})"); } function getAppliedStyles_NestedHierarchy() { t.doc = createDocument("<text:p text:style-name='P1'><text:span text:style-name='S1'><text:span text:style-name='SBold'>A</text:span></text:span></text:p>"); t.appliedStyles = t.formatting.getAppliedStyles(getTextNodes()); r.shouldBe(t, "t.appliedStyles.length", "1"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.length", "3"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'SBold', displayName: 'SBold Display', family: 'text', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'S1', displayName: 'S1 Display', family: 'text', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'P1', displayName: 'P1 Display', family: 'paragraph', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].styleProperties['style:text-properties']", "({'fo:font-name': 'S1 Font', 'fo:font-weight': 'bold'})"); } function getAppliedStyles_CompleteContent_OnlyReportsUniqueStyles() { t.doc = createDocument("<text:p text:style-name='P1'>A<text:span text:style-name='S1'>B</text:span>C</text:p>"); t.appliedStyles = t.formatting.getAppliedStyles(getTextNodes()); r.shouldBe(t, "t.appliedStyles.length", "2"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.length", "1"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'P1', displayName: 'P1 Display', family: 'paragraph', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].styleProperties['style:text-properties']", "({'fo:font-name': 'P1 Font'})"); t.appliedStyles.shift(); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.length", "2"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'S1', displayName: 'S1 Display', family: 'text', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'P1', displayName: 'P1 Display', family: 'paragraph', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].styleProperties['style:text-properties']", "({'fo:font-name': 'S1 Font'})"); } function getAppliedStyles_EmptyArray() { t.appliedStyles = t.formatting.getAppliedStyles([]); r.shouldBe(t, "t.appliedStyles.length", "0"); } function getAppliedStyles_StartsAndEnds_InSameTextNode() { t.doc = createDocument("<text:p text:style-name='P1'><text:span text:style-name='S1'></text:span>ABC</text:p>"); t.appliedStyles = t.formatting.getAppliedStyles(getTextNodes()); r.shouldBe(t, "t.appliedStyles.length", "1"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.length", "1"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'P1', displayName: 'P1 Display', family: 'paragraph', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].styleProperties['style:text-properties']", "({'fo:font-name': 'P1 Font'})"); } function getAppliedStyles_StartsAndEnds_InDifferentTextNodes() { t.doc = createDocument("<text:p text:style-name='P1'>A<text:span text:style-name='S1'>B</text:span>C</text:p>"); t.appliedStyles = t.formatting.getAppliedStyles(getTextNodes()); r.shouldBe(t, "t.appliedStyles.length", "2"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.length", "1"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'P1', displayName: 'P1 Display', family: 'paragraph', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].styleProperties['style:text-properties']", "({'fo:font-name': 'P1 Font'})"); t.appliedStyles.shift(); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.length", "2"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'S1', displayName: 'S1 Display', family: 'text', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'P1', displayName: 'P1 Display', family: 'paragraph', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].styleProperties['style:text-properties']", "({'fo:font-name': 'S1 Font'})"); } function getAppliedStyles_SimpleList() { var xml = "<text:list text:style-name='L2'><text:list-item>" + "<text:p text:style-name='P1'><text:span text:style-name='S1'>A</text:span></text:p>" + "</text:list-item></text:list>"; t.doc = createDocument(xml); t.appliedStyles = t.formatting.getAppliedStyles(getTextNodes()); r.shouldBe(t, "t.appliedStyles.length", "1"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.length", "3"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'S1', displayName: 'S1 Display', family: 'text', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'P1', displayName: 'P1 Display', family: 'paragraph', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'L2', displayName: 'L2 Display', family: 'list-style', isCommonStyle: false})"); r.shouldBe(t, "t.appliedStyles[0].styleProperties['style:text-properties']", "({'fo:font-name': 'S1 Font'})"); } function getAppliedStyles_NestedList() { var xml = "<text:list text:style-name='L1'><text:list-item>" + "<text:list text:style-name='L2'><text:list-item>" + "<text:p text:style-name='P1'><text:span text:style-name='S1'>A</text:span></text:p>" + "</text:list-item></text:list>" + "</text:list-item></text:list>"; t.doc = createDocument(xml); t.appliedStyles = t.formatting.getAppliedStyles(getTextNodes()); r.shouldBe(t, "t.appliedStyles.length", "1"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.length", "4"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'S1', displayName: 'S1 Display', family: 'text', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'P1', displayName: 'P1 Display', family: 'paragraph', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'L2', displayName: 'L2 Display', family: 'list-style', isCommonStyle: false})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'L1', displayName: 'L1 Display', family: 'list-style', isCommonStyle: false})"); r.shouldBe(t, "t.appliedStyles[0].styleProperties['style:text-properties']", "({'fo:font-name': 'S1 Font'})"); } function getAppliedStyles_InvalidNodes() { var i, node, invalidNodes = [ "draw:text-box", "svg:title", "dr3d:scene", "text:note-body", "text:ruby-text", "office:binary-data", "office:event-listeners", "editinfo" ]; for (i = 0; i < invalidNodes.length; i += 1) { if (i > 0) { self.setUp(); } node = "<" + invalidNodes[i] + "><text:span text:style-name='S1'>test</text:span></" + invalidNodes[i] + ">"; t.doc = createDocument("<text:p text:style-name='P1'>" + node + "</text:p>"); t.appliedStyles = t.formatting.getAppliedStyles(getTextNodes()); r.shouldBe(t, "t.appliedStyles.length", "1"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.length", "2"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'S1', displayName: 'S1 Display', family: 'text', isCommonStyle: true})"); r.shouldBe(t, "t.appliedStyles[0].orderedStyles.shift()", "({name: 'P1', displayName: 'P1 Display', family: 'paragraph', isCommonStyle: true})"); if (i < invalidNodes.length - 1) { self.tearDown(); } } } this.tests = function () { return r.name([ getStyleElement_ParagraphStyle, getStyleElement_TextStyle, getStyleElement_ListStyle, getStyleElement_MismatchedFamily_ReturnsNull, updateStyle_UpdatesStyleElement, updateStyle_IgnoresUnknownElementPrefixes, updateStyle_UpdatesStyleElement_EmptyObjectOnSource, createDerivedStyleObject_Style, createDerivedStyleObject_AutomaticStyle_Inherited, createDerivedStyleObject_AutomaticStyle_NonInherited, getStyleAttributes_ReturnsAllStyleAttributes, getContentSize_PageSizePaddingAndMarginSpecified, getContentSize_PageSizePaddingAndMarginNotSpecified, getContentSize_MissingPageDefinition_DocumentHasStandard, getContentSize_NoMasterPageDefined, getAppliedStyles_SimpleHierarchy, getAppliedStyles_NestedHierarchy, getAppliedStyles_EmptyArray, getAppliedStyles_CompleteContent_OnlyReportsUniqueStyles, getAppliedStyles_StartsAndEnds_InSameTextNode, getAppliedStyles_StartsAndEnds_InDifferentTextNodes, getAppliedStyles_SimpleList, getAppliedStyles_NestedList, getAppliedStyles_InvalidNodes ]); }; this.asyncTests = function () { return []; }; }; odf.FormattingTests.prototype.description = function () { "use strict"; return "Test the Formatting class."; };