Add unit tests to the Keep Markup plugin (#1646) Uses JSDOM to simulate the DOM in node.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
diff --git a/package.json b/package.json
index b4e616b..dce27e1 100755
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"main": "prism.js",
"style": "themes/prism.css",
"scripts": {
- "test": "mocha tests/testrunner-tests.js && mocha tests/run.js"
+ "test": "mocha tests/testrunner-tests.js && mocha tests/run.js && mocha tests/plugins/**/*.js"
},
"repository": {
"type": "git",
@@ -29,6 +29,7 @@
"gulp-rename": "^1.2.0",
"gulp-replace": "^1.0.0",
"gulp-uglify": "^3.0.1",
+ "jsdom": "^13.0.0",
"mocha": "^6.0.0",
"pump": "^3.0.0",
"yargs": "^13.2.2"
diff --git a/plugins/keep-markup/prism-keep-markup.js b/plugins/keep-markup/prism-keep-markup.js
index bc136c6..3f87089 100644
--- a/plugins/keep-markup/prism-keep-markup.js
+++ b/plugins/keep-markup/prism-keep-markup.js
@@ -1,4 +1,4 @@
-(function () {
+(function (self, document) {
if (typeof self === 'undefined' || !self.Prism || !self.document || !document.createRange) {
return;
@@ -96,4 +96,4 @@
env.highlightedCode = env.element.innerHTML;
}
});
-}());
+}(self, document));
diff --git a/plugins/keep-markup/prism-keep-markup.min.js b/plugins/keep-markup/prism-keep-markup.min.js
index 5d072e9..a0fa98a 100644
--- a/plugins/keep-markup/prism-keep-markup.min.js
+++ b/plugins/keep-markup/prism-keep-markup.min.js
@@ -1 +1 @@
-"undefined"!=typeof self&&self.Prism&&self.document&&document.createRange&&(Prism.plugins.KeepMarkup=!0,Prism.hooks.add("before-highlight",function(e){if(e.element.children.length){var a=0,s=[],l=function(e,n){var o={};n||(o.clone=e.cloneNode(!1),o.posOpen=a,s.push(o));for(var t=0,d=e.childNodes.length;t<d;t++){var r=e.childNodes[t];1===r.nodeType?l(r):3===r.nodeType&&(a+=r.data.length)}n||(o.posClose=a)};l(e.element,!0),s&&s.length&&(e.keepMarkup=s)}}),Prism.hooks.add("after-highlight",function(n){if(n.keepMarkup&&n.keepMarkup.length){var a=function(e,n){for(var o=0,t=e.childNodes.length;o<t;o++){var d=e.childNodes[o];if(1===d.nodeType){if(!a(d,n))return!1}else 3===d.nodeType&&(!n.nodeStart&&n.pos+d.data.length>n.node.posOpen&&(n.nodeStart=d,n.nodeStartPos=n.node.posOpen-n.pos),n.nodeStart&&n.pos+d.data.length>=n.node.posClose&&(n.nodeEnd=d,n.nodeEndPos=n.node.posClose-n.pos),n.pos+=d.data.length);if(n.nodeStart&&n.nodeEnd){var r=document.createRange();return r.setStart(n.nodeStart,n.nodeStartPos),r.setEnd(n.nodeEnd,n.nodeEndPos),n.node.clone.appendChild(r.extractContents()),r.insertNode(n.node.clone),r.detach(),!1}}return!0};n.keepMarkup.forEach(function(e){a(n.element,{node:e,pos:0})}),n.highlightedCode=n.element.innerHTML}}));
\ No newline at end of file
+"undefined"!=typeof self&&self.Prism&&self.document&&document.createRange&&(Prism.plugins.KeepMarkup=!0,Prism.hooks.add("before-highlight",function(e){if(e.element.children.length){var a=0,s=[],l=function(e,n){var o={};n||(o.clone=e.cloneNode(!1),o.posOpen=a,s.push(o));for(var t=0,d=e.childNodes.length;t<d;t++){var r=e.childNodes[t];1===r.nodeType?l(r):3===r.nodeType&&(a+=r.data.length)}n||(o.posClose=a)};l(e.element,!0),s&&s.length&&(e.keepMarkup=s)}}),Prism.hooks.add("after-highlight",function(n){if(n.keepMarkup&&n.keepMarkup.length){var a=function(e,n){for(var o=0,t=e.childNodes.length;o<t;o++){var d=e.childNodes[o];if(1===d.nodeType){if(!a(d,n))return!1}else 3===d.nodeType&&(!n.nodeStart&&n.pos+d.data.length>n.node.posOpen&&(n.nodeStart=d,n.nodeStartPos=n.node.posOpen-n.pos),n.nodeStart&&n.pos+d.data.length>=n.node.posClose&&(n.nodeEnd=d,n.nodeEndPos=n.node.posClose-n.pos),n.pos+=d.data.length);if(n.nodeStart&&n.nodeEnd){var r=document.createRange();return r.setStart(n.nodeStart,n.nodeStartPos),r.setEnd(n.nodeEnd,n.nodeEndPos),n.node.clone.appendChild(r.extractContents()),r.insertNode(n.node.clone),r.detach(),!1}}return!0};n.keepMarkup.forEach(function(e){a(n.element,{node:e,pos:0})}),n.highlightedCode=n.element.innerHTML}}));
diff --git a/tests/plugins/keep-markup/test.js b/tests/plugins/keep-markup/test.js
new file mode 100644
index 0000000..bb0e78b
--- /dev/null
+++ b/tests/plugins/keep-markup/test.js
@@ -0,0 +1,92 @@
+const expect = require('chai').expect;
+const jsdom = require('jsdom')
+const { JSDOM } = jsdom
+
+require('../../../prism')
+// fake DOM
+global.self = {}
+global.self.Prism = Prism
+global.document = {}
+document.createRange = function () {
+}
+global.self.document = document
+
+require('../../../plugins/keep-markup/prism-keep-markup')
+
+describe('Prism Keep Markup Plugin', function () {
+
+ function execute (code) {
+ const start = [];
+ const end = [];
+ const nodes = [];
+ document.createRange = function () {
+ return {
+ setStart: function (node, offset) {
+ start.push({ node, offset })
+ },
+ setEnd: function (node, offset) {
+ end.push({ node, offset })
+ },
+ extractContents: function () {
+ return new JSDOM('').window.document.createTextNode('')
+ },
+ insertNode: function (node) {
+ nodes.push(node)
+ },
+ detach: function () {
+ }
+ }
+ }
+ const beforeHighlight = Prism.hooks.all['before-highlight'][0]
+ const afterHighlight = Prism.hooks.all['after-highlight'][0]
+ const env = {
+ element: new JSDOM(code).window.document.getElementsByTagName('code')[0],
+ language: "javascript"
+ }
+ beforeHighlight(env)
+ afterHighlight(env)
+ return { start, end, nodes }
+ }
+
+ it('should keep <span> markup', function () {
+ const result = execute(`<code class="language-javascript">x<span>a</span>y</code>`)
+ expect(result.start.length).to.equal(1)
+ expect(result.end.length).to.equal(1)
+ expect(result.nodes.length).to.equal(1)
+ expect(result.nodes[0].nodeName).to.equal('SPAN')
+ })
+ it('should preserve markup order', function () {
+ const result = execute(`<code class="language-javascript">x<a></a><b></b>y</code>`)
+ expect(result.start.length).to.equal(2)
+ expect(result.start[0].offset).to.equal(0)
+ expect(result.start[0].node.textContent).to.equal('y')
+ expect(result.start[1].offset).to.equal(0)
+ expect(result.start[1].node.textContent).to.equal('y')
+ expect(result.end.length).to.equal(2)
+ expect(result.end[0].offset).to.equal(0)
+ expect(result.end[0].node.textContent).to.equal('y')
+ expect(result.end[1].offset).to.equal(0)
+ expect(result.end[1].node.textContent).to.equal('y')
+ expect(result.nodes.length).to.equal(2)
+ expect(result.nodes[0].nodeName).to.equal('A')
+ expect(result.nodes[1].nodeName).to.equal('B')
+ })
+ it('should keep last <span> markup', function () {
+ const result = execute(`<code class="language-javascript">xy<span>a</span></code>`)
+ expect(result.start.length).to.equal(1)
+ expect(result.end.length).to.equal(1)
+ expect(result.nodes.length).to.equal(1)
+ expect(result.nodes[0].nodeName).to.equal('SPAN')
+ })
+ // The markup is removed if it's the last element and the element's name is a single letter: a(nchor), b(old), i(talic)...
+ // https://github.com/PrismJS/prism/issues/1618
+ /*
+ it('should keep last single letter empty markup', function () {
+ const result = execute(`<code class="language-javascript">xy<a></a></code>`)
+ expect(result.start.length).to.equal(1)
+ expect(result.end.length).to.equal(1)
+ expect(result.nodes.length).to.equal(1)
+ expect(result.nodes[0].nodeName).to.equal('A')
+ })
+ */
+})