describe("An array environment", function() {

    it("should accept a single alignment character", function() {
        const parse = getParsed`\begin{array}r1\\20\end{array}`;
        expect(parse[0].type).toBe("array");
        expect(parse[0].cols).toEqual([
            {type: "align", align: "r"},
        ]);
    });

    it("should accept vertical separators", function() {
        const parse = getParsed`\begin{array}{|l||c:r::}\end{array}`;
        expect(parse[0].type).toBe("array");
        expect(parse[0].cols).toEqual([
            {type: "separator", separator: "|"},
            {type: "align", align: "l"},
            {type: "separator", separator: "|"},
            {type: "separator", separator: "|"},
            {type: "align", align: "c"},
            {type: "separator", separator: ":"},
            {type: "align", align: "r"},
            {type: "separator", separator: ":"},
            {type: "separator", separator: ":"},
        ]);
    });

});

describe("A subarray environment", function() {

    it("should accept only a single alignment character", function() {
        const parse = getParsed`\begin{subarray}{c}a \\ b\end{subarray}`;
        expect(parse[0].type).toBe("array");
        expect(parse[0].cols).toEqual([
            {type: "align", align: "c"},
        ]);
        expect`\begin{subarray}{cc}a \\ b\end{subarray}`.not.toParse();
        expect`\begin{subarray}{c}a & b \\ c & d\end{subarray}`.not.toParse();
        expect`\begin{subarray}{c}a \\ b\end{subarray}`.toBuild();
    });

});

describe("A substack function", function() {

    it("should build", function() {
        expect`\sum_{\substack{ 0<i<m \\ 0<j<n }}  P(i,j)`.toBuild();
    });
    it("should accommodate spaces in the argument", function() {
        expect`\sum_{\substack{ 0<i<m \\ 0<j<n }}  P(i,j)`.toBuild();
    });
    it("should accommodate macros in the argument", function() {
        expect`\sum_{\substack{ 0<i<\varPi \\ 0<j<\pi }}  P(i,j)`.toBuild();
    });
    it("should accommodate an empty argument", function() {
        expect`\sum_{\substack{}}  P(i,j)`.toBuild();
    });

});

describe("A smallmatrix environment", function() {

    it("should build", function() {
        expect`\begin{smallmatrix} a & b \\ c & d \end{smallmatrix}`.toBuild();
    });

});

describe("A cases environment", function() {

    it("should parse its input", function() {
        expect`f(a,b)=\begin{cases}a+1&\text{if }b\text{ is odd}\\a&\text{if }b=0\\a-1&\text{otherwise}\end{cases}`
            .toParse();
    });

});

describe("An rcases environment", function() {

    it("should build", function() {
        expect`\begin{rcases} a &\text{if } b \\ c &\text{if } d \end{rcases}⇒…`
            .toBuild();
    });

});

describe("An aligned environment", function() {

    it("should parse its input", function() {
        expect`\begin{aligned}a&=b&c&=d\\e&=f\end{aligned}`.toParse();
    });

    it("should allow cells in brackets", function() {
        expect`\begin{aligned}[a]&[b]\\ [c]&[d]\end{aligned}`.toParse();
    });

    it("should forbid cells in brackets without space", function() {
        expect`\begin{aligned}[a]&[b]\\[c]&[d]\end{aligned}`.not.toParse();
    });

    it("should not eat the last row when its first cell is empty", function() {
        const ae = getParsed`\begin{aligned}&E_1 & (1)\\&E_2 & (2)\\&E_3 & (3)\end{aligned}`[0];
        expect(ae.body).toHaveLength(3);
    });
});

