* feat: v5 BREAKING CHANGE: Mergely is no longer a jQuery plugin. BREAKING CHANGE: Removed `options.autoresize` BREAKING CHANGE: Removed `options.editor_width` BREAKING CHANGE: Removed `options.editor_height` BREAKING CHANGE: Removed `options.fadein` BREAKING CHANGE: Removed `options.fgcolor` BREAKING CHANGE: Removed `options.resize` BREAKING CHANGE: Removed `options.width` BREAKING CHANGE: Removed `options.height` BREAKING CHANGE: Removed `options.loaded` callback BREAKING CHANGE: Removed `options.resized` callback BREAKING CHANGE: Removed styles `.mergely-resizer`, `.mergely-full-screen-0`, and `.mergely-full-screen-8` BREAKING CHANGE: Changed default for `options.change_timeout` changed from `150` to `50`. BREAKING CHANGE: No longer automatically scrolls to first change. feat: CodeMirror is now an explicit dependency. feat: No longer necessary to separately require codemirror/addon/search/searchcursor feat: No longer necessary to separately require codemirror/addon/selection/mark-selection feat: `mergely.js` is now unminimized, and added new minimized version `mergely.min.js` feat: Gutter click now scrolls to any line feat: Mergely now emits `resize` event on resize feat: The UI is now non-blocking as diff now runs in background feat: Added support to provide `options.lhs` and `options.rhs` as strings feat: #16 added titles to editor.mergely.com fix: #165 block of changes at end of file are now distinguishable fix: #140 fixed performance issue with large files fix: Fixed issue where canvas markup was not rendered when `viewport` enabled fix: Fixed timing issue where swap sides may not work as expected. fix: Fixed issue where unmarkup did not emit an updated event. fix: Fixed documentation issue where `merge` incorrectly stated: from the specified `side` to the opposite side. fix: Fixed performance issue scrolling fix: Fixed issue where initial render scrolled to first change, showing it at the bottom (as opposed to middle as expected) fix: Fixed issue where line-diffs failed to diff non-alphanumeric characters * chore: tweaked no-start/end styles * feat: dark mode * chore: updated examples * chore(ci): updated webpack * chore(ci): alpha, beta, next branches * chore(ci): test * chore(ci): package-lock.json * chore(ci): ignore alpha, beta, next on branch * fix: fixes firefox scroll-linked effect issue * fix: fixes firefox scroll-linked effect issue * chore: fix css * chore: debug
264 lines
11 KiB
JavaScript
264 lines
11 KiB
JavaScript
const simple = require('simple-mock');
|
|
require('codemirror')
|
|
require('../src/mergely');
|
|
|
|
|
|
describe('markup', () => {
|
|
let editor;
|
|
let editorId = 0;
|
|
function init(options) {
|
|
editor = new window.Mergely(`#mergely-${editorId - 1}`, options);
|
|
return editor;
|
|
};
|
|
|
|
beforeEach(() => {
|
|
const div = document.createElement('div');
|
|
div.id = `mergely-${editorId++}`;
|
|
div.style.margin = '0px';
|
|
div.style.height = '275px';
|
|
document.querySelector('body').appendChild(div);
|
|
});
|
|
|
|
afterEach(() => {
|
|
if (editor._diffView.settings._debug) {
|
|
return;
|
|
}
|
|
editor && editor.unbind();
|
|
simple.restore();
|
|
editor.el.parentNode.removeChild(editor.el);
|
|
});
|
|
|
|
const LHS_LINE_START = '.mergely.lhs.d.CodeMirror-linebackground.start';
|
|
const LHS_LINE_END = '.mergely.lhs.d.CodeMirror-linebackground.end';
|
|
const LHS_LINE_NO_START = '.mergely.lhs.d.no-start.end.CodeMirror-linebackground';
|
|
const LHS_LINE_NO_END = '.mergely.lhs.d.CodeMirror-linebackground.start.no-end';
|
|
const LHS_LINE = 'span.mergely.d.lhs';
|
|
|
|
const RHS_LINE_NO_START = '.mergely.rhs.a.no-start.end.CodeMirror-linebackground';
|
|
const RHS_LINE_NO_END = '.mergely.rhs.a.CodeMirror-linebackground.start.no-end';
|
|
const RHS_LINE_START = '.mergely.rhs.a.CodeMirror-linebackground.start';
|
|
const RHS_LINE_END = '.mergely.rhs.a.CodeMirror-linebackground.end';
|
|
const RHS_LINE_TEXT = 'span.mergely.a.rhs';
|
|
|
|
const LHS_CHANGE_START = '.mergely.lhs.c.CodeMirror-linebackground.start';
|
|
const LHS_CHANGE_END = '.mergely.lhs.c.CodeMirror-linebackground.end';
|
|
const RHS_CHANGE_START = '.mergely.rhs.c.CodeMirror-linebackground.start';
|
|
const RHS_CHANGE_END = '.mergely.rhs.c.CodeMirror-linebackground.end';
|
|
|
|
const LHS_INLINE_TEXT = '.mergely.lhs.ind';
|
|
const RHS_INLINE_TEXT = '.mergely.rhs.ina';
|
|
|
|
const opts = [
|
|
{
|
|
name: 'Added and removed from the start and end (lhs)',
|
|
lhs: 'the quick brown fox\njumped over the lazy dog\n',
|
|
rhs: '\nthe quick red fox\njumped over the hairy dog\n',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_LINE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE + '.cid-0')).to.have.length(2);
|
|
expect(editor.querySelectorAll(LHS_LINE_NO_START + '.cid-1')).to.have.length(1);
|
|
|
|
expect(editor.querySelectorAll(RHS_LINE_NO_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_START + '.cid-1')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_END + '.cid-1')).to.have.length(1);
|
|
}
|
|
},
|
|
{
|
|
name: 'Added and removed from the start and end (rhs)',
|
|
lhs: '\nthe quick red fox\njumped over the hairy dog\n',
|
|
rhs: 'the quick brown fox\njumped over the lazy dog\n',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_LINE_NO_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_START + '.cid-1')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_END + '.cid-1')).to.have.length(1);
|
|
|
|
expect(editor.querySelectorAll(RHS_LINE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_NO_START + '.cid-1')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_TEXT + '.cid-0')).to.have.length(2);
|
|
}
|
|
},
|
|
{
|
|
name: 'Added and removed from the middle (lhs)',
|
|
lhs: '\nthe quick red fox\njumped over the hairy dog\n\n',
|
|
rhs: '\n\nthe quick brown fox\njumped over the lazy dog\n',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_LINE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE + '.cid-0')).to.have.length(2);
|
|
expect(editor.querySelectorAll(LHS_LINE_NO_START + '.cid-1')).to.have.length(1);
|
|
|
|
expect(editor.querySelectorAll(RHS_LINE_NO_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_START + '.cid-1')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_END + '.cid-1')).to.have.length(1);
|
|
}
|
|
},
|
|
{
|
|
name: 'Added and removed from the middle (rhs)',
|
|
lhs: '\n\nthe quick brown fox\njumped over the lazy dog\n',
|
|
rhs: '\nthe quick red fox\njumped over the hairy dog\n\n',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_LINE_NO_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_START + '.cid-1')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_END + '.cid-1')).to.have.length(1);
|
|
|
|
expect(editor.querySelectorAll(RHS_LINE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_NO_START + '.cid-1')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_TEXT + '.cid-0')).to.have.length(2);
|
|
}
|
|
},
|
|
{
|
|
name: 'One line with the other empty (lhs)',
|
|
lhs: 'the quick brown fox\n',
|
|
rhs: '',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_LINE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_NO_END + '.cid-0')).to.have.length(1);
|
|
}
|
|
},
|
|
{
|
|
name: 'One line with the other empty (rhs)',
|
|
lhs: '',
|
|
rhs: 'the quick brown fox\n',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_LINE_NO_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_END + '.cid-0')).to.have.length(1);
|
|
}
|
|
},
|
|
{
|
|
name: 'Multiple lines with the other empty (lhs)',
|
|
lhs: 'the quick brown fox\njumped over the lazy dog\n',
|
|
rhs: '',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_LINE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_NO_END + '.cid-0')).to.have.length(1);
|
|
}
|
|
},
|
|
{
|
|
name: 'Multiple lines with the other empty (rhs)',
|
|
lhs: '',
|
|
rhs: 'the quick brown fox\njumped over the lazy dog\n',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_LINE_NO_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_END + '.cid-0')).to.have.length(1);
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'Added and removed from the end (lhs)',
|
|
lhs: 'the quick brown fox\njumped over the lazy dog',
|
|
rhs: 'the quick brown fox\njumped over the lazy dog\n\nand the fence',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_LINE_NO_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_END + '.cid-0')).to.have.length(1);
|
|
}
|
|
},
|
|
{
|
|
name: 'Added and removed from the end (rhs)',
|
|
lhs: 'the quick brown fox\njumped over the lazy dog\n\nand the fence',
|
|
rhs: 'the quick brown fox\njumped over the lazy dog',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_LINE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_LINE_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_LINE_NO_START + '.cid-0')).to.have.length(1);
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'No changes (empty)',
|
|
lhs: '',
|
|
rhs: '',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll('.mergely.lhs')).to.have.length(0);
|
|
expect(editor.querySelectorAll('.mergely.rhs')).to.have.length(0);
|
|
}
|
|
},
|
|
{
|
|
name: 'No changes (with text)',
|
|
lhs: 'the quick brown fox\njumped over the lazy dog',
|
|
rhs: 'the quick brown fox\njumped over the lazy dog',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll('.mergely.lhs')).to.have.length(0);
|
|
expect(editor.querySelectorAll('.mergely.rhs')).to.have.length(0);
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'Changed lines (lhs)',
|
|
lhs: 'the quick red fox\njumped over the hairy dog',
|
|
rhs: 'the quick brown fox\njumped over the lazy dog',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
|
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
|
expect(lhs_spans).to.have.length(3);
|
|
expect(lhs_spans[0].innerText).to.equal('ed');
|
|
expect(lhs_spans[1].innerText).to.equal('h');
|
|
expect(lhs_spans[2].innerText).to.equal('ir');
|
|
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
|
expect(rhs_spans).to.have.length(4);
|
|
expect(rhs_spans[0].innerText).to.equal('b');
|
|
expect(rhs_spans[1].innerText).to.equal('own');
|
|
expect(rhs_spans[2].innerText).to.equal('l');
|
|
expect(rhs_spans[3].innerText).to.equal('z');
|
|
}
|
|
},
|
|
{
|
|
name: 'Changed lines (rhs)',
|
|
lhs: 'the quick brown fox\njumped over the lazy dog',
|
|
rhs: 'the quick red fox\njumped over the hairy dog',
|
|
check: (editor) => {
|
|
expect(editor.querySelectorAll(LHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(LHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_CHANGE_START + '.cid-0')).to.have.length(1);
|
|
expect(editor.querySelectorAll(RHS_CHANGE_END + '.cid-0')).to.have.length(1);
|
|
const lhs_spans = editor.querySelectorAll(LHS_INLINE_TEXT + '.cid-0');
|
|
expect(lhs_spans).to.have.length(4);
|
|
expect(lhs_spans[0].innerText).to.equal('b');
|
|
expect(lhs_spans[1].innerText).to.equal('own');
|
|
expect(lhs_spans[2].innerText).to.equal('l');
|
|
expect(lhs_spans[3].innerText).to.equal('z');
|
|
const rhs_spans = editor.querySelectorAll(RHS_INLINE_TEXT + '.cid-0');
|
|
expect(rhs_spans).to.have.length(3);
|
|
expect(rhs_spans[0].innerText).to.equal('ed');
|
|
expect(rhs_spans[1].innerText).to.equal('h');
|
|
expect(rhs_spans[2].innerText).to.equal('ir');
|
|
}
|
|
}
|
|
];
|
|
|
|
// to debug, add `only: true` to the test `opts` above, and run `npm run debug`
|
|
opts.forEach((opt, i) => {
|
|
const itfn = opt.only ? it.only : it;
|
|
const debug = !!opt.only;
|
|
itfn(`markup-case-${i} should markup ${opt.name}`, function (done) {
|
|
const editor = init({
|
|
height: 100,
|
|
license: 'lgpl-separate-notice',
|
|
change_timeout: 0,
|
|
_debug: debug,
|
|
lhs: (setValue) => setValue(opt.lhs),
|
|
rhs: (setValue) => setValue(opt.rhs)
|
|
});
|
|
const test = () => {
|
|
try {
|
|
opt.check(editor.el);
|
|
done();
|
|
} catch (ex) {
|
|
done(ex);
|
|
}
|
|
};
|
|
editor.once('updated', test);
|
|
});
|
|
});
|
|
});
|