import "jest"; import { Node } from "prosemirror-model"; import { describeRootsFactory, DocRefMap, EditorStateFactory, editorStateFactoryFactory } from "../../__specs__/buildEditorState"; import "../../__specs__/expect.toMapEditorState"; import { Command } from "../../types"; import { composeTextLink, dismissEditorOrComposer, editTextLink, removeLink, saveTextLink } from "../commands"; import { LinkPlugin, LinkPluginStateType } from "../LinkPlugin"; import { PortalProviderApi } from "../../util/PortalProvider"; const portalProviderApiStub: PortalProviderApi = { render: () => {}, destroy: () => {} }; const plugin = { idle: new LinkPlugin({ portalProviderApi: portalProviderApiStub, initialState: { type: LinkPluginStateType.IDLE } }), composer: ({ doc, refs }: { doc: Node; refs: DocRefMap }) => { const textFrom = refs.get("t")!; const textTo = refs.get("/t")!; const markFrom = refs.get("a")!; const markTo = refs.get("/a")!; return new LinkPlugin({ portalProviderApi: portalProviderApiStub, initialState: { type: LinkPluginStateType.SHOW_COMPOSER, draft: { markRange: { from: markFrom, to: markTo }, text: doc.textBetween(textFrom, textTo), textRange: { from: textFrom, to: textTo } } } }); }, detail: (url: string, { doc, refs }: { doc: Node; refs: DocRefMap }) => { const from = refs.get("a")!; const to = refs.get("/a")!; return new LinkPlugin({ portalProviderApi: portalProviderApiStub, initialState: { type: LinkPluginStateType.SHOW_DETAIL, link: { range: { from, to }, text: doc.textBetween(from, to), url } } }); }, editor: (url: string, { doc, refs }: { doc: Node; refs: DocRefMap }) => { const from = refs.get("a")!; const to = refs.get("/a")!; return new LinkPlugin({ portalProviderApi: portalProviderApiStub, initialState: { type: LinkPluginStateType.SHOW_EDITOR, link: { range: { from, to }, text: doc.textBetween(from, to), url } } }); } }; function runComposeTextLinkSuite(build: EditorStateFactory) { it("supports empty text selection", () => expect(composeTextLink).toMapEditorState( build(({ p }) => p("hello world{^}"), { plugins: () => [plugin.idle] }), build(({ p }) => p("hello world{^}{t}{/t}{a}{/a}"), { plugins: api => [plugin.composer(api)] }) )); it("supports non-empty text selection", () => expect(composeTextLink).toMapEditorState( build(({ p }) => p("hel{^}lo world{$}"), { plugins: () => [plugin.idle] }), build(({ p }) => p("hel{^}{t}{a}lo world{/a}{/t}{$}"), { plugins: api => [plugin.composer(api)] }) )); } function runEditTextLinkSuite(build: EditorStateFactory) { it("ignores when not on a link", () => expect(editTextLink).not.toMapEditorState( build(({ p }) => p("hel{^}lo world"), { plugins: () => [plugin.idle] }) )); it("ignores when in link but not in detail", () => expect(editTextLink).not.toMapEditorState( build(({ p, a }) => p(a("http://example.com")("hel{^}lo world")), { plugins: () => [plugin.idle] }) )); it("activate editor when in detail", () => expect(editTextLink).toMapEditorState( build(({ p, a }) => p(a("http://example.com")("{a}hel{^}lo world{/a}")), { plugins: api => [plugin.detail("http://example.com", api)] }), build(({ p, a }) => p(a("http://example.com")("{a}hel{^}lo world{/a}")), { plugins: api => [plugin.editor("http://example.com", api)] }) )); } function runSaveTextLinkSuite(build: EditorStateFactory) { const command: Command = (editorState, dispatch) => saveTextLink(editorState, { text: "foo", url: "bar" }, dispatch); it("saves edits to a contiguous range text link", () => expect(command).toMapEditorState( build(({ p, a }) => p(a("http://example.com")("{a}hel{^}lo world{/a}")), { plugins: api => [plugin.editor("http://example.com", api)] }), build(({ p, a }) => p(a("bar")("{a}foo{^}{/a}")), { plugins: api => [plugin.detail("http://bar", api)] }) )); it("saves edits to a non-contiguous range text link", () => expect(command).toMapEditorState( build(({ p, a }) => [p(a("http://example.com")("hello")), p(a("http://example.com")("{a}{^}world{/a}"))], { plugins: api => [plugin.editor("http://example.com", api)] }), build(({ p, a }) => [p(a("http://example.com")("hello")), p(a("bar")("{a}{^}foo{/a}"))], { plugins: api => [plugin.detail("http://bar", api)] }) )); it("saves a new link to a contiguous range text", () => expect(command).toMapEditorState( build(({ p }) => p("{a}{t}{^}hello world{$}{/a}{/t}"), { plugins: api => [plugin.composer(api)] }), build(({ p, a }) => p(a("bar")("{a}{^}foo{$}{/a}")), { plugins: api => [plugin.detail("http://bar", api)] }) )); it("saves a new link to a non-contiguous range text", () => expect(command).toMapEditorState( build(({ p }) => [p("{a}hello"), p("{t}hello world{/t}{/a}")], { plugins: api => [plugin.composer(api)] }), build(({ p, a }) => [p(a("bar")("{a}hello{/a}")), p(a("bar")("foo"))], { plugins: api => [plugin.detail("http://bar", api)] }) )); } function runRemoveLinkSuite(build: EditorStateFactory) { it("removes a link when in SHOW_EDITOR", () => expect(removeLink).toMapEditorState( build(({ p, a }) => p(a("http://example.com")("{a}hello world{^}{/a}")), { plugins: api => [plugin.editor("http://example.com", api)] }), build(({ p }) => p("hello world{^}"), { plugins: () => [plugin.idle] }) )); it("removes a link when in SHOW_DETAIL", () => expect(removeLink).toMapEditorState( build(({ p, a }) => p(a("http://example.com")("{a}hello world{^}{/a}")), { plugins: api => [plugin.detail("http://example.com", api)] }), build(({ p }) => p("{a}hello world{^}{/a}"), { plugins: () => [plugin.idle] }) )); } function runDismissEditorOrComposerSuite(command: Command, build: EditorStateFactory) { it("returns to IDLE when not on a link", () => expect(command).toMapEditorState( build(({ p }) => p("{a}hello world{^}{/a}"), { plugins: api => [plugin.composer(api)] }), build(({ p }) => p("hello world{^}"), { plugins: () => [plugin.idle] }) )); it("returns to SHOW_DETAIL when on a link", () => expect(command).toMapEditorState( build(({ p, a }) => p(a("http://example.com")("{a}hello world{^}{/a}")), { plugins: api => [plugin.composer(api)] }), build(({ p, a }) => p(a("http://example.com")("{a}hello world{^}{/a}")), { plugins: api => [plugin.detail("http://example.com", api)] }) )); } const describeRoots = describeRootsFactory([["doc", editorStateFactoryFactory(child => ({ doc }) => doc(child))]]); describe(composeTextLink.name, () => { describeRoots(editorStateFactory => { runComposeTextLinkSuite(editorStateFactory); }); }); describe(editTextLink.name, () => { describeRoots(editorStateFactory => { runEditTextLinkSuite(editorStateFactory); }); }); describe(saveTextLink.name, () => { describeRoots(editorStateFactory => { runSaveTextLinkSuite(editorStateFactory); }); }); describe(removeLink.name, () => { describeRoots(editorStateFactory => { runRemoveLinkSuite(editorStateFactory); }); }); describe(dismissEditorOrComposer.name, () => { describeRoots(editorStateFactory => { runDismissEditorOrComposerSuite(dismissEditorOrComposer, editorStateFactory); }); });