Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added more plugin tests #1969

Merged
merged 18 commits into from Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion plugins/copy-to-clipboard/prism-copy-to-clipboard.js
Expand Up @@ -39,6 +39,8 @@
var linkCopy = document.createElement('button');
linkCopy.textContent = 'Copy';

var element = env.element;

if (!ClipboardJS) {
callbacks.push(registerClipboard);
} else {
Expand All @@ -50,7 +52,7 @@
function registerClipboard() {
var clip = new ClipboardJS(linkCopy, {
'text': function () {
return env.code;
return element.textContent;
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
}
});

Expand Down
2 changes: 1 addition & 1 deletion plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 21 additions & 20 deletions plugins/unescaped-markup/prism-unescaped-markup.js
@@ -1,44 +1,45 @@
(function () {

if (typeof self === 'undefined' || !self.Prism || !self.document || !Prism.languages.markup) {
if (typeof self === 'undefined' || !self.Prism || !self.document) {
return;
}

Prism.plugins.UnescapedMarkup = true;

Prism.hooks.add('before-highlightall', function (env) {
env.selector += ", [class*='lang-'] script[type='text/plain'], [class*='language-'] script[type='text/plain']" +
", script[type='text/plain'][class*='lang-'], script[type='text/plain'][class*='language-']";
env.selector += ', [class*="lang-"] script[type="text/plain"], [class*="language-"] script[type="text/plain"]' +
', script[type="text/plain"][class*="lang-"], script[type="text/plain"][class*="language-"]';
});

Prism.hooks.add('before-sanity-check', function (env) {
if ((env.element.matches || env.element.msMatchesSelector).call(env.element, "script[type='text/plain']")) {
var code = document.createElement("code");
var pre = document.createElement("pre");
/** @type {HTMLElement} */
var element = env.element;

pre.className = code.className = env.element.className;
if ((element.matches || element.msMatchesSelector).call(element, 'script[type="text/plain"]')) {
var code = document.createElement('code');
var pre = document.createElement('pre');

if (env.element.dataset) {
Object.keys(env.element.dataset).forEach(function (key) {
if (Object.prototype.hasOwnProperty.call(env.element.dataset, key)) {
pre.dataset[key] = env.element.dataset[key];
pre.className = code.className = element.className;

if (element.dataset) {
Object.keys(element.dataset).forEach(function (key) {
if (Object.prototype.hasOwnProperty.call(element.dataset, key)) {
pre.dataset[key] = element.dataset[key];
}
});
}

env.code = env.code.replace(/&lt;\/script(>|&gt;)/gi, "</scri" + "pt>");
code.textContent = env.code;
code.textContent = env.code = env.code.replace(/&lt;\/script(>|&gt;)/gi, '</scri' + 'pt>');

pre.appendChild(code);
env.element.parentNode.replaceChild(pre, env.element);
element.parentNode.replaceChild(pre, element);
env.element = code;
return;
}

var pre = env.element.parentNode;
if (!env.code && pre && pre.nodeName.toLowerCase() == 'pre' &&
env.element.childNodes.length && env.element.childNodes[0].nodeName == "#comment") {
env.element.textContent = env.code = env.element.childNodes[0].textContent;
} else if (!env.code && (element.matches || element.msMatchesSelector).call(element, 'pre > code') &&
element.firstChild && element.firstChild.nodeName == '#comment') {
// <pre><code><!-- actual code --></code></pre>
element.textContent = env.code = element.firstChild.textContent;
}

});
}());
2 changes: 1 addition & 1 deletion plugins/unescaped-markup/prism-unescaped-markup.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions tests/helper/prism-dom-util.js
@@ -0,0 +1,36 @@
const { assert } = require('chai');


module.exports = {
/**
*
* @param {import("./prism-loader").PrismDOM} dom
*/
createUtil(dom) {
const { Prism, document } = dom.window;

const util = {

assert: {

highlight({ language, code, expected }) {
assert.strictEqual(Prism.highlight(code, Prism.languages[language], language), expected);
},

highlightElement({ language = 'none', code, expected }) {
const element = document.createElement('CODE');
element.classList.add('language-' + language);
element.textContent = code;

Prism.highlightElement(element);

assert.strictEqual(element.innerHTML, expected);
}

},

};

return util;
}
}
130 changes: 114 additions & 16 deletions tests/helper/prism-loader.js
Expand Up @@ -2,9 +2,35 @@

const fs = require("fs");
const vm = require("vm");
const { JSDOM } = require('jsdom');
const { getAllFiles } = require("./test-discovery");
const components = require("../../components");
const languagesCatalog = components.languages;
const { languages: languagesCatalog, plugins: pluginsCatalog } = require("../../components");


/**
* Wraps the given value in an array if it's not an array already.
*
* @param {T[] | T} value
* @returns {T[]}
* @template T
*/
function toArray(value) {
return Array.isArray(value) ? value : [value];
}


/**
* @typedef PrismContext
* @property {string[]} loadedLanguages
* @property {Prism} Prism
*
* @typedef {import("jsdom").DOMWindow & { Prism: any }} PrismWindow
*
* @typedef PrismDOM
* @property {PrismWindow} window
* @property {(languages: string | string[]) => void} loadLanguages
* @property {(plugins: string | string[]) => void} loadPlugins
*/


module.exports = {
Expand All @@ -26,20 +52,81 @@ module.exports = {
return context.Prism;
},

/**
* Creates a new JavaScript DOM instance with Prism being loaded.
*
* @returns {PrismDOM}
*/
createPrismDOM() {
const dom = new JSDOM(``, {
runScripts: 'outside-only'
});
const window = dom.window;

const loadLanguageSource = this.loadComponentSource.bind(this);
const loadPluginSource = this.loadPluginSource.bind(this);

window.self = window; // set self for plugins
window.eval(loadLanguageSource('core'));

const loadedLanguages = new Set();
const loadedPlugins = new Set();

return {
window: /** @type {PrismWindow} */ (window),

loadLanguages: function loadLanguages(languages) {
for (const language of toArray(languages)) {
if (!(language in languagesCatalog)) {
throw new Error(`Unknown language '${language}'`);
}

if (loadedLanguages.has(language)) {
continue;
}
loadedLanguages.add(language);

const require = languagesCatalog[language].require;
if (require) {
loadLanguages(require);
}

window.eval(loadLanguageSource(language));
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
}
},

loadPlugins: function loadPlugins(plugins) {
for (const plugin of toArray(plugins)) {
if (!(plugin in pluginsCatalog)) {
throw new Error(`Unknown plugin '${plugin}'`);
}

if (loadedPlugins.has(plugin)) {
continue;
}
loadedPlugins.add(plugin);

const require = pluginsCatalog[plugin].require;
if (require) {
loadPlugins(require);
}

window.eval(loadPluginSource(plugin));
}
},
};
},

/**
* Loads the given languages and appends the config to the given Prism object
*
* @private
* @param {string|string[]} languages
* @param {{loadedLanguages: string[], Prism: Prism}} context
* @returns {{loadedLanguages: string[], Prism: Prism}}
* @param {PrismContext} context
* @returns {PrismContext}
*/
loadLanguages(languages, context) {
if (typeof languages === 'string') {
languages = [languages];
}

for (const language of languages) {
for (const language of toArray(languages)) {
context = this.loadLanguage(language, context);
}

Expand All @@ -52,12 +139,12 @@ module.exports = {
*
* @private
* @param {string} language
* @param {{loadedLanguages: string[], Prism: Prism}} context
* @returns {{loadedLanguages: string[], Prism: Prism}}
* @param {PrismContext} context
* @returns {PrismContext}
*/
loadLanguage(language, context) {
if (!languagesCatalog[language]) {
throw new Error("Language '" + language + "' not found.");
throw new Error(`Language '${language}' not found.`);
}

// the given language was already loaded
Expand Down Expand Up @@ -86,7 +173,7 @@ module.exports = {
* @returns {Prism}
*/
createEmptyPrism() {
const coreSource = this.loadComponentSource("core");
const coreSource = this.loadComponentSource('core');
const context = this.runFileWithContext(coreSource);

for (const testSource of this.getChecks().map(src => this.loadFileSource(src))) {
Expand Down Expand Up @@ -133,7 +220,18 @@ module.exports = {
* @returns {string}
*/
loadComponentSource(name) {
return this.loadFileSource(__dirname + "/../../components/prism-" + name + ".js");
return this.loadFileSource(`${__dirname}/../../components/prism-${name}.js`);
},

/**
* Loads the given plugin's file source as string
*
* @private
* @param {string} name
* @returns {string}
*/
loadPluginSource(name) {
return this.loadFileSource(`${__dirname}/../../plugins/${name}/prism-${name}.js`);
},

/**
Expand All @@ -144,7 +242,7 @@ module.exports = {
* @returns {string}
*/
loadFileSource(src) {
return this.fileSourceCache[src] = this.fileSourceCache[src] || fs.readFileSync(src, "utf8");
return this.fileSourceCache[src] = this.fileSourceCache[src] || fs.readFileSync(src, 'utf8');
},


Expand All @@ -156,7 +254,7 @@ module.exports = {
* @returns {ReadonlyArray<string>}
*/
getChecks() {
return this.checkCache = this.checkCache || getAllFiles(__dirname + "/../checks");
return this.checkCache = this.checkCache || getAllFiles(__dirname + '/../checks');
},


Expand Down