diff --git a/package-lock.json b/package-lock.json index c9647b2c3b..949c916196 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6102,6 +6102,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prettier": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", + "dev": true + }, "pretty-hrtime": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", diff --git a/package.json b/package.json index 487ccd738b..1961040965 100755 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "mocha": "^6.2.0", "node-fetch": "^2.6.0", "npm-run-all": "^4.1.5", + "prettier": "^2.4.1", "pump": "^3.0.0", "refa": "^0.9.1", "regexp-ast-analysis": "^0.2.4", diff --git a/test-suite.html b/test-suite.html index 0dbd535016..c4ae782175 100644 --- a/test-suite.html +++ b/test-suite.html @@ -165,14 +165,21 @@

Explaining the sim

Writing specific tests

-

Sometimes, using the token stream tests is not powerful enough. By creating a test file with the file extension .js instead of .test, you can make Prism highlight arbitrary pieces of code and check their HTML results.

+

Sometimes, using the token stream tests is not powerful enough. By creating a test file with the file extension .html.test instead of .test, you can make Prism highlight arbitrary pieces of code and check their HTML results.

The language is determined by the folder containing the test file lies, as explained in the previous section.

The structure of your test file will look like this, for example:

-
module.exports = {
-	'&#x278a;': '<span class="token entity" title="&#x278a;">&amp;#x278a;</span>',
-	'&#182;': '<span class="token entity" title="&#182;">&amp;#182;</span>',
-};
-

The keys are the codes which will be highlighted by Prism. The values are the expected results, as HTML.

