Skip to content

Commit

Permalink
test(copy-paste): add unit tests for all copy-paste handling
Browse files Browse the repository at this point in the history
  • Loading branch information
thibaudcolas committed Jun 3, 2018
1 parent 0b295ee commit 6e3e6af
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/lib/api/__snapshots__/copypaste.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`copypaste copy/cut listener works 1`] = `"<div data-draftjs-conductor-fragment=\\"{&quot;blocks&quot;:[{&quot;key&quot;:&quot;a&quot;,&quot;text&quot;:&quot;test&quot;,&quot;type&quot;:&quot;unstyled&quot;,&quot;depth&quot;:0,&quot;inlineStyleRanges&quot;:[],&quot;entityRanges&quot;:[],&quot;data&quot;:{}}],&quot;entityMap&quot;:{}}\\"><div></div></div>"`;
3 changes: 2 additions & 1 deletion src/lib/api/copypaste.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
198 changes: 198 additions & 0 deletions src/lib/api/copypaste.test.js
Original file line number Diff line number Diff line change
@@ -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 = `<p>Hello, world!</p>`;
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 = `<div data-draftjs-conductor-fragment='${JSON.stringify(
content,
)}'><p>Hello, world!</p></div>`;
editorState = handleDraftEditorPastedText(html, editorState);
expect(convertToRaw(editorState.getCurrentContent())).toEqual(content);
});

it("invalid JSON", () => {
const editorState = EditorState.createEmpty();
const html = `<div data-draftjs-conductor-fragment='{"blocks":[{"key"'><p>Hello, world!</p></div>`;
expect(handleDraftEditorPastedText(html, editorState)).toBe(false);
});
});
});

0 comments on commit 6e3e6af

Please sign in to comment.