Skip to content

Commit

Permalink
Added more plugin tests (#1969)
Browse files Browse the repository at this point in the history
  • Loading branch information
RunDevelopment committed Oct 5, 2021
1 parent e8f84a6 commit a394a14
Show file tree
Hide file tree
Showing 11 changed files with 723 additions and 511 deletions.
711 changes: 280 additions & 431 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -57,7 +57,7 @@
"gzip-size": "^5.1.1",
"htmlparser2": "^4.0.0",
"http-server": "^0.12.3",
"jsdom": "^13.0.0",
"jsdom": "^16.7.0",
"mocha": "^6.2.0",
"node-fetch": "^2.6.0",
"npm-run-all": "^4.1.5",
Expand Down
58 changes: 58 additions & 0 deletions tests/helper/prism-dom-util.js
@@ -0,0 +1,58 @@
const { assert } = require('chai');
const PrismLoader = require('./prism-loader');

/**
* @typedef {import("./prism-loader").PrismDOM} PrismDOM
* @typedef {import("./prism-loader").PrismWindow} PrismWindow
*/

module.exports = {
/**
* @param {PrismWindow} window
*/
createUtil(window) {
const { Prism, document } = window;

const util = {
assert: {
highlight({ language = 'none', 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;
},

/**
* Creates a Prism DOM instance that will be automatically cleaned up after the given test suite finished.
*
* @param {ReturnType<typeof import('mocha')["suite"]>} suite
* @param {Partial<Record<"languages" | "plugins", string | string[]>>} options
*/
createScopedPrismDom(suite, options = {}) {
const dom = PrismLoader.createPrismDOM();

suite.afterAll(function () {
dom.window.close();
});

if (options.languages) {
dom.loadLanguages(options.languages);
}
if (options.plugins) {
dom.loadPlugins(options.plugins);
}

return dom;
}
};
98 changes: 90 additions & 8 deletions tests/helper/prism-loader.js
@@ -1,18 +1,34 @@
'use strict';

const fs = require('fs');
const { JSDOM } = require('jsdom');
const components = require('../../components.json');
const getLoader = require('../../dependencies');
const languagesCatalog = components.languages;
const coreChecks = require('./checks');
const { languages: languagesCatalog, plugins: pluginsCatalog } = components;


/**
* @typedef {import('../../components/prism-core')} Prism
*/

/**
* @typedef PrismLoaderContext
* @property {import('../../components/prism-core')} Prism The Prism instance.
* @property {Prism} Prism The Prism instance.
* @property {Set<string>} loaded A set of loaded components.
*/

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

/** @type {Map<string, string>} */
const fileSourceCache = new Map();
/** @type {() => any} */
Expand All @@ -39,6 +55,54 @@ 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;

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

/** The set of loaded languages and plugins */
const loaded = new Set();

/**
* Loads the given languages or plugins.
*
* @param {string | string[]} languagesOrPlugins
*/
const load = (languagesOrPlugins) => {
getLoader(components, toArray(languagesOrPlugins), [...loaded]).load(id => {
let source;
if (languagesCatalog[id]) {
source = this.loadComponentSource(id);
} else if (pluginsCatalog[id]) {
source = this.loadPluginSource(id);
} else {
throw new Error(`Language or plugin '${id}' not found.`);
}

window.eval(source);
loaded.add(id);
});
};

return {
dom,
window: /** @type {PrismWindow} */ (window),
document: window.document,
Prism: window.Prism,
loadLanguages: load,
loadPlugins: load,
};
},

/**
* Loads the given languages and appends the config to the given Prism object.
*
Expand All @@ -48,11 +112,7 @@ module.exports = {
* @returns {PrismLoaderContext}
*/
loadLanguages(languages, context) {
if (typeof languages === 'string') {
languages = [languages];
}

getLoader(components, languages, [...context.loaded]).load(id => {
getLoader(components, toArray(languages), [...context.loaded]).load(id => {
if (!languagesCatalog[id]) {
throw new Error(`Language '${id}' not found.`);
}
Expand Down Expand Up @@ -112,6 +172,17 @@ module.exports = {
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`);
},

/**
* Loads the given file source as string
*
Expand All @@ -127,3 +198,14 @@ module.exports = {
return content;
}
};

/**
* 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];
}
71 changes: 71 additions & 0 deletions tests/plugins/copy-to-clipboard/basic-functionality.js
@@ -0,0 +1,71 @@
const { assert } = require('chai');
const { createScopedPrismDom } = require('../../helper/prism-dom-util');


class DummyClipboard {
constructor() {
this.text = '';
}
async readText() {
return this.text;
}
/** @param {string} data */
writeText(data) {
this.text = data;
return Promise.resolve();
}
}

describe('Copy to Clipboard', function () {
const { Prism, document, window } = createScopedPrismDom(this, {
languages: 'javascript',
plugins: 'copy-to-clipboard',
});


it('should work', function () {
const clipboard = new DummyClipboard();
window.navigator.clipboard = clipboard;

document.body.innerHTML = `<pre class="language-none"><code>foo</code></pre>`;
Prism.highlightAll();

const button = document.querySelector('button');
assert.notStrictEqual(button, null);

button.click();

assert.strictEqual(clipboard.text, 'foo');
});

it('should copy the current text even after the code block changes its text', function () {
const clipboard = new DummyClipboard();
window.navigator.clipboard = clipboard;

document.body.innerHTML = `<pre class="language-none"><code>foo</code></pre>`;
Prism.highlightAll();

const button = document.querySelector('button');
assert.notStrictEqual(button, null);

button.click();

assert.strictEqual(clipboard.text, 'foo');

// change text
document.querySelector('code').textContent = 'bar';
// and click
button.click();

assert.strictEqual(clipboard.text, 'bar');

// change text
document.querySelector('code').textContent = 'baz';
Prism.highlightAll();
// and click
button.click();

assert.strictEqual(clipboard.text, 'baz');
});

});
69 changes: 69 additions & 0 deletions tests/plugins/custom-class/basic-functionality.js
@@ -0,0 +1,69 @@
const { createUtil, createScopedPrismDom } = require('../../helper/prism-dom-util');


describe('Custom class', function () {
const { Prism, window } = createScopedPrismDom(this, {
languages: 'javascript',
plugins: 'custom-class'
});
const util = createUtil(window);


it('should set prefix', function () {
Prism.plugins.customClass.prefix('prism-');

util.assert.highlight({
language: 'javascript',
code: `var a = true;`,
expected: `<span class="prism-token prism-keyword">var</span> a <span class="prism-token prism-operator">=</span> <span class="prism-token prism-boolean">true</span><span class="prism-token prism-punctuation">;</span>`
});
});

it('should reset prefix', function () {
Prism.plugins.customClass.prefix('');

util.assert.highlight({
language: 'javascript',
code: `var a = true;`,
expected: `<span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>`
});
});

it('should map class names using a function', function () {
Prism.plugins.customClass.map(function (cls, language) {
return `${language}-${cls}`;
});

util.assert.highlight({
language: 'javascript',
code: `var a = true;`,
expected: `<span class="javascript-token javascript-keyword">var</span> a <span class="javascript-token javascript-operator">=</span> <span class="javascript-token javascript-boolean">true</span><span class="javascript-token javascript-punctuation">;</span>`
});
});

it('should map class names using an object', function () {
Prism.plugins.customClass.map({
boolean: 'b',
keyword: 'kw',
operator: 'op',
punctuation: 'p'
});

util.assert.highlight({
language: 'javascript',
code: `var a = true;`,
expected: `<span class="token kw">var</span> a <span class="token op">=</span> <span class="token b">true</span><span class="token p">;</span>`
});
});

it('should reset map', function () {
Prism.plugins.customClass.map({});

util.assert.highlight({
language: 'javascript',
code: `var a = true;`,
expected: `<span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>`
});
});

});
20 changes: 20 additions & 0 deletions tests/plugins/highlight-keywords/basic-functionality.js
@@ -0,0 +1,20 @@
const { createUtil, createScopedPrismDom } = require('../../helper/prism-dom-util');


describe('Highlight Keywords', function () {
const { window } = createScopedPrismDom(this, {
languages: 'javascript',
plugins: 'highlight-keywords'
});
const util = createUtil(window);


it('should highlight keywords', function () {
util.assert.highlightElement({
language: 'javascript',
code: `import * from ''; const foo;`,
expected: `<span class="token keyword keyword-import">import</span> <span class="token operator">*</span> <span class="token keyword keyword-from">from</span> <span class="token string">''</span><span class="token punctuation">;</span> <span class="token keyword keyword-const">const</span> foo<span class="token punctuation">;</span>`
});
});

});

0 comments on commit a394a14

Please sign in to comment.