From ce53ccaefd077aafcdc10a36bd0fe0fb4442f9ce Mon Sep 17 00:00:00 2001 From: Jamie Peabody Date: Sun, 30 Jan 2022 11:38:25 +0000 Subject: [PATCH] chore: fixed unit tests --- karma.conf.js | 26 +- package.json | 5 +- src/diff-view.js | 120 ++++----- src/diff-worker.js | 5 +- src/mergely.js | 14 +- test/mergely.spec.js | 625 ++++++++++++++++++++----------------------- webpack.dev.js | 14 +- 7 files changed, 384 insertions(+), 425 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 1293c91..59beea8 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,10 +1,17 @@ +const os = require('os'); const path = require('path'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const webpackConfig = require('./webpack.dev.js'); +const ENTROPY_SIZE = 1000000; +const outputPath = `${path.join(os.tmpdir(), "_karma_webpack_")}${Math.floor(Math.random() * ENTROPY_SIZE)}`; + module.exports = function(config) { config.set({ basePath: './', + // browserNoActivityTimeout: 3600000, + // pingTimeout: 60000, + restartOnFileChange: true, browsers: [ 'ChromeHeadless' @@ -17,7 +24,12 @@ module.exports = function(config) { files: [ 'node_modules/codemirror/lib/codemirror.css', 'src/mergely.css', - 'test/**/*.spec.js' + 'test/**/*.spec.js', + { + pattern: `${outputPath}/**/*`, + watched: false, + included: false + } ], preprocessors: { @@ -35,9 +47,17 @@ module.exports = function(config) { singleRun: true, client: { captureConsole: true, - mocha: {} + mocha: { + timeout: 50000 + } + }, + // https://github.com/scottohara/tvmanager/issues/99 + webpack: { + ...webpackConfig, + output: { + path: outputPath + } }, - webpack: webpackConfig, webpackServer: { noInfo: true } diff --git a/package.json b/package.json index 0277b2c..8563a69 100644 --- a/package.json +++ b/package.json @@ -54,15 +54,14 @@ "style-loader": "^3.3.1", "webpack": "^5.66.0", "webpack-cli": "^4.9.1", - "webpack-dev-server": "^4.7.3", - "worker-loader": "^3.0.8" + "webpack-dev-server": "^4.7.3" }, "scripts": { "build": "npm run test && npm run build:dist", "build:dist": "rm -rf lib && webpack --config ./webpack.prod.js", "start": "webpack serve --config webpack.dev.js", "examples": "http-server ./ -p 3000 -o examples/index.html", - "debug": "karma start --browsers=Chrome --single-run=false", + "debug": "karma start --timeout=28000 --browsers=Chrome --single-run=false", "test": "karma start", "release": "standard-version -a" } diff --git a/src/diff-view.js b/src/diff-view.js index cf0cf4f..8eba224 100644 --- a/src/diff-view.js +++ b/src/diff-view.js @@ -6,8 +6,6 @@ const DiffParser = require('./diff-parser'); const LCS = require('./lcs'); const VDoc = require('./vdoc'); -const { default: DiffWorker } = require('./diff-worker.js'); - /** CHANGES: @@ -18,6 +16,7 @@ CSS now prefixes `.mergely-editor`. Current active change gutter line number style changed from `.CodeMirror-linenumber` to `.CodeMirror-gutter-background`. Removed support for jquery-ui merge buttons. API switched from jQuery-style to object methods. +Removed `options.autoupdate` Removed `options.autoresize` Removed `options.fadein` Removed `options.fgcolor` @@ -25,10 +24,12 @@ Removed `options.resize` Removed `options.width` Removed `options.height` Removed `options.resized` +Removed function `mergely.update` Remove styles `.mergely-resizer`, `.mergely-full-screen-0`, and `.mergely-full-screen-8`. Default for `options.change_timeout` changed to 50. No longer necessary to separately require codemirror/addon/search/searchcursor No longer necessary to separately require codemirror/addon/selection/mark-selection +No longer automatically scrolls to first change. FEATURE: Gutter click now scrolls to any line. @@ -39,7 +40,6 @@ FIX: Fixed issue where canvas markup was not rendered when `viewport` enabled. Fixed timing issue where swap sides may not work as expected. Fixed issue where unmarkup did not emit an updated event. -Fixed issue where init triggered an updated event when autoupdate is disabled. Fixed documentation issue where `merge` incorrectly stated: from the specified `side` to the opposite side Fixed performance issue scrolling (find #) Fixed performance issue with large sections of deleted/added text @@ -75,7 +75,6 @@ const traceTimeEnd = console.timeEnd; CodeMirrorDiffView.prototype.init = function(el, options = {}) { this.settings = { - autoupdate: true, rhs_margin: 'right', wrap_lines: false, line_numbers: true, @@ -90,8 +89,6 @@ CodeMirrorDiffView.prototype.init = function(el, options = {}) { bgcolor: '#eee', vpcolor: 'rgba(0, 0, 200, 0.5)', license: 'lgpl', - width: 'auto', - height: 'auto', cmsettings: { styleSelectedText: true }, @@ -100,7 +97,7 @@ CodeMirrorDiffView.prototype.init = function(el, options = {}) { lhs: function(setValue) { }, rhs: function(setValue) { }, loaded: function() { }, - _debug: '', //scroll,draw,calc,diff,markup,change,init + _debug: '', // user supplied options ...options }; @@ -130,31 +127,44 @@ CodeMirrorDiffView.prototype.init = function(el, options = {}) { }; this._vdoc = new VDoc(); + // bind the first 'updated' event listener + el.addEventListener('updated', () => { + if (this.settings._debug.includes('event')) { + trace('event', 'updated'); + } + if (this.settings.loaded) { + this.settings.loaded(); + } + }, { once: true }); + this._setOptions(options); }; CodeMirrorDiffView.prototype.unbind = function() { + if (this._unbound) { + return; + } if (this.settings._debug.includes('api')) { trace('api', 'unbind'); } if (this._changedTimeout != null) { clearTimeout(this._changedTimeout); } - this.editor.lhs.toTextArea(); - this.editor.rhs.toTextArea(); - this._unbound = true; -}; - -CodeMirrorDiffView.prototype.remove = function() { - if (this.settings._debug.includes('api')) { - trace('api', 'remove'); + if (this.editor) { + this.editor.lhs && this.editor.lhs.toTextArea(); + this.editor.rhs && this.editor.rhs.toTextArea(); } - if (!this._unbound) { - this.unbind(); + if (this._diffWorker) { + this._diffWorker.terminate(); } while (this.el.lastChild) { this.el.removeChild(this.el.lastChild); } + if (this._origEl) { + this.el.style = this._origEl.style; + this.el.className = this._origEl.className; + } + this._unbound = true; }; CodeMirrorDiffView.prototype.lhs = function(text) { @@ -177,13 +187,6 @@ CodeMirrorDiffView.prototype.rhs = function(text) { this.editor.rhs.setValue(text); }; -CodeMirrorDiffView.prototype.update = function() { - if (this.settings._debug.includes('api')) { - trace('api', 'update'); - } - this._changing({ force: true }); -}; - CodeMirrorDiffView.prototype.unmarkup = function() { if (this.settings._debug.includes('api')) { trace('api', 'unmarkup'); @@ -216,7 +219,9 @@ CodeMirrorDiffView.prototype.scrollToDiff = function(direction) { && this.settings._debug.includes('debug')) { trace('change', 'current-diff', this._current_diff); } + // _current_diff changed, rerender this._scroll_to_change(this.changes[this._current_diff]); + this._changed(); }; CodeMirrorDiffView.prototype.mergeCurrentChange = function(side) { @@ -241,6 +246,7 @@ CodeMirrorDiffView.prototype.scrollTo = function(side, num) { ed.setCursor(num); ed.centerOnCursor(); this._renderChanges(); + this.el.dispatchEvent(new Event('updated')); }; CodeMirrorDiffView.prototype._setOptions = function(opts) { @@ -281,6 +287,7 @@ CodeMirrorDiffView.prototype._setOptions = function(opts) { // [0:margin] [1:lhs] [2:mid] [3:rhs] [4:margin], swaps 4 with 3 divs[4].parentNode.insertBefore(divs[4], divs[3]); } + this._changing(); } }; @@ -290,10 +297,6 @@ CodeMirrorDiffView.prototype.options = function(opts) { } if (opts) { this._setOptions(opts); - if (this.settings.autoupdate) { - this._clear(); - this._changed(); - } } else { return this.settings; @@ -449,6 +452,10 @@ CodeMirrorDiffView.prototype.bind = function(el) { trace('api', 'bind', el); } const { CodeMirror } = this; + this._origEl = { + style: el.style, + className: el.className + }; el.style.display = 'flex'; el.style.height = '100%'; el.style.position = 'relative'; @@ -528,7 +535,6 @@ CodeMirrorDiffView.prototype.bind = function(el) { left: (editor.offsetWidth - 300) / 2, top: (editor.offsetHeight - 58) / 3 })); - console.log('width', editor.id, editor.offsetWidth); editor.addEventListener('click', () => { splash.style.visibility = 'hidden'; splash.style.opacity = '0'; @@ -587,9 +593,6 @@ CodeMirrorDiffView.prototype.bind = function(el) { if (this.settings._debug.includes('event')) { trace('event#lhs-change'); } - if (!this.settings.autoupdate) { - return; - } this._changing(); if (this.settings._debug.includes('event')) { trace('event#lhs-change [emitted]'); @@ -612,9 +615,6 @@ CodeMirrorDiffView.prototype.bind = function(el) { if (this.settings._debug.includes('event')) { trace('event#rhs-change'); } - if (!this.settings.autoupdate) { - return; - } this._changing(); }); this.editor.rhs.on('scroll', () => { @@ -705,15 +705,6 @@ CodeMirrorDiffView.prototype.bind = function(el) { gutterClicked.call(this, 'rhs', n, ev); }); - el.addEventListener('updated', () => { - if (this.settings._debug.includes('event')) { - trace('event', 'updated'); - } - // this._initializing = false; - if (this.settings.loaded) { - this.settings.loaded(); - } - }, { once: true }); this.editor.lhs.focus(); }; @@ -944,20 +935,14 @@ CodeMirrorDiffView.prototype._scrolling = function({ side, id }) { } }; -CodeMirrorDiffView.prototype._changing = function({ force } = { force: false }) { - if (this.settings._debug.includes('change')) { +CodeMirrorDiffView.prototype._changing = function() { + if (this.settings._debug.includes('change') + || this.settings._debug.includes('api')) { traceTimeStart('change#_changing'); trace('change#_changing [start]'); } const handleChange = () => { this._changedTimeout = null; - if (!force && !this.settings.autoupdate) { - if (this.settings._debug.includes('debug') - && this.settings._debug.includes('change')) { - trace('change', '_changing', 'ignore change', force, this.settings.autoupdate); - } - return; - } this._changed(); }; if (this.settings.change_timeout > 0) { @@ -978,7 +963,8 @@ CodeMirrorDiffView.prototype._changing = function({ force } = { force: false }) }; CodeMirrorDiffView.prototype._changed = function() { - if (this.settings._debug.includes('change')) { + if (this.settings._debug.includes('change') + || this.settings._debug.includes('api')) { traceTimeStart('change#_changed'); trace('change#_changed [start]'); } @@ -1003,19 +989,21 @@ CodeMirrorDiffView.prototype._diff = function() { if (this.settings._debug.includes('debug')) { trace(' change#_diff creating diff worker'); } - this._diffWorker = new DiffWorker(); + this._diffWorker + = new Worker(new URL('./diff-worker.js', import.meta.url)); this._diffWorker.onerror = (ev) => { - console.error(' change#_diff worker error', ev); - for (const key of Object.keys(ev)) { - console.log(key); - } + console.error('Unexpected error with web worker', ev); } this._diffWorker.onmessage = (ev) => { if (this.settings._debug.includes('debug')) { trace(' change#_diff worker got message'); } this._clear(); + // after clear, set the new changes this.changes = ev.data; + if (this.settings._debug.includes('debug')) { + trace(' change#_diff render changes'); + } this._renderChanges(); this.el.dispatchEvent(new Event('updated')); } @@ -1048,7 +1036,7 @@ CodeMirrorDiffView.prototype._renderChanges = function() { } } -CodeMirrorDiffView.prototype._get_viewport_side = function(side) { +CodeMirrorDiffView.prototype._getViewportSide = function(side) { const editor = this.editor[side]; const rect = editor.getWrapperElement().getBoundingClientRect(); const topVisibleLine = editor.lineAtHeight(rect.top, 'window'); @@ -1106,8 +1094,8 @@ CodeMirrorDiffView.prototype._calculateOffsets = function (changes) { || red.getOption('lineWrapping'); const lhschc = !lineWrapping ? led.charCoords({ line: 0 }, mode) : null; const rhschc = !lineWrapping ? red.charCoords({ line: 0 }, mode) : null; - const lhsvp = this._get_viewport_side('lhs'); - const rhsvp = this._get_viewport_side('rhs'); + const lhsvp = this._getViewportSide('lhs'); + const rhsvp = this._getViewportSide('rhs'); for (const change of changes) { const llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0; @@ -1195,8 +1183,8 @@ CodeMirrorDiffView.prototype._markupLineChanges = function (changes) { rhs: red } = this.editor; const current_diff = this._current_diff; - const lhsvp = this._get_viewport_side('lhs'); - const rhsvp = this._get_viewport_side('rhs'); + const lhsvp = this._getViewportSide('lhs'); + const rhsvp = this._getViewportSide('rhs'); const { _vdoc: vdoc } = this; // use the virtual doc to markup all the changes @@ -1368,8 +1356,8 @@ CodeMirrorDiffView.prototype._renderDiff = function(changes) { ex.lhs_margin.removeEventListener('click', this._handleLhsMarginClick); ex.rhs_margin.removeEventListener('click', this._handleRhsMarginClick); - const lhsvp = this._get_viewport_side('lhs'); - const rhsvp = this._get_viewport_side('rhs'); + const lhsvp = this._getViewportSide('lhs'); + const rhsvp = this._getViewportSide('rhs'); const radius = 3; const lhsScrollTop = ex.lhs_scroller.scrollTop; diff --git a/src/diff-worker.js b/src/diff-worker.js index 3de31ec..6536d11 100644 --- a/src/diff-worker.js +++ b/src/diff-worker.js @@ -2,10 +2,11 @@ const diff = require('./diff'); const DiffParser = require('./diff-parser'); onmessage = function (ev) { - console.log('onmessage', ev.data); + if (!ev.data) { + return; + } const { lhs, rhs } = ev.data; const compare = new diff(lhs, rhs, this.settings); const changes = DiffParser(compare.normal_form()); - console.log(changes); postMessage(changes); }; diff --git a/src/mergely.js b/src/mergely.js index 934fc7a..a504847 100644 --- a/src/mergely.js +++ b/src/mergely.js @@ -15,7 +15,6 @@ class Mergely { 'merge', 'mergeCurrentChange', 'options', - 'remove', 'resize', 'rhs', 'scrollTo', @@ -23,8 +22,7 @@ class Mergely { 'search', 'summary', 'swap', - 'unmarkup', - 'update' + 'unmarkup' ]; for (const method of methods) { this[method] = this._diffView[method].bind(this._diffView); @@ -32,11 +30,13 @@ class Mergely { this._listeners = []; this.addEventListener = element.addEventListener.bind(element); this.removeEventListener = element.removeEventListener.bind(element); - // Use `setImmediate` so that clients have opportunity to bind event + + // Uset `setTimeout` so that clients have opportunity to bind event // handlers. - // FIXME: webpack 5 has issue with `setImmediate` - // setImmediate(() => this._diffView.bind(element)); - this._diffView.bind(element) + setTimeout(() => { + // it is possible to call `unbind` before this even fires + this._diffView && this._diffView.bind(element); + }, 10); } unbind() { diff --git a/test/mergely.spec.js b/test/mergely.spec.js index dcb7b43..188077f 100644 --- a/test/mergely.spec.js +++ b/test/mergely.spec.js @@ -4,8 +4,6 @@ const Mergely = require('../src/mergely'); const macbeth = require('./data/macbeth').join('\n'); const defaultOptions = { - autoupdate: true, - autoresize: true, rhs_margin: 'right', wrap_lines: false, line_numbers: true, @@ -15,22 +13,11 @@ const defaultOptions = { ignorews: false, ignorecase: false, ignoreaccents: false, - fadein: 'fast', resize_timeout: 500, change_timeout: 150, - fgcolor: { - a: '#4ba3fa', - c: '#a3a3a3', - d: '#ff7f7f', - ca: '#4b73ff', - cc: '#434343', - cd: '#ff4f4f' - }, bgcolor: '#eee', vpcolor: 'rgba(0, 0, 200, 0.5)', - license: '', - width: 'auto', - height: 'auto', + license: 'lgpl-separate-notice', cmsettings: { styleSelectedText: true } @@ -39,38 +26,54 @@ const defaultOptions = { describe('mergely', function () { let editor; function init(options) { - const div = document.createElement('div'); - div.id = 'mergely'; - div.style.margin = '0px'; - document.querySelector('body').appendChild(div); editor = new window.Mergely('#mergely', options); return editor; }; + before(() => { + const container = document.createElement('div'); + container.style.height = '275px'; + const div = document.createElement('div'); + div.id = 'mergely'; + div.style.margin = '0px'; + container.append(div); + document.querySelector('body').appendChild(container); + }); + afterEach(() => { - editor.remove(); + editor && editor.unbind(); simple.restore(); }); describe('initialization', () => { - it('initializes without arguments', () => { + it('initializes without arguments', (done) => { const editor = init({ height: 100, license: 'lgpl-separate-notice' }); expect(editor).to.exist; - const { children } = editor.el; - expect(children.length).to.equal(5); - expect(children[0].className).to.equal('mergely-margin'); - expect(children[1].className).to.equal('mergely-column'); - expect(children[2].className).to.equal('mergely-canvas'); - expect(children[3].className).to.equal('mergely-column'); - expect(children[4].className).to.equal('mergely-margin'); - expect(editor.get('lhs')).to.equal(''); - expect(editor.get('rhs')).to.equal(''); + editor.once('updated', () => { + const { children } = editor.el; + const items = Array.from(children).map(a => a.className); + // NOTE: if running karma debug, these tests can fail because + // the debugger grabs the focus and the CodeMirror instance + // loses `CodeMirror-focused` + expect(items).to.deep.equal([ + 'mergely-margin', + 'mergely-column', + 'CodeMirror cm-s-default CodeMirror-focused', + 'mergely-canvas', + 'mergely-column', + 'CodeMirror cm-s-default', + 'mergely-margin' + ]); + expect(editor.get('lhs')).to.equal(''); + expect(editor.get('rhs')).to.equal(''); + done(); + }); }); - it('initializes with static arguments for lhs/rhs text', function () { + it('initializes with static arguments for lhs/rhs text', function (done) { const editor = init({ height: 100, license: 'lgpl-separate-notice', @@ -78,82 +81,89 @@ describe('mergely', function () { rhs: (setValue) => setValue('right-hand side text') }); expect(editor).to.exist; - const { children } = editor.el; - expect(children.length).to.equal(5); - expect(children[0].className).to.equal('mergely-margin'); - expect(children[1].className).to.equal('mergely-column'); - expect(children[2].className).to.equal('mergely-canvas'); - expect(children[3].className).to.equal('mergely-column'); - expect(children[4].className).to.equal('mergely-margin'); - expect(editor.get('lhs')).to.equal('left-hand side text'); - expect(editor.get('rhs')).to.equal('right-hand side text'); + editor.once('updated', () => { + const { children } = editor.el; + const items = Array.from(children).map(a => a.className); + // NOTE: if running karma debug, these tests can fail because + // the debugger grabs the focus and the CodeMirror instance + // loses `CodeMirror-focused` + expect(items).to.deep.equal([ + 'mergely-margin', + 'mergely-column', + 'CodeMirror cm-s-default CodeMirror-focused', + 'mergely-canvas', + 'mergely-column', + 'CodeMirror cm-s-default', + 'mergely-margin' + ]); + expect(editor.get('lhs')).to.equal('left-hand side text'); + expect(editor.get('rhs')).to.equal('right-hand side text'); + done(); + }); }); - it('initializes with no sidebar', function () { + it('initializes with no sidebar', function (done) { const editor = init({ height: 100, license: 'lgpl-separate-notice', sidebar: false }); expect(editor).to.exist; - const { children } = editor.el; - expect(children.length).to.equal(5); - expect(children[0].className).to.equal('mergely-margin'); - expect(children[0].style.visibility).to.equal('hidden'); - expect(children[1].className).to.equal('mergely-column'); - expect(children[2].className).to.equal('mergely-canvas'); - expect(children[3].className).to.equal('mergely-column'); - expect(children[4].className).to.equal('mergely-margin'); - expect(children[4].style.visibility).to.equal('hidden'); + editor.once('updated', () => { + const { children } = editor.el; + const items = Array.from(children).map(a => a.className); + // NOTE: if running karma debug, these tests can fail because + // the debugger grabs the focus and the CodeMirror instance + // loses `CodeMirror-focused` + expect(items).to.deep.equal([ + 'mergely-margin', + 'mergely-column', + 'CodeMirror cm-s-default CodeMirror-focused', + 'mergely-canvas', + 'mergely-column', + 'CodeMirror cm-s-default', + 'mergely-margin' + ]); + expect(children[0].style.visibility).to.equal('hidden'); + expect(children[6].style.visibility).to.equal('hidden'); + done(); + }); }); - it('initializes with default options', function () { + it('initializes with default options', function (done) { const editor = init(); - const options = editor.options(); - expect(options).to.deep.include({ - autoupdate: true, - autoresize: true, - rhs_margin: 'right', - wrap_lines: false, - line_numbers: true, - lcs: true, - sidebar: true, - viewport: false, - ignorews: false, - ignorecase: false, - ignoreaccents: false, - fadein: 'fast', - resize_timeout: 500, - change_timeout: 150, - fgcolor: { - a: '#4ba3fa', - c: '#a3a3a3', - d: '#ff7f7f', - ca: '#4b73ff', - cc: '#434343', - cd: '#ff4f4f' - }, - bgcolor: '#eee', - vpcolor: 'rgba(0, 0, 200, 0.5)', - license: 'lgpl', - width: 'auto', - height: 'auto', - cmsettings: { - styleSelectedText: true - }, - lhs_cmsettings: {}, - rhs_cmsettings: {} + editor.once('updated', () => { + const options = editor.options(); + expect(options).to.deep.include({ + rhs_margin: 'right', + wrap_lines: false, + line_numbers: true, + lcs: true, + sidebar: true, + viewport: false, + ignorews: false, + ignorecase: false, + ignoreaccents: false, + resize_timeout: 500, + change_timeout: 50, + bgcolor: '#eee', + vpcolor: 'rgba(0, 0, 200, 0.5)', + license: 'lgpl', + cmsettings: { + styleSelectedText: true + }, + lhs_cmsettings: {}, + rhs_cmsettings: {} + }); + expect(options.lhs).to.be.a('function'); + expect(options.rhs).to.be.a('function'); + expect(options.loaded).to.be.a('function'); + done(); }); - expect(options.lhs).to.be.a('function'); - expect(options.rhs).to.be.a('function'); - expect(options.loaded).to.be.a('function'); - expect(options.resize).to.be.a('function'); - expect(options.resized).to.be.a('function'); }); it('initializes with options', function () { const initOptions = { - autoupdate: false, fgcolor: { a: 'red', c: 'green', @@ -183,57 +193,32 @@ describe('mergely', function () { }); }); - // var worker; - // beforeEach(function() { - // window.Worker = new Worker('/base/Mergely/src/diff-worker.js'); - // }); - describe('clear', () => { - it.only('should clear lhs side', function (done) { + it('should clear lhs side', function (done) { const editor = init({ height: 100, change_timeout: 0, license: 'lgpl-separate-notice', lhs: (setValue) => setValue('left-hand side text'), - rhs: (setValue) => setValue(''), - _debug: 'api,change,debug' + rhs: (setValue) => setValue('right-hand side text'), + debug: 'api,draw,event' }); - // const test = () => { - // try { - // expect(editor.get('lhs')).to.equal(''); - // expect(editor.get('rhs')).to.equal(''); - // const diff = editor.diff(); - // expect(diff).to.equal(''); - // done(); - // } catch (ex) { - // done(ex); - // } - // }; const test = () => { try { - console.log('here') expect(editor.get('lhs')).to.equal(''); - const diff = editor.diff(); - expect(diff).to.equal(''); + expect(editor.get('rhs')).to.equal('right-hand side text'); done(); } catch (ex) { done(ex); } }; - console.log('here-1') - expect(editor.get('lhs')).to.equal('left-hand side text'); - console.log('here-2') - editor.el.addEventListener('updated', test, { once: true }); - console.log('here-3') - // expect(editor.get('lhs')).to.equal(''); - // expect(editor.get('rhs')).to.equal(''); - editor.clear('lhs'); - // editor.clear('rhs'); + editor.once('updated', () => { + editor.clear('lhs'); + test(); + }); }); - }); - describe('cm', () => { - it('should get CodeMirror from lhs and rhs sides', function () { + it('should clear rhs side', function (done) { const editor = init({ height: 100, change_timeout: 0, @@ -241,11 +226,40 @@ describe('mergely', function () { lhs: (setValue) => setValue('left-hand side text'), rhs: (setValue) => setValue('right-hand side text') }); - const lcm = editor.cm('lhs'); - const rcm = editor.cm('rhs'); - expect(lcm).to.not.equal(rcm); - expect(lcm.getValue()).to.equal('left-hand side text'); - expect(rcm.getValue()).to.equal('right-hand side text'); + const test = () => { + try { + expect(editor.get('lhs')).to.equal('left-hand side text'); + expect(editor.get('rhs')).to.equal(''); + done(); + } catch (ex) { + done(ex); + } + }; + editor.once('updated', () => { + editor.clear('rhs'); + test(); + }); + }); + + }); + + describe('cm', () => { + it('should get CodeMirror from lhs and rhs sides', function (done) { + const editor = init({ + height: 100, + change_timeout: 0, + license: 'lgpl-separate-notice', + lhs: (setValue) => setValue('left-hand 2 side text'), + rhs: (setValue) => setValue('right-hand 2 side text') + }); + editor.once('updated', () => { + const lcm = editor.cm('lhs'); + const rcm = editor.cm('rhs'); + expect(lcm).to.not.equal(rcm); + expect(lcm.getValue()).to.equal('left-hand 2 side text'); + expect(rcm.getValue()).to.equal('right-hand 2 side text'); + done(); + }); }); }); @@ -258,8 +272,10 @@ describe('mergely', function () { lhs: (setValue) => setValue('left-hand side text'), rhs: (setValue) => setValue('right-hand side text') }); - expect(editor.get('lhs')).to.equal('left-hand side text'); - expect(editor.get('rhs')).to.equal('right-hand side text'); + editor.once('updated', () => { + expect(editor.get('lhs')).to.equal('left-hand side text'); + expect(editor.get('rhs')).to.equal('right-hand side text'); + }); }); }); @@ -279,9 +295,11 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); - expect(editor.get('lhs')).to.equal('left-hand side text'); - editor.lhs('banana'); + editor.once('updated', () => { + expect(editor.get('lhs')).to.equal('left-hand side text'); + editor.lhs('banana'); + test(); + }); }); }); @@ -295,15 +313,17 @@ describe('mergely', function () { }); const test = () => { try { - expect(editor.get('lhs')).to.equal('banana'); + expect(editor.get('rhs')).to.equal('banana'); done(); } catch (ex) { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); - expect(editor.get('rhs')).to.equal('right-hand side text'); - editor.lhs('banana'); + editor.once('updated', () => { + expect(editor.get('rhs')).to.equal('right-hand side text'); + editor.rhs('banana'); + test(); + }); }); }); @@ -327,10 +347,12 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); - expect(editor.get('lhs')).to.equal('left-hand side text'); - expect(editor.get('rhs')).to.equal('right-hand side text'); - editor.merge('rhs'); + editor.once('updated', () => { + expect(editor.get('lhs')).to.equal('left-hand side text'); + expect(editor.get('rhs')).to.equal('right-hand side text'); + editor.merge('rhs'); + test(); + }); }); it('should merge rhs to lhs', function (done) { @@ -352,10 +374,12 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); - expect(editor.get('lhs')).to.equal('left-hand side text'); - expect(editor.get('rhs')).to.equal('right-hand side text'); - editor.merge('lhs'); + editor.once('updated', () => { + expect(editor.get('lhs')).to.equal('left-hand side text'); + expect(editor.get('rhs')).to.equal('right-hand side text'); + editor.merge('lhs'); + test(); + }); }); }); @@ -697,7 +721,7 @@ describe('mergely', function () { license: 'lgpl-separate-notice', change_timeout: 0, lhs: (setValue) => setValue(opt.lhs), - rhs: (setValue) => setValue(opt.rhs), + rhs: (setValue) => setValue(opt.rhs) }); const test = () => { try { @@ -710,15 +734,15 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); + editor.once('updated', test); }); }); }); describe('options', () => { it('should not change any options if object empty', function (done) { + let currentOptions; const editor = init({ - height: 100, change_timeout: 0, license: 'lgpl-separate-notice', lhs: (setValue) => setValue('left-hand side text'), @@ -727,20 +751,23 @@ describe('mergely', function () { const test = () => { try { const newOptions = editor.options(); - expect(defaultOptions).to.deep.equal(newOptions); + expect(currentOptions).to.deep.equal(newOptions); done(); } catch (ex) { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); - const defaultOptions = editor.options(); - editor.options({}); + editor.once('updated', () => { + currentOptions = editor.options(); + editor.once('updated', test); + editor.options({}); + test(); + }); }); it('should change options', function (done) { + let currentOptions; const editor = init({ - height: 100, change_timeout: 0, license: 'lgpl-separate-notice', lhs: (setValue) => setValue('left-hand side text'), @@ -761,7 +788,7 @@ describe('mergely', function () { try { const newOptions = editor.options(); expect(newOptions).to.deep.equal({ - ...defaultOptions, + ...currentOptions, ...changes }); done(); @@ -769,12 +796,15 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); - const defaultOptions = editor.options(); - editor.options(changes); + editor.once('updated', () => { + currentOptions = editor.options(); + editor.once('updated', test); + editor.options(changes); + test(); + }); }); - it('should ignore white-space', function () { + it('should ignore white-space', function (done) { const editor = init({ height: 100, change_timeout: 0, @@ -783,10 +813,15 @@ describe('mergely', function () { lhs: (setValue) => setValue('\tthis is text'), rhs: (setValue) => setValue('this\tis\ttext\t\t') }); - expect(editor.diff()).to.equal(''); + editor.once('updated', () => { + expect(editor.diff()).to.equal(''); + expect(editor.el.querySelectorAll('.mergely.ch.d')).to.have.length(0); + expect(editor.el.querySelectorAll('.mergely.ch.a')).to.have.length(0); + done(); + }); }); - it('should ignore case', function () { + it('should ignore case', function (done) { const editor = init({ height: 100, change_timeout: 0, @@ -795,10 +830,13 @@ describe('mergely', function () { lhs: (setValue) => setValue('thIS IS text'), rhs: (setValue) => setValue('this is text') }); - expect(editor.diff()).to.equal(''); + editor.once('updated', () => { + expect(editor.diff()).to.equal(''); + done(); + }); }); - it('should ignore accented characters', function () { + it('should ignore accented characters', function (done) { const editor = init({ height: 100, change_timeout: 0, @@ -807,10 +845,13 @@ describe('mergely', function () { lhs: (setValue) => setValue('comunicação'), rhs: (setValue) => setValue('comunicacao') }); - expect(editor.diff()).to.equal(''); + editor.once('updated', () => { + expect(editor.diff()).to.equal(''); + done(); + }); }); - it('should ignore white-space, case, and accented characters', function () { + it('should ignore white-space, case, and accented characters', function (done) { const editor = init({ height: 100, change_timeout: 0, @@ -821,32 +862,28 @@ describe('mergely', function () { lhs: (setValue) => setValue('This is Comunicação'), rhs: (setValue) => setValue('\t\tthis\tis\tcomunicacao') }); - expect(editor.diff()).to.equal(''); + editor.once('updated', () => { + expect(editor.diff()).to.equal(''); + done(); + }); }); }); describe('resize', () => { - it('should trigger resize', function (done) { + it('should trigger update on resize', function (done) { const loaded = simple.stub(); - const resized = simple.stub(); const editor = init({ height: 100, change_timeout: 0, license: 'lgpl-separate-notice', - loaded, - resized + loaded }); - const test = () => { - try { - expect(loaded.calls).to.have.length(1); - expect(resized.calls).to.have.length(3); + editor.once('updated', () => { + editor.once('updated', () => { done(); - } catch (ex) { - done(ex); - } - }; - editor.el.addEventListener('updated', test, { once: true }); - editor.resize(); + }); + editor.resize(); + }); }); }); @@ -862,8 +899,8 @@ describe('mergely', function () { const test = () => { try { const { - _is_change_in_view: isVisible, - _get_viewport_side: getViewport, + _isChangeInView: isVisible, + _getViewportSide: getViewport, changes } = editor._diffView; let vp = getViewport.call(editor._diffView, 'lhs'); @@ -871,29 +908,31 @@ describe('mergely', function () { const change = changes[4]; expect(isVisible('lhs', vp, change)).to.be.false; expect(isVisible('rhs', vp, change)).to.be.false; - editor.el.addEventListener('updated', () => { + editor.once('updated', () => { try { vp = getViewport.call(editor._diffView, 'lhs'); - expect(vp).to.deep.equal({ from: 680, to: 708 }); + expect(vp).to.deep.equal({ from: 687, to: 703 }); expect(isVisible('lhs', vp, change)).to.be.true; expect(isVisible('rhs', vp, change)).to.be.true; done(); } catch (ex) { done(ex); } - }, { once: true }); + }); editor.scrollTo('lhs', 696); } catch (ex) { done(ex); } }; - expect(editor.diff()).to.include('696c696'); - editor.el.addEventListener('updated', test, { once: true }); + editor.once('updated', () => { + expect(editor.diff()).to.include('696c696'); + test(); + }); }); }); describe('scrollToDiff', () => { - it('should scroll next to specific diff and scroll into view', function (done) { + it('should scroll next 4 times and scroll into view', function (done) { const editor = init({ height: 100, change_timeout: 0, @@ -903,37 +942,37 @@ describe('mergely', function () { }); let count = 0; const test = () => { - if (count < 4) { - count += 1; + count += 1; + if (count <= 4) { editor.scrollToDiff('next'); return; } try { const { - _is_change_in_view: isVisible, - _get_viewport_side: getViewport, + _isChangeInView: isVisible, + _getViewportSide: getViewport, changes } = editor._diffView; - const change = changes[4]; - + const change = changes[3]; vp = getViewport.call(editor._diffView, 'lhs'); - expect(vp).to.deep.equal({ from: 666, to: 706 }); + expect(vp).to.deep.equal({ from: 673, to: 689 }); expect(isVisible('lhs', vp, change)).to.be.true; expect(isVisible('rhs', vp, change)).to.be.true; done(); } catch (ex) { done(ex); - } finally { - editor.el.removeEventListener(test); } }; - expect(editor.diff()).to.include('696c696'); - editor.el.addEventListener('updated', test); + // intentional `editor.on` + editor.on('updated', () => { + expect(editor.diff()).to.include('696c696'); + test(); + }); }); }); describe('search', () => { - it('should search', function (done) { + it('should search and scroll to match', function (done) { const editor = init({ height: 100, change_timeout: 0, @@ -943,33 +982,25 @@ describe('mergely', function () { }); let count = 0; const test = () => { - if (count < 2) { - // init, scroll, search - count += 1; - if (count === 2) { - editor.search('lhs', 'knock'); - } - return; - } try { const { - _is_change_in_view: isVisible, - _get_viewport_side: getViewport, + _isChangeInView: isVisible, + _getViewportSide: getViewport, changes } = editor._diffView; const change = changes[4]; const vp = getViewport.call(editor._diffView, 'lhs'); - expect(vp).to.deep.equal({ from: 323, to: 349 }); + expect(vp).to.deep.equal({ from: 325, to: 340 }); done(); } catch (ex) { done(ex); - } finally { - editor.el.removeEventListener(test); } }; - expect(editor.diff()).to.include('696c696'); - editor.el.addEventListener('updated', test); + editor.once('updated', () => { + editor.search('lhs', 'knock'); + test(); + }); }); }); @@ -991,10 +1022,10 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', () => { + editor.once('updated', () => { + editor.once('updated', test); editor.swap(); - editor.el.addEventListener('updated', test, { once: true }); - }, { once: true }); + }); }); }); @@ -1016,21 +1047,21 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', () => { + editor.once('updated', () => { try { const found = document.querySelector('.mergely.ch.d.lhs'); expect(found).to.not.be.null; } catch (ex) { return done(ex); } - editor.el.addEventListener('updated', test, { once: true }); + editor.once('updated', test); editor.unmarkup(); - }, { once: true }); + }); }); }); describe('update', () => { - it('should not trigger update on init when autoupdate is false', (done) => { + it('should trigger update on options', (done) => { const editor = init({ height: 100, change_timeout: 0, @@ -1039,120 +1070,40 @@ describe('mergely', function () { lhs: (setValue) => setValue('left-hand side text'), rhs: (setValue) => setValue('right-hand side text') }); - const updated = simple.stub(); - editor.el.addEventListener('updated', updated, { once: true }); - setTimeout(() => { - const found = document.querySelector('.mergely.ch.d.lhs'); - try { - expect(found).to.be.null; - expect(updated.calls).to.have.length(0); - done(); - } catch (ex) { - done(ex); - } - }, 150); + const test = () => done(); + editor.once('updated', () => { + editor.once('updated', test); + editor.options({ wrap_lines: false }); + }); }); - it('should not trigger update on options when autoupdate is false', (done) => { + it('should trigger update on lhs/rhs change', (done) => { const editor = init({ - height: 100, - change_timeout: 0, - autoupdate: false, - license: 'lgpl-separate-notice', lhs: (setValue) => setValue('left-hand side text'), rhs: (setValue) => setValue('right-hand side text') }); - const updated = simple.stub(); - editor.el.addEventListener('updated', updated, { once: true }); - setTimeout(() => { - const found = document.querySelector('.mergely.ch.d.lhs'); - try { - expect(found).to.be.null; - expect(updated.calls).to.have.length(0); - done(); - } catch (ex) { - done(ex); - } - }, 150); - editor.options({ wrap_lines: false }); - }); - - it('should not trigger update on lhs/rhs change when autoupdate is false', (done) => { - const editor = init({ - height: 100, - change_timeout: 0, - autoupdate: false, - license: 'lgpl-separate-notice', - lhs: (setValue) => setValue('left-hand side text'), - rhs: (setValue) => setValue('right-hand side text') + const test = () => done(); + editor.once('updated', () => { + editor.once('updated', test); + editor.lhs('lhs text'); + editor.rhs('rhs text'); }); - const updated = simple.stub(); - editor.el.addEventListener('updated', updated, { once: true }); - setTimeout(() => { - const found = document.querySelector('.mergely.ch.d.lhs'); - try { - expect(found).to.be.null; - expect(updated.calls).to.have.length(0); - done(); - } catch (ex) { - done(ex); - } - }, 150); - editor.lhs('lhs text'); - editor.rhs('rhs text'); }); - it('should not trigger update on scroll when autoupdate is false', (done) => { + it('should trigger update on scroll', (done) => { const editor = init({ - height: 100, - change_timeout: 0, - autoupdate: false, - license: 'lgpl-separate-notice', lhs: (setValue) => setValue(macbeth), rhs: (setValue) => setValue(macbeth.replace(/place/g, 'space')) }); - const updated = simple.stub(); - editor.el.addEventListener('updated', updated, { once: true }); - setTimeout(() => { - const found = document.querySelector('.mergely.ch.d.lhs'); - try { - expect(found).to.be.null; - expect(updated.calls).to.have.length(0); - done(); - } catch (ex) { - done(ex); - } - }, 150); - editor.scrollTo('lhs', 696); - }); - - it('should manually update when autoupdate is false', (done) => { - const editor = init({ - height: 100, - change_timeout: 0, - autoupdate: false, - license: 'lgpl-separate-notice', - lhs: (setValue) => setValue(macbeth), - rhs: (setValue) => setValue(macbeth.replace(/place/g, 'space')) - }); - const test = () => { - try { - const found = document.querySelector('.mergely.ch.d.lhs'); - expect(found).not.to.be.null; - done(); - } catch (ex) { - done(ex); - } - }; - editor.el.addEventListener('updated', test, { once: true }); - setTimeout(() => { + const test = () => done(); + editor.once('updated', () => { + editor.once('updated', test); editor.scrollTo('lhs', 696); - editor.update(); - }, 80); + }); }); }); - describe('_is_change_in_view', () => { + describe('_isChangeInView', () => { it('should be false when change less-than viewport', function (done) { const editor = init({ height: 100, @@ -1164,7 +1115,7 @@ describe('mergely', function () { }); const test = () => { try { - const { _is_change_in_view: isVisible } = editor._diffView; + const { _isChangeInView: isVisible } = editor._diffView; expect(isVisible('lhs', {from: 10, to: 20}, { 'lhs-line-from': 0, 'lhs-line-to': 9 @@ -1174,7 +1125,7 @@ describe('mergely', function () { done(ex); } } - editor.el.addEventListener('updated', test, { once: true }); + editor.once('updated', test); }); it('should be true when change less-than-equal viewport', function (done) { @@ -1188,7 +1139,7 @@ describe('mergely', function () { }); const test = () => { try { - const { _is_change_in_view: isVisible } = editor._diffView; + const { _isChangeInView: isVisible } = editor._diffView; expect(isVisible('lhs', {from: 10, to: 20}, { 'lhs-line-from': 0, 'lhs-line-to': 10 @@ -1198,7 +1149,7 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); + editor.once('updated', test); }); it('should be false when change greater-than viewport', function (done) { @@ -1212,7 +1163,7 @@ describe('mergely', function () { }); const test = () => { try { - const { _is_change_in_view: isVisible } = editor._diffView; + const { _isChangeInView: isVisible } = editor._diffView; expect(isVisible('lhs', {from: 10, to: 20}, { 'lhs-line-from': 21, 'lhs-line-to': 22 @@ -1222,7 +1173,7 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); + editor.once('updated', test); }); it('should be true when change straddles viewport from', function (done) { @@ -1236,7 +1187,7 @@ describe('mergely', function () { }); const test = () => { try { - const { _is_change_in_view: isVisible } = editor._diffView; + const { _isChangeInView: isVisible } = editor._diffView; expect(isVisible('lhs', {from: 10, to: 20}, { 'lhs-line-from': 5, 'lhs-line-to': 11 @@ -1246,7 +1197,7 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); + editor.once('updated', test); }); it('should be true when change straddles viewport to', function (done) { @@ -1260,7 +1211,7 @@ describe('mergely', function () { }); const test = () => { try { - const { _is_change_in_view: isVisible } = editor._diffView; + const { _isChangeInView: isVisible } = editor._diffView; expect(isVisible('lhs', {from: 10, to: 20}, { 'lhs-line-from': 15, 'lhs-line-to': 21 @@ -1270,7 +1221,7 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); + editor.once('updated', test); }); it('should be true when change encompasses viewport', function (done) { @@ -1284,7 +1235,7 @@ describe('mergely', function () { }); const test = () => { try { - const { _is_change_in_view: isVisible } = editor._diffView; + const { _isChangeInView: isVisible } = editor._diffView; expect(isVisible('lhs', {from: 10, to: 20}, { 'lhs-line-from': 0, 'lhs-line-to': 25 @@ -1294,7 +1245,7 @@ describe('mergely', function () { done(ex); } }; - editor.el.addEventListener('updated', test, { once: true }); + editor.once('updated', test); }); }); @@ -1308,11 +1259,13 @@ describe('mergely', function () { lhs: (setValue) => setValue(xss), rhs: (setValue) => setValue('') }); - // the value is as expected - expect(editor.get('lhs')).to.equal(xss); - // yet, shouldn't find injected script in DOM - const found = document.querySelector('#injected'); - done(found !== null); + editor.once('updated', () => { + // the value is as expected + expect(editor.get('lhs')).to.equal(xss); + // yet, shouldn't find injected script in DOM + const found = document.querySelector('#injected'); + done(found !== null); + }); }); }); }); diff --git a/webpack.dev.js b/webpack.dev.js index 5c2d325..998e2f6 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -23,11 +23,6 @@ module.exports = { }, { loader: 'css-loader' }] - }, { - test: /worker\.js$/, - use: { - loader: 'worker-loader' - } }] }, @@ -50,16 +45,18 @@ module.exports = { entry: { app: [ './examples/app', - './src/mergely', + './src/mergely' ] }, output: { - filename: 'mergely.js', + filename: 'mergely.js' }, optimization: { + chunkIds: 'named' // splitChunks: { chunks: 'all' } + /* splitChunks: { cacheGroups: { vendors: { @@ -71,5 +68,6 @@ module.exports = { minChunks: 1, minSize: 30000 } + */ } -} \ No newline at end of file +}