mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-14 08:36:22 +08:00
refactor(editor): merge inline to std (#11025)
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import { expect, test } from 'vitest';
|
||||
|
||||
import {
|
||||
deltaInsertsToChunks,
|
||||
transformDelta,
|
||||
} from '../../inline/utils/delta-convert.js';
|
||||
|
||||
test('transformDelta', () => {
|
||||
expect(
|
||||
transformDelta({
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
})
|
||||
).toEqual([
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(
|
||||
transformDelta({
|
||||
insert: '\n\naaa\n\nbbb\n\n',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
})
|
||||
).toEqual([
|
||||
'\n',
|
||||
'\n',
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
'\n',
|
||||
'\n',
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
'\n',
|
||||
'\n',
|
||||
]);
|
||||
});
|
||||
|
||||
test('deltaInsertsToChunks', () => {
|
||||
expect(
|
||||
deltaInsertsToChunks([
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
])
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
deltaInsertsToChunks([
|
||||
{
|
||||
insert: '\n\naaa\nbbb\n\n',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
])
|
||||
).toEqual([
|
||||
[],
|
||||
[],
|
||||
[
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
[],
|
||||
]);
|
||||
|
||||
expect(
|
||||
deltaInsertsToChunks([
|
||||
{
|
||||
insert: '\n\naaa\n',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: '\nbbb\n\n',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
])
|
||||
).toEqual([
|
||||
[],
|
||||
[],
|
||||
[
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
[],
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,526 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { InlineEditor } from '../../inline/index.js';
|
||||
|
||||
test('getDeltaByRangeIndex', () => {
|
||||
const yDoc = new Y.Doc();
|
||||
const yText = yDoc.getText('text');
|
||||
yText.applyDelta([
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
const inlineEditor = new InlineEditor(yText);
|
||||
|
||||
expect(inlineEditor.getDeltaByRangeIndex(0)).toEqual({
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(inlineEditor.getDeltaByRangeIndex(1)).toEqual({
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(inlineEditor.getDeltaByRangeIndex(3)).toEqual({
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(inlineEditor.getDeltaByRangeIndex(4)).toEqual({
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('getDeltasByInlineRange', () => {
|
||||
const yDoc = new Y.Doc();
|
||||
const yText = yDoc.getText('text');
|
||||
yText.applyDelta([
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'ccc',
|
||||
attributes: {
|
||||
underline: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
const inlineEditor = new InlineEditor(yText);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 0,
|
||||
length: 0,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 0,
|
||||
length: 1,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 0,
|
||||
length: 3,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 0,
|
||||
length: 4,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 3,
|
||||
length: 1,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 3,
|
||||
length: 3,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 3,
|
||||
length: 4,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
insert: 'ccc',
|
||||
attributes: {
|
||||
underline: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 6,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 4,
|
||||
length: 0,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 4,
|
||||
length: 1,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 4,
|
||||
length: 2,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(
|
||||
inlineEditor.getDeltasByInlineRange({
|
||||
index: 4,
|
||||
length: 4,
|
||||
})
|
||||
).toEqual([
|
||||
[
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
insert: 'ccc',
|
||||
attributes: {
|
||||
underline: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
index: 6,
|
||||
length: 3,
|
||||
},
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
test('cursor with format', () => {
|
||||
const yDoc = new Y.Doc();
|
||||
const yText = yDoc.getText('text');
|
||||
const inlineEditor = new InlineEditor(yText);
|
||||
|
||||
inlineEditor.insertText(
|
||||
{
|
||||
index: 0,
|
||||
length: 0,
|
||||
},
|
||||
'aaa',
|
||||
{
|
||||
bold: true,
|
||||
}
|
||||
);
|
||||
|
||||
inlineEditor.setMarks({
|
||||
italic: true,
|
||||
});
|
||||
|
||||
inlineEditor.insertText(
|
||||
{
|
||||
index: 3,
|
||||
length: 0,
|
||||
},
|
||||
'bbb'
|
||||
);
|
||||
|
||||
expect(inlineEditor.yText.toDelta()).toEqual([
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
bold: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('getFormat', () => {
|
||||
const yDoc = new Y.Doc();
|
||||
const yText = yDoc.getText('text');
|
||||
const inlineEditor = new InlineEditor(yText);
|
||||
|
||||
inlineEditor.insertText(
|
||||
{
|
||||
index: 0,
|
||||
length: 0,
|
||||
},
|
||||
'aaa',
|
||||
{
|
||||
bold: true,
|
||||
}
|
||||
);
|
||||
|
||||
inlineEditor.insertText(
|
||||
{
|
||||
index: 3,
|
||||
length: 0,
|
||||
},
|
||||
'bbb',
|
||||
{
|
||||
italic: true,
|
||||
}
|
||||
);
|
||||
|
||||
expect(inlineEditor.getFormat({ index: 0, length: 0 })).toEqual({});
|
||||
|
||||
expect(inlineEditor.getFormat({ index: 0, length: 1 })).toEqual({
|
||||
bold: true,
|
||||
});
|
||||
|
||||
expect(inlineEditor.getFormat({ index: 0, length: 3 })).toEqual({
|
||||
bold: true,
|
||||
});
|
||||
|
||||
expect(inlineEditor.getFormat({ index: 3, length: 0 })).toEqual({
|
||||
bold: true,
|
||||
});
|
||||
|
||||
expect(inlineEditor.getFormat({ index: 3, length: 1 })).toEqual({
|
||||
italic: true,
|
||||
});
|
||||
|
||||
expect(inlineEditor.getFormat({ index: 3, length: 3 })).toEqual({
|
||||
italic: true,
|
||||
});
|
||||
|
||||
expect(inlineEditor.getFormat({ index: 6, length: 0 })).toEqual({
|
||||
italic: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('incorrect format value `false`', () => {
|
||||
const yDoc = new Y.Doc();
|
||||
const yText = yDoc.getText('text');
|
||||
const inlineEditor = new InlineEditor(yText);
|
||||
|
||||
inlineEditor.insertText(
|
||||
{
|
||||
index: 0,
|
||||
length: 0,
|
||||
},
|
||||
'aaa',
|
||||
{
|
||||
// @ts-expect-error insert incorrect value
|
||||
bold: false,
|
||||
italic: true,
|
||||
}
|
||||
);
|
||||
|
||||
inlineEditor.insertText(
|
||||
{
|
||||
index: 3,
|
||||
length: 0,
|
||||
},
|
||||
'bbb',
|
||||
{
|
||||
underline: true,
|
||||
}
|
||||
);
|
||||
|
||||
expect(inlineEditor.yText.toDelta()).toEqual([
|
||||
{
|
||||
insert: 'aaa',
|
||||
attributes: {
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'bbb',
|
||||
attributes: {
|
||||
underline: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('yText should not contain \r', () => {
|
||||
const yDoc = new Y.Doc();
|
||||
const yText = yDoc.getText('text');
|
||||
yText.insert(0, 'aaa\r');
|
||||
|
||||
expect(yText.toString()).toEqual('aaa\r');
|
||||
expect(() => {
|
||||
new InlineEditor(yText);
|
||||
}).toThrow(
|
||||
'yText must not contain "\\r" because it will break the range synchronization'
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,347 @@
|
||||
import { expect, test } from 'vitest';
|
||||
|
||||
import {
|
||||
intersectInlineRange,
|
||||
isInlineRangeAfter,
|
||||
isInlineRangeBefore,
|
||||
isInlineRangeContain,
|
||||
isInlineRangeEdge,
|
||||
isInlineRangeEdgeAfter,
|
||||
isInlineRangeEdgeBefore,
|
||||
isInlineRangeEqual,
|
||||
isInlineRangeIntersect,
|
||||
isPoint,
|
||||
mergeInlineRange,
|
||||
} from '../../inline/utils/inline-range.js';
|
||||
|
||||
test('isInlineRangeContain', () => {
|
||||
expect(
|
||||
isInlineRangeContain({ index: 0, length: 0 }, { index: 0, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeContain({ index: 0, length: 0 }, { index: 0, length: 2 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeContain({ index: 0, length: 2 }, { index: 0, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeContain({ index: 0, length: 2 }, { index: 0, length: 1 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeContain({ index: 0, length: 2 }, { index: 0, length: 2 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeContain({ index: 1, length: 3 }, { index: 0, length: 0 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeContain({ index: 1, length: 3 }, { index: 0, length: 1 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeContain({ index: 1, length: 3 }, { index: 0, length: 2 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeContain({ index: 1, length: 4 }, { index: 2, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeContain({ index: 1, length: 4 }, { index: 2, length: 3 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeContain({ index: 1, length: 4 }, { index: 2, length: 4 })
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
test('isInlineRangeEqual', () => {
|
||||
expect(
|
||||
isInlineRangeEqual({ index: 0, length: 0 }, { index: 0, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeEqual({ index: 0, length: 2 }, { index: 0, length: 1 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeEqual({ index: 1, length: 3 }, { index: 1, length: 3 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeEqual({ index: 0, length: 0 }, { index: 1, length: 0 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeEqual({ index: 2, length: 0 }, { index: 2, length: 0 })
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('isInlineRangeIntersect', () => {
|
||||
expect(
|
||||
isInlineRangeIntersect({ index: 0, length: 2 }, { index: 0, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeIntersect({ index: 0, length: 2 }, { index: 2, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeIntersect({ index: 0, length: 0 }, { index: 1, length: 0 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeIntersect({ index: 1, length: 0 }, { index: 1, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeIntersect({ index: 1, length: 0 }, { index: 0, length: 1 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeIntersect({ index: 1, length: 0 }, { index: 0, length: 0 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeIntersect({ index: 1, length: 0 }, { index: 2, length: 0 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeIntersect({ index: 1, length: 0 }, { index: 0, length: 2 })
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('isInlineRangeBefore', () => {
|
||||
expect(
|
||||
isInlineRangeBefore({ index: 0, length: 1 }, { index: 2, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeBefore({ index: 2, length: 0 }, { index: 0, length: 1 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeBefore({ index: 0, length: 0 }, { index: 1, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeBefore({ index: 1, length: 0 }, { index: 0, length: 0 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeBefore({ index: 0, length: 0 }, { index: 0, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeBefore({ index: 0, length: 0 }, { index: 0, length: 1 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeBefore({ index: 0, length: 1 }, { index: 0, length: 0 })
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
test('isInlineRangeAfter', () => {
|
||||
expect(
|
||||
isInlineRangeAfter({ index: 2, length: 0 }, { index: 0, length: 1 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeAfter({ index: 0, length: 1 }, { index: 2, length: 0 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeAfter({ index: 1, length: 0 }, { index: 0, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeAfter({ index: 0, length: 0 }, { index: 1, length: 0 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeAfter({ index: 0, length: 0 }, { index: 0, length: 0 })
|
||||
).toEqual(true);
|
||||
|
||||
expect(
|
||||
isInlineRangeAfter({ index: 0, length: 0 }, { index: 0, length: 1 })
|
||||
).toEqual(false);
|
||||
|
||||
expect(
|
||||
isInlineRangeAfter({ index: 0, length: 1 }, { index: 0, length: 0 })
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('isInlineRangeEdge', () => {
|
||||
expect(isInlineRangeEdge(1, { index: 1, length: 0 })).toEqual(true);
|
||||
|
||||
expect(isInlineRangeEdge(1, { index: 0, length: 1 })).toEqual(true);
|
||||
|
||||
expect(isInlineRangeEdge(0, { index: 0, length: 0 })).toEqual(true);
|
||||
|
||||
expect(isInlineRangeEdge(1, { index: 0, length: 0 })).toEqual(false);
|
||||
|
||||
expect(isInlineRangeEdge(0, { index: 1, length: 0 })).toEqual(false);
|
||||
|
||||
expect(isInlineRangeEdge(0, { index: 0, length: 1 })).toEqual(true);
|
||||
});
|
||||
|
||||
test('isInlineRangeEdgeBefore', () => {
|
||||
expect(isInlineRangeEdgeBefore(1, { index: 1, length: 0 })).toEqual(true);
|
||||
|
||||
expect(isInlineRangeEdgeBefore(1, { index: 0, length: 1 })).toEqual(false);
|
||||
|
||||
expect(isInlineRangeEdgeBefore(0, { index: 0, length: 0 })).toEqual(true);
|
||||
|
||||
expect(isInlineRangeEdgeBefore(1, { index: 0, length: 0 })).toEqual(false);
|
||||
|
||||
expect(isInlineRangeEdgeBefore(0, { index: 1, length: 0 })).toEqual(false);
|
||||
|
||||
expect(isInlineRangeEdgeBefore(0, { index: 0, length: 1 })).toEqual(true);
|
||||
});
|
||||
|
||||
test('isInlineRangeEdgeAfter', () => {
|
||||
expect(isInlineRangeEdgeAfter(1, { index: 0, length: 1 })).toEqual(true);
|
||||
|
||||
expect(isInlineRangeEdgeAfter(1, { index: 1, length: 0 })).toEqual(true);
|
||||
|
||||
expect(isInlineRangeEdgeAfter(0, { index: 0, length: 0 })).toEqual(true);
|
||||
|
||||
expect(isInlineRangeEdgeAfter(0, { index: 1, length: 0 })).toEqual(false);
|
||||
|
||||
expect(isInlineRangeEdgeAfter(1, { index: 0, length: 0 })).toEqual(false);
|
||||
|
||||
expect(isInlineRangeEdgeAfter(0, { index: 0, length: 1 })).toEqual(false);
|
||||
|
||||
expect(isInlineRangeEdgeAfter(0, { index: 0, length: 0 })).toEqual(true);
|
||||
});
|
||||
|
||||
test('isPoint', () => {
|
||||
expect(isPoint({ index: 1, length: 0 })).toEqual(true);
|
||||
|
||||
expect(isPoint({ index: 0, length: 2 })).toEqual(false);
|
||||
|
||||
expect(isPoint({ index: 0, length: 0 })).toEqual(true);
|
||||
|
||||
expect(isPoint({ index: 2, length: 0 })).toEqual(true);
|
||||
|
||||
expect(isPoint({ index: 2, length: 2 })).toEqual(false);
|
||||
});
|
||||
|
||||
test('mergeInlineRange', () => {
|
||||
expect(
|
||||
mergeInlineRange({ index: 0, length: 0 }, { index: 1, length: 0 })
|
||||
).toEqual({
|
||||
index: 0,
|
||||
length: 1,
|
||||
});
|
||||
|
||||
expect(
|
||||
mergeInlineRange({ index: 0, length: 0 }, { index: 0, length: 0 })
|
||||
).toEqual({
|
||||
index: 0,
|
||||
length: 0,
|
||||
});
|
||||
|
||||
expect(
|
||||
mergeInlineRange({ index: 1, length: 0 }, { index: 2, length: 0 })
|
||||
).toEqual({
|
||||
index: 1,
|
||||
length: 1,
|
||||
});
|
||||
|
||||
expect(
|
||||
mergeInlineRange({ index: 2, length: 0 }, { index: 1, length: 0 })
|
||||
).toEqual({
|
||||
index: 1,
|
||||
length: 1,
|
||||
});
|
||||
|
||||
expect(
|
||||
mergeInlineRange({ index: 1, length: 3 }, { index: 2, length: 2 })
|
||||
).toEqual({
|
||||
index: 1,
|
||||
length: 3,
|
||||
});
|
||||
|
||||
expect(
|
||||
mergeInlineRange({ index: 2, length: 2 }, { index: 1, length: 1 })
|
||||
).toEqual({
|
||||
index: 1,
|
||||
length: 3,
|
||||
});
|
||||
|
||||
expect(
|
||||
mergeInlineRange({ index: 3, length: 2 }, { index: 2, length: 1 })
|
||||
).toEqual({
|
||||
index: 2,
|
||||
length: 3,
|
||||
});
|
||||
|
||||
expect(
|
||||
mergeInlineRange({ index: 0, length: 4 }, { index: 1, length: 1 })
|
||||
).toEqual({
|
||||
index: 0,
|
||||
length: 4,
|
||||
});
|
||||
|
||||
expect(
|
||||
mergeInlineRange({ index: 1, length: 1 }, { index: 0, length: 4 })
|
||||
).toEqual({
|
||||
index: 0,
|
||||
length: 4,
|
||||
});
|
||||
|
||||
expect(
|
||||
mergeInlineRange({ index: 0, length: 2 }, { index: 1, length: 3 })
|
||||
).toEqual({
|
||||
index: 0,
|
||||
length: 4,
|
||||
});
|
||||
});
|
||||
|
||||
test('intersectInlineRange', () => {
|
||||
expect(
|
||||
intersectInlineRange({ index: 0, length: 0 }, { index: 1, length: 0 })
|
||||
).toEqual(null);
|
||||
|
||||
expect(
|
||||
intersectInlineRange({ index: 0, length: 2 }, { index: 1, length: 1 })
|
||||
).toEqual({ index: 1, length: 1 });
|
||||
|
||||
expect(
|
||||
intersectInlineRange({ index: 0, length: 2 }, { index: 2, length: 0 })
|
||||
).toEqual({ index: 2, length: 0 });
|
||||
|
||||
expect(
|
||||
intersectInlineRange({ index: 1, length: 0 }, { index: 1, length: 0 })
|
||||
).toEqual({ index: 1, length: 0 });
|
||||
|
||||
expect(
|
||||
intersectInlineRange({ index: 1, length: 3 }, { index: 2, length: 2 })
|
||||
).toEqual({ index: 2, length: 2 });
|
||||
|
||||
expect(
|
||||
intersectInlineRange({ index: 1, length: 2 }, { index: 0, length: 3 })
|
||||
).toEqual({ index: 1, length: 2 });
|
||||
|
||||
expect(
|
||||
intersectInlineRange({ index: 1, length: 1 }, { index: 2, length: 2 })
|
||||
).toEqual({ index: 2, length: 0 });
|
||||
|
||||
expect(
|
||||
intersectInlineRange({ index: 2, length: 2 }, { index: 1, length: 3 })
|
||||
).toEqual({ index: 2, length: 2 });
|
||||
|
||||
expect(
|
||||
intersectInlineRange({ index: 2, length: 1 }, { index: 1, length: 1 })
|
||||
).toEqual({ index: 2, length: 0 });
|
||||
|
||||
expect(
|
||||
intersectInlineRange({ index: 0, length: 4 }, { index: 1, length: 2 })
|
||||
).toEqual({ index: 1, length: 2 });
|
||||
});
|
||||
@@ -0,0 +1,173 @@
|
||||
import type { DeltaInsert } from '@blocksuite/store';
|
||||
import { expect, type Page } from '@playwright/test';
|
||||
|
||||
import type { InlineEditor, InlineRange } from '../../inline/index.js';
|
||||
|
||||
const defaultPlaygroundURL = new URL(
|
||||
`http://localhost:${process.env.CI ? 4173 : 5173}/`
|
||||
);
|
||||
|
||||
export async function type(page: Page, content: string) {
|
||||
await page.keyboard.type(content, { delay: 50 });
|
||||
}
|
||||
|
||||
export async function press(page: Page, content: string) {
|
||||
await page.keyboard.press(content, { delay: 50 });
|
||||
await page.waitForTimeout(50);
|
||||
}
|
||||
|
||||
export async function enterInlineEditorPlayground(page: Page) {
|
||||
const url = new URL('examples/inline/index.html', defaultPlaygroundURL);
|
||||
await page.goto(url.toString());
|
||||
}
|
||||
|
||||
export async function focusInlineRichText(
|
||||
page: Page,
|
||||
index = 0
|
||||
): Promise<void> {
|
||||
await page.evaluate(index => {
|
||||
const richTexts = document
|
||||
.querySelector('test-page')
|
||||
?.querySelectorAll('test-rich-text');
|
||||
|
||||
if (!richTexts) {
|
||||
throw new Error('Cannot find test-rich-text');
|
||||
}
|
||||
|
||||
(richTexts[index] as any).inlineEditor.focusEnd();
|
||||
}, index);
|
||||
}
|
||||
|
||||
export async function getDeltaFromInlineRichText(
|
||||
page: Page,
|
||||
index = 0
|
||||
): Promise<DeltaInsert> {
|
||||
await page.waitForTimeout(100);
|
||||
return page.evaluate(index => {
|
||||
const richTexts = document
|
||||
.querySelector('test-page')
|
||||
?.querySelectorAll('test-rich-text');
|
||||
|
||||
if (!richTexts) {
|
||||
throw new Error('Cannot find test-rich-text');
|
||||
}
|
||||
|
||||
const editor = (richTexts[index] as any).inlineEditor as InlineEditor;
|
||||
return editor.yText.toDelta();
|
||||
}, index);
|
||||
}
|
||||
|
||||
export async function getInlineRangeFromInlineRichText(
|
||||
page: Page,
|
||||
index = 0
|
||||
): Promise<InlineRange | null> {
|
||||
await page.waitForTimeout(100);
|
||||
return page.evaluate(index => {
|
||||
const richTexts = document
|
||||
.querySelector('test-page')
|
||||
?.querySelectorAll('test-rich-text');
|
||||
|
||||
if (!richTexts) {
|
||||
throw new Error('Cannot find test-rich-text');
|
||||
}
|
||||
|
||||
const editor = (richTexts[index] as any).inlineEditor as InlineEditor;
|
||||
return editor.getInlineRange();
|
||||
}, index);
|
||||
}
|
||||
|
||||
export async function setInlineRichTextRange(
|
||||
page: Page,
|
||||
inlineRange: InlineRange,
|
||||
index = 0
|
||||
): Promise<void> {
|
||||
await page.evaluate(
|
||||
([inlineRange, index]) => {
|
||||
const richTexts = document
|
||||
.querySelector('test-page')
|
||||
?.querySelectorAll('test-rich-text');
|
||||
|
||||
if (!richTexts) {
|
||||
throw new Error('Cannot find test-rich-text');
|
||||
}
|
||||
|
||||
const editor = (richTexts[index as number] as any)
|
||||
.inlineEditor as InlineEditor;
|
||||
editor.setInlineRange(inlineRange as InlineRange);
|
||||
},
|
||||
[inlineRange, index]
|
||||
);
|
||||
}
|
||||
|
||||
export async function getInlineRichTextLine(
|
||||
page: Page,
|
||||
index: number,
|
||||
i = 0
|
||||
): Promise<readonly [string, number]> {
|
||||
return page.evaluate(
|
||||
([index, i]) => {
|
||||
const richTexts = document.querySelectorAll('test-rich-text');
|
||||
|
||||
if (!richTexts) {
|
||||
throw new Error('Cannot find test-rich-text');
|
||||
}
|
||||
|
||||
const editor = (richTexts[i] as any).inlineEditor as InlineEditor;
|
||||
const result = editor.getLine(index);
|
||||
if (!result) {
|
||||
throw new Error('Cannot find line');
|
||||
}
|
||||
const { line, rangeIndexRelatedToLine } = result;
|
||||
return [line.vTextContent, rangeIndexRelatedToLine] as const;
|
||||
},
|
||||
[index, i]
|
||||
);
|
||||
}
|
||||
|
||||
export async function getInlineRangeIndexRect(
|
||||
page: Page,
|
||||
[richTextIndex, inlineIndex]: [number, number],
|
||||
coordOffSet: { x: number; y: number } = { x: 0, y: 0 }
|
||||
) {
|
||||
const rect = await page.evaluate(
|
||||
({ richTextIndex, inlineIndex: vIndex, coordOffSet }) => {
|
||||
const richText = document.querySelectorAll('test-rich-text')[
|
||||
richTextIndex
|
||||
] as any;
|
||||
const domRange = richText.inlineEditor.toDomRange({
|
||||
index: vIndex,
|
||||
length: 0,
|
||||
});
|
||||
const pointBound = domRange.getBoundingClientRect();
|
||||
return {
|
||||
x: pointBound.left + coordOffSet.x,
|
||||
y: pointBound.top + pointBound.height / 2 + coordOffSet.y,
|
||||
};
|
||||
},
|
||||
{
|
||||
richTextIndex,
|
||||
inlineIndex,
|
||||
coordOffSet,
|
||||
}
|
||||
);
|
||||
return rect;
|
||||
}
|
||||
|
||||
export async function assertSelection(
|
||||
page: Page,
|
||||
richTextIndex: number,
|
||||
rangeIndex: number,
|
||||
rangeLength = 0
|
||||
) {
|
||||
const actual = await page.evaluate(
|
||||
([richTextIndex]) => {
|
||||
const richText =
|
||||
document?.querySelectorAll('test-rich-text')[richTextIndex];
|
||||
// @ts-expect-error getInlineRange
|
||||
const inlineEditor = richText.inlineEditor;
|
||||
return inlineEditor?.getInlineRange();
|
||||
},
|
||||
[richTextIndex]
|
||||
);
|
||||
expect(actual).toEqual({ index: rangeIndex, length: rangeLength });
|
||||
}
|
||||
Reference in New Issue
Block a user