├── doc └── main.md ├── data ├── icon.png ├── doctests.js ├── sidebar.html ├── tests │ ├── http-links.js │ ├── testlist.js │ ├── alert-print-in-code.js │ ├── font-elements.js │ ├── shell-prompts.js │ ├── style-attribute.js │ ├── name-attribute.js │ ├── old-urls.js │ ├── code-in-pre.js │ ├── article-length.js │ ├── example-colon-heading.js │ ├── summary-heading.js │ ├── span-count.js │ ├── pre-without-class.js │ ├── html-comments.js │ ├── different-locale-links.js │ ├── line-length-in-pre.js │ ├── absolute-urls-for-internal-links.js │ ├── empty-brackets.js │ ├── url-in-link-title.js │ ├── empty-elements.js │ ├── incorrectly-wrapped-sidebar-macros.js │ ├── data-macro-note.js │ ├── unnecessary-macro-params.js │ ├── wrong-syntax-class.js │ ├── api-syntax-headlines.js │ ├── macro-syntax-error.js │ ├── wrong-highlighted-line.js │ └── invalid-macros.js ├── runtests.js ├── sidebar.css └── sidebar.js ├── screenshot.png ├── .travis.yml ├── .jpmignore ├── test ├── test-old-urls.js ├── test-font-elements.js ├── test-empty-brackets.js ├── test-http-links.js ├── testrunner.js ├── test-html-comments.js ├── test-unnecessary-macro-params.js ├── test-summary-heading.js ├── test-alert-print-in-code.js ├── test-name-attribute.js ├── test-example-colon-heading.js ├── test-article-length.js ├── test-span-count.js ├── test-shell-prompts.js ├── test-api-syntax-headlines.js ├── test-code-in-pre.js ├── test-line-length-in-pre.js ├── test-absolute-urls-for-internal-links.js ├── test-wrong-syntax-class.js ├── test-style-attribute.js ├── test-different-locale-links.js ├── test-pre-without-class.js ├── test-data-macro-note.js ├── test-empty-elements.js ├── test-incorrectly-wrapped-sidebar-macros.js ├── test-url-in-link-title.js ├── test-invalid-macros.js ├── test-wrong-highlighted-line.js ├── testutils.js └── test-macro-syntax-error.js ├── README.rst ├── package.json ├── lib └── main.js ├── locale ├── fr.properties ├── en-US.properties └── de.properties └── LICENSE /doc/main.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elchi3/mdn-doc-tests/HEAD/data/icon.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Elchi3/mdn-doc-tests/HEAD/screenshot.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "node" 5 | addons: 6 | firefox: "47.0" 7 | env: 8 | global: 9 | - DISPLAY=:99.0 10 | - JPM_FIREFOX_BINARY=/usr/local/bin/firefox 11 | before_install: 12 | - sh -e /etc/init.d/xvfb start 13 | before_script: 14 | - npm install -g jpm 15 | script: 16 | - jpm test 17 | notifications: 18 | email: false -------------------------------------------------------------------------------- /.jpmignore: -------------------------------------------------------------------------------- 1 | # Ignore the .jpmignore file itself 2 | .jpmignore 3 | 4 | # Ignore the Travis CI file and documentation 5 | .travis.yml 6 | doc 7 | README.rst 8 | 9 | # Ignore unit tests 10 | test/ 11 | 12 | # Ignore development files and folders 13 | .git 14 | .project 15 | .settings 16 | *.bat 17 | 18 | # Ignore images in root directory 19 | /*.png 20 | 21 | # Ignore the XPI when it got created previously 22 | *.xpi -------------------------------------------------------------------------------- /data/doctests.js: -------------------------------------------------------------------------------- 1 | // Utilities for all tests 2 | 3 | const ERROR = 1; 4 | const WARNING = 2; 5 | const INFO = 3; 6 | 7 | let docTests = {}; 8 | 9 | function mapMatches(matches, type) { 10 | return matches.map(match => { 11 | return {msg: match, type: type}; 12 | }); 13 | } 14 | 15 | function isNewParagraphHelper(element) { 16 | if (!element || element.localName !== "span") { 17 | return false; 18 | } 19 | 20 | let style = element.getAttribute("style"); 21 | return style && /z-index:\s*9999;/.test(style); 22 | } -------------------------------------------------------------------------------- /test/test-old-urls.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc oldURLs"] = function testOldURLs(assert, done) { 4 | const tests = [ 5 | { 6 | str: 'Web' + 7 | 'Mozilla', 8 | expected: [ 9 | { 10 | msg: 'Web', 11 | type: ERROR 12 | }, 13 | { 14 | msg: 'Mozilla', 15 | type: ERROR 16 | } 17 | ] 18 | } 19 | ]; 20 | 21 | runTests(assert, done, "oldURLs", "'/en/' link", url, tests); 22 | }; 23 | 24 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ==== 2 | MDN documentation tester 3 | ==== 4 | 5 | .. image:: https://travis-ci.org/Elchi3/mdn-doc-tests.svg?branch=master 6 | :target: https://travis-ci.org/Elchi3/mdn-doc-tests 7 | :alt: Build Status 8 | 9 | .. image:: http://img.shields.io/badge/license-MPL2-blue.svg 10 | :target: https://raw.githubusercontent.com/Elchi3/mdn-doc-tests/master/LICENSE 11 | :alt: License 12 | 13 | .. image:: https://raw.github.com/Elchi3/mdn-doc-tests/master/screenshot.png 14 | :alt: Screenshot 15 | 16 | This add-on adds a sidebar with a test suite you can run against MDN docs while editing. The tests check whether there are errors or unnecessary parts within the article source or its layout. 17 | -------------------------------------------------------------------------------- /test/test-font-elements.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc fontElements"] = function testFontElements(assert, done) { 4 | const tests = [ 5 | { 6 | str: 'Some text' + 7 | 'Another text', 8 | expected: [ 9 | { 10 | msg: 'Some text', 11 | type: ERROR 12 | }, 13 | { 14 | msg: 'Another text', 15 | type: ERROR 16 | } 17 | ] 18 | } 19 | ]; 20 | 21 | runTests(assert, done, "fontElements", "", url, tests); 22 | }; 23 | 24 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-empty-brackets.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc emptyBrackets"] = function testEmptyBrackets(assert, done) { 4 | const tests = [ 5 | { 6 | str: '{{ foo() }}' + 7 | '{{bar()}}' + 8 | '{{foobar("abc")}}' + 9 | '{{baz}}', 10 | expected: [ 11 | { 12 | msg: '{{ foo() }}', 13 | type: ERROR 14 | }, 15 | { 16 | msg: '{{bar()}}', 17 | type: ERROR 18 | } 19 | ], 20 | expectedAfterFixing: [] 21 | } 22 | ]; 23 | 24 | runTests(assert, done, "emptyBrackets", "empty brackets", url, tests); 25 | }; 26 | 27 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-http-links.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc httpLinks"] = function testHTTPLinks(assert, done) { 4 | const tests = [ 5 | { 6 | str: 'some page' + 7 | 'some page' + 8 | 'some page' + 9 | 'some page', 10 | expected: [ 11 | { 12 | msg: 'some page', 13 | type: WARNING 14 | } 15 | ] 16 | } 17 | ]; 18 | 19 | runTests(assert, done, "httpLinks", "HTTP link", url, tests); 20 | }; 21 | 22 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/testrunner.js: -------------------------------------------------------------------------------- 1 | // This test runner is called from unit tests only 2 | 3 | let testObj = docTests[self.options.name]; 4 | let rootElement = document.createElement("div"); 5 | let tests = JSON.parse(self.options.tests); 6 | 7 | tests.forEach(test => { 8 | rootElement.innerHTML = test.str; 9 | if (testObj) { 10 | let matches = testObj.check(rootElement); 11 | testObj.errors = matches; 12 | testObj.expected = test.expected; 13 | 14 | if (test.expectedAfterFixing) { 15 | testObj.fix(matches); 16 | let matchesAfterFixing = testObj.check(rootElement); 17 | testObj.errorsAfterFixing = matchesAfterFixing; 18 | testObj.expectedAfterFixing = test.expectedAfterFixing; 19 | } 20 | } 21 | 22 | self.port.emit("processTestResult", testObj, self.options.name); 23 | }); -------------------------------------------------------------------------------- /data/sidebar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MDN documentation tests 6 | 7 | 8 | 9 | 10 |

11 | 12 |
13 |

0

14 |

0

15 |
16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /data/tests/http-links.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for http link usages where https should be used. 3 | * 4 | * Example 1: Mozilla should rather be 5 | * Mozilla. 6 | * 7 | * Implementation notes: This test checks whether links refer to HTTP URLs. Because some URLs 8 | * don't provide HTTPS access, matches are only output as warnings. 9 | */ 10 | 11 | docTests.httpLinks = { 12 | name: "http_links", 13 | desc: "http_links_desc", 14 | check: function checkHTTPLinks(rootElement) { 15 | let httpLinks = rootElement.querySelectorAll("a[href^='http://']"); 16 | let matches = []; 17 | 18 | for (let i = 0; i < httpLinks.length; i++) { 19 | matches.push({ 20 | msg: httpLinks[i].outerHTML, 21 | type: WARNING 22 | }) 23 | } 24 | 25 | return matches; 26 | } 27 | }; -------------------------------------------------------------------------------- /test/test-html-comments.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc htmlComments"] = function testHTMLComments(assert, done) { 4 | const tests = [ 5 | { 6 | str: '' + 7 | '' + 8 | '', 9 | expected: [ 10 | { 11 | msg: '', 12 | type: ERROR 13 | }, 14 | { 15 | msg: '', 16 | type: ERROR 17 | }, 18 | { 19 | msg: '', 20 | type: ERROR 21 | } 22 | ], 23 | expectedAfterFixing: [] 24 | } 25 | ]; 26 | 27 | runTests(assert, done, "htmlComments", "HTML comment", url, tests); 28 | }; 29 | 30 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /data/tests/testlist.js: -------------------------------------------------------------------------------- 1 | // This will also be the ordering of the tests in the sidebar 2 | 3 | exports.testList = [ 4 | "old-urls.js", 5 | "absolute-urls-for-internal-links.js", 6 | "different-locale-links.js", 7 | "empty-elements.js", 8 | "empty-brackets.js", 9 | "style-attribute.js", 10 | "name-attribute.js", 11 | "span-count.js", 12 | "pre-without-class.js", 13 | "summary-heading.js", 14 | "example-colon-heading.js", 15 | "alert-print-in-code.js", 16 | "shell-prompts.js", 17 | "html-comments.js", 18 | "font-elements.js", 19 | "http-links.js", 20 | "data-macro-note.js", 21 | "unnecessary-macro-params.js", 22 | "invalid-macros.js", 23 | "incorrectly-wrapped-sidebar-macros.js", 24 | "macro-syntax-error.js", 25 | "wrong-highlighted-line.js", 26 | "api-syntax-headlines.js", 27 | "wrong-syntax-class.js", 28 | "code-in-pre.js", 29 | "line-length-in-pre.js", 30 | "url-in-link-title.js", 31 | "article-length.js" 32 | ]; -------------------------------------------------------------------------------- /test/test-unnecessary-macro-params.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc unnecessaryMacroParams"] = function testUnnecessaryMacroParams(assert, done) { 4 | const tests = [ 5 | { 6 | str: '{{JSRef}}' + 7 | '{{JSRef()}}' + 8 | '{{JSRef("Global_Objects")}}' + 9 | '{{ JSRef("Global_Objects", "Math") }}' + 10 | '{{csssyntax("font-family")}}', 11 | expected: [ 12 | { 13 | msg: "macro_with_unused_params", 14 | msgParams: ['{{JSRef("Global_Objects")}}'], 15 | type: ERROR 16 | }, 17 | { 18 | msg: "macro_with_unused_params", 19 | msgParams: ['{{ JSRef("Global_Objects", "Math") }}'], 20 | type: ERROR 21 | } 22 | ] 23 | } 24 | ]; 25 | 26 | runTests(assert, done, "unnecessaryMacroParams", "unnecessary macro params", url, tests); 27 | }; 28 | 29 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /data/tests/alert-print-in-code.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test whether discouraged statements are used in example code. 3 | * 4 | * Example 1:
alert("Some message");
should be avoided and 5 | *
console.log("Some message");
be used instead. 6 | * 7 | * Implementation notes: This test checks all
 blocks for the usage of discouraged functions.
 8 |  *  In some cases their usage may not be avoided, like for example on their description pages.
 9 |  *  The test does not account for those cases, though.
10 |  */
11 | 
12 | docTests.alertPrintInCode = {
13 |   name: "alert_print_in_code",
14 |   desc: "alert_print_in_code_desc",
15 |   check: function checkAlertPrintInCode(rootElement) {
16 |     let pres = rootElement.getElementsByTagName("pre");
17 |     let matches = [];
18 |     for (let i = 0; i < pres.length; i++) {
19 |       let preMatches = pres[i].textContent.match(/(?:alert|print|eval|document\.write)\s*\((?:.|\n)+?\)/gi) || [];
20 |       matches = matches.concat(mapMatches(preMatches, ERROR));
21 |     }
22 | 
23 |     return matches;
24 |   }
25 | };


