Skip to content

Commit

Permalink
Cut history tests & fugue fixes
Browse files Browse the repository at this point in the history
* Delete was deleting line block style
* Insert was not working properly
  • Loading branch information
GuilhermeF03 committed May 1, 2024
1 parent 66d6c5d commit 2c82bc6
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 12 deletions.
7 changes: 4 additions & 3 deletions code/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
},
"devDependencies": {
"@testing-library/dom": "^10.1.0",
"@testing-library/react": "^15.0.5",
"@testing-library/react": "^15.0.6",
"@testing-library/user-event": "^14.5.2",
"@types/lodash": "^4.17.0",
"@types/node": "^20.12.7",
"@types/node": "^20.12.8",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
Expand All @@ -67,5 +67,6 @@
"vite-plugin-qrcode": "^0.2.3",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.5.3"
}
},
"packageManager": "[email protected]+sha256.0624e30eff866cdeb363b15061bdb7fd9425b17bc1bb42c22f5f4efdea21f6b3"
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ export default (fugue: Fugue, { socket }: Communication): MarkdownDomainOperatio
function deleteBlockStyles(selection: Selection) {
if (isSelectionEmpty(selection)) return;
const { start, end } = selection;
if (start.column === 0 || start.line !== end.line) {

// Remove block styles if the selection is single position at beginning of a line or multi-line selection
if((start === end && start.column === 0) || start.line !== end.line) {
const newSelection = start.column !== 0 ? { start: { line: start.line + 1, column: 0 }, end } : selection;
const operations = fugue.updateBlockStylesLocalBySelection('paragraph', newSelection);
socket.emit('operation', operations);
Expand Down
8 changes: 6 additions & 2 deletions code/client/src/domain/editor/slate/utils/selection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Editor, Node, Point, Range } from 'slate';
import {Editor, Node, Path, Point, Range} from 'slate';
import { Cursor, emptyCursor, emptySelection, Selection } from '@notespace/shared/types/cursor';
import { first, isEqual } from 'lodash';

Expand Down Expand Up @@ -45,7 +45,11 @@ export function pointToCursor(editor: Editor, point: Point): Cursor {
const children = Node.children(editor, [line]);
const cursor: Cursor = { line, column: point.offset };
for (const entry of children) {
if (entry[1][0] === point.path[0]) break;
// If path has only one element and it is the same as the first element of the point path - same line
if (point.path.length === 1 && point.path[0] === entry[1][0]) break;
// Else verify if the path is the same
if(Path.equals(entry[1], point.path)) break;

cursor.column += first(entry).text.length;
}
return cursor;
Expand Down
Empty file.
126 changes: 126 additions & 0 deletions code/client/tests/editor/slate/handlers/history/cut.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import {describe, test, expect, beforeEach} from "vitest";
import {BaseRemoveTextOperation, Editor} from "slate";
import {Fugue} from "@domain/editor/crdt/fugue";
import {
removeText,
mockEditor,
toBatch,
applyBatch,
getUndoOperations, getRedoOperations, removeNode
} from "@tests/editor/slate/handlers/history/utils";
import {toSlate} from "@domain/editor/slate/utils/slate";
import {InsertTextOperation, RemoveTextOperation} from "@domain/editor/operations/history/types";
import {pointToCursor} from "@domain/editor/slate/utils/selection";
import {nodeInsert} from "@domain/editor/crdt/utils";


let editor: Editor
let fugue : Fugue

beforeEach(() => {
editor = mockEditor();
fugue = new Fugue();
});

describe('Single line', () => {

describe('Single style', () => {

const cutSingleLine = () => {
fugue.insertLocal({line: 0, column: 0}, ...'abcdef'.split(''));
editor.children = toSlate(fugue);

const batch = toBatch(
removeText('abcdef', [0,0], 0)
);
applyBatch(editor, batch);
}

beforeEach(cutSingleLine);

test('Should undo cut', () => {
const {operations} = getUndoOperations(editor, 1)

const operation = operations[0] as InsertTextOperation;

expect(operation.text).toStrictEqual('abcdef'.split(''));
expect(operation.cursor).toEqual({line: 0, column: 0});

});

test('Should redo cut', () => {

const {operations, editorBatch} = getRedoOperations(editor, 1)

const operation = operations[0] as RemoveTextOperation;
const editorOperation = editorBatch!.operations[0] as BaseRemoveTextOperation;

expect(operation.selection).toEqual(
{
start: pointToCursor(editor, {path: editorOperation.path, offset: 0}),
end: pointToCursor(editor, {path: editorOperation.path, offset: editorOperation.offset + editorOperation.text.length - 1})
}
)
});
});

describe('Multiple styles', () => {
const cutMultipleStyles = () => {
fugue.insertLocal({line: 0, column: 0}, ...'abc'.split(''));
fugue.insertLocal({line: 0, column: 3},
nodeInsert('text', ['bold']),
nodeInsert('text', ['italic']),
);
editor.children = toSlate(fugue);

const batch = toBatch(
removeText('abc', [0, 0], 0),
removeText('text', [0, 1], 0),
removeNode({text: ''}, [0, 1]),
removeText('text', [0, 2], 0),
removeNode({text: ''}, [0, 2]),
);
editor.history.undos = [batch];
}

beforeEach(cutMultipleStyles);

test('Should undo cut', () => {
const {operations, editorBatch} = getUndoOperations(editor, 3); // remove node op. with empty text are ignored

const editorOperations = editorBatch!.operations.filter(op =>
op.type === 'remove_text') as BaseRemoveTextOperation[];


for (const i in operations) {
const operation = operations[i] as InsertTextOperation;
const editorOperation = editorOperations[i];

expect(operation.text).toStrictEqual(editorOperation.text.split(''));
expect(operation.cursor).toEqual(pointToCursor(editor, {path: editorOperation.path, offset: editorOperation.offset}));
}
});

test('Should redo cut', () => {
const {operations, editorBatch} = getRedoOperations(editor, 3);

const editorOperations = editorBatch!.operations.filter(op =>
op.type === 'remove_text') as BaseRemoveTextOperation[];

for (const i in operations) {
const operation = operations[i] as RemoveTextOperation;
const editorOperation = editorOperations[i];

expect(operation.selection).toEqual({
start: pointToCursor(editor, {path: editorOperation.path, offset: editorOperation.offset}),
end: pointToCursor(editor, {path: editorOperation.path, offset: editorOperation.offset + editorOperation.text.length - 1})
});
}
});
});
});

// describe('Multiple lines', () => {
//
//
// });
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BaseInsertTextOperation, BaseSetNodeOperation, Editor } from 'slate';
import { Fugue } from '@domain/editor/crdt/fugue';
import {
applyBatch,
deleteText,
removeText,
getRedoOperations,
getUndoOperations,
mockEditor,
Expand Down Expand Up @@ -34,7 +34,7 @@ describe('No style', () => {
fugue.insertLocal({ line: 0, column: 0 }, ...'abc'.split(''));
editor.children = toSlate(fugue);

const batch = toBatch(deleteText('a', [0, 0], 0));
const batch = toBatch(removeText('a', [0, 0], 0));

applyBatch(editor, batch);
};
Expand Down Expand Up @@ -109,7 +109,7 @@ describe('Inline Style', () => {
fugue.insertLocal({ line: 0, column: 0 }, 'a');
editor.children = toSlate(fugue);

const batch = toBatch(deleteText('b', [0, 1], 0), removeNode({ text: '', bold: true }, [0, 1]));
const batch = toBatch(removeText('b', [0, 1], 0), removeNode({ text: '', bold: true }, [0, 1]));
editor.history.undos = [batch];
};

Expand Down
2 changes: 1 addition & 1 deletion code/client/tests/editor/slate/handlers/history/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const insertText = (text: string, path: Path, offset: number): InsertText
return { type: 'insert_text', path, offset, text };
};

export const deleteText = (text: string, path: Path, offset: number): RemoveTextOperation => ({
export const removeText = (text: string, path: Path, offset: number): RemoveTextOperation => ({
type: 'remove_text',
path,
offset,
Expand Down
5 changes: 3 additions & 2 deletions code/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.0",
"@types/node": "^20.12.7",
"@types/node": "^20.12.8",
"@types/supertest": "^6.0.2",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^7.8.0",
Expand All @@ -46,5 +46,6 @@
"tsconfig-paths": "^4.2.0",
"tsx": "^4.8.2",
"typescript": "^5.4.5"
}
},
"packageManager": "[email protected]+sha256.0624e30eff866cdeb363b15061bdb7fd9425b17bc1bb42c22f5f4efdea21f6b3"
}
2 changes: 2 additions & 0 deletions code/server/src/controllers/ws/document/onOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { ForbiddenError, InvalidParameterError } from '@domain/errors/errors';

function onOperation(service: DocumentService) {
return async (socket: Socket, operations: Operation[]) => {

console.log('onOperation:', operations)
if (!operations) {
throw new InvalidParameterError('Operations are required');
}
Expand Down

0 comments on commit 2c82bc6

Please sign in to comment.