medium-editor
Version:
Medium.com WYSIWYG editor clone.
347 lines (276 loc) • 15.9 kB
JavaScript
/*global fireEvent, selectElementContents,
selectElementContentsAndFire */
describe('Core-API', function () {
'use strict';
beforeEach(function () {
setupTestHelpers.call(this);
this.el = this.createElement('div', 'editor', 'lore ipsum');
});
afterEach(function () {
this.cleanupTest();
});
describe('getFocusedElement', function () {
it('should return the element which currently has a data-medium-focused attribute', function () {
var elementOne = this.createElement('div', 'editor', 'lorem ipsum'),
elementTwo = this.createElement('div', 'editor', 'lorem ipsum');
elementTwo.setAttribute('data-medium-focused', true);
var editor = this.newMediumEditor('.editor'),
focused = editor.getFocusedElement();
expect(focused).not.toBe(elementOne);
expect(focused).toBe(elementTwo);
});
it('should return the element focused via call to selectElement', function () {
var elementOne = this.createElement('div', 'editor', 'lorem ipsum'),
elementTwo = this.createElement('div', 'editor', 'lorem ipsum');
elementTwo.setAttribute('data-medium-focused', true);
var editor = this.newMediumEditor('.editor');
editor.selectElement(elementOne.firstChild);
var focused = editor.getFocusedElement();
expect(focused).toBe(elementOne);
});
});
describe('setContent', function () {
it('should set the content of the editor\'s element', function () {
var newHTML = 'Lorem ipsum dolor',
otherHTML = 'something different',
elementOne = this.createElement('div', 'editor', 'lorem ipsum'),
editor = this.newMediumEditor('.editor');
editor.setContent(newHTML);
expect(this.el.innerHTML).toEqual(newHTML);
expect(elementOne.innerHTML).not.toEqual(newHTML);
editor.setContent(otherHTML, 1);
expect(elementOne.innerHTML).toEqual(otherHTML);
expect(this.el.innerHTML).not.toEqual(otherHTML);
});
});
describe('getContent', function () {
it('should retrieve the content of the first element', function () {
var editor = this.newMediumEditor('.editor');
expect(editor.getContent()).toEqual('lore ipsum');
});
it('should retrieve the content of the element at the specified index', function () {
var otherHTML = 'something different';
this.createElement('div', 'editor', otherHTML);
var editor = this.newMediumEditor('.editor');
expect(editor.getContent(1)).toEqual(otherHTML);
});
it('should return null if no element exists', function () {
var editor = this.newMediumEditor('.no-valid-selector');
expect(editor.getContent()).toBeNull();
});
});
describe('resetContent', function () {
it('should reset the content of all editor elements to their initial values', function () {
var initialOne = this.el.innerHTML,
initialTwo = 'Lorem ipsum dolor',
elementTwo = this.createElement('div', 'editor', initialTwo),
editor = this.newMediumEditor('.editor');
editor.setContent('<p>changed content</p>');
expect(this.el.innerHTML).not.toEqual(initialOne);
editor.setContent('<p>changed content</p>', 1);
expect(elementTwo.innerHTML).not.toEqual(initialTwo);
editor.resetContent();
expect(this.el.innerHTML).toEqual(initialOne);
expect(elementTwo.innerHTML).toEqual(initialTwo);
});
it('should reset the content of a specific element when provided', function () {
var initialOne = this.el.innerHTML,
initialTwo = 'Lorem ipsum dolor',
elementTwo = this.createElement('div', 'editor', initialTwo),
editor = this.newMediumEditor('.editor');
editor.setContent('<p>changed content</p>');
expect(this.el.innerHTML).not.toEqual(initialOne);
editor.setContent('<p>changed content</p>', 1);
expect(elementTwo.innerHTML).not.toEqual(initialTwo);
editor.resetContent(elementTwo);
expect(this.el.innerHTML).not.toEqual(initialOne);
expect(elementTwo.innerHTML).toEqual(initialTwo);
});
it('should not reset anything if an invalid element is provided', function () {
var initialOne = this.el.innerHTML,
initialTwo = 'Lorem ipsum dolor',
elementTwo = this.createElement('div', 'editor', initialTwo),
dummyElement = this.createElement('div', 'not-editor', '<p>dummy element</p>'),
editor = this.newMediumEditor('.editor');
editor.setContent('<p>changed content</p>');
expect(this.el.innerHTML).not.toEqual(initialOne);
editor.setContent('<p>changed content</p>', 1);
expect(elementTwo.innerHTML).not.toEqual(initialTwo);
editor.resetContent(dummyElement);
expect(this.el.innerHTML).not.toEqual(initialOne);
expect(elementTwo.innerHTML).not.toEqual(initialTwo);
});
});
describe('saveSelection/restoreSelection', function () {
it('should be applicable if html changes but text does not', function () {
this.el.innerHTML = 'lorem <i>ipsum</i> dolor';
var editor = this.newMediumEditor('.editor', {
toolbar: {
buttons: ['italic', 'underline', 'strikethrough']
}
}),
toolbar = editor.getExtensionByName('toolbar'),
button;
// Save selection around <i> tag
selectElementContents(editor.elements[0].querySelector('i'));
editor.saveSelection();
// Underline entire element
selectElementContents(editor.elements[0]);
button = toolbar.getToolbarElement().querySelector('[data-action="underline"]');
fireEvent(button, 'click');
// Restore selection back to <i> tag and add a <strike> tag
editor.restoreSelection();
button = toolbar.getToolbarElement().querySelector('[data-action="strikethrough"]');
fireEvent(button, 'click');
// Edge breaks this into 3 separate <u> tags for some reason...
var regex = new RegExp('^<u>lorem (<i><strike>|<strike><i>|</u><i><u><strike>)ipsum(</i></strike>|</strike></i>|</strike></u></i><u>) dolor</u>$');
expect(editor.elements[0].innerHTML).toMatch(regex);
});
});
describe('exportSelection', function () {
it('should have an index in the exported selection when it is in the second contenteditable', function () {
this.createElement('div', 'editor', 'lorem <i>ipsum</i> dolor');
var editor = this.newMediumEditor('.editor', {
toolbar: {
buttons: ['italic', 'underline', 'strikethrough']
}
});
selectElementContents(editor.elements[1].querySelector('i'));
var exportedSelection = editor.exportSelection();
expect(Object.keys(exportedSelection).sort()).toEqual(['editableElementIndex', 'end', 'start']);
expect(exportedSelection.editableElementIndex).toEqual(1);
});
});
describe('execAction', function () {
it('should pass opt directly to document.execCommand', function () {
// In order to safely spy on document.execCommand we need to disable functionality
// which overrides document.execCommand in IE & Edge
var origSupported = MediumEditor.Events.prototype.InputEventOnContenteditableSupported;
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = true;
spyOn(document, 'execCommand').and.callThrough();
var editor = this.newMediumEditor('.editor');
editor.execAction('foreColor', { value: 'red' });
expect(document.execCommand).toHaveBeenCalledWith('foreColor', false, 'red');
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = origSupported;
});
it('fontName support old style', function () {
// In order to safely spy on document.execCommand we need to disable functionality
// which overrides document.execCommand in IE & Edge
var origSupported = MediumEditor.Events.prototype.InputEventOnContenteditableSupported;
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = true;
spyOn(document, 'execCommand').and.callThrough();
var editor = this.newMediumEditor('.editor');
editor.execAction('fontName', { name: 'Tahoma' });
expect(document.execCommand).toHaveBeenCalledWith('fontName', false, 'Tahoma');
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = origSupported;
});
it('fontName support new stle', function () {
// In order to safely spy on document.execCommand we need to disable functionality
// which overrides document.execCommand in IE & Edge
var origSupported = MediumEditor.Events.prototype.InputEventOnContenteditableSupported;
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = true;
spyOn(document, 'execCommand').and.callThrough();
var editor = this.newMediumEditor('.editor');
editor.execAction('fontName', { value: 'Tahoma' });
expect(document.execCommand).toHaveBeenCalledWith('fontName', false, 'Tahoma');
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = origSupported;
});
it('fontSize support old style', function () {
// In order to safely spy on document.execCommand we need to disable functionality
// which overrides document.execCommand in IE & Edge
var origSupported = MediumEditor.Events.prototype.InputEventOnContenteditableSupported;
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = true;
spyOn(document, 'execCommand').and.callThrough();
var editor = this.newMediumEditor('.editor');
editor.execAction('fontSize', { size: 14 });
expect(document.execCommand).toHaveBeenCalledWith('fontSize', false, 14);
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = origSupported;
});
it('fontSize support new stle', function () {
// In order to safely spy on document.execCommand we need to disable functionality
// which overrides document.execCommand in IE & Edge
var origSupported = MediumEditor.Events.prototype.InputEventOnContenteditableSupported;
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = true;
spyOn(document, 'execCommand').and.callThrough();
var editor = this.newMediumEditor('.editor');
editor.execAction('fontSize', { value: 14 });
expect(document.execCommand).toHaveBeenCalledWith('fontSize', false, 14);
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = origSupported;
});
it('createLink support old style', function () {
// In order to safely spy on document.execCommand we need to disable functionality
// which overrides document.execCommand in IE & Edge
var origSupported = MediumEditor.Events.prototype.InputEventOnContenteditableSupported;
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = true;
spyOn(document, 'execCommand').and.callThrough();
var editor = this.newMediumEditor('.editor');
selectElementContentsAndFire(editor.elements[0].firstChild);
jasmine.clock().tick(1);
editor.execAction('createLink', { url: 'http://www.test.com' });
expect(document.execCommand).toHaveBeenCalledWith('createLink', false, 'http://www.test.com');
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = origSupported;
});
it('createLink support new style', function () {
// In order to safely spy on document.execCommand we need to disable functionality
// which overrides document.execCommand in IE & Edge
var origSupported = MediumEditor.Events.prototype.InputEventOnContenteditableSupported;
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = true;
spyOn(document, 'execCommand').and.callThrough();
var editor = this.newMediumEditor('.editor');
selectElementContentsAndFire(editor.elements[0].firstChild);
jasmine.clock().tick(1);
editor.execAction('createLink', { value: 'http://www.test.com' });
expect(document.execCommand).toHaveBeenCalledWith('createLink', false, 'http://www.test.com');
MediumEditor.Events.prototype.InputEventOnContenteditableSupported = origSupported;
});
});
describe('checkContentChanged', function () {
it('should trigger editableInput when called after the html has changed', function () {
var editor = this.newMediumEditor('.editor', {
toolbar: {
buttons: ['italic', 'underline', 'strikethrough']
}
}),
spy = jasmine.createSpy('handler');
editor.subscribe('editableInput', spy);
expect(spy).not.toHaveBeenCalled();
selectElementContentsAndFire(this.el.firstChild);
jasmine.clock().tick(1);
this.el.innerHTML = 'lorem ipsum';
expect(spy).not.toHaveBeenCalled();
var obj = { target: this.el, currentTarget: this.el };
editor.checkContentChanged();
expect(spy).toHaveBeenCalledWith(obj, this.el);
});
it('should not trigger editableInput when called after the html has not changed', function () {
var editor = this.newMediumEditor('.editor', {
toolbar: {
buttons: ['italic', 'underline', 'strikethrough']
}
}),
spy = jasmine.createSpy('handler');
editor.subscribe('editableInput', spy);
expect(spy).not.toHaveBeenCalled();
selectElementContentsAndFire(this.el.firstChild);
jasmine.clock().tick(1);
this.el.innerHTML = 'lore ipsum';
expect(spy).not.toHaveBeenCalled();
editor.checkContentChanged();
expect(spy).not.toHaveBeenCalled();
});
});
describe('getEditorFromElement', function () {
it('should return the editor instance the element belongs to', function () {
var elTwo = this.createElement('div', 'editor-two', 'lore ipsum'),
editorOne = this.newMediumEditor('.editor'),
editorTwo = this.newMediumEditor('.editor-two');
expect(editorOne.elements[0]).toBe(this.el);
expect(editorTwo.elements[0]).toBe(elTwo);
expect(MediumEditor.getEditorFromElement(this.el)).toBe(editorOne);
expect(MediumEditor.getEditorFromElement(elTwo)).toBe(editorTwo);
});
it('should return null if the element is not within an editor', function () {
expect(MediumEditor.getEditorFromElement(this.el)).toBeNull();
});
});
});