/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

/**
 * This file tests the CSS syntax highlighter in the MdnDocsWidget object.
 *
 * The CSS syntax highlighter accepts:
 * - a string containing CSS
 * - a DOM node
 *
 * It parses the string and creates a collection of DOM nodes for different
 * CSS token types. These DOM nodes have CSS classes applied to them,
 * to apply the right style for that particular token type. The DOM nodes
 * are returned as children of the node that was passed to the function.
 *
 * This test code defines a number of different strings containing valid and
 * invalid CSS in various forms. For each string it defines the DOM nodes
 * that it expects to get from the syntax highlighter.
 *
 * It then calls the syntax highlighter, and checks that the resulting
 * collection of DOM nodes is what we expected.
 */

"use strict";

const {appendSyntaxHighlightedCSS} = require("devtools/client/shared/widgets/MdnDocsWidget");

/**
 * An array containing the actual test cases.
 *
 * The test code tests every case in the array. If you want to add more
 * test cases, just add more items to the array.
 *
 * Each test case consists of:
 * - description: string describing the salient features of this test case
 * - example: the string to test
 * - expected: an array of objects, one for each DOM node we expect, that
 * captures the information about the node that we expect to test.
 */
const TEST_DATA = [{
  description: "Valid syntax, string value.",
  example: "name: stringValue;",
  expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, numeric value.",
    example: "name: 1;",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "1"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, url value.",
    example: "name: url(./name);",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "url(./name)"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, space before ':'.",
    example: "name : stringValue;",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: " "},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, space before ';'.",
    example: "name: stringValue ;",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: " "},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, trailing space.",
    example: "name: stringValue; ",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"},
             {type: "text", text: " "}
  ]}, {
    description: "Valid syntax, leading space.",
    example: " name: stringValue;",
    expected: [{type: "text", text: " "},
             {type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, two spaces.",
    example: "name:  stringValue;",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: "  "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, no spaces.",
    example: "name:stringValue;",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, two-part value.",
    example: "name: stringValue 1;",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: " "},
             {type: "property-value", text: "1"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, two declarations.",
    example: "name: stringValue;\n" +
           "name: 1;",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"},
             {type: "text", text: "\n"},
             {type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "1"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, commented, numeric value.",
    example: "/* comment */\n" +
           "name: 1;",
    expected: [{type: "comment", text: "/* comment */"},
             {type: "text", text: "\n"},
             {type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "1"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, multiline commented, string value.",
    example: "/* multiline \n" +
           "comment */\n" +
           "name: stringValue;",
    expected: [{type: "comment", text: "/* multiline \ncomment */"},
             {type: "text", text: "\n"},
             {type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, commented, two declarations.",
    example: "/* comment 1 */\n" +
           "name: 1;\n" +
           "/* comment 2 */\n" +
           "name: stringValue;",
    expected: [{type: "comment", text: "/* comment 1 */"},
             {type: "text", text: "\n"},
             {type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "1"},
             {type: "text", text: ";"},
             {type: "text", text: "\n"},
             {type: "comment", text: "/* comment 2 */"},
             {type: "text", text: "\n"},
             {type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, multiline.",
    example: "name: \n" +
           "stringValue;",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " \n"},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"}
  ]}, {
    description: "Valid syntax, multiline, two declarations.",
    example: "name: \n" +
           "stringValue \n" +
           "stringValue2;",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " \n"},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: " \n"},
             {type: "property-value", text: "stringValue2"},
             {type: "text", text: ";"}
  ]}, {
    description: "Invalid: not CSS at all.",
    example: "not CSS at all",
    expected: [{type: "property-name", text: "not"},
             {type: "text", text: " "},
             {type: "property-name", text: "CSS"},
             {type: "text", text: " "},
             {type: "property-name", text: "at"},
             {type: "text", text: " "},
             {type: "property-name", text: "all"}
  ]}, {
    description: "Invalid: switched ':' and ';'.",
    example: "name; stringValue:",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ";"},
             {type: "text", text: " "},
             {type: "property-name", text: "stringValue"},
             {type: "text", text: ":"}
  ]}, {
    description: "Invalid: unterminated comment.",
    example: "/* unterminated comment\n" +
           "name: stringValue;",
    expected: [{type: "comment", text: "/* unterminated comment\nname: stringValue;"}
  ]}, {
    description: "Invalid: bad comment syntax.",
    example: "// invalid comment\n" +
           "name: stringValue;",
    expected: [{type: "text", text: "/"},
             {type: "text", text: "/"},
             {type: "text", text: " "},
             {type: "property-name", text: "invalid"},
             {type: "text", text: " "},
             {type: "property-name", text: "comment"},
             {type: "text", text: "\n"},
             {type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: ";"}
  ]}, {
    description: "Invalid: no trailing ';'.",
    example: "name: stringValue\n" +
           "name: stringValue2",
    expected: [{type: "property-name", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue"},
             {type: "text", text: "\n"},
             {type: "property-value", text: "name"},
             {type: "text", text: ":"},
             {type: "text", text: " "},
             {type: "property-value", text: "stringValue2"},
  ]}
];

/**
 * Iterate through every test case, calling the syntax highlighter,
 * then calling a helper function to check the output.
 */
add_task(function* () {
  let doc = gBrowser.selectedTab.ownerDocument;
  let parent = doc.createElement("div");
  info("Testing all CSS syntax highlighter test cases");
  for (let {description, example, expected} of TEST_DATA) {
    info("Testing: " + description);
    appendSyntaxHighlightedCSS(example, parent);
    checkCssSyntaxHighlighterOutput(expected, parent);
    while (parent.firstChild) {
      parent.firstChild.remove();
    }
  }
});