--------------------------------------------------------------------------------
/test/test-summary-heading.js:
--------------------------------------------------------------------------------
 1 | const {ERROR, WARNING, url, runTests} = require("./testutils");
 2 | 
 3 | exports["test doc summaryHeading"] = function testSummaryHeading(assert, done) {
 4 |   const tests = [
 5 |     {
 6 |       str: '

Summary

' + 7 | '

Summary

' + 8 | '

Summary

' + 9 | '

Summary

', 10 | expected: [ 11 | { 12 | msg: '

Summary

', 13 | type: ERROR 14 | }, 15 | { 16 | msg: '

Summary

', 17 | type: ERROR 18 | }, 19 | { 20 | msg: '

Summary

', 21 | type: ERROR 22 | }, 23 | { 24 | msg: '

Summary

', 25 | type: ERROR 26 | } 27 | ], 28 | expectedAfterFixing: [] 29 | } 30 | ]; 31 | 32 | runTests(assert, done, "summaryHeading", "summary heading", url, tests); 33 | }; 34 | 35 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /data/tests/font-elements.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for deprecated elements that should be removed or replaced by other 3 | * elements. 4 | * 5 | * Example 1: Emphasized text should be replaced by 6 | * Emphasized text or Emphasized text. 7 | * 8 | * Example 2:

Heading

should be replaced by 9 | *

Heading

. 10 | * 11 | * Implementation notes: This test searches for all elements, but doesn't provide 12 | * a hint whether they should be removed or replaced by other elements. 13 | */ 14 | 15 | docTests.fontElements = { 16 | name: "font_elements", 17 | desc: "font_elements_desc", 18 | check: function checkFontElements(rootElement) { 19 | let fontElements = rootElement.getElementsByTagName("font"); 20 | let matches = []; 21 | 22 | for (let i = 0; i < fontElements.length; i++) { 23 | matches.push({ 24 | msg: fontElements[i].outerHTML, 25 | type: ERROR 26 | }) 27 | } 28 | 29 | return matches; 30 | } 31 | }; -------------------------------------------------------------------------------- /test/test-alert-print-in-code.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc alertPrintInCode"] = function testAlertPrintInCode(assert, done) { 4 | const tests = [ 5 | { 6 | str: '
alert("foo")
' + 7 | '
print("bar")
' + 8 | '
let someOthercode = baz; ' +
 9 |            'alert("hello world"); \n let moreCode;
' + 10 | '
document.write("foobar");
', 11 | expected: [ 12 | { 13 | msg: 'alert("foo")', 14 | type: ERROR 15 | }, 16 | { 17 | msg: 'print("bar")', 18 | type: ERROR 19 | }, 20 | { 21 | msg: 'alert("hello world")', 22 | type: ERROR 23 | }, 24 | { 25 | msg: 'document.write("foobar")', 26 | type: ERROR 27 | } 28 | ] 29 | } 30 | ]; 31 | 32 | runTests(assert, done, "alertPrintInCode", "alert()/print()/eval()/document.write()", url, tests); 33 | }; 34 | 35 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-name-attribute.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc nameAttribute"] = function testNameAttributes(assert, done) { 4 | const tests = [ 5 | { 6 | str: '' + 7 | '
' + 8 | '

foo

' + 9 | '

foo bar

' + 10 | '

baz

', 11 | expected: [ 12 | { 13 | msg: 'name=""', 14 | type: ERROR 15 | }, 16 | { 17 | msg: 'name="foo"', 18 | type: ERROR 19 | }, 20 | { 21 | msg: 'name="foo"', 22 | type: ERROR 23 | }, 24 | { 25 | msg: 'name="foo_bar"', 26 | type: ERROR 27 | }, 28 | { 29 | msg: 'name="baz"', 30 | type: ERROR 31 | } 32 | ], 33 | expectedAfterFixing: [] 34 | } 35 | ]; 36 | 37 | runTests(assert, done, "nameAttribute", "'name' attribute", url, tests); 38 | }; 39 | 40 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /data/tests/shell-prompts.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for shell prompts, i.e. lines starting with '>' or '$' in code blocks. 3 | * 4 | * Example 1:
$user: 
should rather be replaced by 5 | *
.
 6 |  *
 7 |  *  Implementation notes: This test checks whether lines within 
 elements start with '$' or 
 8 |  *  '>'.
 9 |  */
10 | 
11 | docTests.shellPrompts = {
12 |   name: "shell_prompts",
13 |   desc: "shell_prompts_desc",
14 |   check: function checkShellPrompts(rootElement) {
15 |     let pres = rootElement.querySelectorAll("pre");
16 |     let matches = [];
17 | 
18 |     for (let i = 0; i < pres.length; i++) {
19 |       let code = pres[i].innerHTML.replace(//g, "\n").replace(" ", " ");
20 |       let shellPrompts = code.match(/^(?:\$|>).*/gm);
21 |       if (shellPrompts) {
22 |         shellPrompts.forEach(function addMatch(shellPrompt) {
23 |           matches.push({
24 |             msg: shellPrompt.replace(/<.+?>/g, ""),
25 |             type: ERROR
26 |           });
27 |         });
28 |       }
29 |     }
30 | 
31 |     return matches;
32 |   }
33 | };


--------------------------------------------------------------------------------
/data/tests/style-attribute.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  *  Title: Test for incorrectly 'style' attributes.
 3 |  *
 4 |  *  Example 1: 

Emphasized text/

should rather be replaced 5 | * by

Emphasized text

. 6 | * 7 | * Implementation notes: This test searches for all 'style' attributes, which are not part of 8 | * CKEditor's new paragraph helper. 9 | */ 10 | 11 | docTests.styleAttribute = { 12 | name: "style_attributes", 13 | desc: "style_attributes_desc", 14 | check: function checkStyleAttribute(rootElement) { 15 | let elementsWithStyleAttribute = rootElement.querySelectorAll("[style]"); 16 | let matches = []; 17 | 18 | for (let i = 0; i < elementsWithStyleAttribute.length; i++) { 19 | let node = elementsWithStyleAttribute[i]; 20 | 21 | // Exclude new paragraph helper 22 | if (isNewParagraphHelper(node) || isNewParagraphHelper(node.firstElementChild)) { 23 | continue; 24 | } 25 | 26 | matches.push({ 27 | msg: 'style="' + node.getAttribute("style") + '"', 28 | type: ERROR 29 | }) 30 | } 31 | 32 | return matches; 33 | } 34 | }; -------------------------------------------------------------------------------- /test/test-example-colon-heading.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc exampleColonHeading"] = function testExampleColonHeading(assert, done) { 4 | const tests = [ 5 | { 6 | str: '

Example

' + 7 | '

Example

' + 8 | '

Example: Foo

' + 9 | '

Example: Using Math.sin

' + 10 | '

Example: Foo

', 11 | expected: [ 12 | { 13 | msg: '

Example: Foo

', 14 | type: ERROR 15 | }, 16 | { 17 | msg: '

Example: Using Math.sin

', 18 | type: ERROR 19 | }, 20 | { 21 | msg: '

Example: Foo

', 22 | type: ERROR 23 | } 24 | ], 25 | expectedAfterFixing: [] 26 | } 27 | ]; 28 | 29 | runTests(assert, done, "exampleColonHeading", "'Example: ' heading", url, tests); 30 | }; 31 | 32 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mdn-doc-tests", 3 | "title": "MDN documentation tester", 4 | "id": "{826e8996-b3c2-11e5-9f22-ba0be0483c18}", 5 | "description": "Checks MDN docs against some tests and cleanup tasks while editing", 6 | "author": "Florian Scholz", 7 | "contributors": [ 8 | "Sebastian Zartner" 9 | ], 10 | "license": "MPL 2.0", 11 | "main": "lib/main.js", 12 | "version": "0.6.0", 13 | "icon": "resource://826e8996-b3c2-11e5-9f22-ba0be0483c18/data/icon.png", 14 | "preferences": [ 15 | { 16 | "name": "autoExpandErrors", 17 | "title": "Automatically expand errors", 18 | "type": "bool", 19 | "value": false 20 | }, 21 | { 22 | "name": "hidePassingTests", 23 | "title": "Hide passing tests", 24 | "type": "bool", 25 | "value": false 26 | }, 27 | { 28 | "name": "longArticleWordCountThreshold", 29 | "title": "Long article word count threshold", 30 | "type": "integer", 31 | "value": 2000, 32 | "hidden": true 33 | } 34 | ], 35 | "permissions": { 36 | "private-browsing": true, 37 | "multiprocess": true 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /data/tests/name-attribute.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for elements with 'name' attributes. 3 | * 4 | * Example 1:

Syntax

should rather be

. 5 | * 6 | * Example 2: The name="" attribute in

paragraph

should rather be 7 | * removed. 8 | * 9 | * Implementation notes: This test checks all elements containing 'name' attributes. 10 | */ 11 | 12 | docTests.nameAttribute = { 13 | name: "name_attributes", 14 | desc: "name_attributes_desc", 15 | check: function checkNameAttribute(rootElement) { 16 | let elementsWithNameAttribute = rootElement.querySelectorAll("[name]"); 17 | let matches = []; 18 | 19 | for (let i = 0; i < elementsWithNameAttribute.length; i++) { 20 | matches.push({ 21 | node: elementsWithNameAttribute[i], 22 | msg: 'name="' + elementsWithNameAttribute[i].getAttribute("name") + '"', 23 | type: ERROR 24 | }) 25 | } 26 | 27 | return matches; 28 | }, 29 | fix: function fixNameAttribute(matches) { 30 | matches.forEach(match => { 31 | match.node.removeAttribute("name"); 32 | }); 33 | } 34 | }; -------------------------------------------------------------------------------- /test/test-article-length.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, INFO, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc articleLength"] = function testArticleLength(assert, done) { 4 | const tests = [ 5 | { 6 | str: Array(100).fill("foo").join(" "), 7 | expected: [ 8 | { 9 | msg: "article_length_info", 10 | msgParams: ["100", "< 1"], 11 | type: INFO 12 | } 13 | ] 14 | }, 15 | { 16 | str: Array(500).fill("foo").join(" "), 17 | expected: [ 18 | { 19 | msg: "article_length_info", 20 | msgParams: ["500", "2"], 21 | type: INFO 22 | } 23 | ] 24 | }, 25 | { 26 | str: Array(3000).fill("foo").join(" "), 27 | expected: [ 28 | { 29 | msg: "article_length_info", 30 | msgParams: ["3000", "11"], 31 | type: INFO 32 | }, 33 | { 34 | msg: "long_article", 35 | type: WARNING 36 | } 37 | ] 38 | } 39 | ]; 40 | 41 | runTests(assert, done, "articleLength", "article length", url, tests); 42 | }; 43 | 44 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /data/tests/old-urls.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for old /en/ MDN URLs. 3 | * 4 | * Example 1: All URLs using MDN links, which contain "/en/" as locale should be replaced by 5 | * "/en-US/" URLs. E.g. CSS should rather be 6 | * CSS. 7 | * 8 | * Implementation notes: This test checks whether a link's 'href' attribute starts with "/en/". 9 | * It does not check whether the link is an internal MDN link, nor does it check different 10 | * locales than the English one. 11 | */ 12 | 13 | docTests.oldURLs = { 14 | name: "old_en_urls", 15 | desc: "old_en_urls_desc", 16 | check: function checkOldURLs(rootElement) { 17 | let links = rootElement.querySelectorAll("a[href]"); 18 | let matches = []; 19 | 20 | for (let i = 0; i < links.length; i++) { 21 | // This check can be removed once querySelectorAll supports case-insensitive search, 22 | // i.e. a[href^='/en/' i] (see bug 888190, fixed in Firefox 47.0) 23 | if (links[i].getAttribute("href").match(/^\/en\//i)) { 24 | matches.push({ 25 | msg: links[i].outerHTML, 26 | type: ERROR 27 | }); 28 | } 29 | } 30 | 31 | return matches; 32 | } 33 | }; -------------------------------------------------------------------------------- /test/test-span-count.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc spanCount"] = function testSpanElements(assert, done) { 4 | const tests = [ 5 | { 6 | str: 'what?' + 7 | '

nope

' + 8 | 'bar' + 9 | '
foobar
' + 10 | 'seoseoseo' + 11 | ' ', // Simulates new paragraph helper 12 | expected: [ 13 | { 14 | msg: 'what?', 15 | type: ERROR 16 | }, 17 | { 18 | msg: 'bar', 19 | type: ERROR 20 | }, 21 | { 22 | msg: '
foobar
', 23 | type: ERROR 24 | } 25 | ], 26 | expectedAfterFixing: [ 27 | { 28 | msg: 'bar', 29 | type: ERROR 30 | } 31 | ] 32 | } 33 | ]; 34 | 35 | runTests(assert, done, "spanCount", "", url, tests); 36 | }; 37 | 38 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-shell-prompts.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc shellPrompts"] = function testShellPrompts(assert, done) { 4 | const tests = [ 5 | { 6 | str: '
somecommand
' + 7 | '
$somecommand
' + 8 | '
$ somecommand
' + 9 | '
>somecommand
' + 10 | '
> somecommand
' + 11 | '
$ somecommand\noutput
$ anothercommand
', 12 | expected: [ 13 | { 14 | msg: '$somecommand', 15 | type: ERROR 16 | }, 17 | { 18 | msg: '$ somecommand', 19 | type: ERROR 20 | }, 21 | { 22 | msg: '>somecommand', 23 | type: ERROR 24 | }, 25 | { 26 | msg: '> somecommand', 27 | type: ERROR 28 | }, 29 | { 30 | msg: '$ somecommand', 31 | type: ERROR 32 | }, 33 | { 34 | msg: '$ anothercommand', 35 | type: ERROR 36 | } 37 | ] 38 | } 39 | ]; 40 | 41 | runTests(assert, done, "shellPrompts", "shell prompts in code samples", url, tests); 42 | }; 43 | 44 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-api-syntax-headlines.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc apiSyntaxHeadlines"] = function testSummaryHeading(assert, done) { 4 | const tests = [ 5 | { 6 | str: '

Syntax

' + 7 | '

Errors

' + 8 | '

Returns

' + 9 | '

Parameters

', 10 | expected: [ 11 | { 12 | msg: "invalid_headline_name", 13 | msgParams: ["Errors"], 14 | type: ERROR 15 | }, 16 | { 17 | msg: "invalid_headline_name", 18 | msgParams: ["Returns"], 19 | type: ERROR 20 | }, 21 | { 22 | msg: "invalid_headline_order", 23 | type: ERROR 24 | }, 25 | { 26 | msg: "invalid_headline_order", 27 | type: ERROR 28 | } 29 | ], 30 | expectedAfterFixing: [ 31 | { 32 | msg: "invalid_headline_order", 33 | type: ERROR 34 | }, 35 | { 36 | msg: "invalid_headline_order", 37 | type: ERROR 38 | } 39 | ] 40 | } 41 | ]; 42 | 43 | runTests(assert, done, "apiSyntaxHeadlines", "API syntax headline", url, tests); 44 | }; 45 | 46 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /data/tests/code-in-pre.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test whether code blocks unexpectedly contain elements, which break the syntax 3 | * highlighting. 4 | * 5 | * Example 1:
var x = 1;
is considered invalid and 6 | * should rather be written as
var x = 1;
. 7 | * 8 | * Implementation notes: This test checks whether
 elements contain  elements.
 9 |  */
10 | 
11 | docTests.codeInPre = {
12 |   name: "code_in_pre",
13 |   desc: "code_in_pre_desc",
14 |   check: function checkCodeInPre(rootElement) {
15 |     let codesInPres = rootElement.querySelectorAll("pre code");
16 |     let matches = [];
17 | 
18 |     for (let i = 0; i < codesInPres.length; i++) {
19 |       matches.push({
20 |         node: codesInPres[i],
21 |         msg: codesInPres[i].outerHTML,
22 |         type: ERROR
23 |       });
24 |     }
25 | 
26 |     return matches;
27 |   },
28 |   fix: function fixCodeInPre(matches) {
29 |     matches.forEach(match => {
30 |       let children = new DocumentFragment();
31 |       for(let i = 0; i < match.node.childNodes.length; i++) {
32 |         children.appendChild(match.node.childNodes[i].cloneNode(true));
33 |       }
34 |       match.node.parentNode.replaceChild(children, match.node);
35 |     });
36 |   }
37 | };


--------------------------------------------------------------------------------
/data/tests/article-length.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  *  Title: Test the length and read time of the article.
 3 |  *
 4 |  *  Example 1: An article length of 1000 words will result in a read time estimation of 4 minutes.
 5 |  * 
 6 |  *  Implementation notes: This test expects a reading speed of 275 words per minute. The text is 
 7 |  *  roughly split by word bounderies using a regular expression. An article exceeding some length
 8 |  *  threshold (2000 words by default) is considered long.
 9 |  */
10 | 
11 | const WORDS_PER_MINUTE = 275;
12 | 
13 | docTests.articleLength = {
14 |   name: "article_length",
15 |   desc: "article_length_desc",
16 |   check: function checkArticleLength(rootElement) {
17 |     let text = rootElement.textContent;
18 |     let wordCount = text.match(/\w+/g).length;
19 |     let readTimeEstimation = Math.round(wordCount / WORDS_PER_MINUTE);
20 |     if (readTimeEstimation === 0) {
21 |       readTimeEstimation = "< 1";
22 |     }
23 |     let matches = [{
24 |       msg: "article_length_info",
25 |       msgParams: [String(wordCount), String(readTimeEstimation)],
26 |       type: INFO
27 |     }];
28 |     if (wordCount > self.options.LONG_ARTICLE_WORD_COUNT_THRESHOLD) {
29 |       matches.push({
30 |         msg: "long_article",
31 |         type: WARNING
32 |       });
33 |     }
34 |     return matches;
35 |   }
36 | };


--------------------------------------------------------------------------------
/data/tests/example-colon-heading.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  *  Title: Test for example headings starting with 'Example:'.
 3 |  *
 4 |  *  Example 1: 

Example: Simple example

should rather be written as

Simple example

5 | * 6 | * Implementation notes: This test checks whether the text of heading elements start with 7 | * 'Example:'. 8 | */ 9 | 10 | const reExample = /^\s*Example:[\s_]*/; 11 | 12 | docTests.exampleColonHeading = { 13 | name: "example_headings", 14 | desc: "example_headings_desc", 15 | 16 | check: function checkExampleColonHeading(rootElement) { 17 | let headlines = rootElement.querySelectorAll("h1, h2, h3, h4, h5, h6"); 18 | let matches = []; 19 | 20 | for (let i = 0; i < headlines.length; i++) { 21 | if (headlines[i].textContent.match(reExample)) { 22 | matches.push({ 23 | node: headlines[i], 24 | msg: headlines[i].outerHTML, 25 | type: ERROR 26 | }) 27 | } 28 | } 29 | 30 | return matches; 31 | }, 32 | 33 | fix: function fixExampleColonHeading(matches) { 34 | matches.forEach(match => { 35 | match.node.textContent = match.node.textContent.replace(reExample, ""); 36 | let id = match.node.getAttribute("id"); 37 | id = id.replace(reExample, ""); 38 | match.node.setAttribute("id", id); 39 | }); 40 | } 41 | }; -------------------------------------------------------------------------------- /data/tests/summary-heading.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for obsolete 'Summary' heading. 3 | * 4 | * Example 1:

Summary

is redundant, because the page title is shown above the article, 5 | * so it should be removed. 6 | * 7 | * Implementation notes: This test searches for all headings, which contain the text 'Summary'. 8 | * In CSS articles the summary headings still need to be kept due to bug 1201600, though the test 9 | * currently still marks them as errors (see issue #209). Also, summaries placed at the end of 10 | * articles are incorrectly recognized as errors (see issue #208). 11 | */ 12 | 13 | docTests.summaryHeading = { 14 | name: "summary_heading", 15 | desc: "summary_heading_desc", 16 | 17 | check: function checkSummaryHeading(rootElement) { 18 | let headlines = rootElement.querySelectorAll("h1, h2, h3, h4, h5, h6"); 19 | let matches = []; 20 | 21 | for (let i = 0; i < headlines.length; i++) { 22 | if (headlines[i].textContent.match(/^\s*Summary\s*$/)) { 23 | matches.push({ 24 | node: headlines[i], 25 | msg: headlines[i].outerHTML, 26 | type: ERROR 27 | }) 28 | } 29 | } 30 | 31 | return matches; 32 | }, 33 | 34 | fix: function fixSummaryHeading(matches) { 35 | matches.forEach(match => match.node.remove()); 36 | } 37 | }; -------------------------------------------------------------------------------- /test/test-code-in-pre.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc codeInPre"] = function testSummaryHeading(assert, done) { 4 | const tests = [ 5 | { 6 | str: '
no code
' + 7 | '
no code
' + 8 | '
some code
' + 9 | '
some codesome more inline code
' + 10 | '
foo\nsome code\nbar
\nsome code with\nline break\nbaz
', 11 | expected: [ 12 | { 13 | msg: 'some code', 14 | type: ERROR 15 | }, 16 | { 17 | msg: 'some code', 18 | type: ERROR 19 | }, 20 | { 21 | msg: 'some more inline code', 22 | type: ERROR 23 | }, 24 | { 25 | msg: 'some code', 26 | type: ERROR 27 | }, 28 | { 29 | msg: 'some code with\nline break', 30 | type: ERROR 31 | } 32 | ], 33 | expectedAfterFixing: [] 34 | } 35 | ]; 36 | 37 | runTests(assert, done, "codeInPre", " in
", url, tests);
38 | };
39 | 
40 | require("sdk/test").run(exports);


--------------------------------------------------------------------------------
/test/test-line-length-in-pre.js:
--------------------------------------------------------------------------------
 1 | const {ERROR, WARNING, url, runTests} = require("./testutils");
 2 | 
 3 | exports["test doc lineLengthInPre"] = function testSummaryHeading(assert, done) {
 4 |   const tests = [
 5 |     {
 6 |       str: '
11111111111111111111111 11111111111111111111111 111111111111 111111111111111 1
' + 7 | '
11111111111111111111111 11111111111111111111111
111111111111 111111111111111 1
' + 8 | '
short\nstuff
' + 9 | '
Code having some link.
' + 10 | '
foo\nsome code\nbar
\n' + 11 | 'some code with\nline break\nbaz' + 12 | '11111111111 111111111111 function{ foo(); 11111111111111 bar 1111111111111111 111
', 13 | expected: [ 14 | { 15 | msg: '11111111111111111111111 11111111111111111111111 111111111111 111111111111111 1', 16 | type: WARNING 17 | }, 18 | { 19 | msg: 'baz11111111111 111111111111 function{ foo(); 11111111111111 bar 1111111111111111 111', 20 | type: WARNING 21 | } 22 | ] 23 | } 24 | ]; 25 | 26 | runTests(assert, done, "lineLengthInPre", "too long line", url, tests); 27 | }; 28 | 29 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-absolute-urls-for-internal-links.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc absoluteURLsForInternalLinks"] = function testAbsoluteURLsForInternalLinks(assert, done) { 4 | const tests = [ 5 | { 6 | str: 'Page' + 7 | 'Anchor' + 8 | 'Anchor' + 9 | 'Anchor' + 10 | 'Anchor' + 11 | 'Anchor', 12 | expected: [ 13 | { 14 | msg: 'Anchor', 15 | type: WARNING 16 | }, 17 | { 18 | msg: 'Anchor', 19 | type: WARNING 20 | }, 21 | { 22 | msg: 'Anchor', 23 | type: WARNING 24 | } 25 | ], 26 | expectedAfterFixing: [] 27 | } 28 | ]; 29 | 30 | runTests(assert, done, "absoluteURLsForInternalLinks", "absolute URLs for internal links", url, tests); 31 | }; 32 | 33 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-wrong-syntax-class.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc wrongSyntaxClass"] = function testWrongSyntaxClass(assert, done) { 4 | const tests = [ 5 | { 6 | str: 'foo

Syntax

\\n
syntax
bar', 7 | expected: [] 8 | }, 9 | { 10 | str: 'foo

Syntax

\\n
syntax examples
bar

Formal syntax

\\n
syntax
', 11 | expected: [] 12 | }, 13 | { 14 | str: 'foo

Syntax

\\n
syntax
bar', 15 | expected: [ 16 | { 17 | msg: "wrong_syntax_class_used", 18 | msgParams: ["brush:js"], 19 | type: ERROR 20 | } 21 | ], 22 | expectedAfterFixing: [] 23 | }, 24 | { 25 | str: 'foo

Syntax

\\n
syntax examples
bar

Formal syntax

\\n
syntax
baz

Other section

', 26 | expected: [ 27 | { 28 | msg: "wrong_syntax_class_used", 29 | msgParams: ["eval"], 30 | type: ERROR 31 | } 32 | ], 33 | expectedAfterFixing: [] 34 | } 35 | ]; 36 | 37 | runTests(assert, done, "wrongSyntaxClass", "syntax box class", url, tests); 38 | }; 39 | 40 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-style-attribute.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc styleAttribute"] = function testStyleAttributes(assert, done) { 4 | const tests = [ 5 | { 6 | str: '' + 7 | '
' + 8 | '
' + 9 | 'test' + 10 | '' + 11 | ' ', // Simulates new paragraph helper 12 | expected: [ 13 | { 14 | msg: 'style=""', 15 | type: ERROR 16 | }, 17 | { 18 | msg: 'style="margin-top:5%"', 19 | type: ERROR 20 | }, 21 | { 22 | msg: 'style="background:#fff; color: rgb(234, 234, 234);"', 23 | type: ERROR 24 | }, 25 | { 26 | msg: 'style="padding: 5px !important"', 27 | type: ERROR 28 | }, 29 | { 30 | msg: 'style="font-family: \'Open Sans\', serif; line-height: 1.5"', 31 | type: ERROR 32 | } 33 | ] 34 | } 35 | ]; 36 | 37 | runTests(assert, done, "styleAttribute", "'style' attribute", url, tests); 38 | }; 39 | 40 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-different-locale-links.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc differentLocaleLinks"] = function testDifferentLocaleLinks(assert, done) { 4 | const tests = [ 5 | { 6 | str: 'Page' + 7 | 'Page' + 8 | 'Page' + 9 | 'Page' + 10 | 'Page' + 11 | 'Page', 12 | expected: [ 13 | { 14 | msg: "link_using_wrong_locale", 15 | msgParams: ["/xx-YY/docs/some/page", "en-US"], 16 | type: ERROR 17 | }, 18 | { 19 | msg: "link_using_wrong_locale", 20 | msgParams: ["http://developer.mozilla.org/xx-YY/docs/some/page", "en-US"], 21 | type: ERROR 22 | }, 23 | { 24 | msg: "link_using_wrong_locale", 25 | msgParams: ["https://developer.mozilla.org/xx-YY/docs/some/page", "en-US"], 26 | type: ERROR 27 | } 28 | ] 29 | } 30 | ]; 31 | 32 | runTests(assert, done, "differentLocaleLinks", "different locale links", url, tests); 33 | }; 34 | 35 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /data/tests/span-count.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for incorrectly used elements. 3 | * 4 | * Example 1: Emphasized text/ should rather be replaced 5 | * by Emphasized text. 6 | * 7 | * Implementation notes: This test searches for all elements, which don't hold the SEO 8 | * summary and are not part of CKEditor's new paragraph helper. 9 | */ 10 | 11 | docTests.spanCount = { 12 | name: "span_elements", 13 | desc: "span_elements_desc", 14 | 15 | check: function checkSpanCount(rootElement) { 16 | let spanElements = rootElement.querySelectorAll("span:not(.seoSummary)"); 17 | let matches = []; 18 | 19 | for (let i = 0; i < spanElements.length; i++) { 20 | let node = spanElements[i]; 21 | 22 | // Exclude new paragraph helper 23 | if (isNewParagraphHelper(node) || isNewParagraphHelper(node.firstElementChild)) { 24 | continue; 25 | } 26 | 27 | matches.push({ 28 | node: node, 29 | msg: node.outerHTML, 30 | type: ERROR 31 | }) 32 | } 33 | 34 | return matches; 35 | }, 36 | 37 | fix: function fixSpanCount(matches) { 38 | matches.forEach(match => { 39 | // Remove element in case it is unstyled 40 | if (!match.node.getAttribute("id") && !match.node.getAttribute("class") && !match.node.getAttribute("style")) { 41 | match.node.remove(); 42 | } 43 | }); 44 | } 45 | }; -------------------------------------------------------------------------------- /data/tests/pre-without-class.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for code blocks without 'class' attribute specifying the syntax highlighting. 3 | * 4 | * Example 1:
var x = 1
should rather be replaced by 5 | *
var x = 1
. 6 | * 7 | * Implementation notes: This test checks all
 elements that have either an empty 'class' 
 8 |  *  attribute or none at all.
 9 |  */
10 | 
11 | docTests.preWithoutClass = {
12 |   name: "pre_without_class",
13 |   desc: "pre_without_class_desc",
14 |   check: function checkPreWithoutClass(rootElement) {
15 |     let presWithoutClass = rootElement.querySelectorAll("pre:-moz-any(:not([class]), [class=''])");
16 |     let matches = [];
17 | 
18 |     for (let i = 0; i < presWithoutClass.length; i++) {
19 |       // If the content is recognized as folder structure, don't add a warning for empty 
20 |       if (presWithoutClass[i].textContent.match(/^\S[^\n\*]*\/\n/)) {
21 |         continue;
22 |       }
23 | 
24 |       let type = WARNING;
25 | 
26 |       // If the content is recognized as code or {{csssyntax}} macro, mark it as error
27 |       if (presWithoutClass[i].textContent.match(/^\s*(?:\/\*.+?\*\/|<.+?>|@[^\s\n]+[^\n]*\{\n|\{\{\s*csssyntax(?:\(\))?\s*\}\})/)) {
28 |         type = ERROR;
29 |       }
30 |       
31 |       matches.push({
32 |         msg: presWithoutClass[i].outerHTML,
33 |         type: type
34 |       })
35 |     }
36 | 
37 |     return matches;
38 |   }
39 | };


--------------------------------------------------------------------------------
/data/tests/html-comments.js:
--------------------------------------------------------------------------------
 1 | /*
 2 |  *  Title: Test for HTML comments (i.e. ).
 3 |  *
 4 |  *  Example 1: Because an HTML comment like
 5 |  *  
 6 |  *  is only visible to article authors, it should rather either be made visible to readers, 
 7 |  *  e.g. by replacing it by 
 8 |  *  

This is a simple example for how the API is used.

9 | * or just be removed. 10 | * 11 | * Implementation notes: This test searches for all HTML comments. Because CKEditor escapes them 12 | * for security reasons, they need to be decoded first before displaying them. 13 | */ 14 | 15 | docTests.htmlComments = { 16 | name: "html_comments", 17 | desc: "html_comments_desc", 18 | check: function checkHTMLComments(rootElement) { 19 | let treeWalker = document.createTreeWalker( 20 | rootElement, 21 | NodeFilter.SHOW_COMMENT 22 | ); 23 | let matches = []; 24 | 25 | while(treeWalker.nextNode()) { 26 | let comment = treeWalker.currentNode.data.replace(/\s*\{cke_protected\}\{C\}(\S+)\s*/, 27 | function(match, data) { return decodeURIComponent(data); }); 28 | matches.push({ 29 | node: treeWalker.currentNode, 30 | msg: comment, 31 | type: ERROR 32 | }); 33 | } 34 | 35 | return matches; 36 | }, 37 | fix: function fixHTMLComments(matches) { 38 | matches.forEach(match => { 39 | match.node.remove(); 40 | }); 41 | } 42 | }; -------------------------------------------------------------------------------- /data/tests/different-locale-links.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for wrong locales in links (). 3 | * 4 | * Example 1: If you are in a German document, internal links should 5 | * contain the "de" locale and not e.g. "en-US". 6 | * 7 | * Implementation notes: This test compares the current locale in the slug (document.URL) 8 | * with the locale used in internal links () 9 | */ 10 | 11 | docTests.differentLocaleLinks = { 12 | name: "different_locale_links", 13 | desc: "different_locale_links_desc", 14 | check: function checkDifferentLocaleLinks(rootElement) { 15 | let [, pageDomain, pageLocale] = document.URL.match(/^(?:https?:\/\/)(.+?)\/([^\/]+)/i) || 16 | ["", "developer.mozilla.org", "en-US"]; 17 | let links = rootElement.getElementsByTagName("a"); 18 | let matches = []; 19 | for (let i = 0; i < links.length; i++) { 20 | let href = links[i].getAttribute("href"); 21 | if (href) { 22 | let [, linkDomain, linkLocale] = href.match(/^(?:https?:\/\/(.+?))?\/([^\/]+)/i) || 23 | [null, null, null]; 24 | if (linkLocale && linkLocale.toLowerCase() !== pageLocale.toLowerCase() && 25 | (!linkDomain || linkDomain === pageDomain)) { 26 | matches.push({ 27 | msg: "link_using_wrong_locale", 28 | msgParams: [href, pageLocale], 29 | type: ERROR 30 | }); 31 | } 32 | } 33 | } 34 | 35 | return matches; 36 | } 37 | }; -------------------------------------------------------------------------------- /data/tests/line-length-in-pre.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for the line length in code blocks. 3 | * 4 | * Example 1: Code blocks with very long lines like 5 | *
This is some code block with a long line exceeding the maximum of 78 characters.
6 | * should either be shortened or split into several lines to avoid the display of horizontal 7 | * scrollbars. 8 | * 9 | * Implementation notes: This test uses a threshold of 78 characters for the maximum length of 10 | * a line.
tags added while editing are replaced by line breaks and all other HTML tags 11 | * are removed. 12 | */ 13 | 14 | docTests.lineLengthInPre = { 15 | name: "pre_line_too_long", 16 | desc: "pre_line_too_long_desc", 17 | check: function checkLineLengthInPre(rootElement) { 18 | let pres = rootElement.getElementsByTagName("pre"); 19 | let matches = []; 20 | 21 | for (let i = 0; i < pres.length; i++) { 22 | // While editing it happens that there are
s added instead of line break characters 23 | // Those need to be replaced by line breaks to correctly recognize long lines 24 | let codeBlock = pres[i].innerHTML.replace(//g, "\n"); 25 | 26 | // Remove all other HTML tags and only display the plain text 27 | codeBlock = codeBlock.replace(/<.+?>/g, ""); 28 | 29 | let longLines = codeBlock.match(/^(?:[^\r\n]|\r(?!\n)){78,}$/gm); 30 | if (longLines) { 31 | matches = matches.concat(longLines); 32 | } 33 | } 34 | 35 | return mapMatches(matches, WARNING); 36 | } 37 | }; -------------------------------------------------------------------------------- /test/test-pre-without-class.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc preWithoutClass"] = function testPresWithoutClass(assert, done) { 4 | const tests = [ 5 | { 6 | str: '
' +
 7 |            '
' +
 8 |            '
folder/\n  file
' + 9 | '
foobar;
' + 10 | '
/* comment */\nvar code;
' + 11 | '
@rule param {\n  descriptor: value;\n}
' + 12 | '
<tag>
' + 13 | '
' +
14 |            '
foo
' + 15 | '
 \n\r foo
', 16 | expected: [ 17 | { 18 | msg: '
foobar;
', 19 | type: WARNING 20 | }, 21 | { 22 | msg: '
/* comment */\nvar code;
', 23 | type: ERROR 24 | }, 25 | { 26 | msg: '
@rule param {\n  descriptor: value;\n}
', 27 | type: ERROR 28 | }, 29 | { 30 | msg: '
<tag>
', 31 | type: ERROR 32 | }, 33 | { 34 | msg: '
',
35 |           type: WARNING
36 |         },
37 |         {
38 |           msg: '
foo
', 39 | type: WARNING 40 | }, 41 | { 42 | msg: '
 \n\n foo
', 43 | type: WARNING 44 | } 45 | ] 46 | } 47 | ]; 48 | 49 | runTests(assert, done, "preWithoutClass", "
 w/o class", url, tests);
50 | };
51 | 
52 | require("sdk/test").run(exports);


--------------------------------------------------------------------------------
/test/test-data-macro-note.js:
--------------------------------------------------------------------------------
 1 | const {ERROR, WARNING, url, runTests} = require("./testutils");
 2 | 
 3 | exports["test doc dataMacroNote"] = function testDataMacroNote(assert, done) {
 4 |   const tests = [
 5 |     {
 6 |       str: '

{{nondatamacro}}

' + 7 | '

{{compat}}

' + 8 | '

{{css_ref}}

' + 9 | '

{{cssanimatedproperties}}

' + 10 | '

{{cssinfo}}

' + 11 | '

{{csssyntax}}

' + 12 | '

{{WebExtBrowserCompat}}

' + 13 | '

{{Compat}}

' + 14 | '

{{Compat}}

', 15 | expected: [ 16 | { 17 | msg: "data_macro_note_missing", 18 | msgParams: ["{{Compat}}"], 19 | type: ERROR 20 | }, 21 | { 22 | msg: "data_macro_source_link_missing", 23 | msgParams: ["{{Compat}}"], 24 | type: ERROR 25 | } 26 | ] 27 | } 28 | ]; 29 | 30 | runTests(assert, done, "dataMacroNote", "data macro note", url, tests); 31 | }; 32 | 33 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-empty-elements.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc emptyElements"] = function testEmptyElements(assert, done) { 4 | const tests = [ 5 | { 6 | str: '

' + 7 | '

\n\r

' + 8 | '

 

' + 9 | '



' + 10 | '

' + 11 | '
foo
' + 12 | '' + 13 | '

' + 14 | '' + 15 | '

some text

' + 16 | '

some text

' + 17 | ' ', // Simulates new paragraph helper 18 | expected: [ 19 | { 20 | msg: '

', 21 | type: ERROR 22 | }, 23 | { 24 | msg: '

\n\n

', 25 | type: ERROR 26 | }, 27 | { 28 | msg: '

 

', 29 | type: ERROR 30 | }, 31 | { 32 | msg: '



', 33 | type: ERROR 34 | }, 35 | { 36 | msg: '

', 37 | type: ERROR 38 | }, 39 | { 40 | msg: '
', 41 | type: WARNING 42 | } 43 | ], 44 | expectedAfterFixing: [ 45 | { 46 | msg: '
', 47 | type: WARNING 48 | } 49 | ] 50 | } 51 | ]; 52 | 53 | runTests(assert, done, "emptyElements", "empty elements", url, tests); 54 | }; 55 | 56 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-incorrectly-wrapped-sidebar-macros.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc incorrectlyWrappedSidebarMacros"] = function testIncorrectlyWrappedSidebarMacros(assert, done) { 4 | const tests = [ 5 | { 6 | str: '
{{CSSRef}}
', 7 | expected: [] 8 | }, 9 | { 10 | str: '
{{HTMLRef}}
', 11 | expected: [] 12 | }, 13 | { 14 | str: '
{{APIRef}}
', 15 | expected: [] 16 | }, 17 | { 18 | str: '
{{JSRef}}
', 19 | expected: [] 20 | }, 21 | { 22 | str: '
{{SVGRefElem}}
', 23 | expected: [] 24 | }, 25 | { 26 | str: '
{{JSSidebar}}
', 27 | expected: [] 28 | }, 29 | { 30 | str: '
{{AddonSidebar}}
', 31 | expected: [] 32 | }, 33 | { 34 | str: '
{{ APIRef("Some API") }}
', 35 | expected: [] 36 | }, 37 | { 38 | str: '

{{CSSRef}}

', 39 | expected: [ 40 | { 41 | msg: "wrong_element_wrapping_sidebar_macro", 42 | msgParams: ["{{CSSRef}}", "p"], 43 | type: ERROR 44 | } 45 | ], 46 | expectedAfterFixing: [] 47 | }, 48 | { 49 | str: '{{ APIRef("Some API") }}', 50 | expected: [ 51 | { 52 | msg: "wrong_element_wrapping_sidebar_macro", 53 | msgParams: ["{{ APIRef(\"Some API\") }}", "span"], 54 | type: ERROR 55 | } 56 | ], 57 | expectedAfterFixing: [] 58 | } 59 | ]; 60 | 61 | runTests(assert, done, "incorrectlyWrappedSidebarMacros", "incorrectly wrapped sidebar macros", 62 | url, tests); 63 | }; 64 | 65 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /data/tests/absolute-urls-for-internal-links.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test whether internal MDN links have absolute URLs. 3 | * 4 | * Example 1: An MDN link to the 'display' CSS property should use a the relative URL 5 | * /en-US/docs/Web/CSS/display and not the absolute URL 6 | * https://developer.mozilla.org/en-US/docs/Web/CSS/display. 7 | * 8 | * Implementation notes: This test checks whether the URL begins with the link 9 | * "https://developer.mozilla.org/", this means that allizom.org links are not covered. 10 | * window.location cannot be used for this, because that would break the unit test, which uses 11 | * about:blank as URL. 12 | */ 13 | 14 | const reAbsoluteURL = /^(?:https?:)?\/\/developer\.mozilla\.org(?=\/)/i; 15 | 16 | docTests.absoluteURLsForInternalLinks = { 17 | name: "absolute_urls_for_internal_links", 18 | desc: "absolute_urls_for_internal_links_desc", 19 | check: function checkAbsoluteURLsForInternalLinks(rootElement) { 20 | let links = rootElement.getElementsByTagName("a"); 21 | let matches = []; 22 | for (let i = 0; i < links.length; i++) { 23 | let href = links[i].getAttribute("href"); 24 | if (href && href.match(reAbsoluteURL)) { 25 | matches.push({ 26 | node: links[i], 27 | msg: links[i].outerHTML, 28 | type: WARNING 29 | }); 30 | } 31 | } 32 | 33 | return matches; 34 | }, 35 | 36 | fix: function fixAbsoluteURLsForInternalLinks(matches) { 37 | matches.forEach(match => { 38 | let href = match.node.getAttribute("href"); 39 | let relativeURL = href.replace(reAbsoluteURL, ""); 40 | match.node.setAttribute("href", relativeURL); 41 | match.node.setAttribute("data-cke-saved-href", relativeURL); 42 | }); 43 | } 44 | }; -------------------------------------------------------------------------------- /data/tests/empty-brackets.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for macros with empty brackets. 3 | * 4 | * Example 1: The {{CompatNo}} macro does not expect any parameters, so the parameter brackets 5 | * are redundant and should be avoided, i.e. it should not be written as {{CompatNo()}}. 6 | * 7 | * Implementation notes: This test checks for macros written with empty brackets and requests to 8 | * remove them. It does not check whether the macros actually require parameters. 9 | */ 10 | 11 | const reMacroWithEmptyBrackets = /\{\{\s*(.*?)\(\)\s*\}\}/gi; 12 | 13 | docTests.emptyBrackets = { 14 | name: "empty_brackets", 15 | desc: "empty_brackets_desc", 16 | 17 | check: function checkEmptyBrackets(rootElement) { 18 | let treeWalker = document.createTreeWalker( 19 | rootElement, 20 | NodeFilter.SHOW_TEXT, 21 | { 22 | acceptNode: (node) => { 23 | return reMacroWithEmptyBrackets.test(node.textContent) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 24 | } 25 | } 26 | ); 27 | let matches = []; 28 | 29 | while(treeWalker.nextNode()) { 30 | let textNodeMatches = treeWalker.currentNode.textContent.match(reMacroWithEmptyBrackets) || []; 31 | textNodeMatches.forEach(match => { 32 | matches.push({ 33 | node: treeWalker.currentNode, 34 | msg: match, 35 | type: ERROR 36 | }); 37 | }); 38 | } 39 | 40 | return matches; 41 | }, 42 | 43 | fix: function fixEmptyBrackets(matches) { 44 | let previousNode = null; 45 | matches.forEach(match => { 46 | if (match.node !== previousNode) { 47 | match.node.textContent = match.node.textContent. 48 | replace(reMacroWithEmptyBrackets, "{{$1}}"); 49 | } 50 | previousNode = match.node; 51 | }); 52 | } 53 | }; -------------------------------------------------------------------------------- /data/tests/url-in-link-title.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for incorrectly used URLs in link titles. 3 | * 4 | * Example 1: The 'title' attribute on 5 | * CSS 6 | * should be removed, because it's redundant. 7 | * 8 | * Example 2: The 'title' attribute on 9 | * CSS 10 | * should be removed, because it's redundant and misleading. 11 | * 12 | * Implementation notes: This test checks whether the 'title' attribute of an element 13 | * contains the same URL or a part of it as within its 'href' attribute. It also handles URLs 14 | * using two-character locales vs. four character locales, e.g. "/en-US/" and "/en/". 15 | */ 16 | 17 | docTests.urlInLinkTitle = { 18 | name: "url_in_link_title", 19 | desc: "url_in_link_title_desc", 20 | check: function checkURLsInTitleAttributes(rootElement) { 21 | let linkElements = rootElement.getElementsByTagName("a"); 22 | let matches = []; 23 | 24 | for (let i = 0; i < linkElements.length; i++) { 25 | let href = (linkElements[i].getAttribute("href") || "").toLowerCase(); 26 | let title = (linkElements[i].getAttribute("title") || "").toLowerCase(); 27 | if (title !== "" && (href.indexOf(title) !== -1 || 28 | (title.match(/[a-z]{2}(?:-[A-Z]{2})?\/docs\/.*?\//) || 29 | title === href.replace(/([a-z]{2})(?:-[a-z]{2})?\/docs\/(.*)/, "$1/$2")))) { 30 | matches.push({ 31 | node: linkElements[i], 32 | msg: linkElements[i].outerHTML, 33 | type: ERROR 34 | }); 35 | } 36 | } 37 | 38 | return matches; 39 | }, 40 | fix: function fixURLsInTitleAttributes(matches) { 41 | matches.forEach(match => { 42 | match.node.removeAttribute("title"); 43 | }); 44 | } 45 | }; -------------------------------------------------------------------------------- /test/test-url-in-link-title.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc urlInLinkTitle"] = function testHTTPLinks(assert, done) { 4 | const tests = [ 5 | { 6 | str: 'Test' + 7 | 'Test' + 8 | 'Test' + 9 | 'Test' + 10 | 'Test' + 11 | 'Test' + 12 | 'Test' + 13 | 'Test' + 14 | 'Web' + 15 | 'Mozilla', 16 | expected: [ 17 | { 18 | msg: 'Test', 19 | type: ERROR 20 | }, 21 | { 22 | msg: 'Test', 23 | type: ERROR 24 | }, 25 | { 26 | msg: 'Test', 27 | type: ERROR 28 | }, 29 | { 30 | msg: 'Test', 31 | type: ERROR 32 | }, 33 | { 34 | msg: 'Web', 35 | type: ERROR 36 | }, 37 | { 38 | msg: 'Mozilla', 39 | type: ERROR 40 | } 41 | ], 42 | expectedAfterFixing: [] 43 | } 44 | ]; 45 | 46 | runTests(assert, done, "urlInLinkTitle", "URL in 'title' attribute of link", url, tests); 47 | }; 48 | 49 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /test/test-invalid-macros.js: -------------------------------------------------------------------------------- 1 | const {ERROR, WARNING, url, runTests} = require("./testutils"); 2 | 3 | exports["test doc invalidMacros"] = function testInvalidMacros(assert, done) { 4 | const tests = [ 5 | { 6 | str: '{{apiref}}' + 7 | '{{bug(123456)}}' + 8 | '{{previous("some page"}}' + 9 | '{{cssinfo(\'font-weight\', \'@font\')}}' + 10 | '{{invalidmacroname}}' + 11 | '{{invalidmacroname(123456)}}' + 12 | '{{invalidmacroname("some page")}}' + 13 | '{{invalidmacroname(\'font-weight\', \'@font\')}}' + 14 | '{{ languages( { "ja": "Ja/Browser_chrome_tests" } ) }}', 15 | expected: [ 16 | { 17 | msg: '{{invalidmacroname}}', 18 | type: WARNING 19 | }, 20 | { 21 | msg: '{{invalidmacroname(123456)}}', 22 | type: WARNING 23 | }, 24 | { 25 | msg: '{{invalidmacroname("some page")}}', 26 | type: WARNING 27 | }, 28 | { 29 | msg: '{{invalidmacroname(\'font-weight\', \'@font\')}}', 30 | type: WARNING 31 | }, 32 | { 33 | msg: 'obsolete_macro', 34 | msgParams: ['{{ languages( { "ja": "Ja/Browser_chrome_tests" } ) }}'], 35 | type: ERROR 36 | } 37 | ], 38 | expectedAfterFixing: [ 39 | { 40 | msg: '{{invalidmacroname}}', 41 | type: WARNING 42 | }, 43 | { 44 | msg: '{{invalidmacroname(123456)}}', 45 | type: WARNING 46 | }, 47 | { 48 | msg: '{{invalidmacroname("some page")}}', 49 | type: WARNING 50 | }, 51 | { 52 | msg: '{{invalidmacroname(\'font-weight\', \'@font\')}}', 53 | type: WARNING 54 | } 55 | ] 56 | } 57 | ]; 58 | 59 | runTests(assert, done, "invalidMacros", "invalid macros", url, tests); 60 | }; 61 | 62 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /data/tests/empty-elements.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for empty elements. 3 | * 4 | * Example 1: Paragraphs only containing a non-breaking space (

 

) should be avoided. 5 | * 6 | * Implementation notes: This test checks for elements containing no text or only space 7 | * characters excluding the new paragraph helper of CKEditor and self-closing elements except 8 | *
and elements. 9 | */ 10 | 11 | docTests.emptyElements = { 12 | name: "empty_elements", 13 | desc: "empty_elements_desc", 14 | check: function checkEmptyElements(rootElement) { 15 | let treeWalker = document.createTreeWalker( 16 | rootElement, 17 | NodeFilter.SHOW_ELEMENT, 18 | { 19 | acceptNode: (node) => { 20 | // matching self-closing elements and excluding them 21 | if(!node.localName.match(/^link|track|param|area|command|col|base|meta|hr|source|img|keygen|br|wbr|input$/i) && 22 | node.textContent.match(/^(?: |\s|\n)*$/)) { 23 | 24 | // Exclude new paragraph helper 25 | if (isNewParagraphHelper(node.firstElementChild)) { 26 | return NodeFilter.FILTER_REJECT; 27 | } 28 | 29 | // Elements containing self-closing elements except
and are considered non-empty 30 | let descendantSelfClosingElements = node.querySelectorAll( 31 | "link,track,param,area,command,col,base,meta,hr,source,img,keygen,input"); 32 | return descendantSelfClosingElements.length === 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 33 | } else { 34 | return NodeFilter.FILTER_SKIP; 35 | } 36 | } 37 | } 38 | ); 39 | let matches = []; 40 | 41 | while(treeWalker.nextNode()) { 42 | matches.push({ 43 | node: treeWalker.currentNode, 44 | msg: treeWalker.currentNode.outerHTML, 45 | type: treeWalker.currentNode.localName === "td" ? WARNING : ERROR 46 | }); 47 | } 48 | 49 | return matches; 50 | }, 51 | 52 | fix: function fixEmptyElements(matches) { 53 | matches.forEach(match => { 54 | if (match.type === ERROR) { 55 | match.node.remove() 56 | } 57 | }); 58 | } 59 | }; -------------------------------------------------------------------------------- /data/tests/incorrectly-wrapped-sidebar-macros.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for sidebar macros that are not wrapped in
elements 3 | * 4 | * Example 1:

{{APIRef}}

should be replaced by
{{APIRef}}
. 5 | * 6 | * Implementation notes: This test checks whether some named macros are wrapped in other elements 7 | * than
s. 8 | */ 9 | 10 | docTests.incorrectlyWrappedSidebarMacros = { 11 | name: "incorrectly_wrapped_sidebar_macros", 12 | desc: "incorrectly_wrapped_sidebar_macros_desc", 13 | 14 | check: function checkIncorrectlyWrappedSidebarMacros(rootElement) { 15 | const allowedMacros = /^(?:apiref|cssref|htmlref|jsref|makesimplequicklinks|mathmlref|svgrefelem)$|sidebar$/i; 16 | 17 | let treeWalker = document.createTreeWalker( 18 | rootElement, 19 | NodeFilter.SHOW_TEXT, 20 | { 21 | acceptNode: (node) => { 22 | return node.textContent.match(/\{\{.*?\}\}/) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 23 | } 24 | } 25 | ); 26 | let matches = []; 27 | 28 | while(treeWalker.nextNode()) { 29 | let reMacroName = /\{\{\s*([^\(\}\s]+).*?\}\}/g; 30 | let macroNameMatch = reMacroName.exec(treeWalker.currentNode.textContent); 31 | while (macroNameMatch) { 32 | if (macroNameMatch[1].match(allowedMacros) !== null && 33 | treeWalker.currentNode.parentElement.localName !== 'div') { 34 | matches.push({ 35 | node: treeWalker.currentNode.parentElement, 36 | msg: "wrong_element_wrapping_sidebar_macro", 37 | msgParams: [macroNameMatch[0], treeWalker.currentNode.parentElement.localName], 38 | type: ERROR 39 | }); 40 | } 41 | macroNameMatch = reMacroName.exec(treeWalker.currentNode.textContent); 42 | } 43 | } 44 | 45 | return matches; 46 | }, 47 | 48 | fix: function fixIncorrectlyWrappedSidebarMacros(matches) { 49 | matches.forEach(match => { 50 | let divElement = document.createElement("div"); 51 | let childNodes = match.node.childNodes; 52 | for(var i = 0; i < childNodes.length; i++) { 53 | divElement.appendChild(childNodes[i].cloneNode(true)); 54 | } 55 | 56 | match.node.parentNode.replaceChild(divElement, match.node); 57 | }); 58 | } 59 | }; -------------------------------------------------------------------------------- /data/tests/data-macro-note.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test that data macros have a note above them pointing to their data sources. 3 | * 4 | * Example 1: If you are using e.g. the {{compat}} or {{cssinfo}} macros, there should 5 | * be a hidden note for contributors in edit-mode, so that they know where the data comes 6 | * from and how to change it. 7 | * 8 | * Implementation notes: This test checks if there is a note for each macro 9 | * (by checking whether the sibling element before the element containing the macro 10 | * has class="hidden") and whether that note contains a link (i.e. an 11 | * element; it doesn't check whether the link is valid). 12 | */ 13 | 14 | const dataMacros = /^(?:compat|css_ref|cssanimatedproperties|cssinfo|csssyntax|webextbrowsercompat)$/i; 15 | 16 | docTests.dataMacroNote = { 17 | name: "data_macro_note", 18 | desc: "data_macro_note_desc", 19 | check: function checkDataMacroNote(rootElement) { 20 | 21 | let treeWalker = document.createTreeWalker( 22 | rootElement, 23 | NodeFilter.SHOW_TEXT, 24 | { 25 | acceptNode: (node) => { 26 | return node.textContent.match(/\{\{.*?\}\}/) ? 27 | NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 28 | } 29 | } 30 | ); 31 | let matches = []; 32 | 33 | while(treeWalker.nextNode()) { 34 | let reMacroName = /\{\{\s*([^\(\}\s]+).*?\}\}/g; 35 | let macroNameMatch = reMacroName.exec(treeWalker.currentNode.textContent); 36 | while (macroNameMatch) { 37 | let noteElement = treeWalker.currentNode.parentNode.previousSibling; 38 | if(dataMacros.test(macroNameMatch[1])) { 39 | if(!noteElement || !noteElement.classList.contains("hidden")) { 40 | matches.push({ 41 | msg: "data_macro_note_missing", 42 | msgParams: [macroNameMatch[0]], 43 | type: ERROR 44 | }); 45 | } else if(!noteElement.querySelector("[href^='http']")) { 46 | matches.push({ 47 | msg: "data_macro_source_link_missing", 48 | msgParams: [macroNameMatch[0]], 49 | type: ERROR 50 | }); 51 | } 52 | } 53 | macroNameMatch = reMacroName.exec(treeWalker.currentNode.textContent); 54 | } 55 | } 56 | 57 | return matches; 58 | } 59 | }; -------------------------------------------------------------------------------- /data/tests/unnecessary-macro-params.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for obsolete macro parameters. 3 | * 4 | * Example 1: Some macros like {{JSRef}} don't require parameters anymore, so they should be 5 | * removed. 6 | * 7 | * Example 2: Some macros like {{cssinfo}} don't require parameters when the related information 8 | * can be read from the page's slug, so they should be removed in those cases. 9 | * 10 | * Implementation notes: This test checks for a specific list of macros, which either have no 11 | * parameters at all or their parameters are redundant. It uses the page title for comparison, so 12 | * the unit test doesn't break while working on about:blank. 13 | */ 14 | 15 | const reMacrosNotRequiringParams = /\{\{\s*(?:JSRef|csssyntax|cssinfo|svginfo)\([^\)]+?\)\s*\}\}/i; 16 | const reMacrosNotRequiringParamsGlobal = new RegExp(reMacrosNotRequiringParams.source, "gi"); 17 | 18 | docTests.unnecessaryMacroParams = { 19 | name: "unnecessary_macro_params", 20 | desc: "unnecessary_macro_params_desc", 21 | check: function checkUnnecessaryMacroParams(rootElement) { 22 | let treeWalker = document.createTreeWalker( 23 | rootElement, 24 | NodeFilter.SHOW_TEXT, 25 | { 26 | acceptNode: (node) => { 27 | return node.textContent.match(reMacrosNotRequiringParams) ? 28 | NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 29 | } 30 | } 31 | ); 32 | let matches = []; 33 | 34 | while(treeWalker.nextNode()) { 35 | let textNodeMatches = treeWalker.currentNode.textContent.match( 36 | reMacrosNotRequiringParamsGlobal) || []; 37 | textNodeMatches.forEach(match => { 38 | let paramMatch = match.match(/(?:csssyntax|cssinfo|svginfo)\((["'])(.+?)\1/i); 39 | if (paramMatch) { 40 | let param = paramMatch[2]; 41 | if (param === document.title.replace(/^(.+?) \| Edit.*$/, "$1")) { 42 | matches.push({ 43 | msg: "macro_with_unnecessary_params_equalling_slug", 44 | msgParams: [match], 45 | type: ERROR 46 | }); 47 | } 48 | } else { 49 | matches.push({ 50 | msg: "macro_with_unused_params", 51 | msgParams: [match], 52 | type: ERROR 53 | }); 54 | } 55 | }); 56 | } 57 | 58 | return matches; 59 | } 60 | }; -------------------------------------------------------------------------------- /data/tests/wrong-syntax-class.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for whether the 'syntax' class is properly used on a syntax block. 3 | * 4 | * Example 1:
 elements following a 'Formal syntax' heading are expected to contain a syntax 
 5 |  *  definition, which needs to be styled using class="syntaxbox".
 6 |  *
 7 |  *  Example 2: 
 elements following a 'Syntax' heading where there is no 'Formal syntax' 
 8 |  *  section are expected to contain a syntax definition, which needs to be styled using 
 9 |  *  class="syntaxbox".
10 |  *
11 |  *  Implementation notes: This test first searches for an 

Formal syntax

heading. If none 12 | * is found, it searches for a

Syntax

heading. If one of those is found, the following 13 | *
 element is expected to hold a syntax definition, which needs to be styled using 
14 |  *  class="syntaxbox".
15 |  */
16 | 
17 | docTests.wrongSyntaxClass = {
18 |   name: "wrong_syntax_class",
19 |   desc: "wrong_syntax_class_desc",
20 |   check: function checkWrongSyntaxClass(rootElement) {
21 |     function checkPre(heading) {
22 |       let element = heading.nextSibling;
23 |       while (element && element.localName !== "h2") {
24 |         if (element.localName === "pre" && element.className !== "syntaxbox") {
25 |           return {
26 |             node: element,
27 |             msg: "wrong_syntax_class_used",
28 |             msgParams: [element.className],
29 |             type: ERROR
30 |           };
31 |           break;
32 |         }
33 |         element = element.nextElementSibling;
34 |       }
35 |     }
36 | 
37 |     let subHeadings = rootElement.getElementsByTagName("h3");
38 |     let formalSyntaxSection = null;
39 |     for (let i = 0; !formalSyntaxSection && i < subHeadings.length; i++) {
40 |       if (subHeadings[i].textContent.match(/Formal syntax/i)) {
41 |         formalSyntaxSection = subHeadings[i];
42 |       }
43 |     }
44 | 
45 |     let syntaxBoxClass;
46 |     let matches = [];
47 |     if (formalSyntaxSection) {
48 |       let match = checkPre(formalSyntaxSection);
49 |       if (match) {
50 |         matches.push(match);
51 |       }
52 |     } else {
53 |       let headings = rootElement.getElementsByTagName("h2");
54 |       let syntaxSection = null;
55 |       for (let i = 0; !syntaxSection && i < headings.length; i++) {
56 |         if (headings[i].textContent.toLowerCase() === "syntax") {
57 |           syntaxSection = headings[i];
58 |         }
59 |       }
60 | 
61 |       if (syntaxSection) {
62 |         let match = checkPre(syntaxSection);
63 |         if (match) {
64 |           matches.push(match);
65 |         }
66 |       }
67 |     }
68 | 
69 |     return matches;
70 |   },
71 |   fix: function fixWrongSyntaxClass(matches) {
72 |     matches.forEach(match => {
73 |       match.node.className = "syntaxbox";
74 |     });
75 |   }
76 | };


--------------------------------------------------------------------------------
/data/runtests.js:
--------------------------------------------------------------------------------
 1 | const RUN_TESTS_DELAY = 500;
 2 | 
 3 | let runTestsTimeout = null;
 4 | 
 5 | window.setTimeout(initializeKeyEventHandler, 1000);
 6 | 
 7 | function initializeKeyEventHandler() {
 8 |   let iframe = document.querySelector("iframe.cke_wysiwyg_frame");
 9 |   iframe.contentWindow.addEventListener("keyup", runTestsAfterTimeout);
10 |   iframe.contentWindow.addEventListener("keydown", resetRunTestsTimeout);
11 |   
12 |   function runTestsAfterTimeout() {
13 |     runTestsTimeout = window.setTimeout(runTests, RUN_TESTS_DELAY);
14 |   }
15 |   
16 |   function resetRunTestsTimeout() {
17 |     window.clearTimeout(runTestsTimeout);
18 |   }
19 |   
20 |   let ckeditor = document.getElementById("id_content");
21 |   ckeditor.addEventListener("keyup", runTestsAfterTimeout);
22 |   ckeditor.addEventListener("keydown", resetRunTestsTimeout);
23 | }
24 | 
25 | function runTest(testObj, id, rootElement) {
26 |   // Only run the test suite if there's a root element
27 |   // (e.g. when in source view there's no root element set)
28 |   if (rootElement) {
29 |     let contentTest = testObj.check(rootElement);
30 |     testObj.errors = contentTest;
31 |     self.port.emit("processTestResult", testObj, id);
32 |   }
33 | };
34 | 
35 | let fixIssues = function(testObj, id) {
36 |   // Only run the fixes if there's a root element
37 |   // (e.g. when in source view there's no root element set)
38 |   if (testObj.fix) {
39 |     testObj.fix(testObj.errors);
40 | 
41 |     // Run test again to update its results
42 |     let iframe = document.querySelector("iframe.cke_wysiwyg_frame");
43 |     let rootElement = iframe.contentDocument.body;
44 |     runTest(testObj, id, rootElement);
45 |   }
46 | };
47 | 
48 | function runTests() {
49 |   let iframe = document.querySelector("iframe.cke_wysiwyg_frame");
50 |   if (iframe) {
51 |     let rootElement = iframe.contentDocument.body;
52 | 
53 |     for (let prop in docTests) {
54 |       runTest(docTests[prop], prop, rootElement);
55 |     }
56 |   }
57 |   self.port.emit("finishedTests");
58 | }
59 | 
60 | self.port.on("runTests", function() {
61 |   runTests();
62 | });
63 | 
64 | self.port.on("fixIssues", function() {
65 |   for (let prop in docTests) {
66 |     fixIssues(docTests[prop], prop);
67 |   }
68 | });
69 | 
70 | window.addEventListener("load", function injectIFrame() {
71 |   window.removeEventListener("load", injectIFrame);
72 | 
73 |   // Using polling to add the spellchecking and initially run the tests,
74 |   // because the iframe is not loaded immediately and there doesn't seem
75 |   // to be a proper event to react to.
76 |   let checkIfIframeLoadedInterval = setInterval(() => {
77 |     let iframe = document.querySelector("iframe.cke_wysiwyg_frame");
78 |     if (iframe) {
79 |       clearInterval(checkIfIframeLoadedInterval);
80 |       iframe.contentDocument.body.setAttribute("spellcheck", "true");
81 |       runTests();
82 |     }
83 |   }, 50);
84 | });
85 | 


--------------------------------------------------------------------------------
/test/test-wrong-highlighted-line.js:
--------------------------------------------------------------------------------
 1 | const {ERROR, WARNING, url, runTests} = require("./testutils");
 2 | 
 3 | exports["test doc wrongHighlightedLine"] = function testWrongHighlightedLines(assert, done) {
 4 |   const tests = [
 5 |     {
 6 |       str: '
foo\nbar
' + 7 | '
foo\nbar
' + 8 | '
foo\nbar
' + 9 | '
foo\nbar\nbaz
' + 10 | '
foo\nbar\nbaz
' + 11 | '
foo\nbar\nbaz\nbax\nbix
' + 12 | '
foo\nbar\nbaz\nbax\nbix
' + 13 | '
foo\nbar
' + 14 | '
foo\nbar
' + 15 | '
foo\nbar
' + 16 | '
foo
bar
' + 17 | '
foo
bar
' + 18 | '
foo
bar
' + 19 | '
foo\nbar\nbaz
' + 20 | '
foo\nbar\nbaz
', 21 | expected: [ 22 | { 23 | msg: "highlighted_line_number_not_positive", 24 | msgParams: ["0", "0"], 25 | type: ERROR 26 | }, 27 | { 28 | msg: "highlighted_line_number_not_positive", 29 | msgParams: ["-1", "-1"], 30 | type: ERROR 31 | }, 32 | { 33 | msg: "highlighted_line_number_too_big", 34 | msgParams: ["3", "2", "3"], 35 | type: ERROR 36 | }, 37 | { 38 | msg: "highlighted_line_number_too_big", 39 | msgParams: ["3", "2", "3"], 40 | type: ERROR 41 | }, 42 | { 43 | msg: "highlighted_line_number_too_big", 44 | msgParams: ["3", "2", "3"], 45 | type: ERROR 46 | }, 47 | { 48 | msg: "highlighted_line_number_too_big", 49 | msgParams: ["3", "2", "3"], 50 | type: ERROR 51 | }, 52 | { 53 | msg: "highlighted_line_number_not_positive", 54 | msgParams: ["-3", "1,-3--5,3"], 55 | type: ERROR 56 | }, 57 | { 58 | msg: "highlighted_line_number_not_positive", 59 | msgParams: ["-5", "1,-3--5,3"], 60 | type: ERROR 61 | }, 62 | { 63 | msg: "invalid_highlighted_range", 64 | msgParams: ["-3", "-5", "1,-3--5,3"], 65 | type: ERROR 66 | }, 67 | { 68 | msg: "highlighted_line_number_too_big", 69 | msgParams: ["6", "3", " 1, 3 - 6 ,2 "], 70 | type: ERROR 71 | } 72 | ], 73 | expectedAfterFixing: [] 74 | } 75 | ]; 76 | 77 | runTests(assert, done, "wrongHighlightedLine", "wrong highlighted line", url, tests); 78 | }; 79 | 80 | require("sdk/test").run(exports); -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | const data = require("sdk/self").data; 2 | const tabs = require("sdk/tabs"); 3 | const localize = require("sdk/l10n").get; 4 | const prefs = require("sdk/simple-prefs").prefs; 5 | const testList = require("../data/tests/testlist").testList; 6 | const editURL= /^https:\/\/developer\.mozilla\.org\/.+(?:\$(?:edit|translate)|\/docs\/new(?:\?|$))/; 7 | const templateURL= /^https:\/\/developer\.mozilla\.org\/.+?\/docs\/(?:templates|Template(?::|%3A))/; 8 | 9 | let tabWorkers = new WeakMap(); 10 | let sidebarWorker = null; 11 | 12 | let sidebar = require("sdk/ui/sidebar").Sidebar({ 13 | id: "mdn-doc-tests", 14 | title: "MDN documentation tester", 15 | url: data.url("sidebar.html"), 16 | onReady: function (sbWorker) { 17 | sidebarWorker = sbWorker; 18 | 19 | let tabWorker = tabWorkers.get(tabs.activeTab); 20 | tabWorker.port.emit("runTests"); 21 | 22 | sidebarWorker.port.on("runTests", function() { 23 | tabWorker.port.emit("runTests"); 24 | tabWorker.port.on("finishedTests", () => sidebarWorker.port.emit("hideProgressBar")); 25 | }); 26 | 27 | sbWorker.port.on("fixIssues", function() { 28 | let tabWorker = tabWorkers.get(tabs.activeTab); 29 | tabWorker.port.emit("fixIssues"); 30 | }); 31 | } 32 | }); 33 | 34 | function initializeTestingEnvironment(tab, overwriteWorker) { 35 | if (editURL.test(tab.url) && !templateURL.test(tab.url)) { 36 | if (!tabWorkers.has(tab) || overwriteWorker) { 37 | let tabWorker = tab.attach({ 38 | contentScriptFile: [ 39 | "./doctests.js", 40 | ...testList.map(test => "./tests/" + test), 41 | "./runtests.js" 42 | ], 43 | contentScriptOptions: { 44 | LONG_ARTICLE_WORD_COUNT_THRESHOLD: prefs.longArticleWordCountThreshold 45 | } 46 | }); 47 | tabWorker.port.on("processTestResult", function(testObj, id){ 48 | testObj.name = localize(testObj.name); 49 | testObj.desc = localize(testObj.desc); 50 | testObj.errors.forEach((error, i, errors) => { 51 | errors[i] = { 52 | msg: localize.apply(this, [error.msg].concat(error.msgParams)), 53 | type: error.type 54 | }; 55 | }); 56 | 57 | if (sidebarWorker) { 58 | sidebarWorker.port.emit("showTestResult", testObj, id, prefs); 59 | sidebarWorker.port.emit("updateProgress", Math.round(100 / testList.length)); 60 | } 61 | }); 62 | 63 | tabWorkers.set(tab, tabWorker); 64 | } 65 | } 66 | toggleSidebar(); 67 | }; 68 | 69 | function destroyTestingEnvironment(tab) { 70 | tabWorkers.remove(tab); 71 | }; 72 | 73 | function handleTabSwitch() { 74 | toggleSidebar(); 75 | let tabWorker = tabWorkers.get(tabs.activeTab) || initializeTestingEnvironment(tabs.activeTab, true); 76 | if (tabWorker) { 77 | tabWorker.port.emit("runTests"); 78 | } 79 | } 80 | 81 | function toggleSidebar() { 82 | if (editURL.test(tabs.activeTab.url) && !templateURL.test(tabs.activeTab.url)) { 83 | sidebar.show(); 84 | } else { 85 | sidebar.hide(); 86 | } 87 | }; 88 | 89 | tabs.on("activate", handleTabSwitch); 90 | tabs.on("ready", initializeTestingEnvironment); 91 | tabs.on("pagehide", destroyTestingEnvironment); 92 | tabs.on("pageshow", initializeTestingEnvironment); 93 | -------------------------------------------------------------------------------- /data/tests/api-syntax-headlines.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test whether the API syntax headlines are named correctly, i.e. 'Parameters', 3 | * 'Return value' and 'Exceptions', and whether they appear in the correct order. 4 | * 5 | * Example 1: The return value should have 'Return value' as headline and not 'Returns'. 6 | * 7 | * Example 2: Exceptions should have 'Exceptions' as headline and not 'Errors' or 'Errors thrown'. 8 | * 9 | * Example 3: Having an 'Exceptions' section before the 'Return value' section should be avoided. 10 | * The correct order of the sections is 'Parameters', 'Return value' and 'Exceptions'. 11 | * 12 | * Implementation notes: This test searches for specific keywords like 'returns' or 'errors' and 13 | * expects the headlines to be

elements under a

Syntax

section. 14 | */ 15 | 16 | const disallowedNames = new Map([["returns", "Return value"], ["errors", "Exceptions"], 17 | ["errors thrown", "Exceptions"]]); 18 | const validOrder = [ 19 | new Set(["parameters"]), 20 | new Set(["return value", "returns"]), 21 | new Set(["exceptions", "errors", "errors thrown"]) 22 | ]; 23 | 24 | docTests.apiSyntaxHeadlines = { 25 | name: "api_syntax_headlines", 26 | desc: "api_syntax_headlines_desc", 27 | 28 | check: function checkAPISyntaxHeadlines(rootElement) { 29 | let headlines = rootElement.getElementsByTagName("h2"); 30 | let syntaxSection = null; 31 | let order = []; 32 | let matches = []; 33 | for (let i = 0; !syntaxSection && i < headlines.length; i++) { 34 | if (headlines[i].textContent === "Syntax") { 35 | syntaxSection = headlines[i]; 36 | } 37 | } 38 | 39 | if (syntaxSection) { 40 | let subHeadingElements = []; 41 | let element = syntaxSection.nextSibling; 42 | while (element && element.localName !== "h2") { 43 | if (element.localName === "h3") { 44 | subHeadingElements.push(element); 45 | } 46 | element = element.nextSibling; 47 | } 48 | for (let i = 0; i < subHeadingElements.length; i++) { 49 | let subHeading = subHeadingElements[i].textContent.toLowerCase(); 50 | for (let j = 0; j < validOrder.length; j++) { 51 | let heading = validOrder[j]; 52 | if (heading.has(subHeading)) { 53 | order.push(j); 54 | } 55 | } 56 | if (disallowedNames.has(subHeading)) { 57 | matches.push({ 58 | node: subHeadingElements[i], 59 | msg: "invalid_headline_name", 60 | msgParams: [subHeadingElements[i].textContent], 61 | type: ERROR 62 | }); 63 | } 64 | } 65 | 66 | // Check the order of the headlines 67 | for (let i = 1; i < order.length; i++) { 68 | if (order[i] < order[i - 1]) { 69 | matches.push({ 70 | msg: "invalid_headline_order", 71 | type: ERROR 72 | }); 73 | } 74 | } 75 | } 76 | 77 | return matches; 78 | }, 79 | 80 | fix: function fixAPISyntaxHeadlines(matches) { 81 | matches.forEach(match => { 82 | switch (match.msg) { 83 | case "invalid_headline_name": 84 | match.node.textContent = disallowedNames.get(match.node.textContent.toLowerCase()); 85 | break; 86 | } 87 | }); 88 | } 89 | }; -------------------------------------------------------------------------------- /test/testutils.js: -------------------------------------------------------------------------------- 1 | // Utility functions that execute unit tests 2 | 3 | const ERROR = 1; 4 | const WARNING = 2; 5 | const INFO = 3; 6 | 7 | const prefs = require("sdk/simple-prefs").prefs; 8 | const testList = require("../data/tests/testlist").testList; 9 | 10 | exports.ERROR = ERROR; 11 | exports.WARNING = WARNING; 12 | exports.INFO = INFO; 13 | exports.url = "about:blank"; 14 | 15 | function mapTestName(name) { 16 | return name.replace(/[A-Z]+/g, match => "-" + match.toLowerCase()) + ".js"; 17 | } 18 | 19 | exports.runTests = function runTests(assert, done, name, desc, url, tests) { 20 | // Skip the test in case it is not available 21 | if (!testList.includes(mapTestName(name))) { 22 | done(); 23 | return; 24 | } 25 | 26 | let tabs = require("sdk/tabs"); 27 | tabs.open({ 28 | url: url, 29 | onReady: tab => { 30 | let worker = tabs.activeTab.attach({ 31 | contentScriptFile: [ 32 | "./doctests.js", 33 | ...testList.map(test => "./tests/" + test), 34 | "../test/testrunner.js"], 35 | contentScriptOptions: {"name": name, "tests": JSON.stringify(tests), 36 | LONG_ARTICLE_WORD_COUNT_THRESHOLD: prefs.longArticleWordCountThreshold} 37 | }); 38 | 39 | let resultCount = 0; 40 | 41 | worker.port.on("processTestResult", function(testObj) { 42 | if (testObj) { 43 | let matches = testObj.errors; 44 | let expected = testObj.expected; 45 | 46 | assert.equal(matches.length, expected.length, 47 | "Number of " + desc + " matches must be " + expected.length); 48 | 49 | matches.forEach((match, i) => { 50 | assert.equal(match.msg, expected[i].msg, 51 | "Error message for " + desc + " match must be correct"); 52 | 53 | assert.equal(match.type, expected[i].type, 54 | "Error type for " + desc + " match must be correct"); 55 | 56 | if (expected[i].msgParams) { 57 | assert.deepEqual(match.msgParams, expected[i].msgParams, 58 | "Error message params for " + desc + " match must be correct"); 59 | } 60 | }); 61 | 62 | // Check whether the issues were fixed as expected 63 | if (testObj.expectedAfterFixing) { 64 | let matches = testObj.errorsAfterFixing; 65 | let expected = testObj.expectedAfterFixing; 66 | 67 | assert.equal(matches.length, expected.length, 68 | "Number of " + desc + " matches after fixing must be " + expected.length); 69 | 70 | matches.forEach((match, i) => { 71 | assert.equal(match.msg, expected[i].msg, 72 | "Error message for " + desc + " match after fixing must be correct"); 73 | 74 | assert.equal(match.type, expected[i].type, 75 | "Error type for " + desc + " match after fixing must be correct"); 76 | 77 | if (expected[i].msgParams) { 78 | assert.deepEqual(match.msgParams, expected[i].msgParams, 79 | "Error message params for " + desc + " match after fixing must be correct"); 80 | } 81 | }); 82 | } 83 | 84 | resultCount++; 85 | } 86 | 87 | if (resultCount === tests.length) { 88 | tabs.activeTab.close(); 89 | done(); 90 | } 91 | }); 92 | } 93 | }); 94 | } -------------------------------------------------------------------------------- /data/tests/macro-syntax-error.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for syntax errors in macro calls. 3 | * 4 | * Example 1: {{macro} misses a closing curly brace, so it will be recognized as error. 5 | * 6 | * Example 2: {{macro('param'}} misses a closing bracket, so it will be recognized as error. 7 | * 8 | * Example 3: {{macro("param"))}} has an additional closing bracket, so it will be recognized as error. 9 | * 10 | * Example 4: {{macro('param)}} and {{macro(param")}} have incorrectly quoted string parameters, 11 | * so they will be recognized as errors. 12 | * 13 | * Implementation notes: This test uses regular expressions to recognize invalid macros. 14 | * It currently fails to properly validate macros containing JSON parameters (see issue #139). 15 | */ 16 | 17 | docTests.macroSyntaxError = { 18 | name: "macro_syntax_error", 19 | desc: "macro_syntax_error_desc", 20 | check: function checkMacroSyntaxError(rootElement) { 21 | function validateStringParams(macro) { 22 | let paramListStartIndex = macro.indexOf("(") + 1; 23 | let paramListEndMatch = macro.match(/\)*\s*\}{1,2}$/); 24 | let paramListEndIndex = macro.length - paramListEndMatch[0].length; 25 | let stringParamQuote = ""; 26 | for (let i = paramListStartIndex; i < paramListEndIndex; i++) { 27 | if (macro[i] === "\"") { 28 | if (stringParamQuote === "") { 29 | stringParamQuote = "\""; 30 | } else if (stringParamQuote === "\"" && macro[i - 1] !== "\\") { 31 | stringParamQuote = ""; 32 | } 33 | } else if (macro[i] === "'") { 34 | if (stringParamQuote === "") { 35 | stringParamQuote = "'"; 36 | } else if (stringParamQuote === "'" && macro[i - 1] !== "\\") { 37 | stringParamQuote = ""; 38 | } 39 | } else if (stringParamQuote === "" && macro[i].match(/[^\s,\d\-\.]/)) { 40 | return false; 41 | } 42 | } 43 | return stringParamQuote === ""; 44 | } 45 | 46 | let treeWalker = document.createTreeWalker( 47 | rootElement, 48 | NodeFilter.SHOW_TEXT, 49 | { 50 | acceptNode: (node) => { 51 | return node.textContent.match(/\{\{[^\(\}]*\([^\}]*\}\}|\{\{[^\}]*?\}(?:(?=[^\}])|$)/) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 52 | } 53 | } 54 | ); 55 | let matches = []; 56 | 57 | while(treeWalker.nextNode()) { 58 | let textNodeMatches = treeWalker.currentNode.textContent.match(/\{\{[^\(\}]*\([^\}]*\}\}|\{\{[^\}]*?\}(?:(?=[^\}])|$)/gi) || []; 59 | textNodeMatches.forEach(macro => { 60 | if (macro.match(/[^\}]\}$/)) { 61 | matches.push({ 62 | msg: "missing_closing_curly_brace", 63 | msgParams: [macro], 64 | type: ERROR 65 | }); 66 | } 67 | if (macro.match(/^\{\{[^\(]+\(.+?[^\)\s]\s*\}\}$/)) { 68 | matches.push({ 69 | msg: "missing_closing_bracket", 70 | msgParams: [macro], 71 | type: ERROR 72 | }); 73 | } 74 | if (!validateStringParams(macro)) { 75 | matches.push({ 76 | msg: "string_parameter_incorrectly_quoted", 77 | msgParams: [macro], 78 | type: ERROR 79 | }); 80 | } 81 | if (macro.match(/\){2,}\}{1,2}$/)) { 82 | matches.push({ 83 | msg: "additional_closing_bracket", 84 | msgParams: [macro], 85 | type: ERROR 86 | }); 87 | } 88 | }); 89 | } 90 | 91 | return matches; 92 | } 93 | }; -------------------------------------------------------------------------------- /data/sidebar.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ok-color: #00882D; 3 | --error-color: #CB000F; 4 | --warning-color: #DBB40F; 5 | --info-color: #0095DD; 6 | --match-background: linear-gradient(rgba(255,255,255,0.2),rgba(255,255,255,0.2)); 7 | --match-highlighted-background: linear-gradient(rgba(255,255,255,0.3),rgba(255,255,255,0.3)); 8 | } 9 | 10 | html, body { 11 | margin: 0; 12 | } 13 | 14 | body { 15 | font-family: 'Open Sans', sans-serif; 16 | line-height: 1.5em; 17 | color: #4D4E53; 18 | display: flex; 19 | flex-direction: column; 20 | box-sizing: border-box; 21 | max-height: 100vh; 22 | padding: 10px; 23 | } 24 | 25 | h2 { 26 | margin-block-end: 0.1em; 27 | font-size: 1em; 28 | } 29 | 30 | button { 31 | min-width: 100px; 32 | max-width: fit-content; 33 | max-width: -moz-fit-content; 34 | } 35 | 36 | #fixIssues { 37 | margin-top: 10px; 38 | display: none; 39 | } 40 | 41 | #summary { 42 | display: none; 43 | } 44 | 45 | #summary > p { 46 | margin-block-start: 10px; 47 | margin-block-end: 10px; 48 | } 49 | 50 | #summary > p:not(:first-child) { 51 | margin-inline-start: 20px; 52 | } 53 | 54 | #summary .errorCount { 55 | display: inline-block; 56 | width: 3em; 57 | text-align: center; 58 | color: #FFF; 59 | padding-inline-end: 0; 60 | } 61 | 62 | progress { 63 | visibility: hidden; 64 | height: 5px; 65 | } 66 | 67 | .visible { 68 | visibility: visible; 69 | } 70 | 71 | li { 72 | list-style-type: none; 73 | } 74 | 75 | #tests { 76 | width: 100%; 77 | margin-block-start: 0; 78 | margin-block-end: 0; 79 | padding-inline-start: 0; 80 | overflow-x: hidden; 81 | overflow-y: auto; 82 | } 83 | 84 | .hidePassingTests .ok { 85 | display: none; 86 | } 87 | 88 | .test { 89 | width: 100%; 90 | color: #FFF; 91 | margin-block-end: 1px; 92 | cursor: default; 93 | } 94 | 95 | .test > div { 96 | display: flex; 97 | } 98 | 99 | .hasErrors > .testHeading, 100 | .hasWarnings > .testHeading, 101 | .hasInfo > .testHeading { 102 | cursor: pointer; 103 | } 104 | 105 | .hasErrors { 106 | background-color: var(--error-color); 107 | } 108 | 109 | .hasWarnings { 110 | background-color: var(--warning-color); 111 | } 112 | 113 | .hasInfo { 114 | background-color: var(--info-color); 115 | } 116 | 117 | .ok { 118 | background-color: var(--ok-color); 119 | } 120 | 121 | .testName { 122 | padding-inline-start: 0.5em; 123 | } 124 | 125 | .errorCount { 126 | flex-grow: 1; 127 | padding-inline-end: 0.5em; 128 | text-align: end; 129 | } 130 | 131 | .errors { 132 | display: none; 133 | padding-inline-start: 0; 134 | } 135 | 136 | .errors > li { 137 | padding-inline-start: 1.5em; 138 | padding-block-start: 0.3em; 139 | padding-block-end: 0.3em; 140 | } 141 | 142 | .error, 143 | .warning, 144 | .info { 145 | background-image: var(--match-background); 146 | } 147 | 148 | .error { 149 | background-color: var(--error-color); 150 | } 151 | 152 | .warning { 153 | background-color: var(--warning-color); 154 | } 155 | 156 | .info { 157 | background-color: var(--info-color); 158 | } 159 | 160 | .error:not(:last-child) { 161 | border-bottom: 2px solid var(--error-color); 162 | } 163 | 164 | .warning:not(:last-child) { 165 | border-bottom: 2px solid var(--warning-color); 166 | } 167 | 168 | .info:not(:last-child) { 169 | border-bottom: 2px solid var(--info-color); 170 | } 171 | 172 | .error:hover, 173 | .warning:hover, 174 | .info:hover { 175 | background-image: var(--match-highlighted-background); 176 | } 177 | 178 | #fixIssues.show, 179 | .show { 180 | display: block; 181 | } -------------------------------------------------------------------------------- /data/tests/wrong-highlighted-line.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Test for incorrect line highlights in code examples. 3 | * 4 | * Example 1: Negative highlights like in 5 | *
var x = 1;
6 | * are invalid. 7 | * 8 | * Example 2: Highlights exceeding the line count like in 9 | *
var x = 1;
10 | * are invalid. 11 | * 12 | * Example 3: Highlighted ranges of lines exceeding the line count like in 13 | *
var x = 1;
14 | * are invalid. 15 | * 16 | * Example 4: Highlighted ranges where the start line is bigger than the end line line in 17 | *
var x = 1;\nvar y = 2;
18 | * are invalid. 19 | * 20 | * Implementation notes: This test searches for all
 elements containing a 'highlight' 
 21 |  *  class, then splits the numbers and ranges wrapped by square brackets following the 'highlight' 
 22 |  *  class and finally checks each item whether its valid.
 23 |  */
 24 | 
 25 | const reHighlighting = /highlight:?\s*\[(.+?)\]/i;
 26 | 
 27 | docTests.wrongHighlightedLine = {
 28 |   name: "wrong_highlighted_line",
 29 |   desc: "wrong_highlighted_line_desc",
 30 | 
 31 |   check: function checkWrongHighlightedLine(rootElement) {
 32 |     let presWithHighlighting = rootElement.querySelectorAll("pre[class*='highlight']");
 33 |     let matches = [];
 34 | 
 35 |     for (let i = 0; i < presWithHighlighting.length; i++) {
 36 |       let match = presWithHighlighting[i].getAttribute("class").match(reHighlighting);
 37 |       if (match) {
 38 |         let numbersAndRanges = match[1].split(",");
 39 |         let lineCount = presWithHighlighting[i].innerHTML.split(/|\n/gi).length;
 40 | 
 41 |         numbersAndRanges.forEach((numberOrRange) => {
 42 |           let start;
 43 |           let end;
 44 |           try {
 45 |             [,start,end] = numberOrRange.match(/^\s*(-?\d+)(?:\s*-\s*(-?\d+))?\s*$/);
 46 |           } catch (e) {}
 47 | 
 48 |           if (start === undefined) {
 49 |             return;
 50 |           }
 51 | 
 52 |           start = Number(start);
 53 |           end = Number(end);
 54 | 
 55 |           if (start <= 0) {
 56 |             matches.push({
 57 |               node: presWithHighlighting[i],
 58 |               msg: "highlighted_line_number_not_positive",
 59 |               msgParams: [String(start), match[1]],
 60 |               type: ERROR
 61 |             });
 62 |           }
 63 |           if (start > lineCount) {
 64 |             matches.push({
 65 |               node: presWithHighlighting[i],
 66 |               msg: "highlighted_line_number_too_big",
 67 |               msgParams: [String(start), String(lineCount), match[1]],
 68 |               type: ERROR
 69 |             });
 70 |           }
 71 |           if (!Number.isNaN(end)) {
 72 |             if (end > lineCount) {
 73 |               matches.push({
 74 |                 node: presWithHighlighting[i],
 75 |                 msg: "highlighted_line_number_too_big",
 76 |                 msgParams: [String(end), String(lineCount), match[1]],
 77 |                 type: ERROR
 78 |               });
 79 |             }
 80 |             if (end <= 0) {
 81 |               matches.push({
 82 |                 node: presWithHighlighting[i],
 83 |                 msg: "highlighted_line_number_not_positive",
 84 |                 msgParams: [String(end), match[1]],
 85 |                 type: ERROR
 86 |               });
 87 |             }
 88 |             if (start > end) {
 89 |               matches.push({
 90 |                 node: presWithHighlighting[i],
 91 |                 msg: "invalid_highlighted_range",
 92 |                 msgParams: [String(start), String(end), match[1]],
 93 |                 type: ERROR
 94 |               });
 95 |             }
 96 |           }
 97 |         });
 98 |       }
 99 |     }
100 | 
101 |     return matches;
102 |   },
103 | 
104 |   fix: function fixWrongHighlightedLine(matches) {
105 |     matches.forEach(match => {
106 |       match.node.className = match.node.className.replace(reHighlighting, "").replace(/;\s*$/, "");
107 |     });
108 |   }
109 | };


--------------------------------------------------------------------------------
/test/test-macro-syntax-error.js:
--------------------------------------------------------------------------------
  1 | const {ERROR, WARNING, url, runTests} = require("./testutils");
  2 | 
  3 | exports["test doc macroSyntaxError"] = function testMacroSyntaxErrors(assert, done) {
  4 |   const tests = [
  5 |     {
  6 |       str: '{{macro}}' +
  7 |            '{{ macro }}' +
  8 |            '{{macro("param")}}' +
  9 |            '{{ macro("param") }}' +
 10 |            '{{macro(123)}}' +
 11 |            '{{macro(123, "param")}}' +
 12 |            '{{macro(\'param\', 123, "param")}}' +
 13 |            '{{macro("param)}}' + // Missing closing double quote
 14 |            '{{macro(\'param)}}' + // Missing closing single quote
 15 |            '{{macro(param)}}' + // Missing quotes
 16 |            '{{macro(param")}}' + // Missing opening double quote
 17 |            '{{macro(param\')}}' + // Missing opening single quote
 18 |            '{{macro(\'param\', 123, "param)}}' + // Missing closing double quote, multiple parameters
 19 |            '{{macro("param"))}}' + // Double closing parameter list bracket
 20 |            '{{macro("param")}' + // Missing closing macro curly brace after double quoted parameter
 21 |            '{{macro(\'param\')}' + // Missing closing macro curly brace after single quoted parameter
 22 |            '{{macro("param"}}' + // Missing closing parameter list bracket after double quoted parameter
 23 |            '{{macro(\'param\'}}' + // Missing closing parameter list bracket after single quoted parameter
 24 |            '{{macro(param"}}' + // Missing opening double quote and missing closing parameter list bracket
 25 |            '{{macro(param"))}}' + // Missing opening double quote and double closing parameter list bracket
 26 |            '{{macro(123, "param()"}}', // Missing closing parameter list bracket after string parameter containing bracket
 27 |       expected: [
 28 |         {
 29 |           msg: "string_parameter_incorrectly_quoted",
 30 |           msgParams: ['{{macro("param)}}'],
 31 |           type: ERROR
 32 |         },
 33 |         {
 34 |           msg: "string_parameter_incorrectly_quoted",
 35 |           msgParams: ["{{macro('param)}}"],
 36 |           type: ERROR
 37 |         },
 38 |         {
 39 |           msg: "string_parameter_incorrectly_quoted",
 40 |           msgParams: ["{{macro(param)}}"],
 41 |           type: ERROR
 42 |         },
 43 |         {
 44 |           msg: "string_parameter_incorrectly_quoted",
 45 |           msgParams: ['{{macro(param")}}'],
 46 |           type: ERROR
 47 |         },
 48 |         {
 49 |           msg: "string_parameter_incorrectly_quoted",
 50 |           msgParams: ["{{macro(param')}}"],
 51 |           type: ERROR
 52 |         },
 53 |         {
 54 |           msg: "string_parameter_incorrectly_quoted",
 55 |           msgParams: ["{{macro('param', 123, \"param)}}"],
 56 |           type: ERROR
 57 |         },
 58 |         {
 59 |           msg: "additional_closing_bracket",
 60 |           msgParams: ['{{macro("param"))}}'],
 61 |           type: ERROR
 62 |         },
 63 |         {
 64 |           msg: "missing_closing_curly_brace",
 65 |           msgParams: ['{{macro("param")}'],
 66 |           type: ERROR
 67 |         },
 68 |         {
 69 |           msg: "missing_closing_curly_brace",
 70 |           msgParams: ["{{macro(\'param\')}"],
 71 |           type: ERROR
 72 |         },
 73 |         {
 74 |           msg: "missing_closing_bracket",
 75 |           msgParams: ['{{macro("param"}}'],
 76 |           type: ERROR
 77 |         },
 78 |         {
 79 |           msg: "missing_closing_bracket",
 80 |           msgParams: ["{{macro(\'param\'}}"],
 81 |           type: ERROR
 82 |         },
 83 |         {
 84 |           msg: "missing_closing_bracket",
 85 |           msgParams: ['{{macro(param"}}'],
 86 |           type: ERROR
 87 |         },
 88 |         {
 89 |           msg: "string_parameter_incorrectly_quoted",
 90 |           msgParams: ['{{macro(param"}}'],
 91 |           type: ERROR
 92 |         },
 93 |         {
 94 |           msg: "string_parameter_incorrectly_quoted",
 95 |           msgParams: ['{{macro(param"))}}'],
 96 |           type: ERROR
 97 |         },
 98 |         {
 99 |           msg: "additional_closing_bracket",
100 |           msgParams: ['{{macro(param"))}}'],
101 |           type: ERROR
102 |         },
103 |         {
104 |           msg: "missing_closing_bracket",
105 |           msgParams: ['{{macro(123, "param()"}}'],
106 |           type: ERROR
107 |         }
108 |       ]
109 |     }
110 |   ];
111 | 
112 |   runTests(assert, done, "macroSyntaxError", "macro syntax error", url, tests);
113 | };
114 | 
115 | require("sdk/test").run(exports);


--------------------------------------------------------------------------------
/data/tests/invalid-macros.js:
--------------------------------------------------------------------------------
  1 | /*
  2 |  *  Title: Test for the usage of invalid macros.
  3 |  *
  4 |  *  Example 1: The usage of 

{{SomeMacro}}

should rather be removed, replaced by a valid 5 | * macro or by static text and {{SomeMacro}} should be deleted. 6 | * 7 | * Implementation notes: This test uses an (incomprehensive) whitelist of allowed macros and a 8 | * list of obsolete macros. Obsolete macros are marked as errors, all others, which are not 9 | * whitelisted are marked as warnings. 10 | */ 11 | 12 | const obsoleteMacros = [ 13 | "languages" 14 | ]; 15 | 16 | docTests.invalidMacros = { 17 | name: "invalid_macros", 18 | desc: "invalid_macros_desc", 19 | check: function checkInvalidMacros(rootElement) { 20 | const allowedMacros = [ 21 | "addonsidebar", 22 | "apiref", 23 | "anch", 24 | "availableinworkers", 25 | "bug", 26 | "canvassidebar", 27 | "chromebug", 28 | "communitybox", 29 | "compat", 30 | "compatandroid", 31 | "compatchrome", 32 | "compatchromemobile", 33 | "compatedge", 34 | "compatgeckodesktop", 35 | "compatgeckofxos", 36 | "compatgeckomobile", 37 | "compatibilitytable", 38 | "compatie", 39 | "compatnightly", 40 | "compatno", 41 | "compatopera", 42 | "compatoperamobile", 43 | "compatsafari", 44 | "compatunknown", 45 | "compatversionunknown", 46 | "compatwebkit", 47 | "cssdata", 48 | "cssinfo", 49 | "cssref", 50 | "csssyntax", 51 | "cssxref", 52 | "defaultapisidebar", 53 | "deprecated_header", 54 | "deprecated_inline", 55 | "discussionlist", 56 | "docstatus", 57 | "domxref", 58 | "draft", 59 | "edgebug", 60 | "embedghlivesample", 61 | "embedlivesample", 62 | "embedyoutube", 63 | "event", 64 | "experimental_inline", 65 | "firefox_for_developers", 66 | "fx_minversion_inline", 67 | "fxos_maxversion_inline", 68 | "fxos_minversion_inline", 69 | "gecko", 70 | "gecko_minversion_inline", 71 | "geckorelease", 72 | "glossary", 73 | "groupdata", 74 | "htmlattrdef", 75 | "htmlattrxref", 76 | "htmlelement", 77 | "htmlref", 78 | "httpheader", 79 | "httpmethod", 80 | "httpsidebar", 81 | "httpstatus", 82 | "includesubnav", 83 | "inheritancediagram", 84 | "interface", 85 | "interfacedata", 86 | "jsfiddlelink", 87 | "jsref", 88 | "jssidebar", 89 | "jsxref", 90 | "js_property_attributes", 91 | "l10n:common", 92 | "l10n:compattable", 93 | "l10n:css", 94 | "l10n:javascript", 95 | "l10n:svg", 96 | "localizationstatusinsection", 97 | "mathmlelement", 98 | "mathmlref", 99 | "next", 100 | "non-standard_header", 101 | "non-standard_inline", 102 | "noscript_inline", 103 | "obsolete_header", 104 | "obsolete_inline", 105 | "optional_inline", 106 | "page", 107 | "previous", 108 | "previousmenunext", 109 | "previousnext", 110 | "promote-mdn", 111 | "property_prefix", 112 | "readonlyinline", 113 | "releasegecko", 114 | "rfc", 115 | "seecompattable", 116 | "sidebarutilities", 117 | "sm_minversion_inline", 118 | "spec2", 119 | "specname", 120 | "svgattr", 121 | "svgdata", 122 | "svgelement", 123 | "svginfo", 124 | "svgref", 125 | "tb_minversion_inline", 126 | "webextapiembedtype", 127 | "webextapiref", 128 | "webextapisidebar", 129 | "webextchromecompat", 130 | "webextexamplesdata", 131 | "webextexamples", 132 | "webglsidebar", 133 | "webkitbug", 134 | "webrtcsidebar", 135 | "xref", 136 | "xulattr", 137 | "xulelem" 138 | ]; 139 | 140 | let treeWalker = document.createTreeWalker( 141 | rootElement, 142 | NodeFilter.SHOW_TEXT, 143 | { 144 | acceptNode: (node) => { 145 | return node.textContent.match(/\{\{.*?\}\}/) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT; 146 | } 147 | } 148 | ); 149 | let matches = []; 150 | 151 | while(treeWalker.nextNode()) { 152 | let reMacroName = /\{\{\s*([^\(\}\s]+).*?\}\}/g; 153 | let macroNameMatch = reMacroName.exec(treeWalker.currentNode.textContent); 154 | while (macroNameMatch) { 155 | if (obsoleteMacros.includes(macroNameMatch[1].toLowerCase())) { 156 | matches.push({ 157 | node: treeWalker.currentNode, 158 | msg: "obsolete_macro", 159 | msgParams: [macroNameMatch[0]], 160 | type: ERROR 161 | }); 162 | } else if (!allowedMacros.includes(macroNameMatch[1].toLowerCase())) { 163 | matches.push({ 164 | msg: macroNameMatch[0], 165 | type: WARNING 166 | }); 167 | } 168 | macroNameMatch = reMacroName.exec(treeWalker.currentNode.textContent); 169 | } 170 | } 171 | 172 | return matches; 173 | }, 174 | 175 | fix: function fixInvalidMacros(matches) { 176 | let reObsoleteMacros = 177 | new RegExp("\\{\\{\\\s*(?:" + obsoleteMacros.join("|") + ").*?\\}\\}", "gi"); 178 | 179 | matches.forEach(match => { 180 | if (!match.node) { 181 | return; 182 | } 183 | 184 | match.node.textContent = match.node.textContent.replace(reObsoleteMacros, ""); 185 | if (match.node.parentNode.textContent.match(/^(\s| )*$/)) { 186 | match.node.parentNode.remove(); 187 | } 188 | }); 189 | }}; 190 | -------------------------------------------------------------------------------- /data/sidebar.js: -------------------------------------------------------------------------------- 1 | const ERROR = 1; 2 | const WARNING = 2; 3 | const INFO = 3; 4 | 5 | let totalErrorCount = 0; 6 | let totalWarningCount = 0; 7 | 8 | addon.port.on("showTestResult", function(test, id, prefs) { 9 | let tests = document.getElementById("tests"); 10 | let matchesCount = test.errors.length; 11 | let status = "ok"; 12 | if (matchesCount !== 0) { 13 | if (test.errors.some(match => match.type === ERROR)) { 14 | status = "hasErrors"; 15 | } else if (test.errors.some(match => match.type === WARNING)) { 16 | status = "hasWarnings"; 17 | } else if (test.errors.every(match => match.type === INFO)) { 18 | status = "hasInfo"; 19 | } 20 | } 21 | let oldWarningCount = 0; 22 | let oldErrorCount = 0; 23 | let newWarningCount = test.errors.filter(match => match.type === WARNING).length; 24 | let newErrorCount = test.errors.filter(match => match.type === ERROR).length; 25 | 26 | tests.classList.toggle("hidePassingTests", prefs.hidePassingTests); 27 | 28 | let testElem = document.getElementById(id); 29 | if (tests.contains(testElem)) { 30 | oldWarningCount = Number(testElem.dataset.warningCount); 31 | oldErrorCount = Number(testElem.dataset.errorCount); 32 | testElem.dataset.errorCount = newErrorCount; 33 | testElem.dataset.warningCount = newWarningCount; 34 | testElem.getElementsByClassName("errorCount")[0].textContent = matchesCount; 35 | testElem.classList.remove("hasErrors", "hasWarnings", "hasInfo", "ok"); 36 | testElem.classList.add(status); 37 | } else { 38 | let testContainer = document.createElement("li"); 39 | testContainer.setAttribute("class", "test " + status); 40 | testContainer.setAttribute("id", id); 41 | testContainer.setAttribute("title", test.desc); 42 | testContainer.dataset.errorCount = newErrorCount; 43 | testContainer.dataset.warningCount = newWarningCount; 44 | let testHeadingContainer = document.createElement("div"); 45 | testHeadingContainer.setAttribute("class", "testHeading"); 46 | let testHeading = document.createElement("span"); 47 | testHeading.setAttribute("class", "testName"); 48 | testHeading.textContent = test.name; 49 | testHeadingContainer.appendChild(testHeading); 50 | let errorCounter = document.createElement("span"); 51 | errorCounter.setAttribute("class", "errorCount"); 52 | errorCounter.textContent = matchesCount; 53 | testHeadingContainer.appendChild(errorCounter); 54 | testContainer.appendChild(testHeadingContainer); 55 | 56 | let errorList = document.createElement("ul"); 57 | errorList.setAttribute("class", "errors"); 58 | testContainer.appendChild(errorList); 59 | 60 | tests.appendChild(testContainer); 61 | 62 | testElem = document.getElementById(id); 63 | 64 | if (prefs.autoExpandErrors && status !== "ok") { 65 | testElem.getElementsByClassName("errors")[0].classList.add("show"); 66 | } 67 | 68 | let fixIssuesButton = document.getElementById("fixIssues"); 69 | fixIssuesButton.classList.add("show"); 70 | } 71 | 72 | let errors = testElem.getElementsByClassName("errors")[0]; 73 | if (status === "ok") { 74 | errors.classList.remove("show"); 75 | } 76 | while (errors.firstChild) { 77 | errors.removeChild(errors.firstChild); 78 | } 79 | test.errors.forEach(function(error) { 80 | let errorContainer = document.createElement("li"); 81 | let errorClass = "error"; 82 | switch (error.type) { 83 | case WARNING: 84 | errorClass = "warning"; 85 | break; 86 | 87 | case INFO: 88 | errorClass = "info"; 89 | break; 90 | } 91 | errorContainer.setAttribute("class", errorClass); 92 | errorContainer.textContent = error.msg; 93 | errors.appendChild(errorContainer); 94 | }); 95 | 96 | totalWarningCount += newWarningCount - oldWarningCount; 97 | totalErrorCount += newErrorCount - oldErrorCount; 98 | 99 | updateErrorSummary(); 100 | }); 101 | 102 | addon.port.on("updateProgress", (progress) => { 103 | let progressBar = document.getElementById("testProgress"); 104 | progressBar.value += progress; 105 | }); 106 | 107 | addon.port.on("hideProgressBar", () => { 108 | let progressBar = document.getElementById("testProgress"); 109 | progressBar.classList.remove("visible"); 110 | }); 111 | 112 | function getParentByClassName(node, className) { 113 | let currentNode = node; 114 | 115 | while (currentNode && (!currentNode.classList || !currentNode.classList.contains(className))) { 116 | currentNode = currentNode.parentNode; 117 | } 118 | 119 | return currentNode; 120 | } 121 | 122 | function updateErrorSummary() { 123 | // Show summary 124 | document.getElementById("summary").style.display = "flex"; 125 | 126 | let totalErrorCounter = document.getElementById("totalErrorCount"); 127 | totalErrorCounter.textContent = totalErrorCount; 128 | if (totalErrorCount === 0) { 129 | totalErrorCounter.classList.remove("hasErrors"); 130 | totalErrorCounter.classList.add("ok"); 131 | } else { 132 | totalErrorCounter.classList.remove("ok"); 133 | totalErrorCounter.classList.add("hasErrors"); 134 | } 135 | 136 | let totalWarningCounter = document.getElementById("totalWarningCount"); 137 | totalWarningCounter.textContent = totalWarningCount; 138 | if (totalWarningCount === 0) { 139 | totalWarningCounter.classList.remove("hasWarnings"); 140 | totalWarningCounter.classList.add("ok"); 141 | } else { 142 | totalWarningCounter.classList.remove("ok"); 143 | totalWarningCounter.classList.add("hasWarnings"); 144 | } 145 | } 146 | 147 | function runTests() { 148 | let progressBar = document.getElementById("testProgress"); 149 | progressBar.value = 0; 150 | progressBar.classList.add("visible"); 151 | addon.port.emit("runTests"); 152 | } 153 | 154 | function fixIssues() { 155 | addon.port.emit("fixIssues"); 156 | } 157 | window.addEventListener("DOMContentLoaded", function loadTestSuite() { 158 | window.removeEventListener("DOMContentLoaded", loadTestSuite); 159 | 160 | document.addEventListener("contextmenu", function blockContextMenu(evt) { 161 | evt.preventDefault(); 162 | }); 163 | 164 | let runTestsButton = document.getElementById("btn-runtests"); 165 | runTestsButton.addEventListener("click", runTests); 166 | 167 | let fixIssuesButton = document.getElementById("fixIssues"); 168 | fixIssuesButton.addEventListener("click", fixIssues); 169 | 170 | let tests = document.getElementById("tests"); 171 | tests.addEventListener("click", (evt) => { 172 | let testHeading = getParentByClassName(evt.originalTarget, "testHeading"); 173 | if (testHeading) { 174 | let testElem = getParentByClassName(testHeading, "test"); 175 | if (!testElem.classList.contains("ok")) { 176 | testElem.getElementsByClassName("errors")[0].classList.toggle("show"); 177 | } 178 | } 179 | }); 180 | }); 181 | -------------------------------------------------------------------------------- /locale/fr.properties: -------------------------------------------------------------------------------- 1 | # Sidebar description 2 | sidebar_description=Ouvrez un document de MDN en édition pour lui faire passer une série de tests : 3 | 4 | # Label for button allowing to run the test suite 5 | run_tests=Lancer les tests 6 | 7 | # Label for the number of errors 8 | errors=Erreurs : 9 | 10 | # Label for the number of warnings 11 | warnings=Avertissements : 12 | 13 | # Name of test checking old en/ URLs 14 | old_en_urls=Adresses anciennes « /en/ » 15 | 16 | # Description of test checking for old en/ URLs 17 | old_en_urls_desc=Les adresses URL qui commencent par /en/ sont obsolètes. Il est préférable d’utiliser /en-US/docs/. 18 | 19 | # Name of test checking whether internal links contain absolute URLs 20 | absolute_urls_for_internal_links= URL absolues pour les liens internes 21 | 22 | # Description of test checking whether internal links contain absolute URLs 23 | absolute_urls_for_internal_links_desc=Les liens internes devraient utiliser une URL relative plutôt qu’une adresse absolue. 24 | 25 | # Name of test checking for empty elements 26 | empty_elements=Éléments vides 27 | 28 | # Description of test checking for empty elements 29 | empty_elements_desc=Des éléments vides tels que

devraient être supprimés. 30 | 31 | # Name of test checking for the {{languages()}} macro 32 | languages_macro=Macro de langages 33 | 34 | # Description of test checking for the {{languages()}} macro 35 | languages_macro_desc=La macro {{languages}} est obsolète et devrait être supprimée. 36 | 37 | # Name of test checking for empty brackets 38 | empty_brackets=Parenthèses vides 39 | 40 | # Description of test checking for empty brackets 41 | empty_brackets_desc=Les macros qui n’ont pas de paramètres comme {{foo()}} n’ont pas besoin de parenthèses de listes de paramètres et devraient donc être supprimées. 42 | 43 | # Name of test checking for ’style’ HTML attributes 44 | style_attributes=Attributs de style 45 | 46 | # Description of test checking for ’style’ HTML attributes 47 | style_attributes_desc=Les éléments HTML ne devraient pas inclure d’attributs « style » mais utiliser plutôt des classes. Ils devraient donc être supprimés quand c’est possible. 48 | 49 | # Name of test checking for ’name’ HTML attributes 50 | name_attributes=Attributs « name » 51 | 52 | # Description of test checking for ’name’ HTML attributes 53 | name_attributes_desc=Les éléments HTML ne devraient pas inclure d’attributs « name » mais plutôt utiliser les ID. Ils doivent donc être supprimés. 54 | 55 | # Name of test checking for elements 56 | span_elements=Éléments 57 | 58 | # Description of test checking for elements 59 | span_elements_desc=L’usage de l’élément devrait être évité, il vaut mieux employer d’autres éléments comme p. ex.

ou . 60 | 61 | # Name of test checking for

 elements without ’class’ attribute
 62 | pre_without_class=
 sans classe
 63 | 
 64 | # Description of test checking for 
 elements without ’class’ attribute
 65 | pre_without_class_desc=Les éléments 
 doivent avoir un attribut ’class’ qui leur est assigné, de manière à faire mieux ressortir la syntaxe.
 66 | 
 67 | # Name of test checking for summary heading
 68 | summary_heading=En-tête avec résumé
 69 | 
 70 | # Description of test checking for summary heading
 71 | summary_heading_desc=Selon le guide stylistique il ne devrait y avoir aucun Résumé en en-tête.
 72 | 
 73 | # Name of test checking for {{JSRef}} macro parameters
 74 | jsref_params=Paramètres de macro JSRef
 75 | 
 76 | # Description of test checking for {{JSRef}} macro parameters
 77 | jsref_params_desc=Les paramètres pour la macro {{JSRef}}, p. ex. {{JSRef(’Global_Objects’, ’Math’)}}, sont obsolètes et devraient donc être supprimés.
 78 | 
 79 | # Name of test checking for ’Example:’ headings
 80 | example_headings=Titres ’Exemple :’ 
 81 | 
 82 | # Description of test checking for ’Example:’ headings
 83 | example_headings_desc=Les titres d’exemples ne devraient pas comprendre « Exemple : ». Ainsi, au lieu de 

Exemple : Foobar

il vaut mieux utiliser

Foobar

. 84 | 85 | # Name of test checking for alert(), print(), eval() and document.write() within code samples 86 | alert_print_in_code=alert, print, eval, document.write 87 | 88 | # Description of test checking for alert(), print(), eval() and document.write() within code samples 89 | alert_print_in_code_desc=N’utilisez pas alert(), print(), eval() ou document.write() dans les exemples de code. 90 | 91 | # Name of test checking for shell prompts within code samples 92 | shell_prompts=Témoin d’invite de commande dans les exemples 93 | 94 | # Description of test checking for shell prompts within code samples 95 | shell_prompts_desc=Les instructions ne doivent pas commencer par « $ » or « > ». Cela ajoute de la confusion et pose problème lorsque les exemples sont copiés. 96 | 97 | # Name of test checking for HTML comments 98 | html_comments=Commentaires en HTML 99 | 100 | # Description of test checking for HTML comments 101 | html_comments_desc=Les commentaires HTML ne sont pas visibles en mode WYSIWYG ni en mode lecture. Ils ne sont pas destinés à commenter la documentation, et devraient donc être supprimés. 102 | 103 | # Name of test checking for elements 104 | font_elements=Éléments 105 | 106 | # Description of test checking for elements 107 | font_elements_desc=L’usage des éléments et obsolète. Soit la balise doit être supprimée complètement soit on peut la remplacer par des CSS. 108 | 109 | # Name of test checking for HTTP links 110 | http_links=Liens HTTP 111 | 112 | # Description of test checking for HTTP links 113 | http_links_desc=Les adresses URL vers des ressources externes devraient utiliser HTTPS et non HTTP, autant que possible. 114 | 115 | # Name of test checking for macro syntax errors 116 | macro_syntax_error=Erreur de syntaxe pour une macro 117 | 118 | # Description of test checking for macro syntax errors 119 | macro_syntax_error_desc=Une macro comprend une erreur de syntaxe, comme une parenthèse non fermée, p.ex. {{jsxref(’Array’}}. 120 | 121 | # Name of test checking for invalid macros 122 | invalid_macros=Macros invalides 123 | 124 | # Description of test checking for invalid macros 125 | invalid_macros_desc=La macro qui est utilisée n’est pas sur liste blanche. 126 | 127 | # Name of test checking for wrong highlighted line within code samples 128 | wrong_highlighted_line=Ligne surlignée à tort 129 | 130 | # Description of test checking for wrong highlighted line within code samples 131 | wrong_highlighted_line_desc=Un bloc de code comprend une ligne surlignée, en-dehors de la zone des lignes de code. 132 | 133 | # Name of test checking the order and names of API syntax headlines 134 | api_syntax_headlines=Syntaxe des sous-titres pour les API 135 | 136 | # Description of test checking the order and names of API syntax headlines 137 | api_syntax_headlines_desc=La syntaxe de sous-titres pour les API doit être, dans cet ordre ’Paramètres’, ’Valeur retournée’ et ’Exceptions’, et non pas ’Retours’, ’Erreurs’ ou ’Erreurs retournées’. 138 | 139 | # Name of test checking whether there are tags within
 blocks
140 | code_in_pre= dans 
141 | 
142 | # Description of test checking whether there are  tags within 
 blocks
143 | code_in_pre_desc=Les balises de  ne devraient pas être utilisées dans des blocs 
, parce qu’elles cassent la syntaxe du surlignement.
144 | 
145 | # Name of test checking whether there are too long lines within 
 blocks
146 | pre_line_too_long=Longueur de ligne trop grande en 
147 | 
148 | # Description of test checking whether there are too long lines within 
 blocks
149 | pre_line_too_long_desc=Pour éviter les barres de défilement horizontales, le nombre maximum de caractères par ligne devrait être de 77 dans les blocs en 
.
150 | 
151 | # Name of test checking whether the CSS class for the syntax definition block is correct
152 | wrong_syntax_class=Mauvaise syntaxe pour une classe
153 | 
154 | # Description of test checking whether the CSS class for the syntax definition block is correct
155 | wrong_syntax_class_desc=La classe CSS utilisée pour le bloc de définition de la syntaxe doit être ’syntaxbox’.
156 | 
157 | # Name of test checking whether there is a URL within the 'title' attribute of a link
158 | url_in_link_title=URL présente dans l’attribut « title » d’un lien
159 | 
160 | # Description of test checking whether there is a URL within the 'title' attribute of a link
161 | url_in_link_title_desc=L’URL du lien est déjà affichée au survol. Répéter l’adresse avec l’attribut « title » est donc superflu et devrait être évité.
162 | 
163 | # Error message for macro missing closing curly brace
164 | missing_closing_curly_brace=Il manque à la macro une accolade fermante : %s
165 | 
166 | # Error message for macro missing a closing bracket
167 | missing_closing_bracket=Il manque à la liste des paramètres une parenthèse fermante : %s
168 | 
169 | # Error message for macro string parameter not being quoted correctly
170 | string_parameter_incorrectly_quoted=La chaîne de paramètres n’est pas citée correctement : %s
171 | 
172 | # Error message for macro having additional closing brackets
173 | additional_closing_bracket=La macro a une ou plusieurs parenthèses fermantes supplémentaires : %s
174 | 
175 | # Error message for highlighted line number being zero or negative
176 | highlighted_line_number_not_positive=Le numéro de ligne surligné %s doit être une nombre positif : %s
177 | 
178 | # Error message for highlighted line number exceeding the line count of the code sample
179 | highlighted_line_number_too_big=Le numéro de ligne surligné %s est supérieur au compte de lignes de %s: %s
180 | 
181 | # Error message for highlighted line number range being invalid (start of range bigger than end of range)
182 | invalid_highlighted_range=Le numéro de fin de ligne surlignée est inférieur %s au numéro de début %s: %s
183 | 
184 | # Error message for invalid headline name
185 | invalid_headline_name=Nom invalide : ’%s’
186 | 
187 | # Error message for invalid headline order
188 | invalid_headline_order=Ordre invalide
189 | 
190 | # Error message for invalid CSS class for the syntax definition block
191 | wrong_syntax_class_used=La classe CSS ’%s’ utilisée pour le bloc de définition de syntaxe est erroné. Elle devrait être ’syntaxbox’.
192 | 
193 | # Title of ’autoExpandErrors’ property (shown in Add-ons Manager)
194 | autoExpandErrors_title=Développe automatiquement les erreurs
195 | 
196 | # Description of ’autoExpandErrors’ property (shown in Add-ons Manager)
197 | autoExpandErrors_description=Les erreurs sont développées automatiquement et les avertissements sont envoyés par les tests quand on les lance.
198 | 


--------------------------------------------------------------------------------
/locale/en-US.properties:
--------------------------------------------------------------------------------
  1 | # Sidebar description
  2 | sidebar_description=Edit an MDN document and test it against the test suite:
  3 | 
  4 | # Label for button allowing to run the test suite
  5 | run_tests=Run tests
  6 | 
  7 | # Label for the number of errors
  8 | errors=Errors:
  9 | 
 10 | # Label for the number of warnings
 11 | warnings=Warnings:
 12 | 
 13 | # Label for button allowing to fix the matched errors and warnings
 14 | fix_issues=Fix issues
 15 | 
 16 | # Name of test checking old en/ URLs
 17 | old_en_urls=Old '/en/' URLs
 18 | 
 19 | # Description of test checking for old en/ URLs
 20 | old_en_urls_desc=URLs starting with /en/ are obsolete. /en-US/docs/ should be used instead.
 21 | 
 22 | # Name of test checking whether internal links contain absolute URLs
 23 | absolute_urls_for_internal_links=Absolute URLs for internal links
 24 | 
 25 | # Description of test checking whether internal links contain absolute URLs
 26 | absolute_urls_for_internal_links_desc=Internal links should use a relative URL path instead of an absolute one.
 27 | 
 28 | # Name of test checking whether there are any links to different locales
 29 | different_locale_links=Different locale links
 30 | 
 31 | # Description of test checking whether internal links contain absolute URLs
 32 | different_locale_links_desc=There should not be any links pointing to other locales.
 33 | 
 34 | # Name of test checking for empty elements
 35 | empty_elements=Empty elements
 36 | 
 37 | # Description of test checking for empty elements
 38 | empty_elements_desc=Empty elements like e.g. 

should be removed. 39 | 40 | # Name of test checking for empty brackets 41 | empty_brackets=Empty brackets 42 | 43 | # Description of test checking for empty brackets 44 | empty_brackets_desc=Macros that don't have parameters like {{foo()}} don't need parameter list brackets. Therefore they should be removed. 45 | 46 | # Name of test checking for 'style' HTML attributes 47 | style_attributes=Style attributes 48 | 49 | # Description of test checking for 'style' HTML attributes 50 | style_attributes_desc=HTML elements should not contain 'style' attributes for styling but use classes instead. So, they should be removed when possible. 51 | 52 | # Name of test checking for 'name' HTML attributes 53 | name_attributes=Name attributes 54 | 55 | # Description of test checking for 'name' HTML attributes 56 | name_attributes_desc=HTML elements should not contain 'name' attributes but use ids instead. So, they should be removed. 57 | 58 | # Name of test checking for elements 59 | span_elements= elements 60 | 61 | # Description of test checking for elements 62 | span_elements_desc=Using elements should be avoided and other elements be used instead, e.g.

or . 63 | 64 | # Name of test checking for

 elements without 'class' attribute
 65 | pre_without_class=
 w/o class
 66 | 
 67 | # Description of test checking for 
 elements without 'class' attribute
 68 | pre_without_class_desc=
 elements must have a 'class' attribute assigned in most cases, so their syntax is highlighted.
 69 | 
 70 | # Name of test checking for summary heading
 71 | summary_heading=Summary heading
 72 | 
 73 | # Description of test checking for summary heading
 74 | summary_heading_desc=According to the article style guide there shouldn't be a Summary heading.
 75 | 
 76 | # Name of test checking for 'Example:' headings
 77 | example_headings='Example:' headings
 78 | 
 79 | # Description of test checking for 'Example:' headings
 80 | example_headings_desc=Example headings should not include 'Example: '. E.g. instead of 

Example: Foobar

just use

Foobar

. 81 | 82 | # Name of test checking for alert(), print(), eval() and document.write() within code samples 83 | alert_print_in_code=alert, print, eval, document.write 84 | 85 | # Description of test checking for alert(), print(), eval() and document.write() within code samples 86 | alert_print_in_code_desc=Don't use alert(), print(), eval() or document.write() in code samples. 87 | 88 | # Name of test checking for shell prompts within code samples 89 | shell_prompts=Shell prompts in code samples 90 | 91 | # Description of test checking for shell prompts within code samples 92 | shell_prompts_desc=Don't start shell instructions with '$' or '>'. It confuses more than it helps and causes problems when copying them. 93 | 94 | # Name of test checking for HTML comments 95 | html_comments=HTML comments 96 | 97 | # Description of test checking for HTML comments 98 | html_comments_desc=HTML comments are not visible in WYSIWYG mode and in reading mode. They are not meant to comment the documentation, so they should be removed. 99 | 100 | # Name of test checking for elements 101 | font_elements= elements 102 | 103 | # Description of test checking for elements 104 | font_elements_desc=Using elements is obsolete. Either the tag should be removed completely or replaced by CSS. 105 | 106 | # Name of test checking for HTTP links 107 | http_links=HTTP links 108 | 109 | # Description of test checking for HTTP links 110 | http_links_desc=URLs to external resources should use HTTPS instead of HTTP, when possible. 111 | 112 | # Name of test checking the notes for data macros linking to the data source 113 | data_macro_note=Data macro note 114 | 115 | # Description of test checking the notes for data macros linking to the data source 116 | data_macro_note_desc=Data macros need a note above them linking to the data source. 117 | 118 | # Name of test checking for macros with unnecessary parameters 119 | unnecessary_macro_params=Unnecessary macro parameters 120 | 121 | # Description of test checking for macros with unnecessary parameters 122 | unnecessary_macro_params_desc=The used macro does not require any parameters, so they should be removed. 123 | 124 | # Name of test checking for macro syntax errors 125 | macro_syntax_error=Macro syntax error 126 | 127 | # Description of test checking for macro syntax errors 128 | macro_syntax_error_desc=A macro has a syntax error like a missing closing bracket, e.g. {{jsxref('Array'}}. 129 | 130 | # Name of test checking for invalid macros 131 | invalid_macros=Invalid macros 132 | 133 | # Description of test checking for invalid macros 134 | invalid_macros_desc=The used macro is not allowed or does not exist 135 | 136 | # Name of test checking incorrectly wrapped sidebar macros 137 | incorrectly_wrapped_sidebar_macros=Incorrectly wrapped sidebar macros 138 | 139 | # Description of test checking incorrectly wrapped sidebar macros 140 | incorrectly_wrapped_sidebar_macros_desc=Sidebar macros must be wrapped into
elements 141 | 142 | # Name of test checking for wrong highlighted line within code samples 143 | wrong_highlighted_line=Wrong highlighted line 144 | 145 | # Description of test checking for wrong highlighted line within code samples 146 | wrong_highlighted_line_desc=A code block has a line highlighted that is outside the range of code lines. 147 | 148 | # Name of test checking the order and names of API syntax headlines 149 | api_syntax_headlines=API syntax headlines 150 | 151 | # Description of test checking the order and names of API syntax headlines 152 | api_syntax_headlines_desc=API syntax headlines must be 'Parameters', 'Return value' and 'Exceptions', in that order, not 'Returns', 'Errors' or 'Errors thrown'. 153 | 154 | # Name of test checking whether there are tags within
 blocks
155 | code_in_pre= in 
156 | 
157 | # Description of test checking whether there are  tags within 
 blocks
158 | code_in_pre_desc= tags should not be used within 
 blocks, because they break syntax highlighting.
159 | 
160 | # Name of test checking whether there are too long lines within 
 blocks
161 | pre_line_too_long=Line length in 
 too long
162 | 
163 | # Description of test checking whether there are too long lines within 
 blocks
164 | pre_line_too_long_desc=To avoid horizontal scrollbars, there should be maximal 77 characters per line in 
 blocks.
165 | 
166 | # Name of test checking whether the CSS class for the syntax definition block is correct
167 | wrong_syntax_class=Wrong syntax class
168 | 
169 | # Description of test checking whether the CSS class for the syntax definition block is correct
170 | wrong_syntax_class_desc=The CSS class used for the syntax definition block must be 'syntaxbox'.
171 | 
172 | # Name of test checking whether there is a URL within the 'title' attribute of a link
173 | url_in_link_title=URL in 'title' attribute
174 | 
175 | # Description of test checking whether there is a URL within the 'title' attribute of a link
176 | url_in_link_title_desc=The URL of a link is already shown on hover, so repeating it within its 'title' attribute is redundant and should be avoided.
177 | 
178 | # Name of test checking the length of the article
179 | article_length=Article length
180 | 
181 | # Description of test checking the length of the article
182 | article_length_desc=Hints regarding the length of the article.
183 | 
184 | # Error message for link using wrong locale
185 | link_using_wrong_locale=Link %s uses wrong locale. Expected '%s'.
186 | 
187 | # Error message for data macro missing a note above it linking to the data source
188 | data_macro_note_missing=The data macro %s is missing a note linking to the data source. The note needs to be hidden using 'class="hidden"'.
189 | 
190 | # Error message for data macro note missing a link referring to the data source
191 | data_macro_source_link_missing=The note for the data macro %s is missing a link to the data source.
192 | 
193 | # Error message for macro with unnecessary parameters equalling slug
194 | macro_with_unnecessary_params_equalling_slug=The parameters of the %s macro are unnecessary, because they will be fetched from the slug, so they should be removed.
195 | 
196 | # Error message for macro with unused parameters
197 | macro_with_unused_params=The parameters of the %s macro are unused, so they should be removed.
198 | 
199 | # Error message for macro missing closing curly brace
200 | missing_closing_curly_brace=Macro is missing a closing curly brace: %s
201 | 
202 | # Error message for macro missing a closing bracket
203 | missing_closing_bracket=Parameters list is missing closing bracket: %s
204 | 
205 | # Error message for macro string parameter not being quoted correctly
206 | string_parameter_incorrectly_quoted=String parameter is not quoted correctly: %s
207 | 
208 | # Error message for macro having additional closing brackets
209 | additional_closing_bracket=Macro has additional closing bracket(s): %s
210 | 
211 | # Error message for sidebar macro wrapped into wrong element
212 | wrong_element_wrapping_sidebar_macro=Sidebar macro %s is wrapped into <%s> element
213 | 
214 | # Error message for obsolete macro
215 | obsolete_macro=The %s macro is obsolete and should be removed.
216 | 
217 | # Error message for highlighted line number being zero or negative
218 | highlighted_line_number_not_positive=Highlighted line number %s must be positive: %s
219 | 
220 | # Error message for highlighted line number exceeding the line count of the code sample
221 | highlighted_line_number_too_big=Highlighted line number %s exceeds the line count of %s: %s
222 | 
223 | # Error message for highlighted line number range being invalid (start of range bigger than end of range)
224 | invalid_highlighted_range=End of highlighted line range %s bigger than its start %s: %s
225 | 
226 | # Error message for invalid headline name
227 | invalid_headline_name=Invalid name '%s'
228 | 
229 | # Error message for invalid headline order
230 | invalid_headline_order=Invalid order
231 | 
232 | # Error message for invalid CSS class for the syntax definition block
233 | wrong_syntax_class_used=The CSS class '%s' used for the syntax definition block is wrong. It must be 'syntaxbox'.
234 | 
235 | # Info message for article length and reading time estimation
236 | article_length_info=The article has %s words. Estimated read time: %s minute(s).
237 | 
238 | # Error message for long article
239 | long_article=The article is very long. Consider to split it up into smaller articles.
240 | 
241 | # Title of 'autoExpandErrors' property (shown in Add-ons Manager)
242 | autoExpandErrors_title=Automatically expand errors
243 | 
244 | # Description of 'autoExpandErrors' property (shown in Add-ons Manager)
245 | autoExpandErrors_description=Immediately display errors and warnings returned by a test when running the test suite.
246 | 


--------------------------------------------------------------------------------
/locale/de.properties:
--------------------------------------------------------------------------------
  1 | # Sidebar description
  2 | sidebar_description=Bearbeite ein MDN-Dokument und teste es mit Hilfe der Testsuite:
  3 | 
  4 | # Label for button allowing to run the test suite
  5 | run_tests=Tests durchführen
  6 | 
  7 | # Label for the number of errors
  8 | errors=Fehler:
  9 | 
 10 | # Label for the number of warnings
 11 | warnings=Warnungen:
 12 | 
 13 | # Label for button allowing to fix the matched errors and warnings
 14 | fix_issues=Probleme beheben
 15 | 
 16 | # Name of test checking old en/ URLs
 17 | old_en_urls=Alte '/en/' URLs
 18 | 
 19 | # Description of test checking for old en/ URLs
 20 | old_en_urls_desc=URLs, die mit /en/ beginnen, sind veraltet. Stattdessen sollte /en-US/docs/ verwendet werden.
 21 | 
 22 | # Name of test checking whether internal links contain absolute URLs
 23 | absolute_urls_for_internal_links=Absolute URLs für interne Links
 24 | 
 25 | # Description of test checking whether internal links contain absolute URLs
 26 | absolute_urls_for_internal_links_desc=Interne Links sollte einen relativen URL-Pfad anstatt eines absoluten verwenden.
 27 | 
 28 | # Name of test checking whether there are any links to different locales
 29 | different_locale_links=Links zu anderen Übersetzungen
 30 | 
 31 | # Description of test checking whether internal links contain absolute URLs
 32 | different_locale_links_desc=Es sollte keine Links zu anderen Übersetzugen geben.
 33 | 
 34 | # Name of test checking for empty elements
 35 | empty_elements=Leere Elemente
 36 | 
 37 | # Description of test checking for empty elements
 38 | empty_elements_desc=Leere Elemente wie z. B. 

sollten entfernt werden. 39 | 40 | # Name of test checking for empty brackets 41 | empty_brackets=Leere Klammern 42 | 43 | # Description of test checking for empty brackets 44 | empty_brackets_desc=Makros, die keine Parameter haben, wie {{foo()}}, benötigen keine Parameterlistenklammern. Daher sollten sie entfernt werden. 45 | 46 | # Name of test checking for 'style' HTML attributes 47 | style_attributes=Style Attribute 48 | 49 | # Description of test checking for 'style' HTML attributes 50 | style_attributes_desc=HTML Elemente sollten keine 'style' Attribute für das Styling verwenden, sondern Klassen. Daher sollten sie, wenn möglich, entfernt werden. 51 | 52 | # Name of test checking for 'name' HTML attributes 53 | name_attributes=Name Attribute 54 | 55 | # Description of test checking for 'name' HTML attributes 56 | name_attributes_desc=HTML Elemente sollten keine 'name' Attribute verwenden, sondern IDs. Daher sollten sie entfernt werden. 57 | 58 | # Name of test checking for elements 59 | span_elements= Elemente 60 | 61 | # Description of test checking for elements 62 | span_elements_desc=Die Verwendung von Elementen sollte vermieden und stattdessen andere Elemente verwendet werden, z. B.

oder . 63 | 64 | # Name of test checking for

 elements without 'class' attribute
 65 | pre_without_class=
 ohne Klasse
 66 | 
 67 | # Description of test checking for 
 elements without 'class' attribute
 68 | pre_without_class_desc=
 Elemente müssen in den meisten Fällen ein 'class' Attribut haben, sodass deren Syntax hervorgehoben wird.
 69 | 
 70 | # Name of test checking for summary heading
 71 | summary_heading='Summary' Überschrift
 72 | 
 73 | # Description of test checking for summary heading
 74 | summary_heading_desc=Laut dem Artikelstyleguide sollte keine Summary Überschrift verwendet werden.
 75 | 
 76 | # Name of test checking for 'Example:' headings
 77 | example_headings='Example:' Überschriften
 78 | 
 79 | # Description of test checking for 'Example:' headings
 80 | example_headings_desc=Beispiel Überschriften sollten nicht 'Example: ' beinhalten. Z. B. sollte anstatt 

Example: Foobar

einfach

Foobar

verwendet werden. 81 | 82 | # Name of test checking for alert(), print(), eval() and document.write() within code samples 83 | alert_print_in_code=alert, print, eval, document.write 84 | 85 | # Description of test checking for alert(), print(), eval() and document.write() within code samples 86 | alert_print_in_code_desc=alert(), print(), eval() oder document.write() sollten in Codebeispielen nicht verwendet werden. 87 | 88 | # Name of test checking for shell prompts within code samples 89 | shell_prompts=Shell Eingaben in Codebeispielen 90 | 91 | # Description of test checking for shell prompts within code samples 92 | shell_prompts_desc=Shell Befehle sollten nicht mit '$' oder '>' beginnen. Dies verwirrt mehr als dass es hilft und verursacht Probleme wenn sie kopiert werden. 93 | 94 | # Name of test checking for HTML comments 95 | html_comments=HTML-Kommentare 96 | 97 | # Description of test checking for HTML comments 98 | html_comments_desc=HTML-Kommentare sind im WYSIWYG-Modus und im Lesemodus nicht sichtbar. Sie sind nicht dazu gedacht, die Dokumentation zu kommentieren und sollten daher entfernt werden. 99 | 100 | # Name of test checking for elements 101 | font_elements= Elemente 102 | 103 | # Description of test checking for elements 104 | font_elements_desc=Die Verwendung von Elementen ist veraltet. Entweder sollte der Tag komplett entfernt oder durch CSS ersetzt werden. 105 | 106 | # Name of test checking for HTTP links 107 | http_links=HTTP Links 108 | 109 | # Description of test checking for HTTP links 110 | http_links_desc=URLs zu externen Ressourcen sollten, wenn möglich, HTTPS anstatt HTTP verwenden. 111 | 112 | # Name of test checking the notes for data macros linking to the data source 113 | data_macro_note=Datenmakro Hinweis 114 | 115 | # Description of test checking the notes for data macros linking to the data source 116 | data_macro_note_desc=Oberhalb von Datenmakros muss ein versteckter Hinweistext stehen, der einen Link zur Datenquelle enthält. 117 | 118 | # Name of test checking for macros with unnecessary parameters 119 | unnecessary_macro_params=Unnötige Makroparameter 120 | 121 | # Description of test checking for macros with unnecessary parameters 122 | unnecessary_macro_params_desc=Das verwendete Makro benötigt keine Parameter. Daher sollten sie entfernt werden. 123 | 124 | # Name of test checking for invalid macros 125 | invalid_macros=Ungültige Makros 126 | 127 | # Description of test checking for invalid macros 128 | invalid_macros_desc=Das verwendete Makro ist nicht erlaubt oder existiert nicht 129 | 130 | # Name of test checking incorrectly wrapped sidebar macros 131 | incorrectly_wrapped_sidebar_macros=Inkorrekt gepackte Sidebar-Makros 132 | 133 | # Description of test checking incorrectly wrapped sidebar macros 134 | incorrectly_wrapped_sidebar_macros_desc=Sidebar-Makros müssen in
Elemente gepackt werden 135 | 136 | # Name of test checking for macro syntax errors 137 | macro_syntax_error=Makrosyntaxfehler 138 | 139 | # Description of test checking for macro syntax errors 140 | macro_syntax_error_desc=Ein Makro hat einen Syntaxfehler wie eine fehlende schließende Klammer, z. B. {{jsxref('Array'}}. 141 | 142 | # Name of test checking for wrong highlighted line within code samples 143 | wrong_highlighted_line=Falsche Zeile hervorgehoben 144 | 145 | # Description of test checking for wrong highlighted line within code samples 146 | wrong_highlighted_line_desc=In einem Codeblock ist eine Zeile hervorgehoben, die sich außerhalb des Bereichs der Codezeilen befindet. 147 | 148 | # Name of test checking the order and names of API syntax headlines 149 | api_syntax_headlines=API Syntaxüberschriften 150 | 151 | # Description of test checking the order and names of API syntax headlines 152 | api_syntax_headlines_desc=API Syntaxüberschriften müssen 'Parameters', 'Return value' und 'Exceptions' sein, in dieser Reihenfolge, nicht 'Returns', 'Errors' oder 'Errors thrown'. 153 | 154 | # Name of test checking whether there are tags within
 blocks
155 | code_in_pre= in 
156 | 
157 | # Description of test checking whether there are  tags within 
 blocks
158 | code_in_pre_desc= Tags sollten nicht innerhalb von 
 Blöcken verwendet werden, da sie die Syntaxhervorhebung stören.
159 | 
160 | # Name of test checking whether there are too long lines within 
 blocks
161 | pre_line_too_long=Zeilenlänge in 
 zu lang
162 | 
163 | # Description of test checking whether there are too long lines within 
 blocks
164 | pre_line_too_long_desc=Um horizontale Scrollleisten zu verhindern, sollten Zeilen in 
 Blöcken maximal 77 Zeichen beinhalten.
165 | 
166 | # Name of test checking whether the CSS class for the syntax definition block is correct
167 | wrong_syntax_class=Falsche Syntax Klasse
168 | 
169 | # Description of test checking whether the CSS class for the syntax definition block is correct
170 | wrong_syntax_class_desc=Die für den Syntaxdefinitionsblock verwendete CSS-Klasse muss 'syntaxbox' sein.
171 | 
172 | # Name of test checking whether there is a URL within the 'title' attribute of a link
173 | url_in_link_title=URL in 'title' Attribut
174 | 
175 | # Description of test checking whether there is a URL within the 'title' attribute of a link
176 | url_in_link_title_desc=Der URL eines Links wird bereits beim Darüberfahren mit dem Mauszeiger angezeigt, dadurch ist die Wiederholung innerhalb des 'title' Attributs überflüssig und sollte vermieden werden.
177 | 
178 | # Name of test checking the length of the article
179 | article_length=Artikellänge
180 | 
181 | # Description of test checking the length of the article
182 | article_length_desc=Hinweise zur Länge des Artikels
183 | 
184 | # Error message for link using wrong locale
185 | link_using_wrong_locale=Link %s verwendet falsche Übersetzung. '%s' erwartet.
186 | 
187 | # Error message for data macro missing a note above it linking to the data source
188 | data_macro_note_missing=Dem Datenmakro %s fehlt ein Hinweistext, der zur Datenquelle verweist. Der Hinweis muss durch 'class="hidden"' versteckt sein.
189 | 
190 | # Error message for data macro note missing a link referring to the data source
191 | data_macro_source_link_missing=Dem Hinweistext für das Datenmakro %s fehlt ein Link zur Datenquelle.
192 | 
193 | # Error message for macro with unnecessary parameters equalling slug
194 | macro_with_unnecessary_params_equalling_slug=Die Parameter des %s Makros sind unnötig, da sie aus dem Slug gelesen werden. Daher sollten sie entfernt werden.
195 | 
196 | # Error message for macro with unused parameters
197 | macro_with_unused_params=Die Parameter des %s Makros werden nicht verwendet. Daher sollten sie entfernt werden.
198 | 
199 | # Error message for macro missing closing curly brace
200 | missing_closing_curly_brace=Makro fehlt schließende geschweifte Klammer: %s
201 | 
202 | # Error message for macro missing a closing bracket
203 | missing_closing_bracket=Parameterliste fehlt schließende Klammer: %s
204 | 
205 | # Error message for macro string parameter not being quoted correctly
206 | string_parameter_incorrectly_quoted=Anführungszeichen für Stringparameter sind nicht korrekt: %s
207 | 
208 | # Error message for macro having additional closing brackets
209 | additional_closing_bracket=Makro hat zusätzliche schließende Klammer(n): %s
210 | 
211 | # Error message for sidebar macro wrapped into wrong element
212 | wrong_element_wrapping_sidebar_macro=Sidebar-Makro %s ist in <%s> Element gepackt
213 | 
214 | # Error message for obsolete macro
215 | obsolete_macro=Das %s Makro ist veraltet und sollte entfernt werden.
216 | 
217 | # Error message for highlighted line number being zero or negative
218 | highlighted_line_number_not_positive=Hervorgehobene Zeilennummer %s muss positiv sein: %s
219 | 
220 | # Error message for highlighted line number exceeding the line count of the code sample
221 | highlighted_line_number_too_big=Hervorgehobene Zeilennummer %s übersteigt die Zeilenanzahl von %s: %s
222 | 
223 | # Error message for highlighted line number range being invalid (start of range bigger than end of range)
224 | invalid_highlighted_range=Ende des Bereichs an hervorgehobenen Zeilen %s ist größer als sein Start %s: %s
225 | 
226 | # Error message for invalid headline name
227 | invalid_headline_name=Ungültiger Name '%s'
228 | 
229 | # Error message for invalid headline order
230 | invalid_headline_order=Ungültige Reihenfolge
231 | 
232 | # Error message for invalid CSS class for the syntax definition block
233 | wrong_syntax_class_used=Die für den Syntaxdefinitionsblock verwendete CSS-Klasse '%s' ist falsch. Sie muss 'syntaxbox' sein.
234 | 
235 | # Info message for article length and reading time estimation
236 | article_length_info=Der Artikel hat %s Wörter. Geschätzte Lesezeit: %s Minute(n).
237 | 
238 | # Error message for long article
239 | long_article=Der Artikel ist sehr lang. Er sollte in kleinere Artikel aufgeteilt werden.
240 | 
241 | # Title of 'autoExpandErrors' property (shown in Add-ons Manager)
242 | autoExpandErrors_title=Fehler automatisch erweitern
243 | 
244 | # Description of 'autoExpandErrors' property (shown in Add-ons Manager)
245 | autoExpandErrors_description=Von Tests zurückgelieferte Fehler und Warnungen sofort anzeigen, wenn die Testsuite ausgeführt wird.


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
  1 | Mozilla Public License Version 2.0
  2 | ==================================
  3 | 
  4 | 1. Definitions
  5 | --------------
  6 | 
  7 | 1.1. "Contributor"
  8 |     means each individual or legal entity that creates, contributes to
  9 |     the creation of, or owns Covered Software.
 10 | 
 11 | 1.2. "Contributor Version"
 12 |     means the combination of the Contributions of others (if any) used
 13 |     by a Contributor and that particular Contributor's Contribution.
 14 | 
 15 | 1.3. "Contribution"
 16 |     means Covered Software of a particular Contributor.
 17 | 
 18 | 1.4. "Covered Software"
 19 |     means Source Code Form to which the initial Contributor has attached
 20 |     the notice in Exhibit A, the Executable Form of such Source Code
 21 |     Form, and Modifications of such Source Code Form, in each case
 22 |     including portions thereof.
 23 | 
 24 | 1.5. "Incompatible With Secondary Licenses"
 25 |     means
 26 | 
 27 |     (a) that the initial Contributor has attached the notice described
 28 |         in Exhibit B to the Covered Software; or
 29 | 
 30 |     (b) that the Covered Software was made available under the terms of
 31 |         version 1.1 or earlier of the License, but not also under the
 32 |         terms of a Secondary License.
 33 | 
 34 | 1.6. "Executable Form"
 35 |     means any form of the work other than Source Code Form.
 36 | 
 37 | 1.7. "Larger Work"
 38 |     means a work that combines Covered Software with other material, in
 39 |     a separate file or files, that is not Covered Software.
 40 | 
 41 | 1.8. "License"
 42 |     means this document.
 43 | 
 44 | 1.9. "Licensable"
 45 |     means having the right to grant, to the maximum extent possible,
 46 |     whether at the time of the initial grant or subsequently, any and
 47 |     all of the rights conveyed by this License.
 48 | 
 49 | 1.10. "Modifications"
 50 |     means any of the following:
 51 | 
 52 |     (a) any file in Source Code Form that results from an addition to,
 53 |         deletion from, or modification of the contents of Covered
 54 |         Software; or
 55 | 
 56 |     (b) any new file in Source Code Form that contains any Covered
 57 |         Software.
 58 | 
 59 | 1.11. "Patent Claims" of a Contributor
 60 |     means any patent claim(s), including without limitation, method,
 61 |     process, and apparatus claims, in any patent Licensable by such
 62 |     Contributor that would be infringed, but for the grant of the
 63 |     License, by the making, using, selling, offering for sale, having
 64 |     made, import, or transfer of either its Contributions or its
 65 |     Contributor Version.
 66 | 
 67 | 1.12. "Secondary License"
 68 |     means either the GNU General Public License, Version 2.0, the GNU
 69 |     Lesser General Public License, Version 2.1, the GNU Affero General
 70 |     Public License, Version 3.0, or any later versions of those
 71 |     licenses.
 72 | 
 73 | 1.13. "Source Code Form"
 74 |     means the form of the work preferred for making modifications.
 75 | 
 76 | 1.14. "You" (or "Your")
 77 |     means an individual or a legal entity exercising rights under this
 78 |     License. For legal entities, "You" includes any entity that
 79 |     controls, is controlled by, or is under common control with You. For
 80 |     purposes of this definition, "control" means (a) the power, direct
 81 |     or indirect, to cause the direction or management of such entity,
 82 |     whether by contract or otherwise, or (b) ownership of more than
 83 |     fifty percent (50%) of the outstanding shares or beneficial
 84 |     ownership of such entity.
 85 | 
 86 | 2. License Grants and Conditions
 87 | --------------------------------
 88 | 
 89 | 2.1. Grants
 90 | 
 91 | Each Contributor hereby grants You a world-wide, royalty-free,
 92 | non-exclusive license:
 93 | 
 94 | (a) under intellectual property rights (other than patent or trademark)
 95 |     Licensable by such Contributor to use, reproduce, make available,
 96 |     modify, display, perform, distribute, and otherwise exploit its
 97 |     Contributions, either on an unmodified basis, with Modifications, or
 98 |     as part of a Larger Work; and
 99 | 
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 |     for sale, have made, import, and otherwise transfer either its
102 |     Contributions or its Contributor Version.
103 | 
104 | 2.2. Effective Date
105 | 
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 | 
110 | 2.3. Limitations on Grant Scope
111 | 
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 | 
118 | (a) for any code that a Contributor has removed from Covered Software;
119 |     or
120 | 
121 | (b) for infringements caused by: (i) Your and any other third party's
122 |     modifications of Covered Software, or (ii) the combination of its
123 |     Contributions with other software (except as part of its Contributor
124 |     Version); or
125 | 
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 |     its Contributions.
128 | 
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 | 
133 | 2.4. Subsequent Licenses
134 | 
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 | 
140 | 2.5. Representation
141 | 
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 | 
146 | 2.6. Fair Use
147 | 
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 | 
152 | 2.7. Conditions
153 | 
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 | 
157 | 3. Responsibilities
158 | -------------------
159 | 
160 | 3.1. Distribution of Source Form
161 | 
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 | 
170 | 3.2. Distribution of Executable Form
171 | 
172 | If You distribute Covered Software in Executable Form then:
173 | 
174 | (a) such Covered Software must also be made available in Source Code
175 |     Form, as described in Section 3.1, and You must inform recipients of
176 |     the Executable Form how they can obtain a copy of such Source Code
177 |     Form by reasonable means in a timely manner, at a charge no more
178 |     than the cost of distribution to the recipient; and
179 | 
180 | (b) You may distribute such Executable Form under the terms of this
181 |     License, or sublicense it under different terms, provided that the
182 |     license for the Executable Form does not attempt to limit or alter
183 |     the recipients' rights in the Source Code Form under this License.
184 | 
185 | 3.3. Distribution of a Larger Work
186 | 
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 | 
198 | 3.4. Notices
199 | 
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 | 
206 | 3.5. Application of Additional Terms
207 | 
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 | 
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 | 
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 | 
232 | 5. Termination
233 | --------------
234 | 
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 | 
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 | 
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 | 
261 | ************************************************************************
262 | *                                                                      *
263 | *  6. Disclaimer of Warranty                                           *
264 | *  -------------------------                                           *
265 | *                                                                      *
266 | *  Covered Software is provided under this License on an "as is"       *
267 | *  basis, without warranty of any kind, either expressed, implied, or  *
268 | *  statutory, including, without limitation, warranties that the       *
269 | *  Covered Software is free of defects, merchantable, fit for a        *
270 | *  particular purpose or non-infringing. The entire risk as to the     *
271 | *  quality and performance of the Covered Software is with You.        *
272 | *  Should any Covered Software prove defective in any respect, You     *
273 | *  (not any Contributor) assume the cost of any necessary servicing,   *
274 | *  repair, or correction. This disclaimer of warranty constitutes an   *
275 | *  essential part of this License. No use of any Covered Software is   *
276 | *  authorized under this License except under this disclaimer.         *
277 | *                                                                      *
278 | ************************************************************************
279 | 
280 | ************************************************************************
281 | *                                                                      *
282 | *  7. Limitation of Liability                                          *
283 | *  --------------------------                                          *
284 | *                                                                      *
285 | *  Under no circumstances and under no legal theory, whether tort      *
286 | *  (including negligence), contract, or otherwise, shall any           *
287 | *  Contributor, or anyone who distributes Covered Software as          *
288 | *  permitted above, be liable to You for any direct, indirect,         *
289 | *  special, incidental, or consequential damages of any character      *
290 | *  including, without limitation, damages for lost profits, loss of    *
291 | *  goodwill, work stoppage, computer failure or malfunction, or any    *
292 | *  and all other commercial damages or losses, even if such party      *
293 | *  shall have been informed of the possibility of such damages. This   *
294 | *  limitation of liability shall not apply to liability for death or   *
295 | *  personal injury resulting from such party's negligence to the       *
296 | *  extent applicable law prohibits such limitation. Some               *
297 | *  jurisdictions do not allow the exclusion or limitation of           *
298 | *  incidental or consequential damages, so this exclusion and          *
299 | *  limitation may not apply to You.                                    *
300 | *                                                                      *
301 | ************************************************************************
302 | 
303 | 8. Litigation
304 | -------------
305 | 
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 | 
313 | 9. Miscellaneous
314 | ----------------
315 | 
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 | 
323 | 10. Versions of the License
324 | ---------------------------
325 | 
326 | 10.1. New Versions
327 | 
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 | 
333 | 10.2. Effect of New Versions
334 | 
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 | 
340 | 10.3. Modified Versions
341 | 
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 | 
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 | 
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 | 
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 | 
358 |   This Source Code Form is subject to the terms of the Mozilla Public
359 |   License, v. 2.0. If a copy of the MPL was not distributed with this
360 |   file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 | 
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 | 
367 | You may add additional accurate notices of copyright ownership.
368 | 
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 | 
372 |   This Source Code Form is "Incompatible With Secondary Licenses", as
373 |   defined by the Mozilla Public License, v. 2.0.
374 | 


--------------------------------------------------------------------------------