From 6e3e6af433ffd89f4a43a23e6a75866d339e9f4c Mon Sep 17 00:00:00 2001 From: Thibaud Colas Date: Mon, 4 Jun 2018 01:08:42 +0300 Subject: [PATCH] test(copy-paste): add unit tests for all copy-paste handling --- .../api/__snapshots__/copypaste.test.js.snap | 3 + src/lib/api/copypaste.js | 3 +- src/lib/api/copypaste.test.js | 198 ++++++++++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 src/lib/api/__snapshots__/copypaste.test.js.snap create mode 100644 src/lib/api/copypaste.test.js diff --git a/src/lib/api/__snapshots__/copypaste.test.js.snap b/src/lib/api/__snapshots__/copypaste.test.js.snap new file mode 100644 index 00000000..d97a63f6 --- /dev/null +++ b/src/lib/api/__snapshots__/copypaste.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`copypaste copy/cut listener works 1`] = `"
"`; diff --git a/src/lib/api/copypaste.js b/src/lib/api/copypaste.js index 956e8853..69e42d0d 100644 --- a/src/lib/api/copypaste.js +++ b/src/lib/api/copypaste.js @@ -78,12 +78,13 @@ export const handleDraftEditorPastedText = ( // Handle the paste if it comes from draftjs-conductor. if (fragmentElt) { - const fragmentAttr = fragmentElt.getAttribute(FRAGMENT_ATTR) || ""; + const fragmentAttr = fragmentElt.getAttribute(FRAGMENT_ATTR); let rawContent; try { // If JSON parsing fails, leave paste handling to Draft.js. // There is no reason for this to happen, unless the clipboard was altered somehow. + // $FlowFixMe rawContent = JSON.parse(fragmentAttr); } catch (error) { return false; diff --git a/src/lib/api/copypaste.test.js b/src/lib/api/copypaste.test.js new file mode 100644 index 00000000..047e5b7a --- /dev/null +++ b/src/lib/api/copypaste.test.js @@ -0,0 +1,198 @@ +import { + EditorState, + convertFromRaw, + convertToRaw, + ContentState, +} from "draft-js"; +import { registerCopySource, handleDraftEditorPastedText } from "./copypaste"; + +jest.mock("draft-js/lib/generateRandomKey", () => () => "a"); +jest.mock("draft-js/lib/getFragmentFromSelection", () => () => ({ + toArray() {}, +})); + +describe("copypaste", () => { + describe("registerCopySource", () => { + it("registers and unregisters works for copy", () => { + const editor = document.createElement("div"); + + const copySource = registerCopySource({ + editor, + _latestEditorState: EditorState.createEmpty(), + }); + + window.getSelection = jest.fn(); + editor.dispatchEvent(new Event("copy")); + expect(window.getSelection).toHaveBeenCalled(); + + copySource.unregister(); + + window.getSelection = jest.fn(); + editor.dispatchEvent(new Event("cut")); + expect(window.getSelection).not.toHaveBeenCalled(); + }); + + it("works for cut", () => { + const editor = document.createElement("div"); + + const copySource = registerCopySource({ + editor, + _latestEditorState: EditorState.createEmpty(), + }); + + window.getSelection = jest.fn(); + editor.dispatchEvent(new Event("cut")); + expect(window.getSelection).toHaveBeenCalled(); + + copySource.unregister(); + + window.getSelection = jest.fn(); + editor.dispatchEvent(new Event("cut")); + expect(window.getSelection).not.toHaveBeenCalled(); + }); + }); + + /** + * jsdom does not implement the DOM selection API, we have to do a lot of overriding. + */ + describe("copy/cut listener", () => { + it("no selection", () => { + const editor = document.createElement("div"); + + registerCopySource({ + editor, + _latestEditorState: EditorState.createEmpty(), + }); + + window.getSelection = jest.fn(() => { + return { + rangeCount: 0, + }; + }); + + const event = new Event("copy"); + event.clipboardData = {}; + event.preventDefault = jest.fn(); + editor.dispatchEvent(event); + + expect(event.preventDefault).not.toHaveBeenCalled(); + }); + + it("no clipboardData, IE11", () => { + const editor = document.createElement("div"); + + registerCopySource({ + editor, + _latestEditorState: EditorState.createEmpty(), + }); + + window.getSelection = jest.fn(() => { + return { + rangeCount: 1, + }; + }); + + const event = new Event("copy"); + event.preventDefault = jest.fn(); + editor.dispatchEvent(event); + + expect(event.preventDefault).not.toHaveBeenCalled(); + }); + + it("works", (done) => { + const editor = document.createElement("div"); + + registerCopySource({ + editor, + _latestEditorState: EditorState.createEmpty(), + }); + + const content = { + blocks: [ + { + key: "a", + type: "unstyled", + text: "test", + }, + ], + entityMap: {}, + }; + + jest + .spyOn(ContentState, "createFromBlockArray") + .mockImplementationOnce(() => { + return convertFromRaw(content); + }); + + window.getSelection = jest.fn(() => { + return { + rangeCount: 1, + toString: () => "toString selection", + getRangeAt() { + return { + cloneContents() { + return document.createElement("div"); + }, + }; + }, + }; + }); + + const event = new Event("copy"); + event.preventDefault = jest.fn(); + event.clipboardData = { + setData(type, data) { + if (type === "text/plain") { + expect(data).toBe("toString selection"); + } else if (type === "text/html") { + expect(data).toMatchSnapshot(); + done(); + } + }, + }; + editor.dispatchEvent(event); + }); + }); + + describe("handleDraftEditorPastedText", () => { + it("no HTML", () => { + const editorState = EditorState.createEmpty(); + expect(handleDraftEditorPastedText(null, editorState)).toBe(false); + }); + + it("HTML from other app", () => { + const editorState = EditorState.createEmpty(); + const html = `

Hello, world!

`; + expect(handleDraftEditorPastedText(html, editorState)).toBe(false); + }); + + it("HTML from draftjs-conductor", () => { + const content = { + blocks: [ + { + data: {}, + depth: 0, + entityRanges: [], + inlineStyleRanges: [], + key: "a", + text: "hello,\nworld!", + type: "unstyled", + }, + ], + entityMap: {}, + }; + let editorState = EditorState.createEmpty(); + const html = `

Hello, world!

`; + editorState = handleDraftEditorPastedText(html, editorState); + expect(convertToRaw(editorState.getCurrentContent())).toEqual(content); + }); + + it("invalid JSON", () => { + const editorState = EditorState.createEmpty(); + const html = `

Hello, world!

`; + expect(handleDraftEditorPastedText(html, editorState)).toBe(false); + }); + }); +});