+
&amp;
+&#x41;
+
+----------------------------------------------------
+
+<span class="token entity named-entity" title="&amp;">&amp;amp;</span>
+<span class="token entity" title="&#x41;">&amp;#x41;</span>
+
+----------------------------------------------------
+
+This is a comment explaining this test case.
+
diff --git a/tests/helper/test-case.js b/tests/helper/test-case.js index e073613a67..3454d77d1c 100644 --- a/tests/helper/test-case.js +++ b/tests/helper/test-case.js @@ -2,6 +2,7 @@ const fs = require('fs'); const { assert } = require('chai'); +const Prettier = require('prettier'); const PrismLoader = require('./prism-loader'); const TokenStreamTransformer = require('./token-stream-transformer'); @@ -217,6 +218,81 @@ class TokenizeJSONRunner { } } +/** + * @implements {Runner} + */ +class HighlightHTMLRunner { + /** + * @param {Prism} Prism + * @param {string} code + * @param {string} language + * @returns {string} + */ + run(Prism, code, language) { + const env = { + element: {}, + language, + grammar: Prism.languages[language], + code, + }; + + Prism.hooks.run('before-highlight', env); + env.highlightedCode = Prism.highlight(env.code, env.grammar, env.language); + Prism.hooks.run('before-insert', env); + env.element.innerHTML = env.highlightedCode; + Prism.hooks.run('after-highlight', env); + Prism.hooks.run('complete', env); + + return env.highlightedCode; + } + /** + * @param {string} actual + * @returns {string} + */ + print(actual) { + return Prettier.format(actual, { + printWidth: 100, + tabWidth: 4, + useTabs: true, + htmlWhitespaceSensitivity: 'ignore', + filepath: 'fake.html', + }); + } + /** + * @param {string} actual + * @param {string} expected + * @returns {boolean} + */ + isEqual(actual, expected) { + return this.normalize(actual) === this.normalize(expected); + } + /** + * @param {string} actual + * @param {string} expected + * @param {(firstDifference: number) => string} message + * @returns {void} + */ + assertEqual(actual, expected, message) { + // We don't calculate the index of the first difference because it's difficult. + assert.deepEqual(this.normalize(actual), this.normalize(expected), message(0)); + } + + /** + * Normalizes the given HTML by removing all leading spaces and trailing spaces. Line breaks will also be normalized + * to enable good diffing. + * + * @param {string} html + * @returns {string} + */ + normalize(html) { + return html + .replace(//g, '>\n') + .replace(/[ \t]*[\r\n]\s*/g, '\n') + .trim(); + } +} + module.exports = { TestCaseFile, @@ -238,7 +314,11 @@ module.exports = { * @param {"none" | "insert" | "update"} updateMode */ runTestCase(languageIdentifier, filePath, updateMode) { - this.runTestCaseWithRunner(languageIdentifier, filePath, updateMode, new TokenizeJSONRunner()); + if (/\.html\.test$/i.test(filePath)) { + this.runTestCaseWithRunner(languageIdentifier, filePath, updateMode, new HighlightHTMLRunner()); + } else { + this.runTestCaseWithRunner(languageIdentifier, filePath, updateMode, new TokenizeJSONRunner()); + } }, /** @@ -347,38 +427,6 @@ module.exports = { mainLanguage: mainLanguage }; }, - - /** - * Runs the given pieces of codes and asserts their result. - * - * Code is provided as the key and expected result as the value. - * - * @param {string} languageIdentifier - * @param {object} codes - */ - runTestsWithHooks(languageIdentifier, codes) { - const usedLanguages = this.parseLanguageNames(languageIdentifier); - const Prism = PrismLoader.createInstance(usedLanguages.languages); - // the first language is the main language to highlight - - for (const code in codes) { - if (codes.hasOwnProperty(code)) { - const env = { - element: {}, - language: usedLanguages.mainLanguage, - grammar: Prism.languages[usedLanguages.mainLanguage], - code: code - }; - Prism.hooks.run('before-highlight', env); - env.highlightedCode = Prism.highlight(env.code, env.grammar, env.language); - Prism.hooks.run('before-insert', env); - env.element.innerHTML = env.highlightedCode; - Prism.hooks.run('after-highlight', env); - Prism.hooks.run('complete', env); - assert.equal(env.highlightedCode, codes[code]); - } - } - } }; /** diff --git a/tests/helper/test-discovery.js b/tests/helper/test-discovery.js index c52fb25e6d..4d97873972 100644 --- a/tests/helper/test-discovery.js +++ b/tests/helper/test-discovery.js @@ -3,8 +3,6 @@ const fs = require('fs'); const path = require('path'); -const SUPPORTED_TEST_FILE_EXT = new Set(['.js', '.test']); - module.exports = { /** @@ -95,7 +93,7 @@ module.exports = { getAllFiles(src) { return fs.readdirSync(src) .filter(fileName => { - return SUPPORTED_TEST_FILE_EXT.has(path.extname(fileName)) + return path.extname(fileName) === '.test' && fs.statSync(path.join(src, fileName)).isFile(); }) .map(fileName => { diff --git a/tests/languages/asciidoc/entity_feature.html.test b/tests/languages/asciidoc/entity_feature.html.test new file mode 100644 index 0000000000..6d21274793 --- /dev/null +++ b/tests/languages/asciidoc/entity_feature.html.test @@ -0,0 +1,7 @@ +➊ +¶ + +---------------------------------------------------- + +&#x278a; +&#182; diff --git a/tests/languages/asciidoc/entity_feature.js b/tests/languages/asciidoc/entity_feature.js deleted file mode 100644 index 22c9b3baeb..0000000000 --- a/tests/languages/asciidoc/entity_feature.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - '➊': '&#x278a;', - '¶': '&#182;' -}; diff --git a/tests/languages/groovy/issue1049.html.test b/tests/languages/groovy/issue1049.html.test new file mode 100644 index 0000000000..0f0c6182bd --- /dev/null +++ b/tests/languages/groovy/issue1049.html.test @@ -0,0 +1,15 @@ +"&" +"&&" +"<" +"<<" +"&lt;" +">" + +---------------------------------------------------- + +"&amp;" +"&amp;&amp;" +"&lt;" +"&lt;&lt;" +"&amp;lt;" +"&gt;" diff --git a/tests/languages/groovy/issue1049.js b/tests/languages/groovy/issue1049.js deleted file mode 100644 index 2ce81537a2..0000000000 --- a/tests/languages/groovy/issue1049.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - '"&"': '"&amp;"', - '"&&"': '"&amp;&amp;"', - '"<"': '"&lt;"', - '"<<"': '"&lt;&lt;"', - '"&lt;"': '"&amp;lt;"', - '">"': '"&gt;"', -}; diff --git a/tests/languages/groovy/string-interpolation_feature.html.test b/tests/languages/groovy/string-interpolation_feature.html.test new file mode 100644 index 0000000000..15541878bd --- /dev/null +++ b/tests/languages/groovy/string-interpolation_feature.html.test @@ -0,0 +1,133 @@ +// Double quoted: interpolation +"$foo" +"${42}" + +// Triple double quoted: interpolation +"""$foo""" +"""${42}""" + +// Slashy string: interpolation +/$foo/ +/${42}/ + +// Dollar slashy string: interpolation +$/$foo/$ +$/${42}/$ + +// Double quoted: no interpolation (escaped) +"\$foo \${42}" + +// Triple double quoted: no interpolation (escaped) +"""\$foo \${42}""" + +// Slashy string: no interpolation (escaped) +/\$foo \${42}/ + +// Dollar slashy string: no interpolation (escaped) +$/$$foo $${42}/$ + +// Single quoted string: no interpolation +'$foo ${42}' + +// Triple single quoted string: no interpolation +'''$foo ${42}''' + +---------------------------------------------------- + +// Double quoted: interpolation + + " + + $ + foo + + " + + + " + + $ + { + 42 + } + + " + + +// Triple double quoted: interpolation + + """ + + $ + foo + + """ + + + """ + + $ + { + 42 + } + + """ + + +// Slashy string: interpolation + + / + + $ + foo + + / + + + / + + $ + { + 42 + } + + / + + +// Dollar slashy string: interpolation + + $/ + + $ + foo + + /$ + + + $/ + + $ + { + 42 + } + + /$ + + +// Double quoted: no interpolation (escaped) +"\$foo \${42}" + +// Triple double quoted: no interpolation (escaped) +"""\$foo \${42}""" + +// Slashy string: no interpolation (escaped) +/\$foo \${42}/ + +// Dollar slashy string: no interpolation (escaped) +$/$$foo $${42}/$ + +// Single quoted string: no interpolation +'$foo ${42}' + +// Triple single quoted string: no interpolation +'''$foo ${42}''' diff --git a/tests/languages/groovy/string-interpolation_feature.js b/tests/languages/groovy/string-interpolation_feature.js deleted file mode 100644 index 5cb2acf579..0000000000 --- a/tests/languages/groovy/string-interpolation_feature.js +++ /dev/null @@ -1,28 +0,0 @@ -module.exports = { - // Double quoted: interpolation - '"$foo"': '"$foo"', - '"${42}"': '"${42}"', - // Triple double quoted: interpolation - '"""$foo"""': '"""$foo"""', - '"""${42}"""': '"""${42}"""', - // Slashy string: interpolation - '/$foo/': '/$foo/', - '/${42}/': '/${42}/', - // Dollar slashy string: interpolation - '$/$foo/$': '$/$foo/$', - '$/${42}/$': '$/${42}/$', - - // Double quoted: no interpolation (escaped) - '"\\$foo \\${42}"': '"\\$foo \\${42}"', - // Triple double quoted: no interpolation (escaped) - '"""\\$foo \\${42}"""': '"""\\$foo \\${42}"""', - // Slashy string: no interpolation (escaped) - '/\\$foo \\${42}/': '/\\$foo \\${42}/', - // Dollar slashy string: no interpolation (escaped) - '$/$$foo $${42}/$': '$/$$foo $${42}/$', - - // Single quoted string: no interpolation - '\'$foo ${42}\'': '\'$foo ${42}\'', - // Triple single quoted string: no interpolation - '\'\'\'$foo ${42}\'\'\'': '\'\'\'$foo ${42}\'\'\'' -}; diff --git a/tests/languages/markdown/code-block_feature.html.test b/tests/languages/markdown/code-block_feature.html.test new file mode 100644 index 0000000000..7a5e8562c1 --- /dev/null +++ b/tests/languages/markdown/code-block_feature.html.test @@ -0,0 +1,49 @@ +```html +Click me! & +``` + +```unknownLanguage +Click me! & +``` + +---------------------------------------------------- + + + ``` + html + + + + < + a + + href + + = + " + #foo + " + + > + + Click me! + + + </ + a + + > + + &amp; + + ``` + + + + ``` + unknownLanguage + + <a href="#foo">Click me!</a> &amp; + + ``` + diff --git a/tests/languages/markdown/code-block_feature.js b/tests/languages/markdown/code-block_feature.js deleted file mode 100644 index e879cfa80e..0000000000 --- a/tests/languages/markdown/code-block_feature.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - '```html\nClick me! &\n```\n': '```html\n<a href="#foo">Click me!</a> &amp;\n```\n', - - '```unknownLanguage\nClick me! &\n```\n': '```unknownLanguage\n<a href="#foo">Click me!</a> &amp;\n```\n', -}; diff --git a/tests/languages/markdown/code_block_language_detection_feature.html.test b/tests/languages/markdown/code_block_language_detection_feature.html.test new file mode 100644 index 0000000000..d47fcba0d3 --- /dev/null +++ b/tests/languages/markdown/code_block_language_detection_feature.html.test @@ -0,0 +1,45 @@ +```js +let a = 0; +``` + +``` c++ +int a = 0; +``` + +``` c# +var a = 0; +``` + +```{r pressure, echo=FALSE} +plot(pressure) +``` + +---------------------------------------------------- + + + ``` + js + let a = 0; + ``` + + + + ``` + c++ + int a = 0; + ``` + + + + ``` + c# + var a = 0; + ``` + + + + ``` + {r pressure, echo=FALSE} + plot(pressure) + ``` + diff --git a/tests/languages/markdown/code_block_language_detection_feature.js b/tests/languages/markdown/code_block_language_detection_feature.js deleted file mode 100644 index c980b6cf5f..0000000000 --- a/tests/languages/markdown/code_block_language_detection_feature.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - - '```js\nlet a = 0;\n```': '```js\nlet a = 0;\n```', - - '``` c++\nint a = 0;\n```': '``` c++\nint a = 0;\n```', - - '``` c#\nvar a = 0;\n```': '``` c#\nvar a = 0;\n```', - - '```{r pressure, echo=FALSE}\nplot(pressure)\n```': '```{r pressure, echo=FALSE}\nplot(pressure)\n```' - -}; diff --git a/tests/languages/markup/entity_feature.html.test b/tests/languages/markup/entity_feature.html.test new file mode 100644 index 0000000000..5bcf3517a7 --- /dev/null +++ b/tests/languages/markup/entity_feature.html.test @@ -0,0 +1,17 @@ +& +ϑ +A +A +⛵ + +---------------------------------------------------- + +&amp; +&thetasym; +&#65; +&#x41; +&#x26f5; + +---------------------------------------------------- + +Checks for HTML/XML character entity references. diff --git a/tests/languages/markup/entity_feature.js b/tests/languages/markup/entity_feature.js deleted file mode 100644 index ec74c9e394..0000000000 --- a/tests/languages/markup/entity_feature.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - '&': '&amp;', - 'ϑ': '&thetasym;', - 'A': '&#65;', - 'A': '&#x41;' -}; diff --git a/tests/languages/markup/entity_feature.test b/tests/languages/markup/entity_feature.test deleted file mode 100644 index 44f0f83d6a..0000000000 --- a/tests/languages/markup/entity_feature.test +++ /dev/null @@ -1,14 +0,0 @@ -& ϑ ⛵   - ----------------------------------------------------- - -[ - ["entity", "&"], - ["entity", "ϑ"], - ["entity", "⛵"], - ["entity", " "] -] - ----------------------------------------------------- - -Checks for HTML/XML character entity references. \ No newline at end of file diff --git a/tests/pattern-tests.js b/tests/pattern-tests.js index 7cfb2d00c2..2dc9ee4c8b 100644 --- a/tests/pattern-tests.js +++ b/tests/pattern-tests.js @@ -10,7 +10,6 @@ const { languages } = require('../components.json'); const { visitRegExpAST } = require('regexpp'); const { transform, combineTransformers, getIntersectionWordSets, JS, Words, NFA, Transformers } = require('refa'); const scslre = require('scslre'); -const path = require('path'); const { argv } = require('yargs'); const RAA = require('regexp-ast-analysis'); @@ -30,11 +29,7 @@ for (const languageIdentifier in testSuite) { } for (const file of testSuite[languageIdentifier]) { - if (path.extname(file) === '.test') { - snippets.push(TestCase.TestCaseFile.readFromFile(file).code); - } else { - snippets.push(...Object.keys(require(file))); - } + snippets.push(TestCase.TestCaseFile.readFromFile(file).code); } } diff --git a/tests/run.js b/tests/run.js index d6a388295e..f030fbc3c7 100644 --- a/tests/run.js +++ b/tests/run.js @@ -28,11 +28,7 @@ for (const language in testSuite) { const fileName = path.basename(filePath, path.extname(filePath)); it("– should pass test case '" + fileName + "'", function () { - if (path.extname(filePath) === '.test') { - TestCase.runTestCase(language, filePath, update ? 'update' : 'insert'); - } else { - TestCase.runTestsWithHooks(language, require(filePath)); - } + TestCase.runTestCase(language, filePath, update ? 'update' : 'insert'); }); } });