medium-editor
Version:
Medium.com WYSIWYG editor clone.
1,017 lines (858 loc) • 47.2 kB
JavaScript
/*global fireEvent, selectElementContents,
selectElementContentsAndFire, getEdgeVersion */
describe('Anchor Button TestCase', function () {
'use strict';
beforeEach(function () {
setupTestHelpers.call(this);
this.el = this.createElement('div', 'editor', 'lorem ipsum');
});
afterEach(function () {
this.cleanupTest();
});
describe('Anchor Form', function () {
it('should add class for visible state and remove it for invisivble', function () {
var editor = this.newMediumEditor('.editor', {
buttonLabels: 'fontawesome'
}),
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar'),
activeClass = anchorExtension.activeClass;
selectElementContentsAndFire(editor.elements[0]);
var button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
expect(anchorExtension.getForm().classList.contains(activeClass)).toBe(true);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
expect(anchorExtension.getForm().classList.contains(activeClass)).toBe(false);
});
it('should not hide the toolbar when mouseup fires inside the anchor form', function () {
var editor = this.newMediumEditor('.editor', {
buttonLabels: 'fontawesome'
}),
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
selectElementContentsAndFire(editor.elements[0]);
var button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
expect(toolbar.isDisplayed()).toBe(true);
expect(anchorExtension.isDisplayed()).toBe(true);
fireEvent(anchorExtension.getInput(), 'mouseup');
expect(toolbar.isDisplayed()).toBe(true);
expect(anchorExtension.isDisplayed()).toBe(true);
});
it('should show the form on shortcut', function () {
var editor = this.newMediumEditor('.editor'),
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar'),
code = 'K'.charCodeAt(0);
selectElementContentsAndFire(editor.elements[0]);
fireEvent(editor.elements[0], 'keydown', {
keyCode: code,
ctrlKey: true,
metaKey: true
});
expect(toolbar.isDisplayed()).toBe(true);
expect(anchorExtension.isDisplayed()).toBe(true);
});
});
describe('Link Creation', function () {
it('should create a link when user presses enter', function () {
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
var editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar'),
button, input;
selectElementContents(editor.elements[0]);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
input = editor.getExtensionByName('anchor').getInput();
input.value = 'http://test.com';
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalled();
// A trailing <br> may be added when insertHTML is used to add the link internally.
expect(this.el.innerHTML.indexOf('<a href="http://test.com">lorem ipsum</a>')).toBe(0);
});
it('should remove the extra white spaces in the link when user presses enter', function () {
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
var editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar'),
button, input;
selectElementContents(editor.elements[0]);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
input = editor.getExtensionByName('anchor').getInput();
input.value = ' test ';
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalled();
// A trailing <br> may be added when insertHTML is used to add the link internally.
expect(this.el.innerHTML.indexOf('<a href="test">lorem ipsum</a>')).toBe(0);
});
it('should not set any href if all user passes is spaces in the link when user presses enter', function () {
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
var editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar'),
button, input;
selectElementContents(editor.elements[0]);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
input = editor.getExtensionByName('anchor').getInput();
input.value = ' ';
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
//Since user only passes empty string in the url, there should be no <a> tag created for the element.
expect(this.el.innerHTML.indexOf('<a href="">lorem ipsum</a>')).toBe(-1);
});
it('should create only one anchor tag when the user selects across a boundary', function () {
this.el.innerHTML = 'Hello world, this <strong>will become a link, but this part won\'t.</strong>';
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
var editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar'),
button, input;
MediumEditor.selection.select(document, this.el.childNodes[0], 'Hello world, '.length,
this.el.childNodes[1].childNodes[0], 'will become a link'.length);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
input = editor.getExtensionByName('anchor').getInput();
input.value = 'http://test.com';
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalled();
expect(this.el.innerHTML).toMatch(/^Hello world, <a href="http:\/\/test\.com\/?">this <strong>will become a link<\/strong><\/a><strong>, but this part won\'t\.<\/strong>(<br>|<strong><\/strong>)?$/);
});
it('should create a link when the user selects text within two paragraphs', function () {
this.el.innerHTML = '<p>Hello <span>world</span>.</p>' +
'<p><strong>Let us make a link</strong> across paragraphs.</p>';
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
var editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar'),
button, input;
MediumEditor.selection.select(document, this.el.querySelector('span'), 0,
this.el.querySelector('strong'), 1);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
input = editor.getExtensionByName('anchor').getInput();
input.value = 'http://test.com';
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalled();
var anchors = this.el.querySelectorAll('a');
// Edge creates 3 links, other browsers create 2
expect(anchors.length).toBeGreaterThan(1);
expect(anchors.length).toBeLessThan(4);
var linkText = '';
Array.prototype.slice.call(anchors).forEach(function (anchor) {
linkText += anchor.textContent;
});
expect(linkText).toBe('world.Let us make a link');
var spans = this.el.querySelectorAll('span');
expect(spans.length).toBe(1);
expect(spans[0].textContent).toBe('world');
var strongs = this.el.querySelectorAll('strong');
expect(strongs.length).toBe(1);
expect(strongs[0].textContent).toBe('Let us make a link');
});
it('shouldn\'t create a link when user presses enter without value', function () {
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
var editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar'),
button,
input;
selectElementContents(editor.elements[0]);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
input = editor.getExtensionByName('anchor').getInput();
input.value = '';
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(editor.elements[0].querySelector('a')).toBeNull();
});
it('should add http:// if need be and linkValidation option is set to true', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('test.com');
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe('http://test.com/');
});
it('should add tel: if need be and linkValidation option is set to true', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('347-999-9999');
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe('tel:347-999-9999');
});
it('should not change protocol when a valid one is included', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
validUrl = 'mailto:test.com',
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(validUrl);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe(validUrl);
});
it('should not change protocol when a tel scheme is included', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
// Specifically using a bad phone number to illustrate that what follows tel is not checked
validUrl = 'tel:abc123!@#',
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(validUrl);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe(validUrl);
});
it('should not change protocol when a maps scheme is included', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
// Specifically using a non-sensical maps string to illustrate that what follows maps is not checked
validUrl = 'maps:abc123!@#',
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(validUrl);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe(validUrl);
});
it('should not change protocol for protocol-relative URLs', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
validUrl = '//test.com/',
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(validUrl);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe(window.location.protocol + validUrl);
});
it('should not change protocol for any alphabetic scheme', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
link,
validUrl = 'abcDEFgHi://test.com/',
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(validUrl);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe(validUrl.toLowerCase());
});
it('should not change fragment identifier when link begins with hash', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
validHashLink = '#!$&\'()*+,;=123abcDEF-._~:@/?',
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(validHashLink);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.getAttribute('href')).toBe(validHashLink);
});
it('should not add a scheme to an absolute path', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
absolutePath = '/test',
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(absolutePath);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.getAttribute('href')).toBe(absolutePath);
});
it('should not add a scheme to an obviously relative path', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
relativePath = 'test/file.html',
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(relativePath);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.getAttribute('href')).toBe(relativePath);
});
it('should add a scheme to a localhost url', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
localhostUrl = 'http://localhost',
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('localhost');
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.getAttribute('href')).toBe(localhostUrl);
});
it('should change spaces to %20 for a valid url if linkValidation option is set to true', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
link,
anchorExtension = editor.getExtensionByName('anchor'),
expectedOpts = {
value: 'http://te%20s%20t.com/',
target: '_self'
};
spyOn(editor, 'execAction').and.callThrough();
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('te s t.com/');
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
expect(editor.execAction).toHaveBeenCalledWith('createLink', expectedOpts);
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe(expectedOpts.value);
});
it('should not change # to %23 for a valid url if linkValidation option is set to true', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
link,
anchorExtension = editor.getExtensionByName('anchor'),
expectedOpts = {
value: 'http://example.com/?query=value#anchor',
target: '_self'
};
spyOn(editor, 'execAction').and.callThrough();
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(expectedOpts.value);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
expect(editor.execAction).toHaveBeenCalledWith('createLink', expectedOpts);
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe(expectedOpts.value);
});
it('should not encode an encoded URL if linkValidation option is set to true', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
link,
anchorExtension = editor.getExtensionByName('anchor'),
expectedOpts = {
value: 'http://a%20b.com/',
target: '_self'
};
spyOn(editor, 'execAction').and.callThrough();
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('a%20b.com/');
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
expect(editor.execAction).toHaveBeenCalledWith('createLink', expectedOpts);
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe(expectedOpts.value);
});
it('should encode query params if linkValidation option is set to true', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
link,
anchorExtension = editor.getExtensionByName('anchor'),
expectedOpts = {
value: 'http://a.com/?q=http%3A%2F%2Fb.com&q2=http%3A%2F%2Fc.com',
target: '_self'
};
spyOn(editor, 'execAction').and.callThrough();
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('a.com/?q=http://b.com&q2=http://c.com');
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
expect(editor.execAction).toHaveBeenCalledWith('createLink', expectedOpts);
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe(expectedOpts.value);
});
it('should not encode an encoded query param if linkValidation option is set to true', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: true
}
}),
link,
anchorExtension = editor.getExtensionByName('anchor'),
expectedOpts = {
value: 'http://a.com/?q=http%3A%2F%2Fb.com&q2=http%3A%2F%2Fc.com',
target: '_self'
};
spyOn(editor, 'execAction').and.callThrough();
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('a.com/?q=http%3A%2F%2Fb.com&q2=http://c.com');
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
expect(editor.execAction).toHaveBeenCalledWith('createLink', expectedOpts);
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.href).toBe(expectedOpts.value);
});
it('should not change spaces to %20 if linkValidation is set to false', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
linkValidation: false
}
}),
link,
anchorExtension = editor.getExtensionByName('anchor'),
expectedOpts = {
value: 'http://te s t.com/',
target: '_self'
};
spyOn(editor, 'execAction').and.callThrough();
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm(expectedOpts.value);
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
// Chrome, Edge, and IE will automatically escape the href once it's set on the link
// So for this case, we'll only look at the call to execAction to see what URL it was trying to set
// since the value of the link's href could be different values
expect(editor.execAction).toHaveBeenCalledWith('createLink', expectedOpts);
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
});
it('should add target="_blank" when "open in a new window" checkbox is checked', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
targetCheckbox: true
}
}),
anchorExtension = editor.getExtensionByName('anchor'),
targetCheckbox,
link;
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('http://test.com');
expect(anchorExtension.isDisplayed()).toBe(true);
targetCheckbox = anchorExtension.getForm().querySelector('input.medium-editor-toolbar-anchor-target');
expect().not.toBeNull(targetCheckbox);
targetCheckbox.checked = true;
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
expect(anchorExtension.isDisplayed()).toBe(false);
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.target).toBe('_blank');
expect(link.rel).toBe('noopener noreferrer');
});
it('should add target="_blank" when respective option is set to true', function () {
var editor = this.newMediumEditor('.editor', {
targetBlank: true
}),
link,
anchorExtension = editor.getExtensionByName('anchor');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('http://test.com');
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.target).toBe('_blank');
expect(link.rel).toBe('noopener noreferrer');
});
it('should create a button when user selects this option and presses enter', function () {
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
var editor = this.newMediumEditor('.editor', {
anchor: {
customClassOption: 'btn btn-default'
}
}),
save,
input,
button,
link,
opts,
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
selectElementContents(editor.elements[0]);
save = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(save, 'click');
input = anchorExtension.getInput();
input.value = 'http://test.com';
button = anchorExtension.getForm().querySelector('input.medium-editor-toolbar-anchor-button');
button.setAttribute('type', 'checkbox');
button.checked = true;
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
opts = {
value: 'http://test.com',
target: '_self',
buttonClass: 'btn btn-default'
};
expect(editor.createLink).toHaveBeenCalledWith(opts);
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull();
expect(link.classList.contains('btn')).toBe(true);
expect(link.classList.contains('btn-default')).toBe(true);
});
it('should create a button when user selects this option, even if selected text carries tags ' +
'and the selection doesn\'t exactly match those tags', function () {
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
this.el.innerHTML = '<p>Hello <b>world</b> !</p>';
var editor = this.newMediumEditor('.editor', {
anchor: {
customClassOption: 'btn btn-default'
}
}),
p = this.el.querySelector('p'),
b = p.childNodes[1],
emptySpacePart = p.childNodes[2],
save,
input,
button,
link,
opts,
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
MediumEditor.selection.select(document, b, 0, emptySpacePart, 1); //select world including space
save = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
input = anchorExtension.getInput();
input.value = 'http://test.com';
button = anchorExtension.getForm().querySelector('input.medium-editor-toolbar-anchor-button');
button.setAttribute('type', 'checkbox');
button.checked = true;
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
opts = {
value: 'http://test.com',
target: '_self',
buttonClass: 'btn btn-default'
};
expect(editor.createLink).toHaveBeenCalledWith(opts);
link = editor.elements[0].querySelector('a');
expect(link).not.toBeNull('Link does not exist');
expect(link.classList.contains('btn')).toBe(true, 'Link does not contain class btn');
expect(link.classList.contains('btn-default')).toBe(true, 'Link does not contain class btn-default');
});
it('should remove the target _blank from the anchor tag when the open in a new window checkbox,' +
' is unchecked and the form is saved', function () {
var editor = this.newMediumEditor('.editor', {
anchor: {
targetCheckbox: true
}
}),
anchorExtension = editor.getExtensionByName('anchor'),
targetCheckbox,
link;
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('http://test.com');
expect(anchorExtension.isDisplayed()).toBe(true);
targetCheckbox = anchorExtension.getForm().querySelector('input.medium-editor-toolbar-anchor-target');
targetCheckbox.checked = true;
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link.target).toBe('_blank');
expect(link.rel).toBe('noopener noreferrer');
selectElementContentsAndFire(editor.elements[0]);
anchorExtension.showForm('http://test.com');
targetCheckbox = anchorExtension.getForm().querySelector('input.medium-editor-toolbar-anchor-target');
targetCheckbox.checked = false;
fireEvent(anchorExtension.getForm().querySelector('a.medium-editor-toolbar-save'), 'click');
link = editor.elements[0].querySelector('a');
expect(link.target).toBe('');
});
it('should fire editableInput only once when the user creates a link open to a new window,' +
' and it should fire at the end of the DOM and selection modifications', function () {
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
this.el.innerHTML = '<p>Lorem ipsum et dolitur sunt.</p>';
var editor = this.newMediumEditor('.editor', {
anchor: {
targetCheckbox: true
}
}),
p = this.el.lastChild,
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar'),
selectionWhenEventsFired = [],
listener = function () {
selectionWhenEventsFired.push(window.getSelection().toString());
};
MediumEditor.selection.select(document, p.firstChild, 'Lorem '.length, p.firstChild, 'Lorem ipsum'.length);
fireEvent(editor.elements[0], 'focus');
jasmine.clock().tick(1);
// Click the 'anchor' button in the toolbar
fireEvent(toolbar.getToolbarElement().querySelector('[data-action="createLink"]'), 'click');
// Input a url and save
var input = anchorExtension.getInput(),
checkbox = anchorExtension.getAnchorTargetCheckbox();
input.value = 'http://www.example.com';
checkbox.checked = true;
editor.subscribe('editableInput', listener);
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalledWith({
value: 'http://www.example.com',
target: '_blank'
});
expect(window.getSelection().toString()).toBe('ipsum', 'selected text should remain selected');
expect(selectionWhenEventsFired.length).toBe(1, 'only one editableInput event should have been registered');
expect(selectionWhenEventsFired[0]).toBe('ipsum', 'selected text should have been the same when event fired');
});
// https://github.com/yabwe/medium-editor/issues/757
it('should not select empty paragraphs when link is created at beginning of paragraph after empty paragraphs', function () {
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
this.el.innerHTML = '<p>Some text</p><p><br/></p><p><br/></p><p>link text more text</p>';
var editor = this.newMediumEditor('.editor'),
lastP = this.el.lastChild,
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
// Select the text 'link text' in the last paragraph
MediumEditor.selection.select(document, lastP.firstChild, 0, lastP.firstChild, 'link text'.length);
fireEvent(editor.elements[0], 'focus');
jasmine.clock().tick(1);
// Click the 'anchor' button in the toolbar
fireEvent(toolbar.getToolbarElement().querySelector('[data-action="createLink"]'), 'click');
// Input a url and save
var input = anchorExtension.getInput();
input.value = 'http://www.example.com';
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalledWith({
value: 'http://www.example.com',
target: '_self'
});
// Make sure the <p> wasn't removed, and the <a> was added to the end
expect(this.el.lastChild).toBe(lastP);
expect(lastP.firstChild.nodeName.toLowerCase()).toBe('a');
// Make sure selection is only the link
var range = window.getSelection().getRangeAt(0);
expect(MediumEditor.util.isDescendant(lastP, range.startContainer, true)).toBe(true, 'The start of the selection is incorrect');
expect(range.startOffset).toBe(0);
expect(MediumEditor.util.isDescendant(lastP.firstChild, range.endContainer, true)).toBe(true, 'The end of the selection is not contained within the link');
});
// https://github.com/yabwe/medium-editor/issues/757
it('should not select empty paragraphs when link is created at beginning of paragraph after another paragraph', function () {
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
this.el.innerHTML = '<p>Some text</p><p>link text more text</p>';
var editor = this.newMediumEditor('.editor'),
lastP = this.el.lastChild,
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
// Select the text 'link text' in the last paragraph
MediumEditor.selection.select(document, lastP.firstChild, 0, lastP.firstChild, 'link text'.length);
fireEvent(editor.elements[0], 'focus');
jasmine.clock().tick(1);
// Click the 'anchor' button in the toolbar
fireEvent(toolbar.getToolbarElement().querySelector('[data-action="createLink"]'), 'click');
// Input a url and save
var input = anchorExtension.getInput();
input.value = 'http://www.example.com';
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalledWith({
value: 'http://www.example.com',
target: '_self'
});
// Make sure the <p> wasn't removed, and the <a> was added to the end
expect(this.el.lastChild).toBe(lastP);
expect(lastP.firstChild.nodeName.toLowerCase()).toBe('a');
// Make sure selection is only the link
var range = window.getSelection().getRangeAt(0);
expect(MediumEditor.util.isDescendant(lastP, range.startContainer, true)).toBe(true, 'The start of the selection is incorrect');
expect(range.startOffset).toBe(0);
expect(MediumEditor.util.isDescendant(lastP.firstChild, range.endContainer, true)).toBe(true, 'The end of the selection is not contained within the link');
});
it('should not remove the <p> container when adding a link inside a top-level <p> with a single text-node child', function () {
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
this.el.innerHTML = '<p>Some text</p><p><br/></p><p><br/></p><p>some text link text</p>';
var editor = this.newMediumEditor('.editor'),
lastP = this.el.lastChild,
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
// Select the text 'link text' in the last paragraph
MediumEditor.selection.select(document, lastP.firstChild, 'some text '.length, lastP.firstChild, 'some text link text'.length);
fireEvent(editor.elements[0], 'focus');
jasmine.clock().tick(1);
// Click the 'anchor' button in the toolbar
fireEvent(toolbar.getToolbarElement().querySelector('[data-action="createLink"]'), 'click');
// Input a url and save
var input = anchorExtension.getInput();
input.value = 'http://www.example.com';
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalledWith({
value: 'http://www.example.com',
target: '_self'
});
// Make sure the <p> wasn't removed, and the <a> was added to the end
expect(this.el.lastChild).toBe(lastP);
expect(lastP.lastChild.nodeName.toLowerCase()).toBe('a');
// Make sure selection is only the link
var range = window.getSelection().getRangeAt(0);
if (range.startContainer === lastP.lastChild.firstChild) {
expect(range.startOffset).toBe(0, 'The start of the selection is not at the front of the link');
} else {
expect(range.startContainer).toBe(lastP.firstChild);
expect(range.startOffset).toBe('some text '.length, 'The start of the selection is not at the front of the link');
}
expect(MediumEditor.util.isDescendant(lastP.lastChild, range.endContainer, true)).toBe(true, 'The end of the selection is incorrect');
});
// https://github.com/yabwe/medium-editor/issues/803
it('should update the href of a link containing only an image', function () {
this.el.innerHTML = '<a href="#"><img src="../demo/img/medium-editor.jpg"></a>';
spyOn(MediumEditor.prototype, 'createLink').and.callThrough();
var editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar'),
button, input,
aTag = this.el.childNodes[0];
selectElementContents(aTag);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
input = editor.getExtensionByName('anchor').getInput();
input.value = 'http://www.google.com';
fireEvent(input, 'keyup', {
keyCode: MediumEditor.util.keyCode.ENTER
});
expect(editor.createLink).toHaveBeenCalled();
// This appears to be broken in Edge < 13, but works correctly in Edge 13 or higher
// So for the sake of sanity, disabling this check for Edge 12.
// TODO: Find a better way to fix this issue if Edge 12 is going to matter
var edgeVersion = getEdgeVersion();
if (!edgeVersion || edgeVersion >= 13) {
expect(this.el.innerHTML).toContain('<a href="http://www.google.com"><img src="../demo/img/medium-editor.jpg"></a>');
}
});
});
describe('Cancel', function () {
it('should close the link form when user clicks on cancel', function () {
spyOn(MediumEditor.extensions.toolbar.prototype, 'showAndUpdateToolbar').and.callThrough();
var editor = this.newMediumEditor('.editor'),
button,
cancel,
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
selectElementContentsAndFire(editor.elements[0]);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
cancel = anchorExtension.getForm().querySelector('a.medium-editor-toolbar-close');
fireEvent(button, 'click');
expect(anchorExtension.isDisplayed()).toBe(true);
fireEvent(cancel, 'click');
expect(toolbar.showAndUpdateToolbar).toHaveBeenCalled();
expect(anchorExtension.isDisplayed()).toBe(false);
});
it('should close the link form when user presses escape', function () {
var editor = this.newMediumEditor('.editor'),
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
selectElementContentsAndFire(editor.elements[0]);
fireEvent(toolbar.getToolbarElement().querySelector('[data-action="createLink"]'), 'click');
expect(anchorExtension.isDisplayed()).toBe(true);
fireEvent(anchorExtension.getInput(), 'keyup', {
keyCode: MediumEditor.util.keyCode.ESCAPE
});
expect(anchorExtension.isDisplayed()).toBe(false);
});
});
describe('Click', function () {
it('should display the anchor form when toolbar is visible', function () {
spyOn(MediumEditor.extensions.anchor.prototype, 'showForm').and.callThrough();
var button,
editor = this.newMediumEditor('.editor'),
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar');
selectElementContentsAndFire(editor.elements[0]);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
expect(toolbar.getToolbarActionsElement().style.display).toBe('none');
expect(anchorExtension.isDisplayed()).toBe(true);
expect(anchorExtension.showForm).toHaveBeenCalled();
});
it('should unlink when selection is a link', function () {
this.el.innerHTML = '<a href="#">link</a>';
var button,
editor = this.newMediumEditor('.editor'),
toolbar = editor.getExtensionByName('toolbar');
spyOn(document, 'execCommand').and.callThrough();
selectElementContentsAndFire(editor.elements[0]);
button = toolbar.getToolbarElement().querySelector('[data-action="createLink"]');
fireEvent(button, 'click');
expect(this.el.innerHTML).toBe('link');
expect(document.execCommand).toHaveBeenCalled();
});
// https://github.com/yabwe/medium-editor/issues/751
it('should allow more than one link creation within a paragraph', function () {
spyOn(MediumEditor.extensions.anchor.prototype, 'showForm').and.callThrough();
this.el.innerHTML = '<p><a href="#">beginning</a> some text middle some text end</p>';
var editor = this.newMediumEditor('.editor'),
anchorExtension = editor.getExtensionByName('anchor'),
toolbar = editor.getExtensionByName('toolbar'),
para = this.el.firstChild;
// Select the text 'middle'
MediumEditor.selection.select(document, para.childNodes[1], 11, para.childNodes[1], 18);
fireEvent(editor.elements[0], 'focus');
jasmine.clock().tick(1);
// Click the 'anchor' button in the toolbar
fireEvent(toolbar.getToolbarElement().querySelector('[data-action="createLink"]'), 'click');
expect(toolbar.getToolbarActionsElement().style.display).toBe('none');
expect(anchorExtension.isDisplayed()).toBe(true);
expect(anchorExtension.showForm).toHaveBeenCalled();
expect(anchorExtension.getInput().value).toBe('');
});
});
});