import "jest"; import { EditorState } from "prosemirror-state"; import { UxCommand } from "../constants"; import { Command } from "../types"; import { uxCommands } from "../uxCommands"; import { describeRootsFactory, EditorStateFactory, editorStateFactoryFactory } from "./buildEditorState"; import "./expect.toMapEditorState"; const describeDocRoot = describeRootsFactory([["doc", editorStateFactoryFactory(children => ({ doc }) => doc(children))]]); function runEnterSuite(command: Command, build: EditorStateFactory) { function test(initial: EditorState, expected: EditorState) { expect(command).toMapEditorState(initial, expected); } for (const type of ["ul", "ol"]) { it(`lifts an empty ${type}[^]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("{^}"))); }), build(({ p }) => p("{^}")) ); }); it(`splits an empty non-last ${type}[^,]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("{^}")), li(p())); }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p()), li(p("{^}")), li(p())); }) ); }); } } function runDeleteBackwardSuite(command: Command, build: EditorStateFactory) { function test(initial: EditorState, expected: EditorState) { expect(command).toMapEditorState(initial, expected); } for (const type of ["ul", "ol"]) { it(`joins paragraph to previous ${type}[p]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [list(li(p("1"))), p("{^}2")]; }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1{^}2"))); }) ); }); it(`joins paragraph to previous ${type}[p[p]]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [list(li(p("1"), list(li(p("1.1"))))), p("{^}2")]; }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1"), list(li(p("1.1{^}2"))))); }) ); }); } } function runDeleteForwardSuite(command: Command, build: EditorStateFactory) { function test(initial: EditorState, expected: EditorState) { expect(command).toMapEditorState(initial, expected); } for (const type of ["ul", "ol"]) { it(`joins the next paragraph to ${type}[p^]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [list(li(p("1{^}"))), p("2")]; }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1{^}2"))); }) ); }); it(`joins the next paragraph to ${type}[p[p^]]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [list(li(p("1"), list(li(p("1.1{^}"))))), p("2")]; }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [list(li(p("1"), list(li(p("1.1{^}2")))))]; }) ); }); it(`empty p lifts and merges next ${type}[p]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [p("{^}"), list(li(p("1")))]; }), build(({ p }) => p("{^}1")) ); }); it(`non-empty p lifts and merges next ${type}[p]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [p("foo{^}"), list(li(p("1")))]; }), build(({ p }) => p("foo{^}1")) ); }); it(`empty p lifts and merges next ${type}[p,p]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [p("{^}"), list(li(p("1")), li(p("2")))]; }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [p("{^}1"), list(li(p("2")))]; }) ); }); it(`non-empty p lifts and merges next ${type}[p,p]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [p("foo{^}"), list(li(p("1")), li(p("2")))]; }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [p("foo{^}1"), list(li(p("2")))]; }) ); }); it(`empty p lifts and merges next ${type}[p[p]]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [p("{^}"), list(li(p("1"), list(li(p("1.1")))))]; }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [p("{^}1"), list(li(p("1.1")))]; }) ); }); it(`non-empty p lifts and merges next ${type}[p[p]]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [p("foo{^}"), list(li(p("1"), list(li(p("1.1")))))]; }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [p("foo{^}1"), list(li(p("1.1")))]; }) ); }); it(`lifts and merges to empty ${type}[p^[p]] into ${type}[p^]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [list(li(p("{^}"), list(li(p("2")))))]; }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("{^}2"))); }) ); }); it(`lifts and merges ${type}[p^[p]] into ${type}[p^]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return [list(li(p("1{^}"), list(li(p("2")))))]; }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1{^}2"))); }) ); }); it(`lifts and merges ${type}[p^[p[p]]] into ${type}[p^[p]]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1{^}"), list(li(p("1.1"), list(li(p("1.1.1"))))))); }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1{^}1.1"), list(li(p("1.1.1"))))); }) ); }); it(`lifts and merges ${type}[p^[p[p[p]]]] into ${type}[p^[p[p]]]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1{^}"), list(li(p("1.1"), list(li(p("1.1.1"), list(li(p("1.1.1.1"))))))))); }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1{^}1.1"), list(li(p("1.1.1"), list(li(p("1.1.1.1"))))))); }) ); }); it(`lifts and merges ${type}[p^[p[p]],p] into ${type}[p^[p],p]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1{^}"), list(li(p("1.1"), list(li(p("1.1.1")))), li(p("1.1.1.1"))))); }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1{^}1.1"), list(li(p("1.1.1")), li(p("1.1.1.1"))))); }) ); }); it(`sinks the next cousin ${type}[p[p^],p] → ${type}[p[p^,p]]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1"), list(li(p("1.1{^}")))), li(p("2"))); }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1"), list(li(p("1.1{^}")), li(p("2"))))); }) ); }); it(`sinks the next cousin ${type}[p[p^],p[p]] → ${type}[p[p^,p[p]]]`, () => { test( build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1"), list(li(p("1.1{^}")))), li(p("2"), list(li(p("2.1"))))); }), build(({ ul, ol, li, p }) => { const list = type == "ul" ? ul : ol; return list(li(p("1"), list(li(p("1.1{^}")), li(p("2"), list(li(p("2.1"))))))); }) ); }); } } function runArrowDownSuite(command: Command, build: EditorStateFactory) { function test(initial: EditorState, expected: EditorState) { expect(command).toMapEditorState(initial, expected); } it("does not change a cursor", () => { expect(command).not.toMapEditorState(build(({ p }) => p("f{^}oo"))); }); it("sets selection to end when all selection", () => { test(build(({ p }) => p("f{^}oo"), { selection: ({ all }) => all() }), build(({ p }) => p("foo{^}"))); }); } function runArrowUpSuite(command: Command, build: EditorStateFactory) { function test(initial: EditorState, expected: EditorState) { expect(command).toMapEditorState(initial, expected); } it("does not change a cursor", () => { expect(command).not.toMapEditorState(build(({ p }) => p("f{^}oo"))); }); it("sets selection to start when all selection", () => { test(build(({ p }) => p("f{^}oo"), { selection: ({ all }) => all() }), build(({ p }) => p("{^}foo"))); }); } describe("Enter", () => { describeDocRoot(build => { runEnterSuite(uxCommands[UxCommand.Enter], build); }); }); describe("DeleteBackward", () => { describeDocRoot(build => { runDeleteBackwardSuite(uxCommands[UxCommand.DeleteBackward], build); }); }); describe("DeleteForward", () => { describeDocRoot(build => { runDeleteForwardSuite(uxCommands[UxCommand.DeleteForward], build); }); }); describe("ArrowDown", () => { describeDocRoot(build => { runArrowDownSuite(uxCommands[UxCommand.ArrowDown], build); }); }); describe("ArrowUp", () => { describeDocRoot(build => { runArrowUpSuite(uxCommands[UxCommand.ArrowUp], build); }); });