describe("AMS environments", function() {
    it("should fail outside display mode", () => {
        expect`\begin{gather}a+b\\c+d\end{gather}`.not.toParse(nonstrictSettings);
        expect`\begin{gather*}a+b\\c+d\end{gather*}`.not.toParse(nonstrictSettings);
        expect`\begin{align}a&=b+c\\d+e&=f\end{align}`.not.toParse(nonstrictSettings);
        expect`\begin{align*}a&=b+c\\d+e&=f\end{align*}`.not.toParse(nonstrictSettings);
        expect`\begin{alignat}{2}10&x+ &3&y = 2\\3&x+&13&y = 4\end{alignat}`.not.toParse(nonstrictSettings);
        expect`\begin{alignat*}{2}10&x+ &3&y = 2\\3&x+&13&y = 4\end{alignat*}`.not.toParse(nonstrictSettings);
        expect`\begin{equation}a=b+c\end{equation}`.not.toParse(nonstrictSettings);
        expect`\begin{split}a &=b+c\\&=e+f\end{split}`.not.toParse(nonstrictSettings);
        expect`\begin{CD}A @>a>> B \\@VbVV @AAcA\\C @= D\end{CD}`.not.toParse(nonstrictSettings);
    });

    const displayMode = new Settings({displayMode: true});
    it("should build if in display mode", () => {
        expect`\begin{gather}a+b\\c+d\end{gather}`.toBuild(displayMode);
        expect`\begin{gather*}a+b\\c+d\end{gather*}`.toBuild(displayMode);
        expect`\begin{align}a&=b+c\\d+e&=f\end{align}`.toBuild(displayMode);
        expect`\begin{align*}a&=b+c\\d+e&=f\end{align*}`.toBuild(displayMode);
        expect`\begin{alignat}{2}10&x+ &3&y = 2\\3&x+&13&y = 4\end{alignat}`.toBuild(displayMode);
        expect`\begin{alignat*}{2}10&x+ &3&y = 2\\3&x+&13&y = 4\end{alignat*}`.toBuild(displayMode);
        expect`\begin{equation}a=b+c\end{equation}`.toBuild(displayMode);
        expect`\begin{equation}\begin{split}a &=b+c\\&=e+f\end{split}\end{equation}`.toBuild(displayMode);
        expect`\begin{split}a &=b+c\\&=e+f\end{split}`.toBuild(displayMode);
        expect`\begin{CD}A @<a<< B @>>b> C @>>> D\\@. @| @AcAA @VVdV \\@. E @= F @>>> G\end{CD}`.toBuild(displayMode);
    });

    it("should build an empty environment", () => {
        expect`\begin{gather}\end{gather}`.toBuild(displayMode);
        expect`\begin{gather*}\end{gather*}`.toBuild(displayMode);
        expect`\begin{align}\end{align}`.toBuild(displayMode);
        expect`\begin{align*}\end{align*}`.toBuild(displayMode);
        expect`\begin{alignat}{2}\end{alignat}`.toBuild(displayMode);
        expect`\begin{alignat*}{2}\end{alignat*}`.toBuild(displayMode);
        expect`\begin{equation}\end{equation}`.toBuild(displayMode);
        expect`\begin{split}\end{split}`.toBuild(displayMode);
        expect`\begin{CD}\end{CD}`.toBuild(displayMode);
    });

    it("{equation} should fail if argument contains two rows.", () => {
        expect`\begin{equation}a=\cr b+c\end{equation}`.not.toParse(displayMode);
    });
    it("{equation} should fail if argument contains two columns.", () => {
        expect`\begin{equation}a &=b+c\end{equation}`.not.toBuild(displayMode);
    });
    it("{split} should fail if argument contains three columns.", () => {
        expect`\begin{equation}\begin{split}a &=b &+c\\&=e &+f\end{split}\end{equation}`.not.toBuild(displayMode);
    });
    it("{array} should fail if body contains more columns than specification.", () => {
        expect`\begin{array}{2}a & b & c\\d & e  f\end{array}`.not.toBuild(displayMode);
    });
});

describe("The CD environment", function() {
    it("should fail if not is display mode", function() {
        expect(`\\begin{CD}A @<a<< B @>>b> C @>>> D\\\\@. @| @AcAA @VVdV \\\\@. E @= F @>>> G\\end{CD}`).not.toParse(
            new Settings({displayMode: false})
        );
    });
    const displaySettings = new Settings({displayMode: true});
    it("should fail if the character after '@' is not in <>AV=|.", function() {
        expect(`\\begin{CD}A @X<a<< B @>>b> C @>>> D\\\\@. @| @AcAA @VVdV \\\\@. E @= F @>>> G\\end{CD}`).not.toParse(displaySettings);
