var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
define(["require", "exports", "chai", "merge-options", "sinon", "sinon-chai", "wed/mode-tree", "../base-config", "../util", "../wed-test-util"], function (require, exports, chai_1, merge_options_1, sinon, sinon_chai_1, mode_tree_1, globalConfig, util_1, wed_test_util_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    merge_options_1 = __importDefault(merge_options_1);
    sinon = __importStar(sinon);
    sinon_chai_1 = __importDefault(sinon_chai_1);
    globalConfig = __importStar(globalConfig);
    chai_1.use(sinon_chai_1.default);
    const options = {
        schema: "/base/build/schemas/tei-simplified-rng.js",
        mode: {
            path: "wed/modes/generic/generic",
            options: {
                metadata: "/base/build/schemas/tei-metadata.json",
            },
            // We set a submode that operates on teiHeader so as to be able to test
            // that input triggers operate only on their own region.
            submode: {
                method: "selector",
                selector: "p",
                mode: {
                    path: "wed/modes/test/test-mode",
                    options: {
                        metadata: "/base/build/schemas/tei-metadata.json",
                        nameSuffix: "1",
                        hide_attributes: true,
                        stylesheets: ["a.css", "b.css"],
                    },
                    submode: {
                        method: "selector",
                        selector: "teiHeader",
                        mode: {
                            path: "wed/modes/test/test-mode",
                            options: {
                                metadata: "/base/build/schemas/tei-metadata.json",
                                nameSuffix: "2",
                                stylesheets: ["b.css", "c.css"],
                            },
                        },
                    },
                },
            },
        },
    };
    describe("ModeTree", () => {
        let setup;
        let editor;
        before(() => {
            setup = new wed_test_util_1.EditorSetup(`${wed_test_util_1.dataPath}/wed_test_data/source_converted.xml`, merge_options_1.default(globalConfig.config, options), document);
            ({ editor } = setup);
            return setup.init();
        });
        after(() => {
            setup.restore();
            // tslint:disable-next-line:no-any
            editor = undefined;
        });
        // tslint:disable-next-line:no-empty
        describe("#init", () => {
            it("resolves to the mode tree", () => __awaiter(this, void 0, void 0, function* () {
                const tree = new mode_tree_1.ModeTree(editor, options.mode);
                const resolved = yield tree.init();
                chai_1.expect(resolved).to.equal(tree);
            }));
            it("rejects if there are any wedOptions errors", () => __awaiter(this, void 0, void 0, function* () {
                const newOptions = merge_options_1.default({}, options.mode);
                const path = "tests/modes/failing-init";
                newOptions.path = path;
                const tree = new mode_tree_1.ModeTree(editor, newOptions);
                yield util_1.expectError(() => tree.init(), Error, /^failed init$/);
            }));
        });
        describe("#getMode", () => {
            let tree;
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                tree = new mode_tree_1.ModeTree(editor, options.mode);
                yield tree.init();
            }));
            it("returns the top mode for the top GUI node", () => {
                const mode = tree.getMode(editor.guiRoot);
                chai_1.expect(mode.getWedOptions()).to.have.nested.property("metadata.name")
                    .equal("Generic");
            });
            it("returns the top mode for the top data node", () => {
                const mode = tree.getMode(editor.dataRoot);
                chai_1.expect(mode.getWedOptions()).to.have.nested.property("metadata.name")
                    .equal("Generic");
            });
            it("returns a submode for a GUI node governed by a submode", () => {
                const p = editor.guiRoot.querySelector(".p._real");
                const mode = tree.getMode(p);
                chai_1.expect(mode.getWedOptions()).to.have.nested.property("metadata.name")
                    .equal("Test1");
            });
            it("returns a submode for a data node governed by a submode", () => {
                const p = editor.dataRoot.querySelector("p");
                const mode = tree.getMode(p);
                chai_1.expect(mode.getWedOptions()).to.have.nested.property("metadata.name")
                    .equal("Test1");
            });
            it("returns the same submode for nodes governed by same submode", () => {
                const ps = editor.dataRoot.querySelectorAll("p");
                const mode = tree.getMode(ps[0]);
                chai_1.expect(mode.getWedOptions()).to.have.nested.property("metadata.name")
                    .equal("Test1");
                for (const p of Array.from(ps)) {
                    chai_1.expect(mode).to.equal(tree.getMode(p));
                }
            });
            it("constrain submodes to the region of their parent mode", () => {
                // We have set a submode that matches teiHeader but that submode is a
                // child of a mode that matches p. The one teiHeader in the document is
                // not a child of p and so should not match the submode. teiHeader should
                // be governed by the top mode.
                const el = editor.dataRoot.querySelector("teiHeader");
                const mode = tree.getMode(el);
                chai_1.expect(mode.getWedOptions()).to.have.nested.property("metadata.name")
                    .equal("Generic");
            });
            it("fails if the node passed was not in the GUI or data trees", () => {
                chai_1.expect(tree.getMode.bind(tree, editor.guiRoot.parentNode)).to.throw(Error, /^did not pass a node in the GUI or data tree$/);
            });
        });
        describe("#getWedOptions", () => {
            let tree;
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                tree = new mode_tree_1.ModeTree(editor, options.mode);
                yield tree.init();
            }));
            it("returns the top options for the top GUI node", () => {
                const opts = tree.getWedOptions(editor.guiRoot);
                chai_1.expect(opts).to.have.nested.property("metadata.name").equal("Generic");
            });
            it("returns the top options for the top data node", () => {
                const opts = tree.getWedOptions(editor.dataRoot);
                chai_1.expect(opts).to.have.nested.property("metadata.name").equal("Generic");
            });
            it("returns the submode options for a GUI node governed by a submode", () => {
                const p = editor.guiRoot.querySelector(".p._real");
                const opts = tree.getWedOptions(p);
                chai_1.expect(opts).to.have.nested.property("metadata.name").equal("Test1");
            });
            it("returns the submode options for a data node governed by a submode", () => {
                const p = editor.dataRoot.querySelector("p");
                const opts = tree.getWedOptions(p);
                chai_1.expect(opts).have.nested.property("metadata.name").equal("Test1");
            });
            it("returns the same submode options for nodes governed by same submode", () => {
                const ps = editor.dataRoot.querySelectorAll("p");
                const opts = tree.getWedOptions(ps[0]);
                chai_1.expect(opts).to.have.nested.property("metadata.name")
                    .equal("Test1");
                for (const p of Array.from(ps)) {
                    chai_1.expect(opts).to.equal(tree.getWedOptions(p));
                }
            });
            it("constrain submodes to the region of their parent mode", () => {
                // We have set a submode that matches teiHeader but that submode is a
                // child of a mode that matches p. The one teiHeader in the document is
                // not a child of p and so should not match the submode. teiHeader should
                // be governed by the top mode.
                const el = editor.dataRoot.querySelector("teiHeader");
                const opts = tree.getWedOptions(el);
                chai_1.expect(opts).to.have.nested.property("metadata.name").equal("Generic");
            });
            it("fails if the node passed was not in the GUI or data trees", () => {
                chai_1.expect(tree.getMode.bind(tree, editor.guiRoot.parentNode)).to.throw(Error, /^did not pass a node in the GUI or data tree$/);
            });
        });
        describe("#getAttributeHandling", () => {
            let tree;
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                tree = new mode_tree_1.ModeTree(editor, options.mode);
                yield tree.init();
            }));
            it("returns the right value for the top GUI node", () => {
                const handling = tree.getAttributeHandling(editor.guiRoot);
                chai_1.expect(handling).to.equal("edit");
            });
            it("returns the right value for the top data node", () => {
                const handling = tree.getAttributeHandling(editor.guiRoot);
                chai_1.expect(handling).to.equal("edit");
            });
            it("returns the right value for a GUI node governed by a submode", () => {
                const p = editor.guiRoot.querySelector(".p._real");
                const handling = tree.getAttributeHandling(p);
                chai_1.expect(handling).to.equal("hide");
            });
            it("returns the right value for a data node governed by a submode", () => {
                const p = editor.dataRoot.querySelector("p");
                const handling = tree.getAttributeHandling(p);
                chai_1.expect(handling).to.equal("hide");
            });
        });
        describe("#getAttributeHidingSpecs", () => {
            let tree;
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                // We modify the default to have test-mode do its default behavior
                // which is to hide *some* attributes.
                const localOptions = merge_options_1.default({}, options);
                localOptions.mode.submode.mode.options.hide_attributes = false;
                tree = new mode_tree_1.ModeTree(editor, localOptions.mode);
                yield tree.init();
            }));
            it("returns the right value for the top GUI node", () => {
                const handling = tree.getAttributeHidingSpecs(editor.guiRoot);
                chai_1.expect(handling).to.be.null;
            });
            it("returns the right value for the top data node", () => {
                const handling = tree.getAttributeHidingSpecs(editor.guiRoot);
                chai_1.expect(handling).to.be.null;
            });
            it("returns the right value for a GUI node governed by a submode", () => {
                const p = editor.guiRoot.querySelector(".p._real");
                const handling = tree.getAttributeHidingSpecs(p);
                chai_1.expect(handling).to.not.be.null;
            });
            it("returns the right value for a data node governed by a submode", () => {
                const p = editor.dataRoot.querySelector("p");
                const handling = tree.getAttributeHidingSpecs(p);
                chai_1.expect(handling).to.not.be.null;
            });
        });
        describe("#getStylesheets", () => {
            let tree;
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                tree = new mode_tree_1.ModeTree(editor, options.mode);
                yield tree.init();
            }));
            it("returns the stylesheets used, without duplicates", () => {
                chai_1.expect(tree.getStylesheets()).to.have
                    .members(["a.css", "b.css", "c.css"]);
            });
        });
        describe("#getStylesheets", () => {
            let tree;
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                tree = new mode_tree_1.ModeTree(editor, options.mode);
                yield tree.init();
            }));
            it("returns the stylesheets used, without duplicates", () => {
                chai_1.expect(tree.getStylesheets()).to.have
                    .members(["a.css", "b.css", "c.css"]);
            });
        });
        describe("#getMaxLabelLevel", () => {
            let tree;
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                tree = new mode_tree_1.ModeTree(editor, options.mode);
                yield tree.init();
            }));
            it("returns the maximum level set", () => {
                chai_1.expect(tree.getMaxLabelLevel()).to.equal(2);
            });
        });
        describe("#getInitialLabelLevel", () => {
            let tree;
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                tree = new mode_tree_1.ModeTree(editor, options.mode);
                yield tree.init();
            }));
            it("returns the level set by the same mode as the maximum", () => {
                chai_1.expect(tree.getInitialLabelLevel()).to.equal(1);
            });
        });
        describe("#getValidators", () => {
            let tree;
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                tree = new mode_tree_1.ModeTree(editor, options.mode);
                yield tree.init();
            }));
            it("returns all the validators", () => {
                // We have two validators because the generic mode does not define one.
                // We use test-mode twice. So two validators.
                const validators = tree.getValidators();
                chai_1.expect(validators).to.have.length(2);
                chai_1.expect(validators).to.have.nested.property("[0].validateDocument");
            });
        });
        describe("#addDecoratorHandlers", () => {
            let tree;
            let sandbox;
            before(() => {
                sandbox = sinon.createSandbox();
            });
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                tree = new mode_tree_1.ModeTree(editor, options.mode);
                yield tree.init();
            }));
            afterEach(() => {
                sandbox.reset();
            });
            after(() => {
                sandbox.restore();
            });
            it("calls addHandlers on all decorators", () => {
                // tslint:disable-next-line:no-any
                const root = tree.root;
                const decorators = 
                // tslint:disable-next-line:no-any
                root.reduceTopFirst((accumulator, node) => {
                    return accumulator.concat(node.decorator);
                }, []);
                const spies = [];
                for (const decorator of decorators) {
                    spies.push(sandbox.spy(decorator, "addHandlers"));
                }
                tree.addDecoratorHandlers();
                for (const spy of spies) {
                    chai_1.expect(spy).to.have.been.calledOnce;
                }
            });
        });
        describe("#startListening", () => {
            let tree;
            let sandbox;
            before(() => {
                sandbox = sinon.createSandbox();
            });
            beforeEach(() => __awaiter(this, void 0, void 0, function* () {
                tree = new mode_tree_1.ModeTree(editor, options.mode);
                yield tree.init();
            }));
            afterEach(() => {
                sandbox.reset();
            });
            after(() => {
                sandbox.restore();
            });
            it("calls startListening on all decorators", () => {
                // tslint:disable-next-line:no-any
                const root = tree.root;
                const decorators = 
                // tslint:disable-next-line:no-any
                root.reduceTopFirst((accumulator, node) => {
                    return accumulator.concat(node.decorator);
                }, []);
                const spies = [];
                for (const decorator of decorators) {
                    spies.push(sandbox.spy(decorator, "startListening"));
                }
                tree.startListening();
                for (const spy of spies) {
                    chai_1.expect(spy).to.have.been.calledOnce;
                }
            });
        });
    });
});
//# sourceMappingURL=mode-tree-test.js.map