From d84c8615154e98ce9e0afb3fad49c95bf8d602cf Mon Sep 17 00:00:00 2001 From: Cory Forsyth Date: Tue, 10 May 2016 12:08:56 -0400 Subject: [PATCH] Explicitly set range in some tests so they pass in Firefox fixes #388 --- tests/acceptance/editor-key-commands-test.js | 171 +++++++++---------- tests/acceptance/editor-sections-test.js | 7 + tests/acceptance/editor-undo-redo-test.js | 31 ++-- tests/unit/editor/editor-events-test.js | 9 + tests/unit/editor/editor-test.js | 9 +- 5 files changed, 127 insertions(+), 100 deletions(-) diff --git a/tests/acceptance/editor-key-commands-test.js b/tests/acceptance/editor-key-commands-test.js index 15fb0ff19..e89528a23 100644 --- a/tests/acceptance/editor-key-commands-test.js +++ b/tests/acceptance/editor-key-commands-test.js @@ -1,13 +1,23 @@ -import { Editor } from 'mobiledoc-kit'; import { MODIFIERS } from 'mobiledoc-kit/utils/key'; import Keycodes from 'mobiledoc-kit/utils/keycodes'; import Browser from 'mobiledoc-kit/utils/browser'; import Helpers from '../test-helpers'; +import Range from 'mobiledoc-kit/utils/cursor/range'; const { module, test } = Helpers; let editor, editorElement; +// In Firefox, if the window isn't active (which can happen when running tests +// at SauceLabs), the editor element won't have the selection. This helper method +// ensures that it has a cursor selection. +// See https://github.com/bustlelabs/mobiledoc-kit/issues/388 +function renderIntoAndFocusTail(treeFn, options={}) { + let editor = Helpers.mobiledoc.renderInto(editorElement, treeFn, options); + editor.selectRange(new Range(editor.post.tailPosition())); + return editor; +} + module('Acceptance: Editor: Key Commands', { beforeEach() { editorElement = $('#editor')[0]; @@ -22,19 +32,17 @@ module('Acceptance: Editor: Key Commands', { function testStatefulCommand({modifierName, key, command, markupName}) { test(`${command} applies markup ${markupName} to highlighted text`, (assert) => { - assert.expect(2); + assert.expect(3); let done = assert.async(); let modifier = MODIFIERS[modifierName]; let modifierKeyCode = Keycodes[modifierName]; let initialText = 'something'; - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker(initialText)]) - ])); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker(initialText)]) + ])); - editor = new Editor({mobiledoc}); - editor.render(editorElement); + assert.ok(editor.hasCursor(), 'precond - editor should have cursor'); assert.hasNoElement(`#editor ${markupName}`, `precond - no ${markupName} text`); Helpers.dom.selectText(editor ,initialText, editorElement); @@ -50,18 +58,17 @@ function testStatefulCommand({modifierName, key, command, markupName}) { test(`${command} toggles ${markupName} for next entered text`, (assert) => { let done = assert.async(); - assert.expect(7); + assert.expect(8); let modifier = MODIFIERS[modifierName]; let modifierKeyCode = Keycodes[modifierName]; let initialText = 'something'; - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker(initialText)]) - ])); - editor = new Editor({mobiledoc}); - editor.render(editorElement); + editor =renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker(initialText)]) + ])); + + assert.ok(editor.hasCursor(), 'has cursor'); assert.hasNoElement(`#editor ${markupName}`, `precond - no ${markupName} text`); Helpers.dom.moveCursorTo(editor, @@ -143,13 +150,11 @@ testStatefulCommand({ test(`cmd-left goes to the beginning of a line (MacOS only)`, (assert) => { let initialText = 'something'; - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker(initialText)]) - ])); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker(initialText)]) + ])); - editor = new Editor({mobiledoc}); - editor.render(editorElement); + assert.ok(editor.hasCursor(), 'has cursor'); let textElement = editor.post.sections.head.markers.head.renderNode.element; @@ -169,13 +174,11 @@ test(`cmd-left goes to the beginning of a line (MacOS only)`, (assert) => { test(`cmd-right goes to the end of a line (MacOS only)`, (assert) => { let initialText = 'something'; - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker(initialText)]) - ])); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker(initialText)]) + ])); - editor = new Editor({mobiledoc}); - editor.render(editorElement); + assert.ok(editor.hasCursor(), 'has cursor'); let textElement = editor.post.sections.head.markers.head.renderNode.element; @@ -195,13 +198,11 @@ test(`cmd-right goes to the end of a line (MacOS only)`, (assert) => { test(`ctrl-k clears to the end of a line`, (assert) => { let initialText = 'something'; - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker(initialText)]) - ])); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker(initialText)]) + ])); - editor = new Editor({mobiledoc}); - editor.render(editorElement); + assert.ok(editor.hasCursor(), 'has cursor'); let textElement = editor.post.sections.head.markers.head.renderNode.element; Helpers.dom.moveCursorTo(editor, textElement, 4); @@ -222,13 +223,11 @@ test(`ctrl-k clears to the end of a line`, (assert) => { test(`ctrl-k clears selected text`, (assert) => { let initialText = 'something'; - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker(initialText)]) - ])); + editor = renderIntoAndFocusTail( ({post, markupSection, marker}) => post([ + markupSection('p', [marker(initialText)]) + ])); - editor = new Editor({mobiledoc}); - editor.render(editorElement); + assert.ok(editor.hasCursor(), 'has cursor'); let textElement = editor.post.sections.head.markers.head.renderNode.element; Helpers.dom.moveCursorTo(editor, textElement, 4, textElement, 8); @@ -248,15 +247,15 @@ test(`ctrl-k clears selected text`, (assert) => { }); test('cmd-k links selected text', (assert) => { - assert.expect(2); + assert.expect(3); let url = 'http://bustle.com'; - let mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker('something')]) - ])); - editor = new Editor({mobiledoc}); - editor.render(editorElement); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker('something')]) + ])); + + assert.ok(editor.hasCursor(), 'has cursor'); + editor.showPrompt = (prompt, defaultUrl, callback) => { assert.ok(true, 'calls showPrompt'); callback(url); @@ -269,18 +268,18 @@ test('cmd-k links selected text', (assert) => { }); test('cmd-k unlinks selected text if it was already linked', (assert) => { - assert.expect(3); + assert.expect(4); let url = 'http://bustle.com'; - let mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker, markup}) => post([ - markupSection('p', [marker('something', [markup('a', {href:url})])]) - ])); - editor = new Editor({mobiledoc}); + editor = renderIntoAndFocusTail(({post, markupSection, marker, markup}) => post([ + markupSection('p', [marker('something', [markup('a', {href:url})])]) + ])); + + assert.ok(editor.hasCursor(), 'has cursor'); + editor.showPrompt = () => { assert.ok(false, 'should not call showPrompt'); }; - editor.render(editorElement); assert.hasElement(`#editor a[href="${url}"]:contains(something)`, 'precond -- has link'); @@ -293,18 +292,17 @@ test('cmd-k unlinks selected text if it was already linked', (assert) => { }); test('new key commands can be registered', (assert) => { - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker('something')]) - ])); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker('something')]) + ])); + + assert.ok(editor.hasCursor(), 'has cursor'); let passedEditor; - editor = new Editor({mobiledoc}); editor.registerKeyCommand({ str: 'ctrl+x', run(editor) { passedEditor = editor; } }); - editor.render(editorElement); Helpers.dom.triggerKeyCommand(editor, 'Y', MODIFIERS.CTRL); @@ -316,18 +314,17 @@ test('new key commands can be registered', (assert) => { }); test('new key commands can be registered without modifiers', (assert) => { - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker('something')]) - ])); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker('something')]) + ])); + + assert.ok(editor.hasCursor(), 'has cursor'); let passedEditor; - editor = new Editor({mobiledoc}); editor.registerKeyCommand({ str: 'X', run(editor) { passedEditor = editor; } }); - editor.render(editorElement); Helpers.dom.triggerKeyCommand(editor, 'Y', MODIFIERS.CTRL); @@ -343,13 +340,14 @@ test('new key commands can be registered without modifiers', (assert) => { }); test('duplicate key commands can be registered with the last registered winning', (assert) => { - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker('something')]) - ])); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker('something')]) + ])); + + assert.ok(editor.hasCursor(), 'has cursor'); let firstCommandRan, secondCommandRan; - editor = new Editor({mobiledoc}); + editor.registerKeyCommand({ str: 'ctrl+x', run() { firstCommandRan = true; } @@ -358,7 +356,6 @@ test('duplicate key commands can be registered with the last registered winning' str: 'ctrl+x', run() { secondCommandRan = true; } }); - editor.render(editorElement); Helpers.dom.triggerKeyCommand(editor, 'X', MODIFIERS.CTRL); @@ -367,13 +364,14 @@ test('duplicate key commands can be registered with the last registered winning' }); test('returning false from key command causes next match to run', (assert) => { - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker('something')]) - ])); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker('something')]) + ])); + + assert.ok(editor.hasCursor(), 'has cursor'); let firstCommandRan, secondCommandRan; - editor = new Editor({mobiledoc}); + editor.registerKeyCommand({ str: 'ctrl+x', run() { firstCommandRan = true; } @@ -385,7 +383,6 @@ test('returning false from key command causes next match to run', (assert) => { return false; } }); - editor.render(editorElement); Helpers.dom.triggerKeyCommand(editor, 'X', MODIFIERS.CTRL); @@ -394,12 +391,11 @@ test('returning false from key command causes next match to run', (assert) => { }); test('key commands can override built-in functionality', (assert) => { - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker('something')]) - ])); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker('something')]) + ])); - editor = new Editor({mobiledoc}); + assert.ok(editor.hasCursor(), 'has cursor'); let passedEditor; editor.registerKeyCommand({ @@ -407,7 +403,6 @@ test('key commands can override built-in functionality', (assert) => { run(editor) { passedEditor = editor; } }); - editor.render(editorElement); assert.equal($('#editor p').length, 1, 'has 1 paragraph to start'); Helpers.dom.moveCursorTo(editor, editorElement.childNodes[0].childNodes[0], 5); @@ -419,12 +414,11 @@ test('key commands can override built-in functionality', (assert) => { }); test('returning false from key command still runs built-in functionality', (assert) => { - const mobiledoc = Helpers.mobiledoc.build( - ({post, markupSection, marker}) => post([ - markupSection('p', [marker('something')]) - ])); + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([ + markupSection('p', [marker('something')]) + ])); - editor = new Editor({mobiledoc}); + assert.ok(editor.hasCursor(), 'has cursor'); let passedEditor; editor.registerKeyCommand({ @@ -435,7 +429,6 @@ test('returning false from key command still runs built-in functionality', (asse } }); - editor.render(editorElement); assert.equal($('#editor p').length, 1, 'has 1 paragraph to start'); Helpers.dom.moveCursorTo(editor, editorElement.childNodes[0].childNodes[0], 5); diff --git a/tests/acceptance/editor-sections-test.js b/tests/acceptance/editor-sections-test.js index bafc7af39..627b63a7b 100644 --- a/tests/acceptance/editor-sections-test.js +++ b/tests/acceptance/editor-sections-test.js @@ -2,6 +2,7 @@ import { Editor } from 'mobiledoc-kit'; import Helpers from '../test-helpers'; import { MOBILEDOC_VERSION } from 'mobiledoc-kit/renderers/mobiledoc/0-2'; import { NO_BREAK_SPACE } from 'mobiledoc-kit/renderers/editor-dom'; +import Range from 'mobiledoc-kit/utils/cursor/range'; const { test, module } = Helpers; @@ -551,6 +552,12 @@ test('inserting multiple spaces renders them with nbsps', (assert) => { editor = new Editor({mobiledoc}); editor.render(editorElement); + // Tests on FF fail if the editor doesn't have a cursor, we must + // render it explicitly + editor.selectRange(new Range(editor.post.tailPosition())); + + assert.ok(editor.hasCursor(), 'precond - has cursor'); + let sp = ' ', nbsp = NO_BREAK_SPACE; Helpers.dom.insertText(editor, sp + sp + sp); assert.equal($('#editor p:eq(0)').text(), diff --git a/tests/acceptance/editor-undo-redo-test.js b/tests/acceptance/editor-undo-redo-test.js index 51b6207b2..55682ea33 100644 --- a/tests/acceptance/editor-undo-redo-test.js +++ b/tests/acceptance/editor-undo-redo-test.js @@ -1,6 +1,7 @@ import { MODIFIERS } from 'mobiledoc-kit/utils/key'; import Helpers from '../test-helpers'; import Position from 'mobiledoc-kit/utils/cursor/position'; +import Range from 'mobiledoc-kit/utils/cursor/range'; const { module, test } = Helpers; @@ -14,6 +15,16 @@ function redo(editor) { Helpers.dom.triggerKeyCommand(editor, 'Z', [MODIFIERS.META, MODIFIERS.SHIFT]); } +// In Firefox, if the window isn't active (which can happen when running tests +// at SauceLabs), the editor element won't have the selection. This helper method +// ensures that it has a cursor selection. +// See https://github.com/bustlelabs/mobiledoc-kit/issues/388 +function renderIntoAndFocusTail(treeFn, options={}) { + let editor = Helpers.mobiledoc.renderInto(editorElement, treeFn, options); + editor.selectRange(new Range(editor.post.tailPosition())); + return editor; +} + module('Acceptance: Editor: Undo/Redo', { beforeEach() { editorElement = $('#editor')[0]; @@ -29,7 +40,7 @@ module('Acceptance: Editor: Undo/Redo', { test('undo/redo the insertion of a character', (assert) => { let done = assert.async(); let expectedBeforeUndo, expectedAfterUndo; - editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker}) => { + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => { expectedBeforeUndo = post([markupSection('p', [marker('abcD')])]); expectedAfterUndo = post([markupSection('p', [marker('abc')])]); return expectedAfterUndo; @@ -66,7 +77,7 @@ test('undo/redo the insertion of a character', (assert) => { test('undo/redo the insertion of multiple characters', (assert) => { let done = assert.async(); let beforeUndo, afterUndo1, afterUndo2; - editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker}) => { + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => { beforeUndo = post([markupSection('p', [marker('abcDE')])]); afterUndo1 = post([markupSection('p', [marker('abcD')])]); afterUndo2 = post([markupSection('p', [marker('abc')])]); @@ -102,7 +113,7 @@ test('undo/redo the insertion of multiple characters', (assert) => { test('undo the deletion of a character', (assert) => { let expectedBeforeUndo, expectedAfterUndo; - editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker}) => { + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => { expectedBeforeUndo = post([markupSection('p', [marker('abc')])]); expectedAfterUndo = post([markupSection('p', [marker('abcD')])]); return expectedAfterUndo; @@ -130,7 +141,7 @@ test('undo the deletion of a character', (assert) => { test('undo the deletion of a range', (assert) => { let expectedBeforeUndo, expectedAfterUndo; - editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker}) => { + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => { expectedBeforeUndo = post([markupSection('p', [marker('ad')])]); expectedAfterUndo = post([markupSection('p', [marker('abcd')])]); return expectedAfterUndo; @@ -162,7 +173,7 @@ test('undo the deletion of a range', (assert) => { test('undo insertion of character to a list item', (assert) => { let done = assert.async(); let expectedBeforeUndo, expectedAfterUndo; - editor = Helpers.mobiledoc.renderInto(editorElement, ({post, listSection, listItem, marker}) => { + editor = renderIntoAndFocusTail(({post, listSection, listItem, marker}) => { expectedBeforeUndo = post([ listSection('ul', [listItem([marker('abcD')])]) ]); @@ -205,7 +216,7 @@ test('undo stack length can be configured (depth 1)', (assert) => { let editorOptions = { undoDepth: 1 }; let beforeUndo, afterUndo; - editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker}) => { + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => { beforeUndo = post([markupSection('p', [marker('abcDE')])]); afterUndo = post([markupSection('p', [marker('abcD')])]); return post([markupSection('p', [marker('abc')])]); @@ -241,7 +252,7 @@ test('undo stack length can be configured (depth 0)', (assert) => { let editorOptions = { undoDepth: 0 }; let beforeUndo; - editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker}) => { + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => { beforeUndo = post([markupSection('p', [marker('abcDE')])]); return post([markupSection('p', [marker('abc')])]); }, editorOptions); @@ -288,7 +299,7 @@ test('take and undo a snapshot based on drag/dropping of text', (assert) => { let done = assert.async(); let text = 'abc'; let beforeUndo, afterUndo; - editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker}) => { + editor = renderIntoAndFocusTail(({post, markupSection, marker}) => { beforeUndo = post([markupSection('p', [marker(text)])]); afterUndo = post([markupSection('p', [marker('a')])]); return afterUndo; @@ -317,7 +328,7 @@ test('take and undo a snapshot when adding a card', (assert) => { }; let beforeUndo, afterUndo; - editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker, cardSection}) => { + editor = renderIntoAndFocusTail(({post, markupSection, marker, cardSection}) => { beforeUndo = post([ markupSection('p', [marker(text)]), cardSection('my-card', {}) @@ -349,7 +360,7 @@ test('take and undo a snapshot when removing an atom', (assert) => { }; let beforeUndo, afterUndo; - editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker, atom}) => { + editor = renderIntoAndFocusTail(({post, markupSection, marker, atom}) => { beforeUndo = post([markupSection('p', [marker(text)])]); afterUndo = post([ markupSection('p', [marker(text), atom('my-atom', 'content', {})]), diff --git a/tests/unit/editor/editor-events-test.js b/tests/unit/editor/editor-events-test.js index 873f9c6a4..00117e27f 100644 --- a/tests/unit/editor/editor-events-test.js +++ b/tests/unit/editor/editor-events-test.js @@ -16,6 +16,10 @@ module('Unit: Editor: events and lifecycle callbacks', { editorElement = $('#editor')[0]; editor = new Editor({mobiledoc}); editor.render(editorElement); + + // Tests in FF can fail if the window is not front-most and + // we don't explicitly render the range + editor.selectRange(new Range(editor.post.tailPosition())); }, afterEach() { @@ -76,6 +80,11 @@ test('cursorDidChange callback fired after mouseup when editor loses focus', (as assert.expect(2); let done = assert.async(); + // Tests in FF can fail if the window is not front-most and + // we don't explicitly render the range + let node = Helpers.dom.findTextNode(editor.element, 'this is the editor'); + Helpers.dom.moveCursorWithoutNotifyingEditorTo(editor, node); + let cursorChanged = 0; editor.cursorDidChange(() => cursorChanged++); diff --git a/tests/unit/editor/editor-test.js b/tests/unit/editor/editor-test.js index db5e2f6c6..4b759f5b7 100644 --- a/tests/unit/editor/editor-test.js +++ b/tests/unit/editor/editor-test.js @@ -427,7 +427,10 @@ test('#insertText when post is blank', (assert) => { return post(); }); - assert.ok(editor.hasCursor(), 'precond - editor has no cursor'); + // Necessary to ensure tests pass on FF when the window is not active + Helpers.dom.selectRange(editorElement, 0, editorElement, 0); + + assert.ok(editor.hasCursor(), 'precond - editor has cursor'); assert.ok(editor.post.isBlank, 'precond - editor has blank post'); editor.insertText('blah blah'); @@ -494,6 +497,8 @@ test('#insertAtom when post is blank', (assert) => { return post(); }, {atoms: [atom]}); + Helpers.dom.selectRange(editorElement, 0, editorElement, 0); + assert.ok(editor.hasCursor(), 'precond - editor has cursor'); assert.ok(editor.post.isBlank, 'precond - post is blank'); editor.insertAtom('the-atom', 'THEATOMTEXT'); @@ -598,6 +603,8 @@ test('#insertCard when post is blank', (assert) => { return post(); }, {cards: [card]}); + Helpers.dom.selectRange(editorElement, 0, editorElement, 0); + assert.ok(editor.hasCursor(), 'precond - editor has cursor'); assert.ok(editor.post.isBlank, 'precond - post is blank');