test('press enter at start of card inserts section before card', (assert) => {
  const mobiledoc = Helpers.mobiledoc.build(({post, cardSection}) => {
    return post([cardSection(positionCard.name)]);
  });

  editor = new Editor({mobiledoc, cards: [positionCard]});
  editor.render(editorElement);

  assert.hasElement('#my-simple-card', 'precond - renders card');
  assert.hasNoElement('#editor p', 'precond - has no markup section');

  editor.selectRange(Range.create(editor.post.sections.tail, 0));
  Helpers.dom.triggerEnter(editor);

  assert.hasElement('#my-simple-card', 'has card after enter');
  assert.hasElement('#editor p', 'markup section is added');

  let { range } = editor;
  assert.ok(editor.post.sections.head.isMarkerable,
            'markup section at head of post');
  assert.ok(editor.post.sections.tail.isCardSection,
            'card section at end of post');
  assert.ok(range.head.section === editor.post.sections.tail,
            'correct cursor position');
  assert.equal(range.head.offset, 0,
            'correct cursor offset');
});
test('deleting at start of empty markup section with prev card deletes the markup section', (assert) => {
  const mobiledoc = Helpers.mobiledoc.build(({post, cardSection, markupSection}) => {
    return post([
      cardSection(positionCard.name),
      markupSection('p')
    ]);
  });

  editor = new Editor({mobiledoc, cards: [positionCard]});
  editor.render(editorElement);

  assert.hasElement('#my-simple-card', 'precond - renders card');
  assert.hasElement('#editor p', 'precond - has blank markup section');

  editor.selectRange(Range.create(editor.post.sections.tail, 0));
  Helpers.dom.triggerDelete(editor);

  assert.hasElement('#my-simple-card', 'has card after delete');
  assert.hasNoElement('#editor p', 'paragraph is gone');

  let { range } = editor;
  assert.ok(range.head.section === editor.post.sections.head,
            'correct cursor position');
  assert.equal(range.head.offset, 1,
            'correct cursor offset');
});
skipInIE11('pasting when on the end of a card is blocked', (assert) => {
  const mobiledoc = Helpers.mobiledoc.build(
    ({post, cardSection, markupSection, marker}) => {
    return post([
      cardSection('my-card'),
      markupSection('p', [marker('abc')])
    ]);
  });
  editor = new Editor({mobiledoc, cards});
  editor.render(editorElement);

  Helpers.dom.selectText(editor, 'abc', editorElement);
  Helpers.dom.triggerCopyEvent(editor);

  editor.selectRange(new Range(editor.post.sections.head.headPosition()));
  Helpers.dom.triggerPasteEvent(editor);

  assert.postIsSimilar(editor.post, Helpers.postAbstract.build(
    ({post, cardSection, markupSection, marker}) => {
      return post([
        cardSection('my-card'),
        markupSection('p', [marker('abc')])
      ]);
    }), 'no paste has occurred');

  editor.selectRange(new Range(editor.post.sections.head.tailPosition()));
  Helpers.dom.triggerPasteEvent(editor);

  assert.postIsSimilar(editor.post, Helpers.postAbstract.build(
    ({post, cardSection, markupSection, marker}) => {
      return post([
        cardSection('my-card'),
        markupSection('p', [marker('abc')])
      ]);
    }), 'no paste has occurred');
});
test('forward-delete when cursor is positioned at start of a card deletes card, replace with empty markup section', (assert) => {
  const mobiledoc = Helpers.mobiledoc.build(({post, cardSection}) => {
    return post([cardSection(positionCard.name)]);
  });

  editor = new Editor({mobiledoc, cards: [positionCard]});
  editor.render(editorElement);

  assert.hasElement('#my-simple-card', 'precond - renders card');
  assert.hasNoElement('#editor p', 'precond - has no markup section');

  editor.selectRange(Range.create(editor.post.sections.head, 0));
  Helpers.dom.triggerDelete(editor, DIRECTION.FORWARD);

  assert.hasNoElement('#my-simple-card', 'removes card after delete');
  assert.hasElement('#editor p', 'has markup section after delete');
});
test('inserting multiple spaces renders them with nbsps', (assert) => {
  let mobiledoc = Helpers.mobiledoc.build(({post, markupSection}) => {
    return post([markupSection()]);
  });
  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(),
               nbsp + nbsp + nbsp,
               'correct nbsps in text');
});
test('forward-delete when cursor is positioned at end of a card and next section is blank deletes next section', (assert) => {
  const mobiledoc = Helpers.mobiledoc.build(({post, cardSection, markupSection}) => {
    return post([
      cardSection(positionCard.name),
      markupSection()
    ]);
  });

  editor = new Editor({mobiledoc, cards: [positionCard]});
  editor.render(editorElement);

  assert.hasElement('#my-simple-card', 'precond - renders card');
  assert.hasElement('#editor p', 'precond - has blank markup section');

  editor.selectRange(Range.create(editor.post.sections.head, 1));
  Helpers.dom.triggerDelete(editor, DIRECTION.FORWARD);

  assert.hasElement('#my-simple-card', 'still has card after delete');
  assert.hasNoElement('#editor p', 'deletes blank markup section');
});
test('delete when cursor is at start of a card and prev section is blank deletes prev section', (assert) => {
  const mobiledoc = Helpers.mobiledoc.build(({post, cardSection, markupSection}) => {
    return post([
      markupSection('p'),
      cardSection(positionCard.name)
    ]);
  });

  editor = new Editor({mobiledoc, cards: [positionCard]});
  editor.render(editorElement);

  assert.hasElement('#my-simple-card', 'precond - renders card');
  assert.hasElement('#editor p', 'precond - has blank markup section');

  editor.selectRange(Range.create(editor.post.sections.tail, 0));
  Helpers.dom.triggerDelete(editor);

  assert.hasElement('#my-simple-card', 'card still exists after delete');
  assert.hasNoElement('#editor p', 'blank markup section deleted');
});
test('select-all and type text works ok', (assert) => {
  let done = assert.async();
  const mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker}) => {
    return post([
      markupSection('p', [marker('abc')])
    ]);
  });
  editor = new Editor({mobiledoc, cards});
  editor.render(editorElement);

  Helpers.dom.moveCursorTo(editor, editorElement.firstChild, 0);
  let post = editor.post;
  editor.selectRange(new Range(post.headPosition(), post.tailPosition()));

  assert.selectedText('abc', 'precond - abc is selected');
  assert.hasElement('#editor p:contains(abc)', 'precond - renders p');

  Helpers.dom.insertText(editor, 'X');
  setTimeout(function() {
    assert.hasNoElement('#editor p:contains(abc)', 'replaces existing text');
    assert.hasElement('#editor p:contains(X)', 'inserts text');
    done();
  }, 0);
});
let editor, editorElement;

const mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker}) => {
  return post([markupSection('p', [marker('this is the editor')])]);
});


module('Unit: Editor: events and lifecycle callbacks', {
  beforeEach() {
    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() {
    if (editor) {
      editor.destroy();
      editor = null;
    }
  }
});

test('cursorDidChange callback fired after mouseup', (assert) => {
  assert.expect(2);
  let done = assert.async();

  let cursorChanged = 0;