Ejemplo n.º 1
0
describe('ContentStateInlineStyle', () => {
  const {
    contentState,
    selectionState,
  } = getSampleStateForTesting();

  function getStyles(block) {
    return block.getCharacterList().map(c => c.getStyle()).flatten().toJS();
  }

  describe('Within a single block', () => {
    const blockKey = selectionState.getStartKey();
    const length = contentState.getBlockForKey(blockKey).getLength();
    const target = selectionState.set('focusOffset', length);

    it('must add styles', () => {
      let modified = ContentStateInlineStyle.add(contentState, target, 'BOLD');
      expect(
        getStyles(modified.getBlockForKey(blockKey))
      ).toEqual(
        Repeat('BOLD', length).toJS()
      );

      const nextTarget = target.set('focusOffset', 2);
      modified = ContentStateInlineStyle.add(modified, nextTarget, 'ITALIC');
      expect(
        getStyles(modified.getBlockForKey(blockKey))
      ).toEqual(
        List(['BOLD', 'ITALIC', 'BOLD', 'ITALIC'])
          .concat(List(Repeat('BOLD', 3))).toJS()
      );
    });

    it('must remove styles', () => {
      // Go ahead and add some styles that we'll then remove.
      let modified = ContentStateInlineStyle.add(
        contentState,
        target,
        'BOLD'
      );
      modified = ContentStateInlineStyle.add(modified, target, 'ITALIC');
      modified = ContentStateInlineStyle.remove(modified, target, 'BOLD');
      expect(
        getStyles(modified.getBlockForKey(blockKey))
      ).toEqual(
        Repeat('ITALIC', length).toJS()
      );

      const nextTarget = target.set('focusOffset', 2);
      modified = ContentStateInlineStyle.remove(modified, nextTarget, 'ITALIC');
      expect(
        getStyles(modified.getBlockForKey(blockKey))
      ).toEqual(
        List(Repeat('ITALIC', 3)).toJS()
      );
    });
  });

  describe('Across multiple blocks', () => {
    const nextBlock = contentState.getBlockAfter(selectionState.getStartKey());
    const target = selectionState.merge({
      focusKey: nextBlock.getKey(),
      focusOffset: nextBlock.getLength(),
    });

    it('must add and remove styles', () => {
      let modified = ContentStateInlineStyle.add(contentState, target, 'BOLD');
      const start = contentState.getBlockForKey(target.getStartKey());

      expect(
        getStyles(modified.getBlockForKey(target.getStartKey()))
      ).toEqual(
        Repeat('BOLD', start.getLength()).toJS()
      );

      expect(
        getStyles(modified.getBlockForKey(nextBlock.getKey()))
      ).toEqual(
        Repeat('BOLD', nextBlock.getLength()).toJS()
      );

      modified = ContentStateInlineStyle.remove(contentState, target, 'BOLD');

      expect(
        getStyles(modified.getBlockForKey(target.getStartKey())).length
      ).toEqual(0);

      expect(
        getStyles(modified.getBlockForKey(nextBlock.getKey())).length
      ).toEqual(0);
    });
  });
});
Ejemplo n.º 2
0
 */

'use strict';

jest.disableAutomock();

jest.mock('generateRandomKey');

const {insertAtomicBlock, moveAtomicBlock} = require('AtomicBlockUtils');
const Entity = require('DraftEntity');
const EditorState = require('EditorState');
const SelectionState = require('SelectionState');

const getSampleStateForTesting = require('getSampleStateForTesting');

const {editorState, contentState, selectionState} = getSampleStateForTesting();

const initialBlock = contentState.getBlockMap().first();
const ENTITY_KEY = Entity.create('TOKEN', 'MUTABLE');
const CHARACTER = ' ';

