Skip to content

Commit

Permalink
Add buildWithText test helper, postEditor#deleteRange
Browse files Browse the repository at this point in the history
Add many additional tests for `deleteRange`
  • Loading branch information
bantic committed Aug 2, 2016
1 parent e8f9ac8 commit 3327408
Show file tree
Hide file tree
Showing 7 changed files with 779 additions and 307 deletions.
57 changes: 44 additions & 13 deletions src/js/editor/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,19 +308,22 @@ class PostEditor {
forEach(groups, group => {
let list = group[0];
forEach(group, listSection => {
if (listSection !== list) {
let currentHead = range.head;
let prevPosition;
// FIXME is there a currentHead if there is no range?
// is the current head a list item in the section
if (currentHead.section.isListItem &&
currentHead.section.parent === listSection) {
prevPosition = list.tailPosition();
}
this._joinListSections(list, listSection);
if (prevPosition) {
updatedHead = prevPosition.moveRight();
}
if (listSection === list) {
return;
}

let currentHead = range.head;
let prevPosition;

// FIXME is there a currentHead if there is no range?
// is the current head a list item in the section
if (!range.isBlank && currentHead.section.isListItem &&
currentHead.section.parent === listSection) {
prevPosition = list.tailPosition();
}
this._joinListSections(list, listSection);
if (prevPosition) {
updatedHead = prevPosition.moveRight();
}
});
});
Expand Down Expand Up @@ -384,6 +387,34 @@ class PostEditor {
}
}

/**
* @return {Position}
*/
deleteAtPosition(position, direction=DIRECTION.BACKWARD, {unit}={unit: 'char'}) {
if (direction === DIRECTION.BACKWARD) {
return this._deleteAtPositionBackward(position, unit);
} else {
return this._deleteAtPositionForward(position, unit);
}
}

_deleteAtPositionBackward(position, unit) {
if (position.isHead() && position.section.isListItem) {
this.toggleSection('p', new Range(position));
return this._range.head;
} else {
let prevPosition = unit === 'word' ? position.moveWord(-1) : position.move(-1);
let range = new Range(prevPosition, position);
return this.deleteRange(range);
}
}

_deleteAtPositionForward(position, unit) {
let nextPosition = unit === 'word' ? position.moveWord(1) : position.move(1);
let range = new Range(position, nextPosition);
return this.deleteRange(range);
}

_joinPositionToPreviousSection(position) {
const {section } = position;
let nextPosition = position.clone();
Expand Down
4 changes: 2 additions & 2 deletions tests/acceptance/editor-sections-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -618,8 +618,8 @@ test('delete with option (Mac) or control (Win) key deletes full word', (assert

let done = assert.async();

let post = Helpers.postAbstract.buildWithText("abc def");
let expected = Helpers.postAbstract.buildWithText("abc ");
let { post } = Helpers.postAbstract.buildFromText("abc def");
let { post: expected } = Helpers.postAbstract.buildFromText("abc ");
editor = Helpers.mobiledoc.renderPostInto(editorElement, post);

editor.selectRange(new Range(editor.post.tailPosition()));
Expand Down
171 changes: 160 additions & 11 deletions tests/helpers/post-abstract.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import PostNodeBuilder from 'mobiledoc-kit/models/post-node-builder';
import Range from 'mobiledoc-kit/utils/cursor/range';
import Position from 'mobiledoc-kit/utils/cursor/position';

/*
* usage:
Expand Down Expand Up @@ -27,24 +29,171 @@ function build(treeFn) {
return treeFn(simpleBuilder);
}

let cardRegex = /\[(.*)\]/;
let markupRegex = /\*/g;
let listStartRegex = /^\* /;
let cursorRegex = /<|>|\|/g;

function parsePositionOffsets(text) {
let offsets = {};

if (cardRegex.test(text)) {
[['|','solo'],['<','start'],['>','end']].forEach(([char, type]) => {
if (text.indexOf(char) !== -1) {
offsets[type] = text.indexOf(char) === 0 ? 0 : 1;
}
});
} else {
if (listStartRegex.test(text)) {
text = text.replace(listStartRegex, '');
}
text = text.replace(markupRegex,'');
if (text.indexOf('|') !== -1) {
offsets.solo = text.indexOf('|');
} else if (text.indexOf('<') !== -1 || text.indexOf('>') !== -1) {
let hasStart = text.indexOf('<') !== -1;
let hasEnd = text.indexOf('>') !== -1;
if (hasStart) {
offsets.start = text.indexOf('<');
text = text.replace(/</g,'');
}
if (hasEnd) {
offsets.end = text.indexOf('>');
}
}
}

return offsets;
}

function parseTextIntoMarkers(text, builder) {
text = text.replace(cursorRegex,'');
let markers = [];

if (text.indexOf('@') !== -1) {
let atomIndex = text.indexOf('@');
let atom = builder.atom('some-atom');
let pieces = [text.slice(0, atomIndex), atom, text.slice(atomIndex+1)];
pieces.forEach(piece => {
if (piece === atom) {
markers.push(piece);
} else if (piece.length) {
markers = markers.concat( parseTextIntoMarkers(piece, builder) );
}
});
} else if (text.indexOf('*') === -1) {
markers.push(builder.marker(text));
} else {
let markup = builder.markup('b');

let startIndex = text.indexOf('*');
let endIndex = text.indexOf('*', startIndex+1);
if (endIndex === -1) { throw new Error('Malformed textWithoutCursor'); }

let pieces = [text.slice(0, startIndex),
text.slice(startIndex+1, endIndex),
text.slice(endIndex+1)];
pieces.forEach((piece, index) => {
let markups = index === 1 ? [markup] : [];
if (piece.length) {
markers.push(builder.marker(piece, markups));
}
});
}

return markers;
}

function parseSingleText(text, builder) {
let section, positions = {};

let offsets = parsePositionOffsets(text);

if (cardRegex.test(text)) {
section = builder.cardSection(cardRegex.exec(text)[1]);
} else {
let type = 'p';
if (listStartRegex.test(text)) {
text = text.replace(listStartRegex,'');
type = 'ul';
}

let markers = parseTextIntoMarkers(text, builder);

switch (type) {
case 'p':
section = builder.markupSection('p', markers);
break;
case 'ul':
section = builder.listItem(markers);
break;
}
}

['start','end','solo'].forEach(type => {
if (offsets[type] !== undefined) {
positions[type] = new Position(section, offsets[type]);
}
});

return { section, positions };
}

/**
* usage:
* Helpers.postAbstract.buildWithText(text) -> post with 1 markupSection ("p") with text `text`
* Helpers.postAbstract.buildWithText([text1, text2]) -> post with 2 markupSections ("p") with texts `text1`, `text2`
* Helpers.postAbstract.buildFromText(text) -> { post } with 1 markupSection ("p") with text `text`
* Helpers.postAbstract.buildFromText([text1, text2]) -> { post } with 2 markupSections ("p") with texts `text1`, `text2`
*/
function buildWithText(textOrArray) {
let builder = new PostNodeBuilder();
if (!Array.isArray(textOrArray)) {
textOrArray = [textOrArray];
}
function buildFromText(texts) {
if (!Array.isArray(texts)) { texts = [texts]; }
let positions = {};

let sections = textOrArray.map(text => {
return builder.createMarkupSection('p', [builder.createMarker(text)]);
let post = build(builder => {
let sections = [];
let curList;
texts.forEach((text, index) => {
let { section, positions: _positions } = parseSingleText(text, builder);
let lastText = index === texts.length - 1;

if (curList) {
if (section.isListItem) {
curList.items.append(section);
} else {
sections.push(curList);
sections.push(section);
curList = null;
}
} else if (section.isListItem) {
curList = builder.listSection('ul', [section]);
} else {
sections.push(section);
}

if (lastText && curList) {
sections.push(curList);
}

if (_positions.start) { positions.start = _positions.start; }
if (_positions.end) { positions.end = _positions.end; }
if (_positions.solo) { positions.solo = _positions.solo; }
});

return builder.post(sections);
});
return builder.createPost(sections);

let range;
if (positions.start) {
if (!positions.end) { throw new Error(`startPos but no endPos ${texts.join('\n')}`); }
range = new Range(positions.start, positions.end);
} else if (positions.solo) {
range = new Range(positions.solo);
}

return { post, range };
}


export default {
build,
buildWithText
buildFromText
};
Loading

0 comments on commit 3327408

Please sign in to comment.