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 __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", "@wedxml/common/browsers", "wed/domtypeguards", "wed/key-constants", "@wedxml/common/test/util", "../base-config", "../util", "../wed-test-util"], function (require, exports, browsers, domtypeguards_1, keyConstants, util_1, globalConfig, util_2, wed_test_util_1) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    browsers = __importStar(browsers);
    keyConstants = __importStar(keyConstants);
    globalConfig = __importStar(globalConfig);
    const assert = chai.assert;
    const _slice = Array.prototype.slice;
    describe("wed validation errors:", () => {
        let setup;
        let editor;
        let caretManager;
        let controller;
        let processRunner;
        let refreshRunner;
        let guiRoot;
        before(() => __awaiter(this, void 0, void 0, function* () {
            setup = new wed_test_util_1.EditorSetup(`${wed_test_util_1.dataPath}/wed_test_data/source_converted.xml`, globalConfig.config, document);
            ({ editor } = setup);
            yield setup.init();
            // tslint:disable-next-line:no-any
            editor.validator._validateUpTo(editor.dataRoot, -1);
            // tslint:disable-next-line:no-any
            processRunner = editor.validationController.processErrorsRunner;
            // tslint:disable-next-line:no-any
            refreshRunner = editor.validationController.refreshErrorsRunner;
            caretManager = editor.caretManager;
            // tslint:disable-next-line:no-any
            controller = editor.validationController;
            guiRoot = editor.guiRoot;
        }));
        beforeEach(() => {
            // Force the processing of errors
            controller.processErrors();
        });
        afterEach(() => {
            setup.reset();
        });
        after(() => {
            setup.restore();
            // tslint:disable-next-line:no-any
            editor = undefined;
            // tslint:disable-next-line:no-any
            caretManager = undefined;
            // tslint:disable-next-line:no-any
            controller = undefined;
        });
        it("validation errors added by the mode", () => {
            const errors = controller.copyErrorList();
            const last = errors[errors.length - 1];
            assert.equal(last.ev.error.toString(), "Test");
        });
        it("refreshErrors does not change the number of errors", () => __awaiter(this, void 0, void 0, function* () {
            yield processRunner.onCompleted();
            const count = controller.copyErrorList().length;
            const listCount = editor.$errorList.children("li").length;
            const markerCount = guiRoot.getElementsByClassName("wed-validation-error")
                .length;
            controller.refreshErrors();
            yield refreshRunner.onCompleted();
            assert.equal(count, controller.copyErrorList().length, "the number of recorded errors should be the same");
            assert.equal(listCount, editor.$errorList.children("li").length, "the number of errors in the panel should be the same");
            assert.equal(markerCount, guiRoot.getElementsByClassName("wed-validation-error").length, "the number of markers should be the same");
        }));
        // tslint:disable-next-line:mocha-no-side-effect-code
        const itNoIE = browsers.MSIE ? it.skip : it;
        // This cannot be run on IE due to the way IE screws up the
        // formatting of contenteditable elements.
        // tslint:disable-next-line:mocha-no-side-effect-code
        itNoIE("errors for inline elements in a correct position", () => __awaiter(this, void 0, void 0, function* () {
            yield processRunner.onCompleted();
            const p = guiRoot.querySelectorAll(".body .p")[12];
            const dataP = editor.toDataNode(p);
            const dataMonogr = dataP.childNodes[0];
            const monogr = $.data(dataMonogr, "wed_mirror_node");
            assert.equal(dataMonogr.tagName, "monogr");
            let pError;
            let pErrorIx = 0;
            let monogrError;
            let monogrErrorIx = 0;
            let i = 0;
            for (const error of controller.copyErrorList()) {
                if (pError === undefined && error.ev.node === dataP) {
                    pError = error;
                    pErrorIx = i;
                }
                if (monogrError === undefined && error.ev.node === dataMonogr) {
                    monogrError = error;
                    monogrErrorIx = i;
                }
                i++;
            }
            // Make sure we found our errors.
            assert.isDefined(pError, "no error for our paragraph");
            assert.isDefined(monogrError, "no error for our monogr");
            // Find the corresponding markers
            // tslint:disable-next-line:no-any
            const markers = editor.errorLayer.el.children;
            const pMarker = markers[pErrorIx];
            const monogrMarker = markers[monogrErrorIx];
            assert.isDefined(pMarker, "should have an error for our paragraph");
            assert.isDefined(monogrMarker, "should have an error for our monogr");
            const pMarkerRect = pMarker.getBoundingClientRect();
            // The pMarker should appear to the right of the start label for the
            // paragraph and overlap with the start label for monogr.
            const pStartLabel = wed_test_util_1.firstGUI(p);
            assert.isTrue(pStartLabel.classList.contains("__start_label"), "should should have a start label for the paragraph");
            const pStartLabelRect = pStartLabel.getBoundingClientRect();
            assert.isTrue(pMarkerRect.left >= pStartLabelRect.right, "the paragraph error marker should be to the right of the \
start label for the paragraph");
            // We used to check the top too, but the changes in caret size make that
            // impractical. So we check only the bottom position.
            assert.isTrue(Math.abs(pMarkerRect.bottom - pStartLabelRect.bottom) <= 5, "the paragraph error marker should have a bottom which is \
within 5 pixels of the bottom of the start label for the paragraph");
            const monogrStartLabel = wed_test_util_1.firstGUI(monogr);
            assert.isTrue(monogrStartLabel.classList.contains("__start_label"), "should should have a start label for the paragraph");
            const monogrStartLabelRect = monogrStartLabel.getBoundingClientRect();
            assert.isTrue(Math.abs(pMarkerRect.left - monogrStartLabelRect.left) <= 5, "the paragraph error marker have a left side within 5 pixels \
of the left side of the start label for the monogr");
            // The monogrMarker should be to the right of the monogrStartLabel.
            const monogrMarkerRect = monogrMarker.getBoundingClientRect();
            assert.isTrue(monogrMarkerRect.left >= monogrStartLabelRect.right, "the monogr error marker should be to the right of the \
start label for the monogr");
            monogrMarker.scrollIntoView();
            // We used to check the top too, but the changes in caret size make that
            // impractical. So we check only the bottom position.
            assert.isTrue(Math.abs(monogrMarkerRect.bottom -
                monogrStartLabelRect.bottom) <= 5, "the monogr error marker should have a bottom which is \
within 5 pixels of the bottom of the start label for the monogr");
        }));
        it("the attributes error are not linked", () => __awaiter(this, void 0, void 0, function* () {
            editor.setLabelVisibilityLevel(0);
            yield processRunner.onCompleted();
            let cases = 0;
            for (const { ev, item } of controller.copyErrorList()) {
                if (!domtypeguards_1.isAttr(ev.node)) {
                    continue;
                }
                assert.isTrue(item.getElementsByTagName("a").length === 0, "there should be no link in the item");
                assert.equal(item.title, "This error belongs to an attribute which is not currently displayed.", "the item should have the right title");
                cases++;
            }
            assert.equal(cases, 5);
        }));
        function assertNewMarkers(orig, after, event) {
            // Make sure all markers are new.
            const note = ` after ${event}`;
            for (const item of orig) {
                assert.notInclude(after, item, `the list of markers should be new${note}`);
            }
            // We do not compare the number of errors, because changing the label
            // visibility may change the number of errors shown to the user.
        }
        describe("recreates errors when", () => {
            it("changing label visibility level", () => __awaiter(this, void 0, void 0, function* () {
                // Changing label visibility does not merely refresh the errors but
                // recreates them because errors that were visible may become invisible or
                // errors that were invisible may become visible.
                yield processRunner.onCompleted();
                // tslint:disable-next-line:no-any
                const errorLayer = editor.errorLayer.el;
                let orig = _slice.call(errorLayer.children);
                // Reduce the visibility level.
                editor.type(keyConstants.LOWER_LABEL_VISIBILITY);
                let after;
                yield util_1.waitForSuccess(() => {
                    after = _slice.call(errorLayer.children);
                    assertNewMarkers(orig, after, "decreasing the level");
                });
                orig = after;
                // Increase visibility level
                editor.type(keyConstants.INCREASE_LABEL_VISIBILITY);
                yield util_1.waitForSuccess(() => {
                    assertNewMarkers(orig, _slice.call(errorLayer.children), "increasing the level");
                });
            }));
            it("moving into or out of a label with autohidden attributes", () => __awaiter(this, void 0, void 0, function* () {
                // Moving into or ouot of a label with autohidden attributes does not
                // merely refresh the errors but recreates them because errors that were
                // visible may become invisible or errors that were invisible may become
                // visible.
                function getError() {
                    const errors = controller.copyErrorList();
                    let found;
                    for (const error of errors) {
                        if (error.item.textContent === "attribute not allowed here: xxx") {
                            found = error;
                        }
                    }
                    assert.isDefined(found);
                    return found;
                }
                yield processRunner.onCompleted();
                // tslint:disable-next-line:no-any
                const errorLayer = editor.errorLayer.el;
                let orig = _slice.call(errorLayer.children);
                const divs = editor.dataRoot.querySelectorAll("body>div");
                const div = divs[divs.length - 1];
                // We check that there is an error for the "xxx" attribute, which has no
                // link (=== no marker).
                assert.isUndefined(getError().marker);
                // Move into the label.
                editor.caretManager.setCaret(div, 0);
                editor.caretManager.move("left");
                let after;
                yield processRunner.onCompleted();
                yield util_1.waitForSuccess(() => {
                    after = _slice.call(errorLayer.children);
                    assertNewMarkers(orig, after, "moving into the label");
                    // Now it has a link (=== has a marker).
                    assert.isDefined(getError().marker);
                });
                orig = after;
                // Move out of the label.
                editor.caretManager.move("right");
                yield processRunner.onCompleted();
                yield util_1.waitForSuccess(() => {
                    assertNewMarkers(orig, _slice.call(errorLayer.children), "moving out of the label");
                    // No link again.
                    assert.isUndefined(getError().marker);
                });
            }));
        });
        it("refreshes error positions when pasting", () => __awaiter(this, void 0, void 0, function* () {
            yield refreshRunner.onCompleted();
            // Paste.
            const initial = editor.dataRoot.querySelector("body>p").firstChild;
            caretManager.setCaret(initial, 0);
            const initialValue = initial.textContent;
            // Synthetic event
            const event = util_2.makeFakePasteEvent({
                types: ["text/plain"],
                getData: () => "abcdef",
            });
            editor.$guiRoot.trigger(event);
            assert.equal(initial.nodeValue, `abcdef${initialValue}`);
            // refreshRunner returns to an incomplete states, which means there will be
            // a refresh.
            assert.isFalse(refreshRunner.completed);
        }));
        it("refreshes error positions when typing text", () => __awaiter(this, void 0, void 0, function* () {
            yield refreshRunner.onCompleted();
            // Text node inside title.
            const initial = guiRoot.getElementsByClassName("title")[0].childNodes[1];
            const parent = initial.parentNode;
            caretManager.setCaret(initial, 0);
            editor.type("blah");
            assert.equal(initial.nodeValue, "blahabcd");
            assert.equal(parent.childNodes.length, 3);
            // refreshRunner returns to an incomplete states, which means there will be
            // a refresh.
            assert.isFalse(refreshRunner.completed);
        }));
        it("refreshes error positions when typing DELETE", () => __awaiter(this, void 0, void 0, function* () {
            yield refreshRunner.onCompleted();
            // Text node inside title.
            const initial = guiRoot.getElementsByClassName("title")[0].childNodes[1];
            const parent = initial.parentNode;
            caretManager.setCaret(initial, 0);
            editor.type(keyConstants.DELETE);
            assert.equal(initial.nodeValue, "bcd");
            assert.equal(parent.childNodes.length, 3);
            // refreshRunner returns to an incomplete states, which means there will be
            // a refresh.
            assert.isFalse(refreshRunner.completed);
        }));
        it("refreshes error positions when typing BACKSPACE", () => __awaiter(this, void 0, void 0, function* () {
            yield refreshRunner.onCompleted();
            // Text node inside title.
            const initial = guiRoot.getElementsByClassName("title")[0].childNodes[1];
            const parent = initial.parentNode;
            caretManager.setCaret(initial, 4);
            editor.type(keyConstants.BACKSPACE);
            assert.equal(initial.nodeValue, "abc");
            assert.equal(parent.childNodes.length, 3);
            // refreshRunner returns to an incomplete states, which means there will be
            // a refresh.
            assert.isFalse(refreshRunner.completed);
        }));
    });
});
//# sourceMappingURL=wed-validation-error-test.js.map