jodit
Version:
Jodit is awesome and usefully wysiwyg editor with filebrowser
1,932 lines (1,587 loc) • 56.9 kB
JavaScript
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2020 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
describe('Test plugins', function() {
getBox().style.width = 'auto';
describe('Copy format plugin', function() {
it('Should copy fontWeight from element and paste it in new selection', function() {
getBox().style.width = 'auto';
const editor = getJodit();
editor.value = '<div>text <strong>test</strong> post</div>';
editor.s.focus();
editor.s.setCursorIn(editor.editor.querySelector('strong'));
expect(getButton('copyformat', editor)).is.not.null;
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('false');
simulateEvent('click', 0, getButton('copyformat', editor));
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('true');
const range = editor.s.createRange();
range.selectNode(editor.editor.firstChild.lastChild);
editor.s.selectRange(range);
simulateEvent('mouseup', 0, editor.editor);
expect(editor.value.replace('700', 'bold')).equals(
'<div>text <strong>test</strong><span style="font-weight: bold;"> post</span></div>'
);
});
it('Should copy fontSize from element and paste it in new selection', function() {
getBox().style.width = 'auto';
const editor = getJodit();
editor.value =
'<div>text <span style="font-size: 11px;">test</span> post</div>';
editor.s.focus();
editor.s.setCursorIn(editor.editor.querySelector('span'));
expect(getButton('copyformat', editor)).is.not.null;
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('false');
clickButton('copyformat', editor);
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('true');
const range = editor.s.createRange(true);
range.selectNode(editor.editor.firstChild.lastChild);
simulateEvent('mouseup', 0, editor.editor);
expect(editor.value).equals(
'<div>text <span style="font-size: 11px;">test</span><span style="font-size: 11px;"> post</span></div>'
);
});
describe('Test', function() {
it('Should copy fontSize and color from element and paste it in new selection', function() {
getBox().style.width = 'auto';
const editor = getJodit();
editor.value =
'<div>text <span style="font-size: 11px;color: rgb(255, 0, 0);">test</span> post</div>';
editor.s.focus();
editor.s.setCursorIn(editor.editor.querySelector('span'));
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('false');
clickButton('copyformat', editor);
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('true');
const range = editor.s.createRange(true);
range.selectNode(editor.editor.firstChild.lastChild);
simulateEvent('mouseup', 0, editor.editor);
expect(sortAttributes(editor.value)).equals(
'<div>text <span style="color:#FF0000;font-size:11px">test</span><span style="color:#FF0000;font-size:11px"> post</span></div>'
);
});
});
it('Should toggle active state after double click', function() {
getBox().style.width = 'auto';
const editor = getJodit();
editor.value =
'text <span style="font-size: 11px;color: rgb(255, 0, 0);">test</span> post';
editor.s.focus();
editor.s.setCursorIn(editor.editor.querySelector('span'));
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('false');
clickButton('copyformat', editor);
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('true');
clickButton('copyformat', editor);
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('false');
const sel = editor.s.sel,
range = editor.s.createRange();
range.selectNode(editor.editor.firstChild.lastChild);
sel.removeAllRanges();
sel.addRange(range);
simulateEvent('mouseup', 0, editor.editor);
expect(sortAttributes(editor.value)).equals(
'<p>text <span style="color:#FF0000;font-size:11px">test</span> post</p>'
);
});
describe('For image', function() {
it('Should copy format from one image to another', function() {
getBox().style.width = 'auto';
const editor = getJodit(),
html =
'<p><img src="tests/artio.jpg" ' +
'style="height: 100px;width: 100px; margin: 20px; border-image: none; border:1px solid #CCCCCC; border-radius: 50%;"> test ' +
'<img style="height: 100px;width: 100px;" src="tests/artio.jpg"></p>';
editor.value = html;
editor.s.focus();
expect(sortAttributes(editor.value)).equals(
sortAttributes(html)
);
simulateEvent(
'mousedown',
0,
editor.editor.querySelector('img')
);
clickButton('copyformat', editor);
simulateEvent(
'mousedown',
0,
editor.editor.querySelectorAll('img')[1]
);
simulateEvent(
'mouseup',
0,
editor.editor.querySelectorAll('img')[1]
);
expect(sortAttributes(editor.value)).equals(
sortAttributes(
'<p><img src="tests/artio.jpg" ' +
'style="border-image:none;border-radius:50%;border:1px solid #CCCCCC;height:100px;margin:20px;width:100px"> test ' +
'<img src="tests/artio.jpg" ' +
'style="border-image:none;border-color:#CCCCCC;border-radius:50%;border-style:solid;border-width:1px;height:100px;margin:20px;width:100px"></p>'
)
);
});
});
describe('Set cursor inside em[style=background] > strong elements', function() {
it('Should copy fontWeight from strong element, copy italic and background style from em and paste it in new selection', function() {
getBox().style.width = 'auto';
const editor = getJodit();
editor.value =
'<div>text <em style="background-color: #ff0000"><strong>test</strong></em> post</div>';
editor.s.focus();
editor.s.setCursorIn(editor.editor.querySelector('strong'));
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('false');
clickButton('copyformat', editor);
expect(
getButton('copyformat', editor).getAttribute('aria-pressed')
).equals('true');
const range = editor.s.createRange(true);
range.selectNode(editor.editor.firstChild.lastChild);
simulateEvent('mouseup', 0, editor.editor);
expect(
sortAttributes(
editor
.getEditorValue()
.replace(/700/g, 'bold')
.replace(/rgb\(255, 0, 0\)/g, '#ff0000')
)
).equals(
'<div>text <em style="background-color:#ff0000"><strong>test</strong></em><span style="background-color:#ff0000;font-style:italic;font-weight:bold"> post</span></div>'
);
});
});
});
describe('Add new Line plugin', function() {
it('Should not add new line element in container before first use', function() {
const editor = getJodit();
expect(
editor.container.querySelectorAll('.jodit-add-new-line').length
).equals(0);
});
const moveCursorUnder = function(editor, elm) {
simulateEvent('mousemove', 0, editor.editor, function(e) {
const pos = Jodit.modules.Helpers.position(elm, editor);
e.clientX = pos.left + 5;
e.clientY = pos.top + 5;
});
};
it('Should show .jodit-add-new-line after user move mouse under Table,Ifrmae or IMG ', function() {
const editor = getJodit();
editor.value =
'<table>' +
'<tbody>' +
'<tr><td>1</td></tr>' +
'<tr><td>2</td></tr>' +
'<tr><td>3</td></tr>' +
'<tr><td>4</td></tr>' +
'</tbody>' +
'</table>';
window.scrollTo(
0,
Jodit.modules.Helpers.offset(
editor.editor,
editor,
editor.ownerDocument
).top
); // elementFromPoint works only with visible part of view
moveCursorUnder(editor, editor.editor.firstChild);
const newline = editor.container.querySelector(
'.jodit-add-new-line'
);
expect(newline).not.is.null;
expect(editor.ownerWindow.getComputedStyle(newline).display).equals(
'block'
);
});
it('Should add new paragraph after user clicked on newline ', function() {
const editor = getJodit();
editor.value =
'<table><tbody>' +
'<tr><td>2</td></tr>' +
'<tr><td>2</td></tr>' +
'<tr><td>3</td></tr>' +
'<tr><td>4</td></tr>' +
'</tbody></table>';
window.scrollTo(
0,
Jodit.modules.Helpers.offset(
editor.editor,
editor,
editor.ownerDocument
).top
); // elementFromPoint works only with visible part of view
moveCursorUnder(editor, editor.editor.firstChild);
const newline = editor.container.querySelector(
'.jodit-add-new-line'
);
expect(newline).not.is.null;
expect(editor.ownerWindow.getComputedStyle(newline).display).equals(
'block'
);
simulateEvent('mousedown', 0, newline.querySelector('span'));
expect(editor.getElementValue()).equals(
'<p></p><table><tbody>' +
'<tr><td>2</td></tr>' +
'<tr><td>2</td></tr>' +
'<tr><td>3</td></tr>' +
'<tr><td>4</td></tr>' +
'</tbody></table>'
);
});
it('Should add new paragraph after user clicked on newline below table', function() {
const editor = getJodit();
editor.value =
'<table><tbody>' +
'<tr><td>3</td></tr>' +
'<tr><td>2</td></tr>' +
'</tbody></table>';
window.scrollTo(
0,
Jodit.modules.Helpers.offset(
editor.editor,
editor,
editor.ownerDocument
).top
); // elementFromPoint works only with visible part of view
simulateEvent('mousemove', 0, editor.editor, function(data) {
const pos = Jodit.modules.Helpers.position(
editor.editor.firstChild,
editor
);
data.clientX = pos.left + 5;
data.clientY = pos.top + (pos.height - 5);
});
const newline = editor.container.querySelector(
'.jodit-add-new-line'
);
expect(newline).not.is.null;
expect(editor.ownerWindow.getComputedStyle(newline).display).equals(
'block'
);
simulateEvent('mousedown', 0, newline.querySelector('span'));
expect(editor.getElementValue()).equals(
'<table><tbody>' +
'<tr><td>3</td></tr>' +
'<tr><td>2</td></tr>' +
'</tbody></table><p></p>'
);
});
it('Should add new paragraph after user clicked on newline below table in IFRAME mode', function() {
const editor = getJodit({
ifarme: true
});
editor.value =
'<table><tbody>' +
'<tr><td>3</td></tr>' +
'<tr><td>2</td></tr>' +
'</tbody></table>';
window.scrollTo(
0,
Jodit.modules.Helpers.offset(
editor.editor,
editor,
editor.ownerDocument
).top
); // elementFromPoint works only with visible part of view
simulateEvent('mousemove', 0, editor.editor, function(data) {
const pos = Jodit.modules.Helpers.position(
editor.editor.firstChild,
editor
);
data.clientX = pos.left + 5;
data.clientY = pos.top + (pos.height - 5);
});
const newline = editor.container.querySelector(
'.jodit-add-new-line'
);
expect(newline).not.is.null;
expect(editor.ownerWindow.getComputedStyle(newline).display).equals(
'block'
);
simulateEvent('mousedown', 0, newline.querySelector('span'));
expect(editor.getElementValue()).equals(
'<table><tbody>' +
'<tr><td>3</td></tr>' +
'<tr><td>2</td></tr>' +
'</tbody></table><p></p>'
);
});
describe('Insert line on top of IMG element that was inside P element', function() {
it('Should insert new P before parent P element', function() {
const editor = getJodit();
editor.value =
'<p><img src="tests/artio.jpg" style="width: 100px; height: 100px;" alt=""></p>';
window.scrollTo(
0,
Jodit.modules.Helpers.offset(
editor.editor,
editor,
editor.ownerDocument
).top
); // elementFromPoint works only with visible part of view
const img = editor.editor.querySelector('img');
expect(null).does.not.equal(img);
moveCursorUnder(editor, img);
const newline = editor.container.querySelector(
'.jodit-add-new-line'
);
expect(null).does.not.equal(newline);
expect(
editor.ownerWindow.getComputedStyle(newline).display
).equals('block');
simulateEvent('mousedown', 0, newline.querySelector('span'));
editor.s.insertHTML('stop');
expect(
'<p>stop</p><p><img alt="" src="tests/artio.jpg" style="height:100px;width:100px"></p>'
).equals(sortAttributes(editor.value));
});
});
});
describe('Edit image tests', function() {
describe('Image editor', function() {
describe('Crop mode', function() {
describe('Enable ratio', function() {
it('Should deny crop image without ratio', function(done) {
const area = appendTestArea();
const editor = new Jodit(area, {
observer: {
timeout: 0
},
uploader: {
url:
'https://xdsoft.net/jodit/connector/index.php?action=upload'
},
filebrowser: {
ajax: {
url:
'https://xdsoft.net/jodit/connector/index.php'
}
},
disablePlugins: 'mobile'
});
editor.value =
'<img alt="" src="https://xdsoft.net/jodit/files/th.jpg">';
simulateEvent(
'dblclick',
0,
editor.editor.querySelector('img')
);
const dialog = getOpenedDialog(editor);
expect(dialog).is.not.null;
expect(
dialog.querySelectorAll('[data-ref="editImage"]')
.length
).equals(1);
editor.filebrowser.events.on(
'afterImageEditor',
function() {
const imageEditor = getOpenedDialog(editor);
expect(imageEditor).is.not.null;
expect(
imageEditor.querySelectorAll(
'[data-area=crop]'
).length
).equals(1);
expect(
imageEditor.querySelectorAll(
'[data-area=crop].jodit-image-editor_active'
).length
).equals(0);
simulateEvent(
'click',
0,
imageEditor.querySelector(
'[data-area=crop] > div'
)
);
expect(
imageEditor.querySelectorAll(
'[data-area=crop].jodit-image-editor_active'
).length
).equals(1);
const cropper = imageEditor.querySelector(
'.jodit-image-editor__croper'
);
expect(cropper).not.is.null;
const oldRatio =
cropper.offsetWidth / cropper.offsetHeight;
simulateEvent(
'mousedown',
0,
cropper.querySelector('.jodit_bottomright'),
function(e) {
const pos = Jodit.modules.Helpers.offset(
cropper,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width;
e.clientY = pos.top + pos.height;
}
);
simulateEvent(
'mousemove',
0,
editor.ownerWindow,
function(e) {
const pos = Jodit.modules.Helpers.offset(
cropper,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width - 50;
e.clientY = pos.top + pos.height - 150;
}
);
simulateEvent(
'mouseup',
0,
editor.ownerWindow,
function(e) {
const pos = Jodit.modules.Helpers.offset(
cropper,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width - 50;
e.clientY = pos.top + pos.height - 150;
}
);
expect(
Math.abs(
cropper.offsetWidth /
cropper.offsetHeight -
oldRatio
) < 0.02
).is.true;
done();
}
);
simulateEvent(
'click',
0,
dialog.querySelector('[data-ref="editImage"]')
);
}).timeout(7000);
});
describe('Disable ratio', function() {
it('Should allow crop image without ratio', function(done) {
const area = appendTestArea();
const editor = new Jodit(area, {
observer: {
timeout: 0
},
uploader: {
url:
'https://xdsoft.net/jodit/connector/index.php?action=upload'
},
filebrowser: {
ajax: {
url:
'https://xdsoft.net/jodit/connector/index.php'
}
}
});
editor.value =
'<img alt="" src="https://xdsoft.net/jodit/files/th.jpg">';
simulateEvent(
'dblclick',
0,
editor.editor.querySelector('img')
);
const dialog = getOpenedDialog(editor);
expect(dialog).is.not.null;
expect(
dialog.querySelectorAll('[data-ref="editImage"]')
.length
).equals(1);
editor.filebrowser.events.on(
'afterImageEditor',
function() {
const imageEditor = getOpenedDialog(editor);
expect(imageEditor).is.not.null;
expect(
imageEditor.querySelectorAll(
'[data-area=crop]'
).length
).equals(1);
expect(
imageEditor.querySelectorAll(
'[data-area=crop].jodit-image-editor_active'
).length
).equals(0);
simulateEvent(
'click',
0,
imageEditor.querySelector(
'[data-area=crop] > div'
)
);
expect(
imageEditor.querySelectorAll(
'[data-area=crop].jodit-image-editor_active'
).length
).equals(1);
const cropper = imageEditor.querySelector(
'.jodit-image-editor__croper'
);
expect(cropper).not.is.null;
const oldRatio =
cropper.offsetWidth / cropper.offsetHeight;
const disableRatioBtn = imageEditor
.querySelector(
'[data-area=crop].jodit-image-editor_active'
)
.querySelector(
'.jodit-button_radio_group button:last-child'
);
expect(disableRatioBtn).not.is.null;
simulateEvent('click', 0, disableRatioBtn);
simulateEvent(
'mousedown',
0,
cropper.querySelector('.jodit_bottomright'),
function(e) {
const pos = Jodit.modules.Helpers.offset(
cropper,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width;
e.clientY = pos.top + pos.height;
}
);
simulateEvent(
'mousemove',
0,
editor.ownerWindow,
function(e) {
const pos = Jodit.modules.Helpers.offset(
cropper,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width - 50;
e.clientY = pos.top + pos.height - 150;
}
);
simulateEvent(
'mouseup',
0,
editor.ownerWindow,
function(e) {
const pos = Jodit.modules.Helpers.offset(
cropper,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width - 50;
e.clientY = pos.top + pos.height - 150;
}
);
expect(
Math.abs(
cropper.offsetWidth /
cropper.offsetHeight -
oldRatio
) > 1
).is.true;
done();
}
);
simulateEvent(
'click',
0,
dialog.querySelector('[data-ref="editImage"]')
);
}).timeout(7000);
});
});
describe('Resize mode', function() {
describe('Enable ratio', function() {
it('Should deny resize image without ratio', function(done) {
const area = appendTestArea();
const editor = new Jodit(area, {
observer: {
timeout: 0
},
uploader: {
url:
'https://xdsoft.net/jodit/connector/index.php?action=upload'
},
filebrowser: {
ajax: {
url:
'https://xdsoft.net/jodit/connector/index.php'
}
}
});
editor.value = '<img src="tests/artio.jpg">';
simulateEvent(
'dblclick',
0,
editor.editor.querySelector('img')
);
const dialog = getOpenedDialog(editor);
editor.filebrowser.events.on(
'afterImageEditor',
function() {
const imageEditor = getOpenedDialog(editor);
expect(imageEditor).is.not.null;
expect(
imageEditor.querySelectorAll(
'[data-area=resize]'
).length
).equals(1);
expect(
imageEditor.querySelectorAll(
'[data-area=resize].jodit-image-editor_active'
).length
).equals(1); // default mode
simulateEvent(
'click',
0,
imageEditor.querySelector(
'[data-area=resize] > div'
)
);
expect(
imageEditor.querySelectorAll(
'[data-area=resize].jodit-image-editor_active'
).length
).equals(1);
const resizer = imageEditor.querySelector(
'.jodit-image-editor__resizer'
);
expect(resizer).not.is.null;
const oldRatio =
resizer.offsetWidth / resizer.offsetHeight;
simulateEvent(
'mousedown',
0,
resizer.querySelector('.jodit_bottomright'),
function(e) {
const pos = Jodit.modules.Helpers.offset(
resizer,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width;
e.clientY = pos.top + pos.height;
}
);
simulateEvent(
'mousemove',
0,
editor.ownerWindow,
function(e) {
const pos = Jodit.modules.Helpers.offset(
resizer,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width - 250;
e.clientY = pos.top + pos.height - 150;
}
);
simulateEvent(
'mouseup',
0,
editor.ownerWindow,
function(e) {
const pos = Jodit.modules.Helpers.offset(
resizer,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width - 250;
e.clientY = pos.top + pos.height - 150;
}
);
expect(
Math.abs(
resizer.offsetWidth /
resizer.offsetHeight -
oldRatio
) < 0.05
).is.true;
done();
}
);
simulateEvent(
'click',
0,
dialog.querySelector('[data-ref="editImage"]')
);
}).timeout(7000);
});
describe('Disable ratio', function() {
it('Should allow resize image without ratio', function(done) {
const area = appendTestArea();
const editor = new Jodit(area, {
observer: {
timeout: 0
},
uploader: {
url:
'https://xdsoft.net/jodit/connector/index.php?action=upload'
},
filebrowser: {
ajax: {
url:
'https://xdsoft.net/jodit/connector/index.php'
}
}
});
editor.value = '<img src="tests/artio.jpg">';
simulateEvent(
'dblclick',
0,
editor.editor.querySelector('img')
);
const dialog = getOpenedDialog(editor);
editor.filebrowser.events.on(
'afterImageEditor',
function() {
const imageEditor = getOpenedDialog(editor);
expect(imageEditor).is.not.null;
expect(
imageEditor.querySelectorAll(
'[data-area=resize]'
).length
).equals(1);
expect(
imageEditor.querySelectorAll(
'[data-area=resize].jodit-image-editor_active'
).length
).equals(1); // default mode
simulateEvent(
'click',
0,
imageEditor.querySelector(
'[data-area=resize] > div'
)
);
expect(
imageEditor.querySelectorAll(
'[data-area=resize].jodit-image-editor_active'
).length
).equals(1);
const disableRatioBtn = imageEditor
.querySelector(
'[data-area=resize].jodit-image-editor_active'
)
.querySelector(
'.jodit-button_radio_group button:last-child'
);
expect(disableRatioBtn).not.is.null;
simulateEvent('click', 0, disableRatioBtn);
const resizer = imageEditor.querySelector(
'.jodit-image-editor__resizer'
);
expect(resizer).not.is.null;
const oldRatio =
resizer.offsetWidth / resizer.offsetHeight;
simulateEvent(
'mousedown',
0,
resizer.querySelector('.jodit_bottomright'),
function(e) {
const pos = Jodit.modules.Helpers.offset(
resizer,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width;
e.clientY = pos.top + pos.height;
}
);
simulateEvent(
'mousemove',
0,
editor.ownerWindow,
function(e) {
const pos = Jodit.modules.Helpers.offset(
resizer,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width - 50;
e.clientY = pos.top + pos.height - 150;
}
);
simulateEvent(
'mouseup',
0,
editor.ownerWindow,
function(e) {
const pos = Jodit.modules.Helpers.offset(
resizer,
editor,
editor.ownerDocument
);
e.clientX = pos.left + pos.width - 50;
e.clientY = pos.top + pos.height - 150;
}
);
expect(
Math.abs(
resizer.offsetWidth /
resizer.offsetHeight -
oldRatio
) > 1
).is.true;
done();
}
);
simulateEvent(
'click',
0,
dialog.querySelector('[data-ref="editImage"]')
);
}).timeout(7000);
});
});
});
});
describe('Indent plugin', function() {
it('Should set active outdent button if current container has marginLeft', function() {
const area = appendTestArea();
const editor = new Jodit(area, {
toolbarAdaptive: false,
buttons: 'indent,outdent'
});
editor.value = '<p>text</p>';
editor.s.setCursorIn(editor.editor.firstChild.firstChild);
simulateEvent('mousedown', 0, editor.editor.firstChild);
expect(getButton('outdent', editor).hasAttribute('disabled')).is
.true;
editor.editor.firstChild.style.marginLeft = '100px';
simulateEvent('mousedown', 0, editor.editor.firstChild);
expect(getButton('outdent', editor).hasAttribute('disabled')).is
.false;
});
describe('Press Indent button', function() {
it('Should increase indent for current blocks', function() {
const area = appendTestArea();
const editor = new Jodit(area, {
toolbarAdaptive: false,
buttons: 'indent,outdent',
indentMargin: 5
});
editor.value = '<h1>test</h1><p>text</p><p>text</p>';
const range = editor.s.createRange();
range.setStartBefore(editor.editor.firstChild);
range.setEndAfter(editor.editor.firstChild.nextSibling);
editor.s.selectRange(range);
clickButton('indent', editor);
clickButton('indent', editor);
clickButton('indent', editor);
expect(
'<h1 style="margin-left: 15px;">test</h1><p style="margin-left: 15px;">text</p><p>text</p>'
).equals(editor.value);
clickButton('outdent', editor);
clickButton('outdent', editor);
clickButton('outdent', editor);
expect('<h1>test</h1><p>text</p><p>text</p>').equals(
editor.value
);
});
});
describe('Run indent command for inline elements', function() {
it('should wrap elements in block and change margin for it', function() {
const area = appendTestArea();
const editor = new Jodit(area);
editor.value = '<p>a<br>b<br>c<br></p>';
editor.s.setCursorAfter(editor.editor.firstChild.lastChild);
editor.execCommand('indent');
expect(sortAttributes(editor.value)).equals(
'<p style="margin-left:10px">a<br>b<br>c<br></p>'
);
});
});
});
describe('Symbols plugin', function() {
it('Should create symbol button in toolbar and after click open dialog with symbols', function() {
const area = appendTestArea();
const editor = new Jodit(area, {
toolbarAdaptive: false,
buttons: 'symbol'
});
editor.value = 'test';
const btn = getButton('symbol', editor);
expect(null).does.not.equal(btn);
simulateEvent('click', 0, btn);
const dialog = getOpenedDialog(editor);
expect(null).does.not.equal(dialog);
});
describe('Symbols dialog', function() {
it('Should have focus on first element after open', function() {
const area = appendTestArea();
const editor = new Jodit(area, {
toolbarAdaptive: false,
buttons: 'symbol'
});
editor.value = 'test';
const btn = getButton('symbol', editor);
expect(null).does.not.equal(btn);
simulateEvent('click', 0, btn);
const dialog = getOpenedDialog(editor);
expect(null).does.not.equal(dialog);
expect(dialog.querySelector('a')).equals(
editor.ownerDocument.activeElement
);
});
describe('Press key left', function() {
it('Should select previous element', function() {
const area = appendTestArea();
const editor = new Jodit(area, {
toolbarAdaptive: false,
buttons: 'symbol'
});
editor.value = 'test';
const btn = getButton('symbol', editor);
simulateEvent('click', 0, btn);
const dialog = getOpenedDialog(editor);
expect(null).does.not.equal(dialog);
const currentActive = dialog.getElementsByTagName('a')[10];
simulateEvent(
'keydown',
Jodit.KEY_LEFT,
currentActive,
function(data) {
data.target = currentActive;
}
);
expect(editor.ownerDocument.activeElement).equals(
dialog.getElementsByTagName('a')[9]
);
});
});
describe('Press key right', function() {
it('Should select next element', function() {
const area = appendTestArea();
const editor = new Jodit(area, {
toolbarAdaptive: false,
buttons: 'symbol'
});
editor.value = 'test';
const btn = getButton('symbol', editor);
simulateEvent('click', 0, btn);
const dialog = getOpenedDialog(editor);
expect(null).does.not.equal(dialog);
const currentActive = dialog.getElementsByTagName('a')[10];
simulateEvent(
'keydown',
Jodit.KEY_RIGHT,
currentActive,
function(data) {
data.target = currentActive;
}
);
expect(editor.ownerDocument.activeElement).equals(
dialog.getElementsByTagName('a')[11]
);
});
});
describe('Press key top', function() {
it('Should select element above', function() {
const area = appendTestArea();
const editor = new Jodit(area, {
toolbarAdaptive: false,
buttons: 'symbol'
});
editor.value = 'test';
const btn = getButton('symbol', editor);
simulateEvent('click', 0, btn);
const dialog = getOpenedDialog(editor);
expect(null).does.not.equal(dialog);
let currentActive = dialog.getElementsByTagName('a')[30];
simulateEvent(
'keydown',
Jodit.KEY_UP,
currentActive,
function(data) {
data.target = currentActive;
}
);
expect(editor.ownerDocument.activeElement).equals(
dialog.getElementsByTagName('a')[13]
);
currentActive = dialog.getElementsByTagName('a')[10];
simulateEvent(
'keydown',
Jodit.KEY_UP,
currentActive,
function(data) {
data.target = currentActive;
}
);
expect(editor.ownerDocument.activeElement).equals(
dialog.getElementsByTagName('a')[197]
);
});
});
describe('Press key bottom', function() {
it('Should select element below', function() {
const area = appendTestArea();
const editor = new Jodit(area, {
toolbarAdaptive: false,
buttons: 'symbol'
});
editor.value = 'test';
const btn = getButton('symbol', editor);
simulateEvent('click', 0, btn);
const dialog = getOpenedDialog(editor);
expect(null).does.not.equal(dialog);
let currentActive = dialog.getElementsByTagName('a')[30];
simulateEvent(
'keydown',
Jodit.KEY_DOWN,
currentActive,
function(data) {
data.target = currentActive;
}
);
expect(editor.ownerDocument.activeElement).equals(
dialog.getElementsByTagName('a')[47]
);
currentActive = dialog.getElementsByTagName('a')[200];
simulateEvent(
'keydown',
Jodit.KEY_DOWN,
currentActive,
function(data) {
data.target = currentActive;
}
);
expect(editor.ownerDocument.activeElement).equals(
dialog.getElementsByTagName('a')[13]
);
});
});
describe('Press Enter or mousdown on element', function() {
it('Should insert character', function() {
const area = appendTestArea();
const editor = new Jodit(area, {
toolbarAdaptive: false,
buttons: 'symbol'
});
editor.value = '';
const btn = getButton('symbol', editor);
simulateEvent('click', 0, btn);
let dialog = getOpenedDialog(editor);
expect(dialog).is.not.null;
const currentActive = dialog.getElementsByTagName('a')[5];
simulateEvent('keydown', Jodit.KEY_ENTER, currentActive);
expect(editor.value).equals('<p>&</p>');
simulateEvent('click', 0, btn);
dialog = getOpenedDialog(editor);
expect(dialog).is.not.null;
const currentActive2 = dialog.getElementsByTagName(
'a'
)[125];
simulateEvent('mousedown', 0, currentActive2);
expect(editor.value).equals('<p>&½</p>');
});
});
});
describe('Symbols popup', function() {
it('Should create popup this symbols', function() {
const area = appendTestArea();
const editor = new Jodit(area, {
toolbarAdaptive: false,
buttons: 'symbol',
usePopupForSpecialCharacters: true
});
editor.value = 'test';
const range = editor.s.createRange(true);
range.setStart(editor.editor.firstChild, 0);
range.collapse(true);
const btn = getButton('symbol', editor);
simulateEvent('click', 0, btn);
const dialog = getOpenedDialog(editor);
expect(null).equals(dialog);
const popup = getOpenedPopup(editor);
expect(null).does.not.equal(popup);
const currentActive = popup.getElementsByTagName('a')[125];
simulateEvent('mousedown', 0, currentActive);
expect(editor.value).equals('<p>½test</p>');
expect(popup.parentNode).is.null;
});
});
});
describe('Hotkeys', function() {
describe('Override default shortcuts for some commands', function() {
it('Should work default shortcuts for another commands', function() {
const area = appendTestArea(),
editor = new Jodit(area, {
commandToHotkeys: {
bold: 'ctrl+shift+b',
italic: ['ctrl+i', 'ctrl+shift+i']
}
});
editor.value = 'test test test';
const range = editor.s.createRange(true);
range.setStart(editor.editor.firstChild.firstChild, 4);
range.setEnd(editor.editor.firstChild.firstChild, 8);
// standart ctrl+u
simulateEvent('keydown', 85, editor.editor, function(data) {
// data.shiftKey = true;
data.ctrlKey = true;
});
expect(editor.value).equals('<p>test<u> tes</u>t test</p>');
});
describe('Replace ctrl+b to ctrl+shift+b for bold command', function() {
it('Should not execute bold on ctrl+b', function() {
const area = appendTestArea(),
editor = new Jodit(area, {
commandToHotkeys: {
bold: 'ctrl+shift+b',
italic: ['ctrl+i', 'ctrl+shift+i']
}
});
editor.value = 'test test test';
const range = editor.s.createRange(true);
range.setStart(editor.editor.firstChild.firstChild, 4);
range.setEnd(editor.editor.firstChild.firstChild, 8);
// standart ctrl+b
simulateEvent('keydown', 66, editor.editor, function(data) {
// data.shiftKey = true;
data.ctrlKey = true;
});
expect(editor.value).equals('<p>test test test</p>'); // should not sork
simulateEvent('keydown', 66, editor.editor, function(data) {
data.shiftKey = true;
data.ctrlKey = true;
});
expect(editor.value).equals(
'<p>test<strong> tes</strong>t test</p>'
);
});
it('Should execute bold on ctrl+shift+b', function() {
const area = appendTestArea(),
editor = new Jodit(area, {
commandToHotkeys: {
bold: 'ctrl+shift+b',
italic: ['ctrl+i', 'ctrl+shift+i']
}
});
editor.value = 'test test test';
const range = editor.s.createRange(true);
range.setStart(editor.editor.firstChild.firstChild, 4);
range.setEnd(editor.editor.firstChild.firstChild, 8);
simulateEvent('keydown', 66, editor.editor, function(data) {
data.shiftKey = true;
data.ctrlKey = true;
});
expect(editor.value).equals(
'<p>test<strong> tes</strong>t test</p>'
);
});
});
describe('Add ctrl+shift+i to default ctrl+i shortcut for italic command', function() {
it('Should work with each of shortcuts', function() {
const area = appendTestArea(),
editor = new Jodit(area, {
commandToHotkeys: {
bold: 'ctrl+shift+b',
italic: ['ctrl+i', 'ctrl+shift+i']
}
});
editor.value = 'test test test';
const range = editor.s.createRange(true);
range.setStart(editor.editor.firstChild.firstChild, 4);
range.setEnd(editor.editor.firstChild.firstChild, 8);
editor.s.selectRange(range);
// standart ctrl+i
simulateEvent('keydown', 'i', editor.editor, function(
data
) {
// data.shiftKey = true;
data.ctrlKey = true;
});
expect(editor.value).equals(
'<p>test<em> tes</em>t test</p>'
);
editor.value = 'test test test';
const range2 = editor.s.createRange(true);
range2.setStart(editor.editor.firstChild.firstChild, 4);
range2.setEnd(editor.editor.firstChild.firstChild, 8);
// standart ctrl+shift+i
simulateEvent('keydown', 'i', editor.editor, function(
data
) {
data.shiftKey = true;
data.ctrlKey = true;
});
expect(editor.value).equals(
'<p>test<em> tes</em>t test</p>'
);
// standart ctrl+shift+7
simulateEvent('keydown', '7', editor.editor, function(
data
) {
data.shiftKey = true;
data.ctrlKey = true;
});
expect(editor.value.replace('<br>', '')).equals(
'<ol><li>test<em> tes</em>t test</li></ol>'
);
});
});
});
});
describe('Sticky plugin', function() {
describe('Without scrolling', function() {
it('Should not have `jodit_sticky` class and toolbar must be in normal state', function() {
const area = appendTestArea(),
editor = new Jodit(area);
editor.value = '<p>stop</p>'.repeat(100);
expect(false).equals(
editor.container.classList.contains('jodit_sticky')
);
});
});
describe('Create editor in page with long text', function() {
describe('and scroll page to bottom', function() {
it('Should add to editor class `jodit_sticky` and toolbar must be always on the top', function() {
const editor = getJodit();
editor.value = '<p>stop</p>'.repeat(100);
const offset = Jodit.modules.Helpers.offset(
editor.container,
editor,
editor.ownerDocument
);
window.scroll(0, offset.top + offset.height / 2); // scroll page to bottom
simulateEvent('scroll', 0, window);
expect(true).equals(
editor.container.classList.contains('jodit_sticky')
);
expect(0).equals(
editor.toolbar.container.getBoundingClientRect().top
);
});
describe('On mobile devices - with toolbarDisableStickyForMobile = true', function() {
it('Should not add to editor class `jodit_sticky`', function() {
getBox().style.width = '370px'; // IPhone 7
const area = appendTestArea(),
editor = new Jodit(area);
editor.value = '<p>stop</p>'.repeat(100);
const offset = Jodit.modules.Helpers.offset(
editor.container,
editor,
editor.ownerDocument
);
window.scroll(0, offset.top + offset.height / 2); // scroll page to bottom
simulateEvent('scroll', 0, window);
expect(false).equals(
editor.container.classList.contains('jodit_sticky')
);
expect(0).does.not.equal(
editor.toolbar.container.getBoundingClientRect().top
);
getBox().style.width = 'auto'; // IPhone 7
});
});
describe('In iframe mode', function() {
it('Should work some way', function() {
const editor = getJodit({
iframe: true
});
editor.value = '<p>stop</p>'.repeat(100);
const offset = Jodit.modules.Helpers.offset(
editor.container,
editor,
editor.ownerDocument
);
window.scroll(0, offset.top + offset.height / 2); // scroll page to bottom
simulateEvent('scroll', 0, window);
expect(true).equals(
editor.container.classList.contains('jodit_sticky')
);
expect(0).equals(
editor.toolbar.container.getBoundingClientRect().top
);
});
});
describe('add offset for toolbar', function() {
it('Should add offset for sticky toolbar', function() {
const area = appendTestArea(),
editor = new Jodit(area, {
toolbarStickyOffset: 100
});
editor.value = '<p>stop</p>'.repeat(100);
const offset = Jodit.modules.Helpers.offset(
editor.container,
editor,
editor.ownerDocument
);
window.scroll(0, offset.top + offset.height / 2); // scroll page to bottom
simulateEvent('scroll', 0, window);
expect(true).equals(
editor.container.classList.contains('jodit_sticky')
);
expect(100).equals(
editor.toolbar.container.getBoundingClientRect().top
);
});
});
describe('with toolbarSticky false', function() {
it('Should do nothing with toolbar', function() {
const area = appendTestArea(),
editor = new Jodit(area, {
toolbarStickyOffset: 100,
toolbarSticky: false
});
editor.value = '<p>stop</p>'.repeat(100);
const offset = Jodit.modules.Helpers.offset(
editor.container,
editor,
editor.ownerDocument
);
window.scroll(0, offset.top + offset.height / 2); // scroll page to bottom
simulateEvent('scroll', 0, window);
expect(false).equals(
editor.container.classList.contains('jodit_sticky')
);
expect(100).does.not.equal(
editor.toolbar.container.getBoundingClientRect().top
);
expect(0).does.not.equal(
editor.toolbar.container.getBoundingClientRect().top
);
});
});
});
describe('and scroll page to the top', function() {
it('Should remove class `jodit_sticky` from editor and toolbar must have normal position', function() {
fillBoxBr(100);
const area = appendTestArea(),
editor = new Jodit(area),
brs = [0, 0, 0, 0, 0, 0, 0, 0, 0].map(function() {
return editor.ownerDocument.createElement('br');
});
brs.forEach(function(br) {
editor.container.parentNode.insertBefore(
br,
editor.container
);
});
editor.value = '<p>stop</p>'.repeat(100);
const offset = Jodit.modules.Helpers.offset(
editor.container,
editor,
editor.ownerDocument
);
window.scroll(0, offset.top - 200); // scroll page above editor
simulateEvent('scroll', 0, window);
expect(false).equals(
editor.container.classList.contains('jodit_sticky')
);
expect(5).to.be.above(
Math.abs(
200 -
Jodit.modules.Helpers.position(
editor.toolbar.container
).top
)
);
brs.forEach(function(br) {
br.parentNode.removeChild(br);
});
});
});
});
});
describe('Size plugin', function() {
describe('In iframe mode after change mode', function() {
it('Should set min-height to iframe', function() {
const editor = getJodit({
iframe: true,
minHeight: 300
});
editor.value = '';
editor.toggleMode();
editor.toggleMode();
expect(editor.editor.offsetHeight).to.be.above(180);
});
});
describe('Set height', function() {
it('Should set container height', function() {
const editor = getJodit({
height: 222
});
expect(editor.container.offsetHeight).equals(222);
});
});
});
describe('Fullsize plugin', function() {
describe('Toggle fullsize', function() {
it('Should resize all boxes to first state', function() {
const editor = getJodit({
observer: {
timeout: 0
}
});
const chacksizes = ['container', 'workplace', 'editor'];
const sizes = chacksizes.map(function(key) {
return editor[key].offsetHeight;
}),
equal = function(a, b) {
return Math.abs(a - b) <= 2;
};
editor.toggleFullSize(true);
chacksizes.map(function(key, index) {
expect(
equal(editor[key].offsetHeight, sizes[index])
).is.false;
});
editor.toggleFullSize(false);
chacksizes.map(function(key, index) {
expect(
equal(editor[key].offsetHeight, sizes[index])
).is.true;
});
});
});
});
describe('Path plugin', function() {
describe('After init', function() {
describe('With showXPathInStatusbar=true', function() {
it('Should show status bar', function() {
const editor = getJodit({
language: 'en',
showXPathInStatusbar: true,
showCharsCounter: false,
showWordsCounter: false,
observer: {
timeout: 0
}
});
editor.value = '<p>Simple text</p>';
const statusbar = editor.container.querySelector(
'.jodit-status-bar'
);
expect(
editor.ownerWindow.getComputedStyle(statusbar).display
).equals('flex');
});
it('Should show path to selection element', function() {
const editor = getJodit({
language: 'en',
showXPathInStatusbar: true,
observer: {
timeout: 0
}
});
editor.value = '<p>Simple text <a href="#">sss</a></p>';
editor.s.setCursorIn(editor.editor.querySelector('a'));
const statusbar = editor.container.querySelector(
'.jodit-status-bar .jodit-xpath'
);
expect(statusbar).is.not.null;
expect(statusbar.firstChild.textContent.trim()).equals('');
expect(statusbar.childNodes[1].textContent).equals('p');
expect(statusbar.childNodes[2].textContent).equals('a');
});
describe('After change selection', function() {
it('Should change path to selection element', function() {
const editor = getJodit({
language: 'en',
showXPathInStatusbar: true,
observer: {
timeout: 0
}
});
editor.value =
'<p>Simple text <a href="#">sss</a><span>s</span></p>';
editor.s.setCursorIn(editor.editor.querySelector('a'));
const statusbar = editor.container.querySelector(
'.jodit-status-bar .jodit-xpath'
);
expect(statusbar).is.not.null;
expect(statusbar.firstChild.innerText).equals('');
expect(statusbar.childNodes[1].textContent).equals('p');
expect(statusbar.childNodes[2].textContent).equals('a');
editor.s.setCursorIn(
editor.editor.querySelector('span')
);
expect(statusbar.firstChild.innerText).equals('');
expect(statusbar.childNodes[1].textContent).equals('p');
expect(statusbar.childNodes[2].textContent).equals(
'span'
);
});
});
describe('After click on element of path', function() {
it('Should select this element', function() {
const editor = getJodit({
language: 'en',
showXPathInStatusbar: true,
observer: {
timeout: 0
}
});
editor.value =
'<p>Simple text <a href="#">sss</a><span>s</span></p>';
editor.s.setCursorIn(editor.editor.querySelector('a'));
const statusbar = editor.container.querySelector(
'.jodit-status-bar .jodit-xpath'
);
expect(statusbar).is.not.null;
expect(statusbar.firstChild.innerText).equals('');
expect(statusbar.childNodes[1].textContent).equals('p');
expect(statusbar.childNodes[2].textContent).equals('a');
simulateEvent(
'click',
0,
statusbar.childNodes[2].firstChild
); // click on A
expect(
Jodit.modules.Helpers.trim(editor.s.sel.toString())
).equals('sss');
expect(statusbar.childNodes[2].textContent).equals('a');
simulateEvent(
'click',
0,
statusbar.childNodes[1].firstChild
); // click on P
expect(
Jodit.modules.Helpers.trim(editor.s.sel.toString())
).equals('Simple text ssss');
expect(statusbar.childNodes.length).equals(3);
});
});
describe('Context menu on element of path', function() {
it('Should open context menu', function() {
const editor = getJodit({
language: 'en',
showXPathInStatusbar: true,
observer: {
timeout: 0
}
});
editor.value =
'<p>Simple text <a href="#">sss</a><span>s</span></p>';
editor.