element-web/test/components/views/rooms/wysiwyg_composer/hooks/useSuggestion-test.tsx

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

428 lines
18 KiB
TypeScript
Raw Normal View History

Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import {
Suggestion,
findSuggestionInText,
getMappedSuggestion,
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
processCommand,
processEmojiReplacement,
processMention,
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
processSelectionChange,
} from "../../../../../../src/components/views/rooms/wysiwyg_composer/hooks/useSuggestion";
function createMockPlainTextSuggestionPattern(props: Partial<Suggestion> = {}): Suggestion {
return {
mappedSuggestion: { keyChar: "/", type: "command", text: "some text", ...props.mappedSuggestion },
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
node: document.createTextNode(""),
startOffset: 0,
endOffset: 0,
...props,
};
}
function createMockCustomSuggestionPattern(props: Partial<Suggestion> = {}): Suggestion {
return {
mappedSuggestion: { keyChar: "", type: "custom", text: "🙂", ...props.mappedSuggestion },
node: document.createTextNode(":)"),
startOffset: 0,
endOffset: 2,
...props,
};
}
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
describe("processCommand", () => {
it("does not change parent hook state if suggestion is null", () => {
// create a mockSuggestion using the text node above
const mockSetSuggestion = jest.fn();
const mockSetText = jest.fn();
// call the function with a null suggestion
processCommand("should not be seen", null, mockSetSuggestion, mockSetText);
// check that the parent state setter has not been called
expect(mockSetText).not.toHaveBeenCalled();
});
it("can change the parent hook state when required", () => {
// create a div and append a text node to it with some initial text
const editorDiv = document.createElement("div");
const initialText = "text";
const textNode = document.createTextNode(initialText);
editorDiv.appendChild(textNode);
// create a mockSuggestion using the text node above
const mockSuggestion = createMockPlainTextSuggestionPattern({ node: textNode });
const mockSetSuggestion = jest.fn();
const mockSetText = jest.fn();
const replacementText = "/replacement text";
processCommand(replacementText, mockSuggestion, mockSetSuggestion, mockSetText);
// check that the text has changed and includes a trailing space
expect(mockSetText).toHaveBeenCalledWith(`${replacementText} `);
});
});
describe("processEmojiReplacement", () => {
it("does not change parent hook state if suggestion is null", () => {
// create a mockSuggestion using the text node above
const mockSetSuggestion = jest.fn();
const mockSetText = jest.fn();
// call the function with a null suggestion
processEmojiReplacement(null, mockSetSuggestion, mockSetText);
// check that the parent state setter has not been called
expect(mockSetText).not.toHaveBeenCalled();
});
it("can change the parent hook state when required", () => {
// create a div and append a text node to it with some initial text
const editorDiv = document.createElement("div");
const initialText = ":)";
const textNode = document.createTextNode(initialText);
editorDiv.appendChild(textNode);
// create a mockSuggestion using the text node above
const mockSuggestion = createMockCustomSuggestionPattern({ node: textNode });
const mockSetSuggestion = jest.fn();
const mockSetText = jest.fn();
const replacementText = "🙂";
processEmojiReplacement(mockSuggestion, mockSetSuggestion, mockSetText);
// check that the text has changed and includes a trailing space
expect(mockSetText).toHaveBeenCalledWith(replacementText);
});
});
describe("processMention", () => {
// TODO refactor and expand tests when mentions become <a> tags
it("returns early when suggestion is null", () => {
const mockSetSuggestion = jest.fn();
const mockSetText = jest.fn();
processMention("href", "displayName", new Map(), null, mockSetSuggestion, mockSetText);
expect(mockSetSuggestion).not.toHaveBeenCalled();
expect(mockSetText).not.toHaveBeenCalled();
});
it("can insert a mention into a text node", () => {
// make a text node and an editor div, set the cursor inside the text node and then
// append node to editor, then editor to document
const textNode = document.createTextNode("@a");
const mockEditor = document.createElement("div");
mockEditor.appendChild(textNode);
document.body.appendChild(mockEditor);
document.getSelection()?.setBaseAndExtent(textNode, 1, textNode, 1);
// call the util function
const href = "href";
const displayName = "displayName";
const mockSetSuggestionData = jest.fn();
const mockSetText = jest.fn();
processMention(
href,
displayName,
new Map([["style", "test"]]),
{ node: textNode, startOffset: 0, endOffset: 2 } as unknown as Suggestion,
mockSetSuggestionData,
mockSetText,
);
// check that the editor has a single child
expect(mockEditor.children).toHaveLength(1);
const linkElement = mockEditor.firstElementChild as HTMLElement;
// and that the child is an <a> tag with the expected attributes and content
expect(linkElement).toBeInstanceOf(HTMLAnchorElement);
expect(linkElement).toHaveAttribute(href, href);
expect(linkElement).toHaveAttribute("contenteditable", "false");
expect(linkElement).toHaveAttribute("style", "test");
expect(linkElement.textContent).toBe(displayName);
expect(mockSetText).toHaveBeenCalledWith();
expect(mockSetSuggestionData).toHaveBeenCalledWith(null);
});
});
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
describe("processSelectionChange", () => {
function createMockEditorRef(element: HTMLDivElement | null = null): React.RefObject<HTMLDivElement> {
return { current: element } as React.RefObject<HTMLDivElement>;
}
function appendEditorWithTextNodeContaining(initialText = ""): [HTMLDivElement, Node] {
// create the elements/nodes
const mockEditor = document.createElement("div");
const textNode = document.createTextNode(initialText);
// append text node to the editor, editor to the document body
mockEditor.appendChild(textNode);
document.body.appendChild(mockEditor);
return [mockEditor, textNode];
}
const mockSetSuggestion = jest.fn();
beforeEach(() => {
mockSetSuggestion.mockClear();
});
it("returns early if current editorRef is null", () => {
const mockEditorRef = createMockEditorRef(null);
// we monitor for the call to document.createNodeIterator to indicate an early return
const nodeIteratorSpy = jest.spyOn(document, "createNodeIterator");
processSelectionChange(mockEditorRef, jest.fn());
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
expect(nodeIteratorSpy).not.toHaveBeenCalled();
// tidy up to avoid potential impacts on other tests
nodeIteratorSpy.mockRestore();
});
it("calls setSuggestion with null if selection is not a cursor", () => {
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
const [mockEditor, textNode] = appendEditorWithTextNodeContaining("content");
const mockEditorRef = createMockEditorRef(mockEditor);
// create a selection in the text node that has different start and end locations ie it
// is not a cursor
document.getSelection()?.setBaseAndExtent(textNode, 0, textNode, 4);
// process the selection and check that we do not attempt to set the suggestion
processSelectionChange(mockEditorRef, mockSetSuggestion);
expect(mockSetSuggestion).toHaveBeenCalledWith(null);
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
});
it("calls setSuggestion with null if selection cursor is not inside a text node", () => {
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
const [mockEditor] = appendEditorWithTextNodeContaining("content");
const mockEditorRef = createMockEditorRef(mockEditor);
// create a selection that points at the editor element, not the text node it contains
document.getSelection()?.setBaseAndExtent(mockEditor, 0, mockEditor, 0);
// process the selection and check that we do not attempt to set the suggestion
processSelectionChange(mockEditorRef, mockSetSuggestion);
expect(mockSetSuggestion).toHaveBeenCalledWith(null);
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
});
it("calls setSuggestion with null if we have an existing suggestion but no command match", () => {
const [mockEditor, textNode] = appendEditorWithTextNodeContaining("content");
const mockEditorRef = createMockEditorRef(mockEditor);
// create a selection in the text node that has identical start and end locations, ie it is a cursor
document.getSelection()?.setBaseAndExtent(textNode, 0, textNode, 0);
// the call to process the selection will have an existing suggestion in state due to the second
// argument being non-null, expect that we clear this suggestion now that the text is not a command
processSelectionChange(mockEditorRef, mockSetSuggestion);
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
expect(mockSetSuggestion).toHaveBeenCalledWith(null);
});
it("calls setSuggestion with the expected arguments when text node is valid command", () => {
const commandText = "/potentialCommand";
const [mockEditor, textNode] = appendEditorWithTextNodeContaining(commandText);
const mockEditorRef = createMockEditorRef(mockEditor);
// create a selection in the text node that has identical start and end locations, ie it is a cursor
document.getSelection()?.setBaseAndExtent(textNode, 3, textNode, 3);
// process the change and check the suggestion that is set looks as we expect it to
processSelectionChange(mockEditorRef, mockSetSuggestion);
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
expect(mockSetSuggestion).toHaveBeenCalledWith({
mappedSuggestion: {
keyChar: "/",
type: "command",
text: "potentialCommand",
},
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
node: textNode,
startOffset: 0,
endOffset: commandText.length,
});
});
it("does not treat a command outside the first text node to be a suggestion", () => {
const [mockEditor] = appendEditorWithTextNodeContaining("some text in first node");
const [, commandTextNode] = appendEditorWithTextNodeContaining("/potentialCommand");
const mockEditorRef = createMockEditorRef(mockEditor);
// create a selection in the text node that has identical start and end locations, ie it is a cursor
document.getSelection()?.setBaseAndExtent(commandTextNode, 3, commandTextNode, 3);
// process the change and check the suggestion that is set looks as we expect it to
processSelectionChange(mockEditorRef, mockSetSuggestion);
expect(mockSetSuggestion).toHaveBeenCalledWith(null);
});
});
describe("findSuggestionInText", () => {
const command = "/someCommand";
const userMention = "@userMention";
const roomMention = "#roomMention";
const mentionTestCases = [userMention, roomMention];
const allTestCases = [command, userMention, roomMention];
it("returns null if content does not contain any mention or command characters", () => {
expect(findSuggestionInText("hello", 1, true)).toBeNull();
});
it("returns null if content contains a command but is not the first text node", () => {
expect(findSuggestionInText(command, 1, false)).toBeNull();
});
it("returns null if the offset is outside the content length", () => {
expect(findSuggestionInText("hi", 30, true)).toBeNull();
expect(findSuggestionInText("hi", -10, true)).toBeNull();
});
it.each(allTestCases)("returns an object when the whole input is special case: %s", (text) => {
const expected = {
mappedSuggestion: getMappedSuggestion(text),
startOffset: 0,
endOffset: text.length,
};
// test for cursor immediately before and after special character, before end, at end
expect(findSuggestionInText(text, 0, true)).toEqual(expected);
expect(findSuggestionInText(text, 1, true)).toEqual(expected);
expect(findSuggestionInText(text, text.length - 2, true)).toEqual(expected);
expect(findSuggestionInText(text, text.length, true)).toEqual(expected);
});
it("returns null when a command is followed by other text", () => {
const followingText = " followed by something";
// check for cursor inside and outside the command
expect(findSuggestionInText(command + followingText, command.length - 2, true)).toBeNull();
expect(findSuggestionInText(command + followingText, command.length + 2, true)).toBeNull();
});
it.each(mentionTestCases)("returns an object when a %s is followed by other text", (mention) => {
const followingText = " followed by something else";
expect(findSuggestionInText(mention + followingText, mention.length - 2, true)).toEqual({
mappedSuggestion: getMappedSuggestion(mention),
startOffset: 0,
endOffset: mention.length,
});
});
it("returns null if there is a command surrounded by text", () => {
const precedingText = "text before the command ";
const followingText = " text after the command";
expect(
findSuggestionInText(precedingText + command + followingText, precedingText.length + 4, true),
).toBeNull();
});
it.each(mentionTestCases)("returns an object if %s is surrounded by text", (mention) => {
const precedingText = "I want to mention ";
const followingText = " in my message";
const textInput = precedingText + mention + followingText;
const expected = {
mappedSuggestion: getMappedSuggestion(mention),
startOffset: precedingText.length,
endOffset: precedingText.length + mention.length,
};
// when the cursor is immediately before the special character
expect(findSuggestionInText(textInput, precedingText.length, true)).toEqual(expected);
// when the cursor is inside the mention
expect(findSuggestionInText(textInput, precedingText.length + 3, true)).toEqual(expected);
// when the cursor is right at the end of the mention
expect(findSuggestionInText(textInput, precedingText.length + mention.length, true)).toEqual(expected);
});
it("returns null for text content with an email address", () => {
const emailInput = "send to user@test.com";
expect(findSuggestionInText(emailInput, 15, true)).toBeNull();
});
it("returns null for double slashed command", () => {
const doubleSlashCommand = "//not a command";
expect(findSuggestionInText(doubleSlashCommand, 4, true)).toBeNull();
});
it("returns null for slash separated text", () => {
const slashSeparatedInput = "please to this/that/the other";
expect(findSuggestionInText(slashSeparatedInput, 21, true)).toBeNull();
});
it("returns an object for a mention that contains punctuation", () => {
const mentionWithPunctuation = "@userX14#5a_-";
const precedingText = "mention ";
const mentionInput = precedingText + mentionWithPunctuation;
expect(findSuggestionInText(mentionInput, 12, true)).toEqual({
mappedSuggestion: getMappedSuggestion(mentionWithPunctuation),
startOffset: precedingText.length,
endOffset: precedingText.length + mentionWithPunctuation.length,
});
});
it("returns null when user inputs any whitespace after the special character", () => {
const mentionWithSpaceAfter = "@ somebody";
expect(findSuggestionInText(mentionWithSpaceAfter, 2, true)).toBeNull();
});
it("returns an object for an emoji suggestion", () => {
const emoiticon = ":)";
const precedingText = "hello ";
const mentionInput = precedingText + emoiticon;
expect(findSuggestionInText(mentionInput, precedingText.length, true, true)).toEqual({
mappedSuggestion: getMappedSuggestion(emoiticon, true),
startOffset: precedingText.length,
endOffset: precedingText.length + emoiticon.length,
});
});
});
describe("getMappedSuggestion", () => {
it("returns null when the first character is not / # @", () => {
expect(getMappedSuggestion("Zzz")).toBe(null);
});
it("returns the expected mapped suggestion when first character is # or @", () => {
expect(getMappedSuggestion("@user-mention")).toEqual({
type: "mention",
keyChar: "@",
text: "user-mention",
});
expect(getMappedSuggestion("#room-mention")).toEqual({
type: "mention",
keyChar: "#",
text: "room-mention",
});
});
it("returns the expected mapped suggestion when first character is /", () => {
expect(getMappedSuggestion("/command")).toEqual({
type: "command",
keyChar: "/",
text: "command",
});
});
it("returns the expected mapped suggestion when the text is a plain text emoiticon", () => {
expect(getMappedSuggestion(":)", true)).toEqual({
type: "custom",
keyChar: "",
text: "🙂",
});
});
Commands for plain text editor (#10567) * add the handlers for when autocomplete is open plus rough / handling * hack in using the wysiwyg autocomplete * switch to using onSelect for the behaviour * expand comment * add a handle command function to replace text * add event firing step * fix TS errors for RefObject * extract common functionality to new util * use util for plain text mode * use util for rich text mode * remove unused imports * make util able to handle either type of keyboard event * fix TS error for mxClient * lift all new code into main component prior to extracting to custom hook * shift logic into custom hook * rename ref to editorRef for clarity * remove comment * try to add cypress test for behaviour * remove unused imports * fix various lint/TS errors for CI * update cypress test * add test for pressing escape to close autocomplete * expand cypress tests * add typing while autocomplete open test * refactor to single piece of state and update comments * update comment * extract functions for testing * add first tests * improve tests * remove console log * call useSuggestion hook from different location * update useSuggestion hook tests * improve cypress tests * remove unused import * fix selector in cypress test * add another set of util tests * remove .only * remove .only * remove import * improve cypress tests * remove .only * add comment * improve comments * tidy up tests * consolidate all cypress tests to one * add early return * fix typo, add documentation * add early return, tidy up comments * change function expression to function declaration * add documentation * fix broken test * add check to cypress tests * update types * update comment * update comments * shift ref declaration inside the hook * remove unused import * update cypress test and add comments * update usePlainTextListener comments * apply suggested changes to useSuggestion * update tests --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
2023-04-27 07:37:47 +00:00
});