Unit test typescriptification - editor utils (#8226)

* editor/mock ts

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test/editor/serialize-test.js -> test/editor/serialize-test.ts

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix ts in serialize-test

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test/editor/range-test.js -> test/editor/range-test.ts

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test/editor/position-test.js -> test/editor/position-test.ts

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix position-test

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test/editor/operations-test.js -> test/editor/operations-test.ts

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test/editor/model-test.js -> test/editor/model-test.ts

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix ts in model-test

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test/editor/history-test.js -> test/editor/history-test.ts

Signed-off-by: Kerry Archibald <kerrya@element.io>

* fix ts in history-test

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test/editor/diff-test.js -> test/editor/diff-test.ts

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test/editor/deserialize-test.js -> test/editor/deserialize-test.ts

Signed-off-by: Kerry Archibald <kerrya@element.io>

* ts in deserialize-test

Signed-off-by: Kerry Archibald <kerrya@element.io>

* test/editor/caret-test.js -> test/editor/caret-test.ts

Signed-off-by: Kerry Archibald <kerrya@element.io>

* ts caret-test

Signed-off-by: Kerry Archibald <kerrya@element.io>
pull/21833/head
Kerry 2022-04-06 13:40:13 +02:00 committed by GitHub
parent efb44e1b7d
commit b9da2255c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 112 additions and 90 deletions

View File

@ -24,7 +24,7 @@ describe('editor/caret: DOM position for caret', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([ const model = new EditorModel([
pc.plain("hello"), pc.plain("hello"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 0, offset: 5 }); getLineAndNodePosition(model, { index: 0, offset: 5 });
expect(lineIndex).toBe(0); expect(lineIndex).toBe(0);
@ -35,7 +35,7 @@ describe('editor/caret: DOM position for caret', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([ const model = new EditorModel([
pc.plain("hello"), pc.plain("hello"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 0, offset: 0 }); getLineAndNodePosition(model, { index: 0, offset: 0 });
expect(lineIndex).toBe(0); expect(lineIndex).toBe(0);
@ -46,7 +46,7 @@ describe('editor/caret: DOM position for caret', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([ const model = new EditorModel([
pc.plain("hello"), pc.plain("hello"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 0, offset: 2 }); getLineAndNodePosition(model, { index: 0, offset: 2 });
expect(lineIndex).toBe(0); expect(lineIndex).toBe(0);
@ -61,7 +61,7 @@ describe('editor/caret: DOM position for caret', function() {
pc.plain("hello"), pc.plain("hello"),
pc.newline(), pc.newline(),
pc.plain("world"), pc.plain("world"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 2, offset: 5 }); getLineAndNodePosition(model, { index: 2, offset: 5 });
expect(lineIndex).toBe(1); expect(lineIndex).toBe(1);
@ -74,7 +74,7 @@ describe('editor/caret: DOM position for caret', function() {
pc.plain("hello"), pc.plain("hello"),
pc.newline(), pc.newline(),
pc.plain("world"), pc.plain("world"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 2, offset: 0 }); getLineAndNodePosition(model, { index: 2, offset: 0 });
expect(lineIndex).toBe(1); expect(lineIndex).toBe(1);
@ -88,7 +88,7 @@ describe('editor/caret: DOM position for caret', function() {
pc.newline(), pc.newline(),
pc.newline(), pc.newline(),
pc.plain("world"), pc.plain("world"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 1, offset: 1 }); getLineAndNodePosition(model, { index: 1, offset: 1 });
expect(lineIndex).toBe(1); expect(lineIndex).toBe(1);
@ -102,7 +102,7 @@ describe('editor/caret: DOM position for caret', function() {
pc.newline(), pc.newline(),
pc.newline(), pc.newline(),
pc.plain("world"), pc.plain("world"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 3, offset: 0 }); getLineAndNodePosition(model, { index: 3, offset: 0 });
expect(lineIndex).toBe(2); expect(lineIndex).toBe(2);
@ -117,7 +117,7 @@ describe('editor/caret: DOM position for caret', function() {
pc.plain("hello"), pc.plain("hello"),
pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Alice", "@alice:hs.tld"),
pc.plain("!"), pc.plain("!"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 1, offset: 0 }); getLineAndNodePosition(model, { index: 1, offset: 0 });
expect(lineIndex).toBe(0); expect(lineIndex).toBe(0);
@ -130,7 +130,7 @@ describe('editor/caret: DOM position for caret', function() {
pc.plain("hello"), pc.plain("hello"),
pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Alice", "@alice:hs.tld"),
pc.plain("!"), pc.plain("!"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 1, offset: 2 }); getLineAndNodePosition(model, { index: 1, offset: 2 });
expect(lineIndex).toBe(0); expect(lineIndex).toBe(0);
@ -141,7 +141,7 @@ describe('editor/caret: DOM position for caret', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([ const model = new EditorModel([
pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Alice", "@alice:hs.tld"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 0, offset: 0 }); getLineAndNodePosition(model, { index: 0, offset: 0 });
expect(lineIndex).toBe(0); expect(lineIndex).toBe(0);
@ -153,7 +153,7 @@ describe('editor/caret: DOM position for caret', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([ const model = new EditorModel([
pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Alice", "@alice:hs.tld"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 0, offset: 1 }); getLineAndNodePosition(model, { index: 0, offset: 1 });
expect(lineIndex).toBe(0); expect(lineIndex).toBe(0);
@ -166,7 +166,7 @@ describe('editor/caret: DOM position for caret', function() {
const model = new EditorModel([ const model = new EditorModel([
pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Alice", "@alice:hs.tld"),
pc.userPill("Bob", "@bob:hs.tld"), pc.userPill("Bob", "@bob:hs.tld"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 0, offset: 1 }); getLineAndNodePosition(model, { index: 0, offset: 1 });
expect(lineIndex).toBe(0); expect(lineIndex).toBe(0);
@ -179,7 +179,7 @@ describe('editor/caret: DOM position for caret', function() {
const model = new EditorModel([ const model = new EditorModel([
pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Alice", "@alice:hs.tld"),
pc.userPill("Bob", "@bob:hs.tld"), pc.userPill("Bob", "@bob:hs.tld"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 1, offset: 0 }); getLineAndNodePosition(model, { index: 1, offset: 0 });
expect(lineIndex).toBe(0); expect(lineIndex).toBe(0);
@ -192,7 +192,7 @@ describe('editor/caret: DOM position for caret', function() {
const model = new EditorModel([ const model = new EditorModel([
pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Alice", "@alice:hs.tld"),
pc.userPill("Bob", "@bob:hs.tld"), pc.userPill("Bob", "@bob:hs.tld"),
]); ], pc);
const { offset, lineIndex, nodeIndex } = const { offset, lineIndex, nodeIndex } =
getLineAndNodePosition(model, { index: 1, offset: 1 }); getLineAndNodePosition(model, { index: 1, offset: 1 });
expect(lineIndex).toBe(0); expect(lineIndex).toBe(0);

View File

@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { MatrixEvent } from 'matrix-js-sdk/src/matrix';
import { parseEvent } from "../../src/editor/deserialize"; import { parseEvent } from "../../src/editor/deserialize";
import { createPartCreator } from "./mock"; import { createPartCreator } from "./mock";
@ -28,7 +29,7 @@ function htmlMessage(formattedBody, msgtype = "m.text") {
formatted_body: formattedBody, formatted_body: formattedBody,
}; };
}, },
}; } as unknown as MatrixEvent;
} }
function textMessage(body, msgtype = "m.text") { function textMessage(body, msgtype = "m.text") {
@ -39,7 +40,7 @@ function textMessage(body, msgtype = "m.text") {
body, body,
}; };
}, },
}; } as unknown as MatrixEvent;
} }
function mergeAdjacentParts(parts) { function mergeAdjacentParts(parts) {

View File

@ -15,35 +15,37 @@ limitations under the License.
*/ */
import HistoryManager, { MAX_STEP_LENGTH } from "../../src/editor/history"; import HistoryManager, { MAX_STEP_LENGTH } from "../../src/editor/history";
import EditorModel from "../../src/editor/model";
import DocumentPosition from "../../src/editor/position";
describe('editor/history', function() { describe('editor/history', function() {
it('push, then undo', function() { it('push, then undo', function() {
const history = new HistoryManager(); const history = new HistoryManager();
const parts = ["hello"]; const parts = ["hello"];
const model = { serializeParts: () => parts.slice() }; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel;
const caret1 = {}; const caret1 = new DocumentPosition(0, 0);
const result1 = history.tryPush(model, caret1); const result1 = history.tryPush(model, caret1, 'insertText', {});
expect(result1).toEqual(true); expect(result1).toEqual(true);
parts[0] = "hello world"; parts[0] = "hello world";
history.tryPush(model, {}); history.tryPush(model, new DocumentPosition(0, 0), 'insertText', {});
expect(history.canUndo()).toEqual(true); expect(history.canUndo()).toEqual(true);
const undoState = history.undo(model); const undoState = history.undo(model);
expect(undoState.caret).toEqual(caret1); expect(undoState.caret).toBe(caret1);
expect(undoState.parts).toEqual(["hello"]); expect(undoState.parts).toEqual(["hello"]);
expect(history.canUndo()).toEqual(false); expect(history.canUndo()).toEqual(false);
}); });
it('push, undo, then redo', function() { it('push, undo, then redo', function() {
const history = new HistoryManager(); const history = new HistoryManager();
const parts = ["hello"]; const parts = ["hello"];
const model = { serializeParts: () => parts.slice() }; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel;
history.tryPush(model, {}); history.tryPush(model, new DocumentPosition(0, 0), 'insertText', {});
parts[0] = "hello world"; parts[0] = "hello world";
const caret2 = {}; const caret2 = new DocumentPosition(0, 0);
history.tryPush(model, caret2); history.tryPush(model, caret2, 'insertText', {});
history.undo(model); history.undo(model);
expect(history.canRedo()).toEqual(true); expect(history.canRedo()).toEqual(true);
const redoState = history.redo(); const redoState = history.redo();
expect(redoState.caret).toEqual(caret2); expect(redoState.caret).toBe(caret2);
expect(redoState.parts).toEqual(["hello world"]); expect(redoState.parts).toEqual(["hello world"]);
expect(history.canRedo()).toEqual(false); expect(history.canRedo()).toEqual(false);
expect(history.canUndo()).toEqual(true); expect(history.canUndo()).toEqual(true);
@ -51,52 +53,53 @@ describe('editor/history', function() {
it('push, undo, push, ensure you can`t redo', function() { it('push, undo, push, ensure you can`t redo', function() {
const history = new HistoryManager(); const history = new HistoryManager();
const parts = ["hello"]; const parts = ["hello"];
const model = { serializeParts: () => parts.slice() }; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel;
history.tryPush(model, {}); history.tryPush(model, new DocumentPosition(0, 0), "insertText", {});
parts[0] = "hello world"; parts[0] = "hello world";
history.tryPush(model, {}); history.tryPush(model, new DocumentPosition(0, 0), "insertText", {});
history.undo(model); history.undo(model);
parts[0] = "hello world!!"; parts[0] = "hello world!!";
history.tryPush(model, {}); history.tryPush(model, new DocumentPosition(0, 0), "insertText", {});
expect(history.canRedo()).toEqual(false); expect(history.canRedo()).toEqual(false);
}); });
it('not every keystroke stores a history step', function() { it('not every keystroke stores a history step', function() {
const history = new HistoryManager(); const history = new HistoryManager();
const parts = ["hello"]; const parts = ["hello"];
const model = { serializeParts: () => parts.slice() }; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel;
const firstCaret = {}; const firstCaret = new DocumentPosition(0, 0);
history.tryPush(model, firstCaret); history.tryPush(model, firstCaret, "insertText", {});
const diff = { added: "o" }; const diff = { added: "o" };
let keystrokeCount = 0; let keystrokeCount = 0;
do { do {
parts[0] = parts[0] + diff.added; parts[0] = parts[0] + diff.added;
keystrokeCount += 1; keystrokeCount += 1;
} while (!history.tryPush(model, {}, "insertText", diff)); } while (!history.tryPush(model, new DocumentPosition(0, 0), "insertText", diff));
const undoState = history.undo(model); const undoState = history.undo(model);
expect(undoState.caret).toEqual(firstCaret); expect(undoState.caret).toBe(firstCaret);
expect(undoState.parts).toEqual(["hello"]); expect(undoState.parts).toEqual(["hello"]);
expect(history.canUndo()).toEqual(false); expect(history.canUndo()).toEqual(false);
expect(keystrokeCount).toEqual(MAX_STEP_LENGTH + 1); // +1 before we type before checking expect(keystrokeCount).toEqual(MAX_STEP_LENGTH + 1); // +1 before we type before checking
}); });
it('history step is added at word boundary', function() { it('history step is added at word boundary', function() {
const history = new HistoryManager(); const history = new HistoryManager();
const model = { serializeParts: () => parts.slice() }; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel;
const parts = ["h"]; const parts = ["h"];
let diff = { added: "h" }; let diff = { added: "h" };
expect(history.tryPush(model, {}, "insertText", diff)).toEqual(false); const blankCaret = new DocumentPosition(0, 0);
expect(history.tryPush(model, blankCaret, "insertText", diff)).toEqual(false);
diff = { added: "i" }; diff = { added: "i" };
parts[0] = "hi"; parts[0] = "hi";
expect(history.tryPush(model, {}, "insertText", diff)).toEqual(false); expect(history.tryPush(model, blankCaret, "insertText", diff)).toEqual(false);
diff = { added: " " }; diff = { added: " " };
parts[0] = "hi "; parts[0] = "hi ";
const spaceCaret = {}; const spaceCaret = new DocumentPosition(1, 1);
expect(history.tryPush(model, spaceCaret, "insertText", diff)).toEqual(true); expect(history.tryPush(model, spaceCaret, "insertText", diff)).toEqual(true);
diff = { added: "y" }; diff = { added: "y" };
parts[0] = "hi y"; parts[0] = "hi y";
expect(history.tryPush(model, {}, "insertText", diff)).toEqual(false); expect(history.tryPush(model, blankCaret, "insertText", diff)).toEqual(false);
diff = { added: "o" }; diff = { added: "o" };
parts[0] = "hi yo"; parts[0] = "hi yo";
expect(history.tryPush(model, {}, "insertText", diff)).toEqual(false); expect(history.tryPush(model, blankCaret, "insertText", diff)).toEqual(false);
diff = { added: "u" }; diff = { added: "u" };
parts[0] = "hi you"; parts[0] = "hi you";
@ -108,11 +111,11 @@ describe('editor/history', function() {
it('keystroke that didn\'t add a step can undo', function() { it('keystroke that didn\'t add a step can undo', function() {
const history = new HistoryManager(); const history = new HistoryManager();
const parts = ["hello"]; const parts = ["hello"];
const model = { serializeParts: () => parts.slice() }; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel;
const firstCaret = {}; const firstCaret = new DocumentPosition(0, 0);
history.tryPush(model, {}); history.tryPush(model, firstCaret, "insertText", {});
parts[0] = "helloo"; parts[0] = "helloo";
const result = history.tryPush(model, {}, "insertText", { added: "o" }); const result = history.tryPush(model, new DocumentPosition(0, 0), "insertText", { added: "o" });
expect(result).toEqual(false); expect(result).toEqual(false);
expect(history.canUndo()).toEqual(true); expect(history.canUndo()).toEqual(true);
const undoState = history.undo(model); const undoState = history.undo(model);
@ -122,25 +125,27 @@ describe('editor/history', function() {
it('undo after keystroke that didn\'t add a step is able to redo', function() { it('undo after keystroke that didn\'t add a step is able to redo', function() {
const history = new HistoryManager(); const history = new HistoryManager();
const parts = ["hello"]; const parts = ["hello"];
const model = { serializeParts: () => parts.slice() }; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel;
history.tryPush(model, {}); history.tryPush(model, new DocumentPosition(0, 0), "insertText", {});
parts[0] = "helloo"; parts[0] = "helloo";
const caret = { last: true }; const caret = new DocumentPosition(1, 1);
history.tryPush(model, caret, "insertText", { added: "o" }); history.tryPush(model, caret, "insertText", { added: "o" });
history.undo(model); history.undo(model);
expect(history.canRedo()).toEqual(true); expect(history.canRedo()).toEqual(true);
const redoState = history.redo(); const redoState = history.redo();
expect(redoState.caret).toEqual(caret); expect(redoState.caret).toBe(caret);
expect(redoState.parts).toEqual(["helloo"]); expect(redoState.parts).toEqual(["helloo"]);
}); });
it('overwriting text always stores a step', function() { it('overwriting text always stores a step', function() {
const history = new HistoryManager(); const history = new HistoryManager();
const parts = ["hello"]; const parts = ["hello"];
const model = { serializeParts: () => parts.slice() }; const model = { serializeParts: () => parts.slice() } as unknown as EditorModel;
const firstCaret = {}; const firstCaret = new DocumentPosition(0, 0);
history.tryPush(model, firstCaret); history.tryPush(model, firstCaret, 'insertText', {});
const diff = { at: 1, added: "a", removed: "e" }; const diff = { at: 1, added: "a", removed: "e" };
const result = history.tryPush(model, {}, "insertText", diff); const secondCaret = new DocumentPosition(1, 1);
const result = history.tryPush(model, secondCaret, "insertText", diff);
expect(result).toEqual(true); expect(result).toEqual(true);
}); });
}); });

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2019, 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,9 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { Room, MatrixClient } from "matrix-js-sdk/src/matrix";
import AutocompleteWrapperModel from "../../src/editor/autocomplete";
import { PartCreator } from "../../src/editor/parts"; import { PartCreator } from "../../src/editor/parts";
class MockAutoComplete { class MockAutoComplete {
public _updateCallback;
public _partCreator;
public _completions;
public _part;
constructor(updateCallback, partCreator, completions) { constructor(updateCallback, partCreator, completions) {
this._updateCallback = updateCallback; this._updateCallback = updateCallback;
this._partCreator = partCreator; this._partCreator = partCreator;
@ -52,20 +60,21 @@ class MockAutoComplete {
// MockClient & MockRoom are only used for avatars in room and user pills, // MockClient & MockRoom are only used for avatars in room and user pills,
// which is not tested // which is not tested
class MockClient {
getRooms() { return []; }
getRoom() { return null; }
}
class MockRoom { class MockRoom {
getMember() { return null; } getMember() { return null; }
} }
export function createPartCreator(completions = []) { export function createPartCreator(completions = []) {
const autoCompleteCreator = (partCreator) => { const autoCompleteCreator = (partCreator) => {
return (updateCallback) => new MockAutoComplete(updateCallback, partCreator, completions); return (updateCallback) =>
new MockAutoComplete(updateCallback, partCreator, completions) as unknown as AutocompleteWrapperModel;
}; };
return new PartCreator(new MockRoom(), new MockClient(), autoCompleteCreator); const room = new MockRoom() as unknown as Room;
const client = {
getRooms: jest.fn().mockReturnValue([]),
getRoom: jest.fn().mockReturnValue(null),
} as unknown as MatrixClient;
return new PartCreator(room, client, autoCompleteCreator);
} }
export function createRenderer() { export function createRenderer() {

View File

@ -16,13 +16,14 @@ limitations under the License.
import EditorModel from "../../src/editor/model"; import EditorModel from "../../src/editor/model";
import { createPartCreator, createRenderer } from "./mock"; import { createPartCreator, createRenderer } from "./mock";
import DocumentOffset from "../../src/editor/offset";
describe('editor/model', function() { describe('editor/model', function() {
describe('plain text manipulation', function() { describe('plain text manipulation', function() {
it('insert text into empty document', function() { it('insert text into empty document', function() {
const renderer = createRenderer(); const renderer = createRenderer();
const model = new EditorModel([], createPartCreator(), renderer); const model = new EditorModel([], createPartCreator(), renderer);
model.update("hello", "insertText", { offset: 5, atNodeEnd: true }); model.update("hello", "insertText", new DocumentOffset(5, true));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(renderer.caret.index).toBe(0); expect(renderer.caret.index).toBe(0);
expect(renderer.caret.offset).toBe(5); expect(renderer.caret.offset).toBe(5);
@ -34,7 +35,7 @@ describe('editor/model', function() {
const renderer = createRenderer(); const renderer = createRenderer();
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello")], pc, renderer); const model = new EditorModel([pc.plain("hello")], pc, renderer);
model.update("hello world", "insertText", { offset: 11, atNodeEnd: true }); model.update("hello world", "insertText", new DocumentOffset(11, true));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(renderer.caret.index).toBe(0); expect(renderer.caret.index).toBe(0);
expect(renderer.caret.offset).toBe(11); expect(renderer.caret.offset).toBe(11);
@ -46,7 +47,7 @@ describe('editor/model', function() {
const renderer = createRenderer(); const renderer = createRenderer();
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.plain("world")], pc, renderer); const model = new EditorModel([pc.plain("world")], pc, renderer);
model.update("hello world", "insertText", { offset: 6, atNodeEnd: false }); model.update("hello world", "insertText", new DocumentOffset(6, false));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(renderer.caret.index).toBe(0); expect(renderer.caret.index).toBe(0);
expect(renderer.caret.offset).toBe(6); expect(renderer.caret.offset).toBe(6);
@ -60,7 +61,7 @@ describe('editor/model', function() {
const renderer = createRenderer(); const renderer = createRenderer();
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello")], pc, renderer); const model = new EditorModel([pc.plain("hello")], pc, renderer);
model.update("hello\n", "insertText", { offset: 6, atNodeEnd: true }); model.update("hello\n", "insertText", new DocumentOffset(6, true));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(renderer.caret.index).toBe(1); expect(renderer.caret.index).toBe(1);
expect(renderer.caret.offset).toBe(1); expect(renderer.caret.offset).toBe(1);
@ -74,7 +75,7 @@ describe('editor/model', function() {
const renderer = createRenderer(); const renderer = createRenderer();
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello")], pc, renderer); const model = new EditorModel([pc.plain("hello")], pc, renderer);
model.update("hello\n\n\nworld!", "insertText", { offset: 14, atNodeEnd: true }); model.update("hello\n\n\nworld!", "insertText", new DocumentOffset(14, true));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(renderer.caret.index).toBe(4); expect(renderer.caret.index).toBe(4);
expect(renderer.caret.offset).toBe(6); expect(renderer.caret.offset).toBe(6);
@ -99,7 +100,7 @@ describe('editor/model', function() {
pc.newline(), pc.newline(),
pc.plain("world"), pc.plain("world"),
], pc, renderer); ], pc, renderer);
model.update("hello\nwarm\nworld", "insertText", { offset: 10, atNodeEnd: true }); model.update("hello\nwarm\nworld", "insertText", new DocumentOffset(10, true));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(renderer.caret.index).toBe(2); expect(renderer.caret.index).toBe(2);
expect(renderer.caret.offset).toBe(4); expect(renderer.caret.offset).toBe(4);
@ -124,7 +125,7 @@ describe('editor/model', function() {
pc.plain("try "), pc.plain("try "),
pc.roomPill("#someroom"), pc.roomPill("#someroom"),
], pc, renderer); ], pc, renderer);
model.update("try foo#someroom", "insertText", { offset: 7, atNodeEnd: false }); model.update("try foo#someroom", "insertText", new DocumentOffset(7, false));
expect(renderer.caret.index).toBe(0); expect(renderer.caret.index).toBe(0);
expect(renderer.caret.offset).toBe(7); expect(renderer.caret.offset).toBe(7);
expect(model.parts.length).toBe(2); expect(model.parts.length).toBe(2);
@ -141,7 +142,7 @@ describe('editor/model', function() {
pc.roomPill("#someroom"), pc.roomPill("#someroom"),
pc.plain("?"), pc.plain("?"),
], pc, renderer); ], pc, renderer);
model.update("try #some perhapsroom?", "insertText", { offset: 17, atNodeEnd: false }); model.update("try #some perhapsroom?", "insertText", new DocumentOffset(17, false));
expect(renderer.caret.index).toBe(2); expect(renderer.caret.index).toBe(2);
expect(renderer.caret.offset).toBe(8); expect(renderer.caret.offset).toBe(8);
expect(model.parts.length).toBe(3); expect(model.parts.length).toBe(3);
@ -156,7 +157,7 @@ describe('editor/model', function() {
const renderer = createRenderer(); const renderer = createRenderer();
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.roomPill("#someroom")], pc, renderer); const model = new EditorModel([pc.roomPill("#someroom")], pc, renderer);
model.update("#someroo", "deleteContentBackward", { offset: 8, atNodeEnd: true }); model.update("#someroo", "deleteContentBackward", new DocumentOffset(8, true));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(renderer.caret.index).toBe(-1); expect(renderer.caret.index).toBe(-1);
expect(renderer.caret.offset).toBe(0); expect(renderer.caret.offset).toBe(0);
@ -166,7 +167,7 @@ describe('editor/model', function() {
const renderer = createRenderer(); const renderer = createRenderer();
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.roomPill("#someroom")], pc, renderer); const model = new EditorModel([pc.roomPill("#someroom")], pc, renderer);
model.update("someroom", "deleteContentForward", { offset: 0, atNodeEnd: false }); model.update("someroom", "deleteContentForward", new DocumentOffset(0, false));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(renderer.caret.index).toBe(-1); expect(renderer.caret.index).toBe(-1);
expect(renderer.caret.offset).toBe(0); expect(renderer.caret.offset).toBe(0);
@ -179,7 +180,7 @@ describe('editor/model', function() {
const pc = createPartCreator([{ resourceId: "@alice", label: "Alice" }]); const pc = createPartCreator([{ resourceId: "@alice", label: "Alice" }]);
const model = new EditorModel([pc.plain("hello ")], pc, renderer); const model = new EditorModel([pc.plain("hello ")], pc, renderer);
model.update("hello @a", "insertText", { offset: 8, atNodeEnd: true }); model.update("hello @a", "insertText", new DocumentOffset(8, true));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(renderer.caret.index).toBe(1); expect(renderer.caret.index).toBe(1);
@ -190,6 +191,8 @@ describe('editor/model', function() {
expect(model.parts[1].type).toBe("pill-candidate"); expect(model.parts[1].type).toBe("pill-candidate");
expect(model.parts[1].text).toBe("@a"); expect(model.parts[1].text).toBe("@a");
// this is a hacky mock function
// @ts-ignore
model.autoComplete.tryComplete(); // see MockAutoComplete model.autoComplete.tryComplete(); // see MockAutoComplete
expect(renderer.count).toBe(2); expect(renderer.count).toBe(2);
@ -207,7 +210,7 @@ describe('editor/model', function() {
const pc = createPartCreator([{ resourceId: "#riot-dev" }]); const pc = createPartCreator([{ resourceId: "#riot-dev" }]);
const model = new EditorModel([pc.plain("hello ")], pc, renderer); const model = new EditorModel([pc.plain("hello ")], pc, renderer);
model.update("hello #r", "insertText", { offset: 8, atNodeEnd: true }); model.update("hello #r", "insertText", new DocumentOffset(8, true));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(renderer.caret.index).toBe(1); expect(renderer.caret.index).toBe(1);
@ -218,6 +221,8 @@ describe('editor/model', function() {
expect(model.parts[1].type).toBe("pill-candidate"); expect(model.parts[1].type).toBe("pill-candidate");
expect(model.parts[1].text).toBe("#r"); expect(model.parts[1].text).toBe("#r");
// this is a hacky mock function
// @ts-ignore
model.autoComplete.tryComplete(); // see MockAutoComplete model.autoComplete.tryComplete(); // see MockAutoComplete
expect(renderer.count).toBe(2); expect(renderer.count).toBe(2);
@ -235,9 +240,11 @@ describe('editor/model', function() {
const pc = createPartCreator([{ resourceId: "#riot-dev" }]); const pc = createPartCreator([{ resourceId: "#riot-dev" }]);
const model = new EditorModel([pc.plain("hello ")], pc, renderer); const model = new EditorModel([pc.plain("hello ")], pc, renderer);
model.update("hello #r", "insertText", { offset: 8, atNodeEnd: true }); model.update("hello #r", "insertText", new DocumentOffset(8, true));
// this is a hacky mock function
// @ts-ignore
model.autoComplete.tryComplete(); // see MockAutoComplete model.autoComplete.tryComplete(); // see MockAutoComplete
model.update("hello #riot-dev!!", "insertText", { offset: 17, atNodeEnd: true }); model.update("hello #riot-dev!!", "insertText", new DocumentOffset(17, true));
expect(renderer.count).toBe(3); expect(renderer.count).toBe(3);
expect(renderer.caret.index).toBe(2); expect(renderer.caret.index).toBe(2);
@ -256,7 +263,7 @@ describe('editor/model', function() {
const pc = createPartCreator([{ resourceId: "#define-room" }]); const pc = createPartCreator([{ resourceId: "#define-room" }]);
const model = new EditorModel([pc.plain("try ")], pc, renderer); const model = new EditorModel([pc.plain("try ")], pc, renderer);
model.update("try #define", "insertFromPaste", { offset: 11, atNodeEnd: true }); model.update("try #define", "insertFromPaste", new DocumentOffset(11, true));
expect(model.autoComplete).toBeFalsy(); expect(model.autoComplete).toBeFalsy();
expect(renderer.caret.index).toBe(0); expect(renderer.caret.index).toBe(0);
@ -271,7 +278,7 @@ describe('editor/model', function() {
const pc = createPartCreator([{ resourceId: "#define-room" }]); const pc = createPartCreator([{ resourceId: "#define-room" }]);
const model = new EditorModel([pc.plain("try ")], pc, renderer); const model = new EditorModel([pc.plain("try ")], pc, renderer);
model.update("try #define", "insertFromDrop", { offset: 11, atNodeEnd: true }); model.update("try #define", "insertFromDrop", new DocumentOffset(11, true));
expect(model.autoComplete).toBeFalsy(); expect(model.autoComplete).toBeFalsy();
expect(renderer.caret.index).toBe(0); expect(renderer.caret.index).toBe(0);
@ -286,14 +293,14 @@ describe('editor/model', function() {
const pc = createPartCreator([{ resourceId: "#room:server" }]); const pc = createPartCreator([{ resourceId: "#room:server" }]);
const model = new EditorModel([], pc, renderer); const model = new EditorModel([], pc, renderer);
model.update("#roo", "insertText", { offset: 4, atNodeEnd: true }); model.update("#roo", "insertText", new DocumentOffset(4, true));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(model.parts.length).toBe(1); expect(model.parts.length).toBe(1);
expect(model.parts[0].type).toBe("pill-candidate"); expect(model.parts[0].type).toBe("pill-candidate");
expect(model.parts[0].text).toBe("#roo"); expect(model.parts[0].text).toBe("#roo");
model.update("#room:s", "insertText", { offset: 7, atNodeEnd: true }); model.update("#room:s", "insertText", new DocumentOffset(7, true));
expect(renderer.count).toBe(2); expect(renderer.count).toBe(2);
expect(model.parts.length).toBe(1); expect(model.parts.length).toBe(1);
@ -306,7 +313,7 @@ describe('editor/model', function() {
const pc = createPartCreator([{ resourceId: "@alice", label: "Alice" }]); const pc = createPartCreator([{ resourceId: "@alice", label: "Alice" }]);
const model = new EditorModel([], pc, renderer); const model = new EditorModel([], pc, renderer);
model.update("foo@a", "insertText", { offset: 5, atNodeEnd: true }); model.update("foo@a", "insertText", new DocumentOffset(5, true));
expect(renderer.count).toBe(1); expect(renderer.count).toBe(1);
expect(model.parts.length).toBe(1); expect(model.parts.length).toBe(1);

View File

@ -37,7 +37,7 @@ describe('editor/position', function() {
it('move first position forwards in empty model', function() { it('move first position forwards in empty model', function() {
const model = new EditorModel([], createPartCreator(), createRenderer()); const model = new EditorModel([], createPartCreator(), createRenderer());
const pos = model.positionForOffset(0, true); const pos = model.positionForOffset(0, true);
const pos2 = pos.forwardsWhile(() => true); const pos2 = pos.forwardsWhile(model, () => true);
expect(pos).toBe(pos2); expect(pos).toBe(pos2);
}); });
it('move forwards within one part', function() { it('move forwards within one part', function() {

View File

@ -21,55 +21,55 @@ import { createPartCreator } from "./mock";
describe('editor/serialize', function() { describe('editor/serialize', function() {
it('user pill turns message into html', function() { it('user pill turns message into html', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.userPill("Alice", "@alice:hs.tld")]); const model = new EditorModel([pc.userPill("Alice", "@alice:hs.tld")], pc);
const html = htmlSerializeIfNeeded(model, {}); const html = htmlSerializeIfNeeded(model, {});
expect(html).toBe("<a href=\"https://matrix.to/#/@alice:hs.tld\">Alice</a>"); expect(html).toBe("<a href=\"https://matrix.to/#/@alice:hs.tld\">Alice</a>");
}); });
it('room pill turns message into html', function() { it('room pill turns message into html', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.roomPill("#room:hs.tld")]); const model = new EditorModel([pc.roomPill("#room:hs.tld")], pc);
const html = htmlSerializeIfNeeded(model, {}); const html = htmlSerializeIfNeeded(model, {});
expect(html).toBe("<a href=\"https://matrix.to/#/#room:hs.tld\">#room:hs.tld</a>"); expect(html).toBe("<a href=\"https://matrix.to/#/#room:hs.tld\">#room:hs.tld</a>");
}); });
it('@room pill turns message into html', function() { it('@room pill turns message into html', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.atRoomPill("@room")]); const model = new EditorModel([pc.atRoomPill("@room")], pc);
const html = htmlSerializeIfNeeded(model, {}); const html = htmlSerializeIfNeeded(model, {});
expect(html).toBeFalsy(); expect(html).toBeFalsy();
}); });
it('any markdown turns message into html', function() { it('any markdown turns message into html', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.plain("*hello* world")]); const model = new EditorModel([pc.plain("*hello* world")], pc);
const html = htmlSerializeIfNeeded(model, {}); const html = htmlSerializeIfNeeded(model, {});
expect(html).toBe("<em>hello</em> world"); expect(html).toBe("<em>hello</em> world");
}); });
it('displaynames ending in a backslash work', function() { it('displaynames ending in a backslash work', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.userPill("Displayname\\", "@user:server")]); const model = new EditorModel([pc.userPill("Displayname\\", "@user:server")], pc);
const html = htmlSerializeIfNeeded(model, {}); const html = htmlSerializeIfNeeded(model, {});
expect(html).toBe("<a href=\"https://matrix.to/#/@user:server\">Displayname\\</a>"); expect(html).toBe("<a href=\"https://matrix.to/#/@user:server\">Displayname\\</a>");
}); });
it('displaynames containing an opening square bracket work', function() { it('displaynames containing an opening square bracket work', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.userPill("Displayname[[", "@user:server")]); const model = new EditorModel([pc.userPill("Displayname[[", "@user:server")], pc);
const html = htmlSerializeIfNeeded(model, {}); const html = htmlSerializeIfNeeded(model, {});
expect(html).toBe("<a href=\"https://matrix.to/#/@user:server\">Displayname[[</a>"); expect(html).toBe("<a href=\"https://matrix.to/#/@user:server\">Displayname[[</a>");
}); });
it('displaynames containing a closing square bracket work', function() { it('displaynames containing a closing square bracket work', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.userPill("Displayname]", "@user:server")]); const model = new EditorModel([pc.userPill("Displayname]", "@user:server")], pc);
const html = htmlSerializeIfNeeded(model, {}); const html = htmlSerializeIfNeeded(model, {});
expect(html).toBe("<a href=\"https://matrix.to/#/@user:server\">Displayname]</a>"); expect(html).toBe("<a href=\"https://matrix.to/#/@user:server\">Displayname]</a>");
}); });
it('escaped markdown should not retain backslashes', function() { it('escaped markdown should not retain backslashes', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.plain('\\*hello\\* world')]); const model = new EditorModel([pc.plain('\\*hello\\* world')], pc);
const html = htmlSerializeIfNeeded(model, {}); const html = htmlSerializeIfNeeded(model, {});
expect(html).toBe('*hello* world'); expect(html).toBe('*hello* world');
}); });
it('escaped markdown should convert HTML entities', function() { it('escaped markdown should convert HTML entities', function() {
const pc = createPartCreator(); const pc = createPartCreator();
const model = new EditorModel([pc.plain('\\*hello\\* world < hey world!')]); const model = new EditorModel([pc.plain('\\*hello\\* world < hey world!')], pc);
const html = htmlSerializeIfNeeded(model, {}); const html = htmlSerializeIfNeeded(model, {});
expect(html).toBe('*hello* world &lt; hey world!'); expect(html).toBe('*hello* world &lt; hey world!');
}); });