const assertAtomic = state => {
  expect(
    state
      .getCurrentContent()
      .getBlockMap()
      .map(block => ({
        key: block.getKey(),
        type: block.getType(),
        text: block.getText(),
      }))
Ejemplo n.º 3
0
describe('splitBlockInContentState', () => {
  var {
    contentState,
    selectionState,
  } = getSampleStateForTesting();

  function checkForCharacterList(block) {
    expect(List.isList(block.getCharacterList())).toBe(true);
  }

  function getInlineStyles(block) {
    return block.getCharacterList().map(c => c.getStyle()).toJS();
  }

  function getEntities(block) {
    return block.getCharacterList().map(c => c.getEntity()).toJS();
  }

  it('must be restricted to collapsed selections', () => {
    expect(() => {
      var nonCollapsed = selectionState.set('focusOffset', 1);
      return splitBlockInContentState(contentState, nonCollapsed);
    }).toThrow();

    expect(() => {
      return splitBlockInContentState(contentState, selectionState);
    }).not.toThrow();
  });

  it('must split at the beginning of a block', () => {
    var initialBlock = contentState.getBlockMap().first();
    var afterSplit = splitBlockInContentState(contentState, selectionState);
    var afterBlockMap = afterSplit.getBlockMap();
    expect(afterBlockMap.size).toBe(4);

    var preSplitBlock = afterBlockMap.first();

    expect(preSplitBlock.getKey()).toBe(initialBlock.getKey());
    expect(preSplitBlock.getText()).toBe('');
    expect(getInlineStyles(preSplitBlock)).toEqual([]);
    expect(getEntities(preSplitBlock)).toEqual([]);

    var postSplitBlock = afterBlockMap.skip(1).first();
    expect(preSplitBlock.getKey()).not.toBe(postSplitBlock.getKey());
    expect(preSplitBlock.getType()).toBe(postSplitBlock.getType());

    expect(postSplitBlock.getKey()).not.toBe(initialBlock.getKey());
    expect(postSplitBlock.getType()).toBe(initialBlock.getType());
    expect(postSplitBlock.getText()).toBe(initialBlock.getText());
    expect(
      getInlineStyles(initialBlock)
    ).toEqual(
      getInlineStyles(postSplitBlock)
    );
    expect(
      getEntities(postSplitBlock)
    ).toEqual(
      getEntities(initialBlock)
    );

    checkForCharacterList(preSplitBlock);
    checkForCharacterList(postSplitBlock);
  });

  it('must split within a block', () => {
    var initialBlock = contentState.getBlockMap().first();
    var SPLIT_OFFSET = 3;
    var selection = selectionState.merge({
      anchorOffset: SPLIT_OFFSET,
      focusOffset: SPLIT_OFFSET,
    });

    var afterSplit = splitBlockInContentState(contentState, selection);
    var afterBlockMap = afterSplit.getBlockMap();
    expect(afterBlockMap.size).toBe(4);

    var preSplitBlock = afterBlockMap.first();
    var postSplitBlock = afterBlockMap.skip(1).first();

    expect(preSplitBlock.getKey()).toBe(initialBlock.getKey());
    expect(preSplitBlock.getText()).toBe(
      initialBlock.getText().slice(0, SPLIT_OFFSET)
    );
    expect(getInlineStyles(preSplitBlock)).toEqual(
      getInlineStyles(initialBlock).slice(0, SPLIT_OFFSET)
    );
    expect(getEntities(preSplitBlock)).toEqual(
      getEntities(initialBlock).slice(0, SPLIT_OFFSET)
    );

    expect(preSplitBlock.getKey()).not.toBe(postSplitBlock.getKey());
    expect(preSplitBlock.getType()).toBe(postSplitBlock.getType());

    expect(postSplitBlock.getKey()).not.toBe(initialBlock.getKey());
    expect(postSplitBlock.getType()).toBe(initialBlock.getType());
    expect(postSplitBlock.getText()).toBe(
      initialBlock.getText().slice(SPLIT_OFFSET)
    );
    expect(getInlineStyles(postSplitBlock)).toEqual(
      getInlineStyles(initialBlock).slice(SPLIT_OFFSET)
    );
    expect(getEntities(postSplitBlock)).toEqual(
      getEntities(initialBlock).slice(SPLIT_OFFSET)
    );

    checkForCharacterList(preSplitBlock);
    checkForCharacterList(postSplitBlock);
  });

  it('must split at the end of a block', () => {
    var initialBlock = contentState.getBlockMap().first();
    var end = initialBlock.getLength();
    var selection = selectionState.merge({
      anchorOffset: end,
      focusOffset: end,
    });

    var afterSplit = splitBlockInContentState(contentState, selection);
    var afterBlockMap = afterSplit.getBlockMap();
    expect(afterBlockMap.size).toBe(4);

    var preSplitBlock = afterBlockMap.first();
    var postSplitBlock = afterBlockMap.skip(1).first();

    expect(preSplitBlock.getKey()).toBe(initialBlock.getKey());

    expect(preSplitBlock.getKey()).not.toBe(postSplitBlock.getKey());
    expect(preSplitBlock.getType()).toBe(postSplitBlock.getType());
    expect(preSplitBlock.getText()).toBe(initialBlock.getText());
    expect(
      getInlineStyles(preSplitBlock)
    ).toEqual(
      getInlineStyles(initialBlock)
    );
    expect(
      getEntities(preSplitBlock)
    ).toEqual(
      getEntities(initialBlock)
    );

    expect(postSplitBlock.getKey()).not.toBe(initialBlock.getKey());
    expect(postSplitBlock.getType()).toBe(initialBlock.getType());
    expect(postSplitBlock.getText()).toBe('');
    expect(getInlineStyles(postSplitBlock)).toEqual([]);
    expect(getEntities(postSplitBlock)).toEqual([]);

    checkForCharacterList(preSplitBlock);
    checkForCharacterList(postSplitBlock);
  });
});
'use strict';

jest.disableAutomock();

const BlockMapBuilder = require('BlockMapBuilder');
const CharacterMetadata = require('CharacterMetadata');
const ContentBlock = require('ContentBlock');
const ContentBlockNode = require('ContentBlockNode');
const ContentState = require('ContentState');
const DraftEntityInstance = require('DraftEntityInstance');
const Immutable = require('immutable');

const convertFromDraftStateToRaw = require('convertFromDraftStateToRaw');
const getSampleStateForTesting = require('getSampleStateForTesting');

const {contentState} = getSampleStateForTesting();

const treeContentState = contentState.set(
  'blockMap',
  BlockMapBuilder.createFromArray([
    new ContentBlockNode({
      key: 'A',
      children: Immutable.List.of('B', 'E'),
    }),
    new ContentBlockNode({
      parent: 'A',
      key: 'B',
      nextSibling: 'C',
      children: Immutable.List.of('C', 'D'),
    }),
    new ContentBlockNode({
describe('applyEntityToContentState', () => {
  var {
    contentState,
  } = getSampleStateForTesting();

  function checkForCharacterList(block) {
    expect(Immutable.List.isList(block.getCharacterList())).toBe(true);
  }

  function getEntities(block) {
    return block.getCharacterList().map(c => c.getEntity()).toJS();
  }

  describe('Apply entity within single block', () => {
    var target = contentState.getBlockMap().first();
    var targetSelection = new SelectionState({
      anchorKey: target.getKey(),
      anchorOffset: 0,
      focusKey: target.getKey(),
      focusOffset: target.getLength(),
    });

    function applyAndCheck(entityKey) {
      var withNewEntity = applyEntityToContentState(
        contentState,
        targetSelection,
        entityKey
      );
      var first = withNewEntity.getBlockMap().first();

      checkForCharacterList(first);
      expect(getEntities(first)).toEqual(
        Immutable.Repeat(entityKey, first.getLength()).toArray()
      );
    }

    it('must apply entity key', () => {
      applyAndCheck('x');
    });

    it('must apply null entity', () => {
      applyAndCheck(null);
    });
  });

  describe('Apply entity across multiple blocks', () => {
    var blockMap = contentState.getBlockMap();
    var first = blockMap.first();
    var last = contentState.getBlockAfter(first.getKey());

    var targetSelection = new SelectionState({
      anchorKey: first.getKey(),
      anchorOffset: 0,
      focusKey: last.getKey(),
      focusOffset: last.getLength(),
    });

    function applyAndCheck(entityKey) {
      var withNewEntity = applyEntityToContentState(
        contentState,
        targetSelection,
        entityKey
      );
      var first = withNewEntity.getBlockMap().first();
      var last = withNewEntity.getBlockAfter(first.getKey());

      checkForCharacterList(first);
      expect(getEntities(first)).toEqual(
        Immutable.Repeat(entityKey, first.getLength()).toArray()
      );
      checkForCharacterList(last);
      expect(getEntities(last)).toEqual(
        Immutable.Repeat(entityKey, last.getLength()).toArray()
      );
    }

    it('must apply entity key', () => {
      applyAndCheck('x');
    });

    it('must apply null entity', () => {
      applyAndCheck(null);
    });
  });
});
describe('insertFragmentIntoContentState', () => {
  var sample = getSampleStateForTesting();
  var content = sample.contentState;
  var selection = sample.selectionState;

  var block = content.getBlockMap().first();
  var data = new Immutable.Map({a: 1});
  var secondData = new Immutable.Map({b: 2});

  function createFragment() {
    var fragmentArray = [
      new ContentBlock({
        key: 'j',
        type: 'unstyled',
        text: 'xx',
        characterList: CharacterMetadata.EMPTY,
        data: data,
      }),
    ];
    return BlockMapBuilder.createFromArray(fragmentArray);
  }

  function createMultiblockFragment() {
    var fragmentArray = [
      new ContentBlock({
        key: 'j',
        type: 'unstyled',
        text: 'xx',
        characterList: CharacterMetadata.EMPTY,
        data: data,
      }),
      new ContentBlock({
        key: 'k',
        type: 'unstyled',
        text: 'yy',
        characterList: CharacterMetadata.EMPTY,
        data: secondData,
      }),
    ];
    return BlockMapBuilder.createFromArray(fragmentArray);
  }

  it('must throw if no fragment is provided', () => {
    var fragment = BlockMapBuilder.createFromArray([]);
    expect(() => {
      insertFragmentIntoContentState(
        content,
        selection,
        fragment
      );
    }).toThrow();
  });

  it('must apply fragment to the start', () => {
    var fragment = createFragment();
    var modified = insertFragmentIntoContentState(
      content,
      selection,
      fragment
    );

    var newBlock = modified.getBlockMap().first();

    expect(newBlock.getText().slice(0, 2)).toBe('xx');
    expect(newBlock.getData()).toBe(data);
  });

  it('must apply fragment to within block', () => {
    var target = selection.merge({
      focusOffset: 2,
      anchorOffset: 2,
      isBackward: false,
    });

    var fragment = createFragment();

    var modified = insertFragmentIntoContentState(
      content,
      target,
      fragment
    );

    var newBlock = modified.getBlockMap().first();

    expect(newBlock.getText().slice(2, 4)).toBe('xx');
    expect(newBlock.getData()).toBe(data);
  });

  it('must apply fragment at the end', () => {
    var length = block.getLength();
    var target = selection.merge({
      focusOffset: length,
      anchorOffset: length,
      isBackward: false,
    });

    var fragment = createFragment();
    var modified = insertFragmentIntoContentState(
      content,
      target,
      fragment
    );

    var newBlock = modified.getBlockMap().first();

    expect(newBlock.getText().slice(length, length + 2)).toBe('xx');
    expect(newBlock.getData()).toBe(data);
  });

  it('must apply multiblock fragments', () => {
    var fragment = createMultiblockFragment();
    var modified = insertFragmentIntoContentState(
      content,
      selection,
      fragment
    );

    var newBlock = modified.getBlockMap().first();
    var secondBlock = modified.getBlockMap().toArray()[1];

    expect(newBlock.getText()).toBe('xx');
    expect(newBlock.getData()).toBe(data);
    expect(secondBlock.getText().slice(0, 2)).toBe('yy');
    expect(secondBlock.getData()).toBe(secondData);
  });

});
Ejemplo n.º 7
0
describe('removeEntitiesAtEdges', () => {
  var {
    List,
    Repeat,
  } = Immutable;

  var {
    contentState,
    selectionState,
  } = getSampleStateForTesting();

  var selectionOnEntity = selectionState.merge({
    anchorKey: 'b',
    anchorOffset: 2,
    focusKey: 'b',
    focusOffset: 2,
  });

  function getEntities(block) {
    return block.getCharacterList().map(c => c.getEntity()).toJS();
  }

  function expectSameBlockMap(before, after) {
    expect(before.getBlockMap()).toBe(after.getBlockMap());
  }

  function expectNullEntities(block) {
    expect(
      getEntities(block)
    ).toEqual(
      List(Repeat(null, block.getLength())).toJS()
    );
  }

  function setEntityMutability(mutability) {
    contentState.getEntityMap().__get = () => ({
      getMutability: () => mutability,
    });
  }

  describe('Within a single block', () => {
    it('must not affect blockMap if there are no entities', () => {
      var result = removeEntitiesAtEdges(contentState, selectionState);
      expectSameBlockMap(contentState, result);
    });

    describe('Handling different mutability types', () => {
      it('must not remove mutable entities', () => {
        setEntityMutability('MUTABLE');
        var result = removeEntitiesAtEdges(contentState, selectionOnEntity);
        expectSameBlockMap(contentState, result);
      });

      it('must remove immutable entities', () => {
        setEntityMutability('IMMUTABLE');
        var result = removeEntitiesAtEdges(contentState, selectionOnEntity);
        expect(result.getBlockMap()).not.toBe(contentState.getBlockMap());
        expectNullEntities(result.getBlockForKey('b'));
      });

      it('must remove segmented entities', () => {
        setEntityMutability('SEGMENTED');
        var result = removeEntitiesAtEdges(contentState, selectionOnEntity);
        expect(result.getBlockMap()).not.toBe(contentState.getBlockMap());
        expectNullEntities(result.getBlockForKey('b'));
      });
    });

    describe('Removal for a collapsed cursor', () => {
      setEntityMutability('IMMUTABLE');

      it('must not remove if cursor is at start of entity', () => {
        var selection = selectionOnEntity.merge({
          anchorOffset: 0,
          focusOffset: 0,
        });
        var result = removeEntitiesAtEdges(contentState, selection);
        expectSameBlockMap(contentState, result);
      });

      it('must remove if cursor is within entity', () => {
        var result = removeEntitiesAtEdges(contentState, selectionOnEntity);
        expectNullEntities(result.getBlockForKey('b'));
      });

      it('must not remove if cursor is at end of entity', () => {
        var length = contentState.getBlockForKey('b').getLength();
        var selection = selectionOnEntity.merge({
          anchorOffset: length,
          focusOffset: length,
        });
        var result = removeEntitiesAtEdges(contentState, selection);
        expectSameBlockMap(contentState, result);
      });
    });

    describe('Removal for a non-collapsed cursor', () => {
      setEntityMutability('IMMUTABLE');

      it('must remove for non-collapsed cursor within a single entity', () => {
        setEntityMutability('IMMUTABLE');
        var selection = selectionOnEntity.set('anchorOffset', 1);
        var result = removeEntitiesAtEdges(contentState, selection);
        expectNullEntities(result.getBlockForKey('b'));
      });

      it('must remove for non-collapsed cursor on multiple entities', () => {
        var block = contentState.getBlockForKey('b');
        var newBlock = applyEntityToContentBlock(block, 3, 5, '456');
        var newBlockMap = contentState.getBlockMap().set('b', newBlock);
        var newContent = contentState.set('blockMap', newBlockMap);
        var selection = selectionOnEntity.merge({
          anchorOffset: 1,
          focusOffset: 4,
        });
        var result = removeEntitiesAtEdges(newContent, selection);
        expectNullEntities(result.getBlockForKey('b'));
      });

      it('must ignore an entity that is entirely within the selection', () => {
        var block = contentState.getBlockForKey('b');

        // Remove entity from beginning and end of block.
        var newBlock = applyEntityToContentBlock(block, 0, 1, null);
        newBlock = applyEntityToContentBlock(newBlock, 4, 5, null);

        var newBlockMap = contentState.getBlockMap().set('b', newBlock);
        var newContent = contentState.set('blockMap', newBlockMap);
        var selection = selectionOnEntity.merge({
          anchorOffset: 0,
          focusOffset: 5,
        });
        var result = removeEntitiesAtEdges(newContent, selection);
        expectSameBlockMap(newContent, result);
      });
    });
  });

  describe('Across multiple blocks', () => {
    setEntityMutability('IMMUTABLE');

    it('must remove entity at start of selection', () => {
      var selection = selectionState.merge({
        anchorKey: 'b',
        anchorOffset: 3,
        focusKey: 'c',
        focusOffset: 3,
      });
      var result = removeEntitiesAtEdges(contentState, selection);
      expectNullEntities(result.getBlockForKey('b'));
      expectNullEntities(result.getBlockForKey('c'));
    });

    it('must remove entity at end of selection', () => {
      var selection = selectionState.merge({
        anchorKey: 'a',
        anchorOffset: 3,
        focusKey: 'b',
        focusOffset: 3,
      });
      var result = removeEntitiesAtEdges(contentState, selection);
      expectNullEntities(result.getBlockForKey('a'));
      expectNullEntities(result.getBlockForKey('b'));
    });

    it('must remove entities at both ends of selection', () => {
      var cBlock = contentState.getBlockForKey('c');
      var len = cBlock.getLength();
      var modifiedC = applyEntityToContentBlock(cBlock, 0, len, '456');
      var newBlockMap = contentState.getBlockMap().set('c', modifiedC);
      var newContent = contentState.set('blockMap', newBlockMap);
      var selection = selectionState.merge({
        anchorKey: 'b',
        anchorOffset: 3,
        focusKey: 'c',
        focusOffset: 3,
      });
      var result = removeEntitiesAtEdges(newContent, selection);
      expectNullEntities(result.getBlockForKey('b'));
      expectNullEntities(result.getBlockForKey('c'));
    });
  });
});
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @emails isaac, oncall+ui_infra
 */

jest.disableAutomock();

var getEntityKeyForSelection = require('getEntityKeyForSelection');
var getSampleStateForTesting = require('getSampleStateForTesting');

var {
  contentState,
  selectionState,
} = getSampleStateForTesting();

selectionState = selectionState.merge({
  anchorKey: 'b',
  focusKey: 'b',
});

function setEntityMutability(mutability) {
  contentState.getEntityMap().get = () => ({
    getMutability: () => mutability,
  });
}
describe('getEntityKeyForSelection', () => {
  describe('collapsed selection', () => {
    var collapsed = selectionState.merge({
      anchorOffset: 2,
describe('removeRangeFromContentState', () => {
  var {
    contentState,
    selectionState,
  } = getSampleStateForTesting();

  function checkForCharacterList(block) {
    expect(Immutable.List.isList(block.getCharacterList())).toBe(true);
  }

  function getInlineStyles(block) {
    return block.getCharacterList().map(c => c.getStyle()).toJS();
  }

  function getEntities(block) {
    return block.getCharacterList().map(c => c.getEntity()).toJS();
  }

  function expectBlockToBeSlice(block, comparison, start, end) {
    expect(block.getType()).toBe(comparison.getType());
    expect(block.getText()).toBe(comparison.getText().slice(start, end));
    expect(
      getInlineStyles(block),
    ).toEqual(
      getInlineStyles(comparison).slice(start, end),
    );
    expect(
      getEntities(block),
    ).toEqual(
      getEntities(comparison).slice(start, end),
    );
    checkForCharacterList(block);
  }

  it('must return the input ContentState if selection is collapsed', () => {
    expect(
      removeRangeFromContentState(contentState, selectionState),
    ).toBe(contentState);
  });

  describe('Removal within a single block', () => {
    it('must remove from the beginning of the block', () => {
      // Remove from 0 to 3.
      var selection = selectionState.set('focusOffset', 3);
      var afterRemoval = removeRangeFromContentState(contentState, selection);
      var afterBlockMap = afterRemoval.getBlockMap();
      var alteredBlock = afterBlockMap.first();
      var originalBlock = contentState.getBlockMap().first();

      expectBlockToBeSlice(alteredBlock, originalBlock, 3);
    });

    it('must remove from within the block', () => {
      // Remove from 2 to 4.
      var selection = selectionState.merge({
        anchorOffset: 2,
        focusOffset: 4,
      });
      var afterRemoval = removeRangeFromContentState(contentState, selection);
      var afterBlockMap = afterRemoval.getBlockMap();
      var alteredBlock = afterBlockMap.first();
      var originalBlock = contentState.getBlockMap().first();

      expect(alteredBlock).not.toBe(originalBlock);
      expect(alteredBlock.getType()).toBe(originalBlock.getType());
      expect(alteredBlock.getText()).toBe(
        originalBlock.getText().slice(0, 2) +
        originalBlock.getText().slice(4),
      );

      var stylesToJS = getInlineStyles(originalBlock);
      expect(getInlineStyles(alteredBlock)).toEqual(
        stylesToJS.slice(0, 2).concat(stylesToJS.slice(4)),
      );
      var entitiesToJS = getEntities(originalBlock);
      expect(getEntities(alteredBlock)).toEqual(
        entitiesToJS.slice(0, 2).concat(entitiesToJS.slice(4)),
      );
    });

    it('nust remove to the end of the block', () => {
      // Remove from 3 to end.
      var originalBlock = contentState.getBlockMap().first();
      var selection = selectionState.merge({
        anchorOffset: 3,
        focusOffset: originalBlock.getLength(),
      });
      var afterRemoval = removeRangeFromContentState(contentState, selection);
      var afterBlockMap = afterRemoval.getBlockMap();
      var alteredBlock = afterBlockMap.first();

      expectBlockToBeSlice(alteredBlock, originalBlock, 0, 3);
    });
  });

  describe('Removal across two blocks', () => {
    describe('Removal from the start of A', () => {
      it('must remove from the start of A to the start of B', () => {
        var selection = selectionState.set('focusKey', 'b');
        var afterRemoval = removeRangeFromContentState(contentState, selection);
        var afterBlockMap = afterRemoval.getBlockMap();
        expect(afterBlockMap.size).toBe(2);
        var alteredBlock = afterBlockMap.first();

        // Block B is removed. Its contents replace the contents of block A,
        // while the `type` of block A is preserved.
        var blockB = contentState.getBlockMap().skip(1).first();
        var sameContentDifferentType = blockB.set(
          'type',
          contentState.getBlockMap().first().getType(),
        );

        expectBlockToBeSlice(alteredBlock, sameContentDifferentType, 0);
      });

      it('must remove from the start of A to within B', () => {
        var selection = selectionState.merge({
          focusKey: 'b',
          focusOffset: 3,
        });

        var afterRemoval = removeRangeFromContentState(contentState, selection);
        var afterBlockMap = afterRemoval.getBlockMap();
        expect(afterBlockMap.size).toBe(2);
        var alteredBlock = afterBlockMap.first();

        // A slice of block B contents replace the contents of block A,
        // while the `type` of block A is preserved. Block B is removed.
        var blockB = contentState.getBlockMap().skip(1).first();
        var sameContentDifferentType = blockB.set(
          'type',
          contentState.getBlockMap().first().getType(),
        );

        expectBlockToBeSlice(alteredBlock, sameContentDifferentType, 3);
      });

      it('must remove from the start of A to the end of B', () => {
        var blockB = contentState.getBlockMap().skip(1).first();
        var selection = selectionState.merge({
          focusKey: 'b',
          focusOffset: blockB.getLength(),
        });

        var afterRemoval = removeRangeFromContentState(contentState, selection);
        var afterBlockMap = afterRemoval.getBlockMap();
        expect(afterBlockMap.size).toBe(2);
        var alteredBlock = afterBlockMap.first();

        // Block A is effectively just emptied out, while block B is removed.
        var emptyBlock = new ContentBlock({
          text: '',
          type: contentState.getBlockMap().first().getType(),
          characterList: Immutable.List(),
        });

        expectBlockToBeSlice(alteredBlock, emptyBlock);
      });
    });

    describe('Removal from within A', () => {
      var selectionWithinA = selectionState.set('anchorOffset', 3);

      it('must remove from within A to the start of B', () => {
        var selection = selectionWithinA.set('focusKey', 'b');

        var originalBlockA = contentState.getBlockMap().first();
        var originalBlockB = contentState.getBlockMap().skip(1).first();
        var afterRemoval = removeRangeFromContentState(contentState, selection);
        var afterBlockMap = afterRemoval.getBlockMap();
        expect(afterBlockMap.size).toBe(2);
        var alteredBlock = afterBlockMap.first();

        expect(alteredBlock).not.toBe(originalBlockA);
        expect(alteredBlock).not.toBe(originalBlockB);
        expect(alteredBlock.getType()).toBe(originalBlockA.getType());
        expect(alteredBlock.getText()).toBe(
          originalBlockA.getText().slice(0, 3) +
          originalBlockB.getText(),
        );

        var stylesToJS = getInlineStyles(originalBlockA);
        expect(getInlineStyles(alteredBlock)).toEqual(
          stylesToJS.slice(0, 3).concat(getInlineStyles(originalBlockB)),
        );
        var entitiesToJS = getEntities(originalBlockA);
        expect(getEntities(alteredBlock)).toEqual(
          entitiesToJS.slice(0, 3).concat(getEntities(originalBlockB)),
        );

        checkForCharacterList(alteredBlock);
      });

      it('must remove from within A to within B', () => {
        var selection = selectionWithinA.merge({
          focusKey: 'b',
          focusOffset: 3,
        });

        var originalBlockA = contentState.getBlockMap().first();
        var originalBlockB = contentState.getBlockMap().skip(1).first();
        var afterRemoval = removeRangeFromContentState(contentState, selection);
        var afterBlockMap = afterRemoval.getBlockMap();
        expect(afterBlockMap.size).toBe(2);
        var alteredBlock = afterBlockMap.first();

        expect(alteredBlock).not.toBe(originalBlockA);
        expect(alteredBlock).not.toBe(originalBlockB);
        expect(alteredBlock.getType()).toBe(originalBlockA.getType());
        expect(alteredBlock.getText()).toBe(
          originalBlockA.getText().slice(0, 3) +
          originalBlockB.getText().slice(3),
        );

        var stylesToJS = getInlineStyles(originalBlockA);
        expect(getInlineStyles(alteredBlock)).toEqual(
          stylesToJS.slice(0, 3).concat(
            getInlineStyles(originalBlockB).slice(3),
          ),
        );
        var entitiesToJS = getEntities(originalBlockA);
        expect(getEntities(alteredBlock)).toEqual(
          entitiesToJS.slice(0, 3).concat(
            getEntities(originalBlockB).slice(3),
          ),
        );

        checkForCharacterList(alteredBlock);
      });

      it('must remove from within A to the end of B', () => {
        var blockB = contentState.getBlockMap().skip(1).first();
        var selection = selectionWithinA.merge({
          focusKey: 'b',
          focusOffset: blockB.getLength(),
        });

        var originalBlockA = contentState.getBlockMap().first();
        var originalBlockB = contentState.getBlockMap().skip(1).first();
        var afterRemoval = removeRangeFromContentState(contentState, selection);
        var afterBlockMap = afterRemoval.getBlockMap();
        expect(afterBlockMap.size).toBe(2);
        var alteredBlock = afterBlockMap.first();

        expect(alteredBlock).not.toBe(originalBlockA);
        expect(alteredBlock).not.toBe(originalBlockB);
        expect(alteredBlock.getType()).toBe(originalBlockA.getType());
        expect(alteredBlock.getText()).toBe(
          originalBlockA.getText().slice(0, 3),
        );

        var stylesToJS = getInlineStyles(originalBlockA);
        expect(getInlineStyles(alteredBlock)).toEqual(
          stylesToJS.slice(0, 3),
        );
        var entitiesToJS = getEntities(originalBlockA);
        expect(getEntities(alteredBlock)).toEqual(
          entitiesToJS.slice(0, 3),
        );

        checkForCharacterList(alteredBlock);
      });
    });

    describe('Removal from the end of A', () => {
      var initialBlock = contentState.getBlockMap().first();
      var selectionFromEndOfA = selectionState.merge({
        anchorOffset: initialBlock.getLength(),
        focusOffset: initialBlock.getLength(),
      });

      it('must remove from the end of A to the start of B', () => {
        var selection = selectionFromEndOfA.merge({
          focusKey: 'b',
          focusOffset: 0,
        });

        var originalBlockA = contentState.getBlockMap().first();
        var originalBlockB = contentState.getBlockMap().skip(1).first();
        var afterRemoval = removeRangeFromContentState(contentState, selection);
        var afterBlockMap = afterRemoval.getBlockMap();
        expect(afterBlockMap.size).toBe(2);
        var alteredBlock = afterBlockMap.first();

        expect(alteredBlock).not.toBe(originalBlockA);
        expect(alteredBlock).not.toBe(originalBlockB);
        expect(alteredBlock.getType()).toBe(originalBlockA.getType());
        expect(alteredBlock.getText()).toBe(
          originalBlockA.getText() +
          originalBlockB.getText(),
        );

        var stylesToJS = getInlineStyles(originalBlockA);
        expect(getInlineStyles(alteredBlock)).toEqual(
          stylesToJS.concat(getInlineStyles(originalBlockB)),
        );
        var entitiesToJS = getEntities(originalBlockA);
        expect(getEntities(alteredBlock)).toEqual(
          entitiesToJS.concat(getEntities(originalBlockB)),
        );

        checkForCharacterList(alteredBlock);
      });

      it('must remove from the end of A to within B', () => {
        var selection = selectionFromEndOfA.merge({
          focusKey: 'b',
          focusOffset: 3,
        });

        var originalBlockA = contentState.getBlockMap().first();
        var originalBlockB = contentState.getBlockMap().skip(1).first();
        var afterRemoval = removeRangeFromContentState(contentState, selection);
        var afterBlockMap = afterRemoval.getBlockMap();
        expect(afterBlockMap.size).toBe(2);
        var alteredBlock = afterBlockMap.first();

        expect(alteredBlock).not.toBe(originalBlockA);
        expect(alteredBlock).not.toBe(originalBlockB);
        expect(alteredBlock.getType()).toBe(originalBlockA.getType());
        expect(alteredBlock.getText()).toBe(
          originalBlockA.getText() +
          originalBlockB.getText().slice(3),
        );

        var stylesToJS = getInlineStyles(originalBlockA);
        expect(getInlineStyles(alteredBlock)).toEqual(
          stylesToJS.concat(
            getInlineStyles(originalBlockB).slice(3),
          ),
        );
        var entitiesToJS = getEntities(originalBlockA);
        expect(getEntities(alteredBlock)).toEqual(
          entitiesToJS.concat(
            getEntities(originalBlockB).slice(3),
          ),
        );
        checkForCharacterList(alteredBlock);
      });

      it('must remove from the end of A to the end of B', () => {
        var originalBlockA = contentState.getBlockMap().first();
        var originalBlockB = contentState.getBlockMap().skip(1).first();

        var selection = selectionFromEndOfA.merge({
          focusKey: 'b',
          focusOffset: originalBlockB.getLength(),
        });

        var afterRemoval = removeRangeFromContentState(contentState, selection);
        var afterBlockMap = afterRemoval.getBlockMap();
        expect(afterBlockMap.size).toBe(2);
        var alteredBlock = afterBlockMap.first();

        // no-op for the first block, since no new content is appended.
        expect(alteredBlock).toBe(originalBlockA);
        expect(alteredBlock).not.toBe(originalBlockB);
        expect(alteredBlock.getType()).toBe(originalBlockA.getType());

        expectBlockToBeSlice(alteredBlock, originalBlockA);
        checkForCharacterList(alteredBlock);
      });
    });
  });

  describe('Removal across more than two blocks', () => {
    var selection = selectionState.merge({
      anchorOffset: 3,
      focusKey: 'c',
      focusOffset: 3,
    });

    it('must remove blocks entirely within the selection', () => {
      var originalBlockA = contentState.getBlockMap().first();
      var originalBlockB = contentState.getBlockMap().skip(1).first();
      var originalBlockC = contentState.getBlockMap().skip(2).first();

      var afterRemoval = removeRangeFromContentState(contentState, selection);
      var afterBlockMap = afterRemoval.getBlockMap();
      expect(afterBlockMap.size).toBe(1);
      var alteredBlock = afterBlockMap.first();

      expect(alteredBlock).not.toBe(originalBlockA);
      expect(alteredBlock).not.toBe(originalBlockB);
      expect(alteredBlock).not.toBe(originalBlockC);
      expect(alteredBlock.getType()).toBe(originalBlockA.getType());
      expect(alteredBlock.getText()).toBe(
        originalBlockA.getText().slice(0, 3) +
        originalBlockC.getText().slice(3),
      );

      var stylesToJS = getInlineStyles(originalBlockA);
      expect(getInlineStyles(alteredBlock)).toEqual(
        stylesToJS.slice(0, 3).concat(
          getInlineStyles(originalBlockC).slice(3),
        ),
      );
      var entitiesToJS = getEntities(originalBlockA);
      expect(getEntities(alteredBlock)).toEqual(
        entitiesToJS.slice(0, 3).concat(
          getEntities(originalBlockC).slice(3),
        ),
      );

      checkForCharacterList(alteredBlock);
    });
  });
});
Ejemplo n.º 10
0
describe('MediaUtils', () => {
  const {
    editorState,
    contentState,
    selectionState,
  } = getSampleStateForTesting();
  const originalFirstBlock = contentState.getBlockMap().first();
  const entityKey = 'abc';
  const character = ' ';

  function assertMediaBlock(block) {
    expect(block.getType()).toBe('media');
    expect(block.getText()).toBe(character);
    expect(block.getCharacterList().first().getEntity()).toBe(entityKey);
  }

  describe('Collapsed cursor', () => {
    it('must insert media at start of block', () => {
      const resultEditor = insertMedia(editorState, entityKey, character);
      const resultContent = resultEditor.getCurrentContent();

      // Empty block inserted above content.
      const firstBlock = resultContent.getBlockMap().first();
      expect(firstBlock.getType()).toBe('unstyled');
      expect(firstBlock.getText()).toBe('');

      const secondBlock = resultContent.getBlockMap().skip(1).first();
      assertMediaBlock(secondBlock);

      const thirdBlock = resultContent.getBlockMap().skip(2).first();
      expect(thirdBlock.getText()).toBe(originalFirstBlock.getText());
    });

    it('must insert media within a block, via split', () => {
      const targetSelection = selectionState.merge({
        anchorOffset: 2,
        focusOffset: 2,
      });
      const targetEditor = EditorState.forceSelection(
        editorState,
        targetSelection
      );

      const resultEditor = insertMedia(targetEditor, entityKey, character);
      const resultContent = resultEditor.getCurrentContent();

      const firstBlock = resultContent.getBlockMap().first();
      expect(firstBlock.getType()).toBe(originalFirstBlock.getType());
      expect(
        firstBlock.getText()
      ).toBe(
        originalFirstBlock.getText().slice(0, 2)
      );

      const secondBlock = resultContent.getBlockMap().skip(1).first();
      assertMediaBlock(secondBlock);

      const thirdBlock = resultContent.getBlockMap().skip(2).first();
      expect(thirdBlock.getType()).toBe(originalFirstBlock.getType());
      expect(thirdBlock.getText()).toBe(originalFirstBlock.getText().slice(2));
    });

    it('must insert media after a block', () => {
      const targetSelection = selectionState.merge({
        anchorOffset: originalFirstBlock.getLength(),
        focusOffset: originalFirstBlock.getLength(),
      });
      const targetEditor = EditorState.forceSelection(
        editorState,
        targetSelection
      );

      const resultEditor = insertMedia(targetEditor, entityKey, character);
      const resultContent = resultEditor.getCurrentContent();

      const firstBlock = resultContent.getBlockMap().first();
      expect(firstBlock.getType()).toBe(originalFirstBlock.getType());
      expect(firstBlock.getText()).toBe(originalFirstBlock.getText());

      const secondBlock = resultContent.getBlockMap().skip(1).first();
      assertMediaBlock(secondBlock);

      const thirdBlock = resultContent.getBlockMap().skip(2).first();
      expect(thirdBlock.getType()).toBe(originalFirstBlock.getType());
      expect(thirdBlock.getText()).toBe('');
    });
  });

  describe('Non-collapsed cursor', () => {
    it('must insert media at start of block', () => {
      const targetSelection = selectionState.merge({
        anchorOffset: 0,
        focusOffset: 2,
      });
      const targetEditor = EditorState.forceSelection(
        editorState,
        targetSelection
      );

      const resultEditor = insertMedia(targetEditor, entityKey, character);
      const resultContent = resultEditor.getCurrentContent();

      const firstBlock = resultContent.getBlockMap().first();
      expect(firstBlock.getType()).toBe(originalFirstBlock.getType());
      expect(firstBlock.getText()).toBe('');

      const secondBlock = resultContent.getBlockMap().skip(1).first();
      assertMediaBlock(secondBlock);

      const thirdBlock = resultContent.getBlockMap().skip(2).first();
      expect(thirdBlock.getType()).toBe(originalFirstBlock.getType());
      expect(thirdBlock.getText()).toBe(originalFirstBlock.getText().slice(2));
    });

    it('must insert media within a block', () => {
      const targetSelection = selectionState.merge({
        anchorOffset: 1,
        focusOffset: 2,
      });
      const targetEditor = EditorState.forceSelection(
        editorState,
        targetSelection
      );

      const resultEditor = insertMedia(targetEditor, entityKey, character);
      const resultContent = resultEditor.getCurrentContent();

      const firstBlock = resultContent.getBlockMap().first();
      expect(firstBlock.getType()).toBe(originalFirstBlock.getType());
      expect(
        firstBlock.getText()
      ).toBe(
        originalFirstBlock.getText().slice(0, 1)
      );

      const secondBlock = resultContent.getBlockMap().skip(1).first();
      assertMediaBlock(secondBlock);

      const thirdBlock = resultContent.getBlockMap().skip(2).first();
      expect(thirdBlock.getType()).toBe(originalFirstBlock.getType());
      expect(thirdBlock.getText()).toBe(originalFirstBlock.getText().slice(2));
    });

    it('must insert media at end of block', () => {
      const origLength = originalFirstBlock.getLength();
      const targetSelection = selectionState.merge({
        anchorOffset: origLength - 2,
        focusOffset: origLength,
      });
      const targetEditor = EditorState.forceSelection(
        editorState,
        targetSelection
      );

      const resultEditor = insertMedia(targetEditor, entityKey, character);
      const resultContent = resultEditor.getCurrentContent();

      const firstBlock = resultContent.getBlockMap().first();
      expect(firstBlock.getType()).toBe(originalFirstBlock.getType());
      expect(
        firstBlock.getText()
      ).toBe(
        originalFirstBlock.getText().slice(0, origLength - 2)
      );

      const secondBlock = resultContent.getBlockMap().skip(1).first();
      assertMediaBlock(secondBlock);

      const thirdBlock = resultContent.getBlockMap().skip(2).first();
      expect(thirdBlock.getType()).toBe(originalFirstBlock.getType());
      expect(thirdBlock.getText()).toBe('');
    });

    it('must insert media for cross-block selection', () => {
      const originalThirdBlock = contentState.getBlockMap().skip(2).first();

      const targetSelection = selectionState.merge({
        anchorOffset: 2,
        focusKey: originalThirdBlock.getKey(),
        focusOffset: 2,
      });
      const targetEditor = EditorState.forceSelection(
        editorState,
        targetSelection
      );

      const resultEditor = insertMedia(targetEditor, entityKey, character);
      const resultContent = resultEditor.getCurrentContent();

      const firstBlock = resultContent.getBlockMap().first();
      expect(firstBlock.getType()).toBe(originalFirstBlock.getType());
      expect(
        firstBlock.getText()
      ).toBe(
        originalFirstBlock.getText().slice(0, 2)
      );

      const secondBlock = resultContent.getBlockMap().skip(1).first();
      assertMediaBlock(secondBlock);

      // Third block gets original first block's type, but sliced text from
      // original second block.
      const thirdBlock = resultContent.getBlockMap().skip(2).first();
      expect(thirdBlock.getType()).toBe(originalFirstBlock.getType());
      expect(thirdBlock.getText()).toBe(originalThirdBlock.getText().slice(2));
    });
  });
});