Skip to content

Commit

Permalink
add test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
JiuqingSong committed Jun 10, 2024
1 parent b296c39 commit c46cd18
Show file tree
Hide file tree
Showing 3 changed files with 447 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,32 @@ import type {
Selectable,
} from 'roosterjs-content-model-types';

interface SegmentItem {
/**
* @internal Export for test only
*/
export interface SegmentItem {
paragraph: ContentModelParagraph;
segments: ContentModelSegment[];
}

interface TableItem {
/**
* @internal Export for test only
*/
export interface TableItem {
tableRows: ContentModelTableRow[];
}

interface IndexedSegmentNode extends Node {
/**
* @internal Export for test only
*/
export interface IndexedSegmentNode extends Node {
__roosterjsContentModel: SegmentItem;
}

interface IndexedTableElement extends HTMLTableElement {
/**
* @internal Export for test only
*/
export interface IndexedTableElement extends HTMLTableElement {
__roosterjsContentModel: TableItem;
}

Expand Down Expand Up @@ -89,7 +101,7 @@ function getIndexedSegmentItem(node: Node | null): SegmentItem | null {
* Implementation of DomIndexer
*/
export class DomIndexerImpl implements DomIndexer {
constructor(public readonly persistCache?: boolean) {}
constructor(private readonly persistCache?: boolean) {}

onSegment(segmentNode: Node, paragraph: ContentModelParagraph, segment: ContentModelSegment[]) {
const indexedText = segmentNode as IndexedSegmentNode;
Expand Down Expand Up @@ -206,6 +218,37 @@ export class DomIndexerImpl implements DomIndexer {
return false;
}

reconcileChildList(addedNodes: ArrayLike<Node>, removedNodes: ArrayLike<Node>): boolean {
if (!this.persistCache) {
return false;
}

let canHandle = true;
const context: ReconcileChildListContext = {
segIndex: -1,
};

// First process added nodes
const addedNode = addedNodes[0];

if (addedNodes.length == 1 && isNodeOfType(addedNode, 'TEXT_NODE')) {
canHandle = this.reconcileAddedNode(addedNode, context);
} else if (addedNodes.length > 0) {
canHandle = false;
}

// Second, process removed nodes
const removedNode = removedNodes[0];

if (canHandle && removedNodes.length == 1) {
canHandle = this.reconcileRemovedNode(removedNode, context);
} else if (removedNodes.length > 0) {
canHandle = false;
}

return canHandle && !context.pendingTextNode;
}

private isCollapsed(selection: RangeSelectionForCache): boolean {
const { start, end } = selection;

Expand Down Expand Up @@ -331,33 +374,6 @@ export class DomIndexerImpl implements DomIndexer {
return selectable;
}

reconcileChildList(addedNodes: ArrayLike<Node>, removedNodes: ArrayLike<Node>): boolean {
let canHandle = true;
const context: ReconcileChildListContext = {
segIndex: -1,
};

// First process added nodes
const addedNode = addedNodes[0];

if (addedNodes.length == 1 && isNodeOfType(addedNode, 'TEXT_NODE')) {
canHandle = this.reconcileAddedNode(addedNode, context);
} else if (addedNodes.length > 0) {
canHandle = false;
}

// Second, process removed nodes
const removedNode = removedNodes[0];

if (canHandle && removedNodes.length == 1) {
canHandle = this.reconcileRemovedNode(removedNode, context);
} else if (removedNodes.length > 0) {
canHandle = false;
}

return canHandle && !context.pendingTextNode;
}

private reconcileAddedNode(node: Text, context: ReconcileChildListContext): boolean {
let segmentItem: SegmentItem | null = null;
let index = -1;
Expand Down Expand Up @@ -409,6 +425,11 @@ export class DomIndexerImpl implements DomIndexer {
context.paragraph = segmentItem.paragraph;
context.segIndex = segmentItem.paragraph.segments.indexOf(segmentItem.segments[0]);

if (context.segIndex < 0) {
// Indexed segment is not under paragraph, something wrong happens, we cannot keep handling
return false;
}

for (let i = 0; i < segmentItem.segments.length; i++) {
const index = segmentItem.paragraph.segments.indexOf(segmentItem.segments[i]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as setSelection from 'roosterjs-content-model-dom/lib/modelApi/selection/setSelection';
import { createRange } from 'roosterjs-content-model-dom/test/testUtils';
import { DomIndexerImpl } from '../../../lib/corePlugin/cache/domIndexerImpl';
import { DomIndexerImpl, IndexedSegmentNode } from '../../../lib/corePlugin/cache/domIndexerImpl';
import {
CacheSelection,
ContentModelDocument,
Expand Down Expand Up @@ -737,3 +737,181 @@ describe('domIndexerImpl.reconcileSelection', () => {
expect(model.hasRevertedRangeSelection).toBeFalsy();
});
});

describe('domIndexerImpl.reconcileChildList', () => {
it('Empty array', () => {
const domIndexer = new DomIndexerImpl(true);
const result = domIndexer.reconcileChildList([], []);

expect(result).toBeTrue();
});

it('Removed BR, not indexed', () => {
const domIndexer = new DomIndexerImpl(true);
const br = document.createElement('br');
const result = domIndexer.reconcileChildList([], [br]);

expect(result).toBeFalse();
});

it('Removed BR, indexed, segment is not under paragraph', () => {
const domIndexer = new DomIndexerImpl(true);
const br: Node = document.createElement('br');

const paragraph = createParagraph();
const segment = createBr();

(br as IndexedSegmentNode).__roosterjsContentModel = {
paragraph: paragraph,
segments: [segment],
};

const result = domIndexer.reconcileChildList([], [br]);

expect(result).toBeFalse();
expect(paragraph).toEqual({
blockType: 'Paragraph',
format: {},
segments: [],
});
});

it('Removed BR, indexed, segment is under paragraph', () => {
const domIndexer = new DomIndexerImpl(true);
const br: Node = document.createElement('br');

const paragraph = createParagraph();
const segment1 = createText('test1');
const segment2 = createBr();
const segment3 = createText('test3');

paragraph.segments.push(segment1, segment2, segment3);

(br as IndexedSegmentNode).__roosterjsContentModel = {
paragraph: paragraph,
segments: [segment2],
};

const result = domIndexer.reconcileChildList([], [br]);

expect(result).toBeTrue();
expect(paragraph).toEqual({
blockType: 'Paragraph',
format: {},
segments: [segment1, segment3],
});
});

it('Removed two BR, indexed', () => {
const domIndexer = new DomIndexerImpl(true);
const br1: Node = document.createElement('br');
const br2: Node = document.createElement('br');

const paragraph = createParagraph();
const segment1 = createBr();
const segment2 = createBr();
const segment3 = createText('test3');

paragraph.segments.push(segment1, segment2, segment3);

(br1 as IndexedSegmentNode).__roosterjsContentModel = {
paragraph: paragraph,
segments: [segment1],
};

(br2 as IndexedSegmentNode).__roosterjsContentModel = {
paragraph: paragraph,
segments: [segment2],
};

const result = domIndexer.reconcileChildList([], [br1, br2]);

expect(result).toBeFalse();
expect(paragraph).toEqual({
blockType: 'Paragraph',
format: {},
segments: [segment1, segment2, segment3],
});
});

it('Added BR', () => {
const domIndexer = new DomIndexerImpl(true);
const br: Node = document.createElement('br');

const result = domIndexer.reconcileChildList([br], []);

expect(result).toBeFalse();
});

it('Added Text', () => {
const domIndexer = new DomIndexerImpl(true);
const br: Text = document.createTextNode('test');

const result = domIndexer.reconcileChildList([], [br]);

expect(result).toBeFalse();
});

it('Added Text, remove BR', () => {
const domIndexer = new DomIndexerImpl(true);
const br: Node = document.createElement('br');
const text: Text = document.createTextNode('test');

const paragraph = createParagraph();
const segment = createBr({
fontSize: '10pt',
});

paragraph.segments.push(segment);

(br as IndexedSegmentNode).__roosterjsContentModel = {
paragraph: paragraph,
segments: [segment],
};

const result = domIndexer.reconcileChildList([text], [br]);

expect(result).toBeTrue();
expect(paragraph).toEqual({
blockType: 'Paragraph',
format: {},
segments: [
{
segmentType: 'Text',
text: 'test',
format: {
fontSize: '10pt',
},
},
],
});
});

it('Added two Texts, remove BR', () => {
const domIndexer = new DomIndexerImpl(true);
const br: Node = document.createElement('br');
const text1: Text = document.createTextNode('test1');
const text2: Text = document.createTextNode('test2');

const paragraph = createParagraph();
const segment = createBr({
fontSize: '10pt',
});

paragraph.segments.push(segment);

(br as IndexedSegmentNode).__roosterjsContentModel = {
paragraph: paragraph,
segments: [segment],
};

const result = domIndexer.reconcileChildList([text1, text2], [br]);

expect(result).toBeFalse();
expect(paragraph).toEqual({
blockType: 'Paragraph',
format: {},
segments: [segment],
});
});
});
Loading

0 comments on commit c46cd18

Please sign in to comment.