Commit c1a0c1bde87323205b39a8d34dab6406993f43ce

Golmote 2016-07-11T20:37:47

Added TestCase.runTestsWithHooks + add missing tests. Updated documentation for test suite.

diff --git a/components/prism-groovy.js b/components/prism-groovy.js
index 7092517..7ecef42 100644
--- a/components/prism-groovy.js
+++ b/components/prism-groovy.js
@@ -8,7 +8,7 @@ Prism.languages.groovy = Prism.languages.extend('clike', {
 		{
 			pattern: /("|'|\/)(?:\\?.)*?\1/,
 			greedy: true
-		},
+		}
 	],
 	'number': /\b(?:0b[01_]+|0x[\da-f_]+(?:\.[\da-f_p\-]+)?|[\d_]+(?:\.[\d_]+)?(?:e[+-]?[\d]+)?)[glidf]?\b/i,
 	'operator': {
@@ -48,7 +48,7 @@ Prism.hooks.add('wrap', function(env) {
 				pattern = /([^\$])(\$(\{.*?\}|[\w\.]+))/;
 			}
 
-			// To prevent double HTML-ecoding we have to decode env.content first
+			// To prevent double HTML-encoding we have to decode env.content first
 			env.content = env.content.replace(/&amp;/g, '&').replace(/&lt;/g, '<');
 
 			env.content = Prism.highlight(env.content, {
diff --git a/test-suite.html b/test-suite.html
index b84ee46..ba54a91 100644
--- a/test-suite.html
+++ b/test-suite.html
@@ -61,7 +61,7 @@
 &lt;/code>&lt;pre>
 ...</code></pre>
 
-		<p>If you need to load the languages in a given order, but you don't want to use the first language as main language, you can mark the main language with an exclamation mark: <code>php!+markup</code>. This will use <code>php</code> as main language. (You can only define one main language. The test runner will fail all tests in directories with more than one main language.)</p>
+		<p>If you need to load the languages in a given order, but you don't want to use the last language as main language, you can mark the main language with an exclamation mark: <code>php!+markup</code>. This will use <code>php</code> as main language. (You can only define one main language. The test runner will fail all tests in directories with more than one main language.)</p>
 
 		<p><em>Note: by loading multiple languages you can do integration tests (ensure that loading two or more languages together won't break anything).</em></p>
 	</section>
@@ -124,15 +124,27 @@ This is a comment explaining this test case.</code></pre>
 	</section>
 </section>
 
+<section id="writing-specific-tests">
+	<h1>Writing specific tests</h1>
+
+	<p>Sometimes, using the token stream tests is not powerful enough. By creating a test file with the file extension <code>.js</code> instead of <code>.test</code>, you can make Prism highlight arbitrary pieces of code and check their HTML results.</p>
+	<p>The language is determined by the folder containing the test file lies, as explained in the previous section.</p>
+	<p>The structure of your test file will look like this, for example:</p>
+	<pre><code>module.exports = {
+	'&amp;#x278a;': '&lt;span class="token entity" title="&amp;#x278a;">&amp;amp;#x278a;&lt;/span>',
+	'&amp;#182;': '&lt;span class="token entity" title="&amp;#182;">&amp;amp;#182;&lt;/span>',
+};</code></pre>
+	<p>The keys are the codes which will be highlighted by Prism. The values are the expected results, as HTML.</p>
+</section>
 
 <section id="test-runner-tests">
-	<h2>Test runner tests</h2>
+	<h1>Test runner tests</h1>
 	<p>The test runner itself is tested in a separate test case. You can find all “test core” related tests in <code>tests/testrunner-tests.js</code>.</p>
 	<p>You shouldn't need to touch this file ever, except you modify the test runner code.</p>
 </section>
 
 <section id="internal-structure">
-	<h2>Internal structure</h2>
+	<h1>Internal structure</h1>
 	<p>The global test flow is at follows:</p>
 	<ol>
 		<li>Run all internal tests (test the test runner).</li>
diff --git a/tests/helper/test-case.js b/tests/helper/test-case.js
index 80902fe..3a874fe 100644
--- a/tests/helper/test-case.js
+++ b/tests/helper/test-case.js
@@ -72,7 +72,7 @@ module.exports = {
 	/**
 	 * Parses the language names and finds the main language.
 	 *
-	 * It is either the first language or the language followed by a exclamation mark “!”.
+	 * It is either the last language or the language followed by a exclamation mark “!”.
 	 * There should only be one language with an exclamation mark.
 	 *
 	 * @param {string} languageIdentifier
@@ -141,5 +141,37 @@ module.exports = {
 			// the JSON can't be parsed (e.g. it could be empty)
 			return null;
 		}
+	},
+
+	/**
+	 * 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: function (languageIdentifier, codes) {
+		var usedLanguages = this.parseLanguageNames(languageIdentifier);
+		var Prism = PrismLoader.createInstance(usedLanguages.languages);
+		// the first language is the main language to highlight
+
+		for (var code in codes) {
+			if (codes.hasOwnProperty(code)) {
+				var env = {
+					element: {},
+					language: usedLanguages.mainLanguage,
+					grammar: Prism.languages[usedLanguages.mainLanguage],
+					code: code
+				};
+				Prism.hooks.run('before-highlight', env);
+				env.highlightedCode = Prism.highlight(env.code, Prism.languages[usedLanguages.mainLanguage], usedLanguages.mainLanguage);
+				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 622b619..b2aade1 100644
--- a/tests/helper/test-discovery.js
+++ b/tests/helper/test-discovery.js
@@ -104,9 +104,7 @@ module.exports = {
 	getAllFiles: function (src) {
 		return fs.readdirSync(src).filter(
 			function (fileName) {
-				// only find files that have the ".test" extension
-				return ".test" === path.extname(fileName) &&
-					fs.statSync(path.join(src, fileName)).isFile();
+				return fs.statSync(path.join(src, fileName)).isFile();
 			}
 		).map(
 			function (fileName) {
diff --git a/tests/languages/asciidoc/entity_feature.js b/tests/languages/asciidoc/entity_feature.js
new file mode 100644
index 0000000..2e99cd1
--- /dev/null
+++ b/tests/languages/asciidoc/entity_feature.js
@@ -0,0 +1,4 @@
+module.exports = {
+	'&#x278a;': '<span class="token entity" title="&#x278a;">&amp;#x278a;</span>',
+	'&#182;': '<span class="token entity" title="&#182;">&amp;#182;</span>'
+};
\ No newline at end of file
diff --git a/tests/languages/groovy/string-interpolation_feature.js b/tests/languages/groovy/string-interpolation_feature.js
new file mode 100644
index 0000000..ddecdfd
--- /dev/null
+++ b/tests/languages/groovy/string-interpolation_feature.js
@@ -0,0 +1,28 @@
+module.exports = {
+	// Double quoted: interpolation
+	'"$foo"': '<span class="token string gstring">"<span class="token expression"><span class="token punctuation">$</span>foo</span>"</span>',
+	'"${42}"': '<span class="token string gstring">"<span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span><span class="token number">42</span><span class="token punctuation">}</span></span>"</span>',
+	// Triple double quoted: interpolation
+	'"""$foo"""': '<span class="token string gstring">"""<span class="token expression"><span class="token punctuation">$</span>foo</span>"""</span>',
+	'"""${42}"""': '<span class="token string gstring">"""<span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span><span class="token number">42</span><span class="token punctuation">}</span></span>"""</span>',
+	// Slashy string: interpolation
+	'/$foo/': '<span class="token string regex">/<span class="token expression"><span class="token punctuation">$</span>foo</span>/</span>',
+	'/${42}/': '<span class="token string regex">/<span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span><span class="token number">42</span><span class="token punctuation">}</span></span>/</span>',
+	// Dollar slashy string: interpolation
+	'$/$foo/$': '<span class="token string gstring">$/<span class="token expression"><span class="token punctuation">$</span>foo</span>/$</span>',
+	'$/${42}/$': '<span class="token string gstring">$/<span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span><span class="token number">42</span><span class="token punctuation">}</span></span>/$</span>',
+
+	// Double quoted: no interpolation (escaped)
+	'"\\$foo \\${42}"': '<span class="token string gstring">"\\$foo \\${42}"</span>',
+	// Triple double quoted: no interpolation (escaped)
+	'"""\\$foo \\${42}"""': '<span class="token string gstring">"""\\$foo \\${42}"""</span>',
+	// Slashy string: no interpolation (escaped)
+	'/\\$foo \\${42}/': '<span class="token string regex">/\\$foo \\${42}/</span>',
+	// Dollar slashy string: no interpolation (escaped)
+	'$/$$foo $${42}/$': '<span class="token string gstring">$/$$foo $${42}/$</span>',
+
+	// Single quoted string: no interpolation
+	'\'$foo ${42}\'': '<span class="token string">\'$foo ${42}\'</span>',
+	// Triple single quoted string: no interpolation
+	'\'\'\'$foo ${42}\'\'\'': '<span class="token string">\'\'\'$foo ${42}\'\'\'</span>'
+};
\ No newline at end of file
diff --git a/tests/languages/handlebars/handlebars_in_markup_feature.js b/tests/languages/handlebars/handlebars_in_markup_feature.js
new file mode 100644
index 0000000..13f51db
--- /dev/null
+++ b/tests/languages/handlebars/handlebars_in_markup_feature.js
@@ -0,0 +1,4 @@
+module.exports = {
+	'<div>{{{intro}}}</div>': '<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><span class="token handlebars"><span class="token delimiter punctuation">{{{</span><span class="token variable">intro</span><span class="token delimiter punctuation">}}}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>',
+	'<div class="{{foo}}">': '<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span><span class="token handlebars"><span class="token delimiter punctuation">{{</span><span class="token variable">foo</span><span class="token delimiter punctuation">}}</span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span>'
+};
\ No newline at end of file
diff --git a/tests/languages/markup+php/php_in_markup_feature.js b/tests/languages/markup+php/php_in_markup_feature.js
new file mode 100644
index 0000000..3b94913
--- /dev/null
+++ b/tests/languages/markup+php/php_in_markup_feature.js
@@ -0,0 +1,6 @@
+module.exports = {
+	'<div><?php echo $foo; ?></div>': '<span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span></span><span class="token php"><span class="token delimiter">&lt;?php</span> <span class="token keyword">echo</span> <span class="token variable">$foo</span><span class="token punctuation">;</span> <span class="token delimiter">?></span></span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></span>',
+	'<div><? echo $foo; ?></div>': '<span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span></span><span class="token php"><span class="token delimiter">&lt;?</span> <span class="token keyword">echo</span> <span class="token variable">$foo</span><span class="token punctuation">;</span> <span class="token delimiter">?></span></span><span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></span>',
+	'<div class="<?php echo $foo; ?>">': '<span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span><span class="token php"><span class="token delimiter">&lt;?php</span> <span class="token keyword">echo</span> <span class="token variable">$foo</span><span class="token punctuation">;</span> <span class="token delimiter">?></span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span>',
+	'<div class="<? echo $foo; ?>">': '<span class="token markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span><span class="token php"><span class="token delimiter">&lt;?</span> <span class="token keyword">echo</span> <span class="token variable">$foo</span><span class="token punctuation">;</span> <span class="token delimiter">?></span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span>'
+};
\ No newline at end of file
diff --git a/tests/languages/markup/entity_feature.js b/tests/languages/markup/entity_feature.js
new file mode 100644
index 0000000..e5ac159
--- /dev/null
+++ b/tests/languages/markup/entity_feature.js
@@ -0,0 +1,4 @@
+module.exports = {
+	'&amp;': '<span class="token entity" title="&amp;">&amp;amp;</span>',
+	'&thetasym;': '<span class="token entity" title="&thetasym;">&amp;thetasym;</span>'
+};
\ No newline at end of file
diff --git a/tests/languages/smarty/smarty_in_markup_feature.js b/tests/languages/smarty/smarty_in_markup_feature.js
new file mode 100644
index 0000000..d72c408
--- /dev/null
+++ b/tests/languages/smarty/smarty_in_markup_feature.js
@@ -0,0 +1,4 @@
+module.exports = {
+	'<div>{$foo}</div>': '<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><span class="token smarty"><span class="token delimiter punctuation">{</span><span class="token variable">$foo</span><span class="token delimiter punctuation">}</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>',
+	'<div class="{$foo}">': '<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span><span class="token smarty"><span class="token delimiter punctuation">{</span><span class="token variable">$foo</span><span class="token delimiter punctuation">}</span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span>'
+};
\ No newline at end of file
diff --git a/tests/run-child.js b/tests/run-child.js
index c7313a4..ea93f1f 100644
--- a/tests/run-child.js
+++ b/tests/run-child.js
@@ -1,16 +1,21 @@
 "use strict";
 
 var TestCase = require("./helper/test-case");
+var path = require("path");
 var argv = require("yargs").argv;
 
 if (argv.language) {
 	process.on('message', function (data) {
 		if (data.filePath) {
 			try {
-				TestCase.runTestCase(argv.language, data.filePath);
+				if (path.extname(data.filePath) === '.test') {
+					TestCase.runTestCase(argv.language, data.filePath);
+				} else {
+					TestCase.runTestsWithHooks(argv.language, require(data.filePath));
+				}
 				process.send({success: true});
 			} catch (e) {
-				process.send({error: e});
+				process.send({error: e.message});
 			}
 		}
 	});
diff --git a/tests/run.js b/tests/run.js
index 3acfa90..8f7cf27 100644
--- a/tests/run.js
+++ b/tests/run.js
@@ -49,7 +49,7 @@ for (var language in testSuite) {
 				                // over and over again.
 				                setTimeout(function() {
 					                if (o.error) {
-						                throw o.error;
+						                throw new Error(o.error);
 					                } else if (o.success) {
 						                done();
 					                }