UNPKG

medium-editor

Version:
1,017 lines (858 loc) 47.2 kB
/*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>&nbsp;!</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(''); }); }); });