import { afterAll, beforeEach, describe, expect, test } from '@jest/globals';
import {
  getTokens,
  setupTex,
  tex2mml,
  expectTexError,
  trapErrors,
} from '#helpers';
import '#js/input/tex/mathtools/MathtoolsConfiguration';
import '#js/input/tex/ams/AmsConfiguration';
import '#js/input/tex/boldsymbol/BoldsymbolConfiguration';
import '#js/input/tex/bbox/BboxConfiguration';

import { Configuration } from '#js/input/tex/Configuration.js';
import { ConfigurationType } from '#js/input/tex/HandlerTypes.js';
import { AbstractTags } from '#js/input/tex/Tags.js';

beforeEach(() => setupTex(['base', 'ams', 'boldsymbol', 'mathtools']));

/**********************************************************************************/

describe('Mathtools Spacing Control', () => {
  test('mathllap', () => {
    expect(
      tex2mml(
        '\\mathord{=}\\mathllap{x}\\quad \\mathord{=}\\mathllap[\\scriptstyle]{x}'
      )
    ).toMatchSnapshot();
  });

  test('mathrlap', () => {
    expect(
      tex2mml(
        '\\mathrlap{x}\\mathord{=}\\quad \\mathrlap[\\scriptstyle]{x}\\mathord{=}'
      )
    ).toMatchSnapshot();
  });

  test('mathclap', () => {
    expect(
      tex2mml('X = \\sum_{\\mathclap{1\\le i\\le j\\le n}} X_{ij}')
    ).toMatchSnapshot();
  });

  test('clap', () => {
    expect(
      tex2mml('X = \\sum_{\\clap{long text under}} X_{ij}')
    ).toMatchSnapshot();
  });

  test('mathmakebox', () => {
    expect(tex2mml('\\mathmakebox[3em][l]{x+y}')).toMatchSnapshot();
  });

  test('mathmakebox [r]', () => {
    expect(tex2mml('\\mathmakebox[3em][r]{x+y}')).toMatchSnapshot();
  });

  test('mathmakebox [R]', () => {
    expect(tex2mml('\\mathmakebox[3em][R]{x+y}')).toMatchSnapshot();
  });

  test('mathmbox', () => {
    expect(tex2mml('\\mathmbox{x+y}')).toMatchSnapshot();
  });

  test('textllap', () => {
    expect(tex2mml('\\mathord{==}\\textllap{a b}')).toMatchSnapshot();
  });

  test('textrlap', () => {
    expect(tex2mml('\\textrlap{a b}\\mathord{==}')).toMatchSnapshot();
  });

  test('textclap', () => {
    expect(tex2mml('ab\\sum_{\\textclap{long text}}cd')).toMatchSnapshot();
  });

  test('cramped', () => {
    expect(
      tex2mml(
        'x^2\\cramped{x^2}\\quad{\\scriptstyle x^2}\\cramped[\\scriptstyle]{x^2}'
      )
    ).toMatchSnapshot();
  });

  test('crampedllap', () => {
    expect(tex2mml('\\mathord{==}\\crampedllap{x^2}')).toMatchSnapshot();
  });

  test('crampedrlap', () => {
    expect(tex2mml('\\crampedrlap{x^2}\\mathord{==}')).toMatchSnapshot();
  });

  test('crampedclap', () => {
    expect(tex2mml('\\sum_{\\crampedclap{x^2}}')).toMatchSnapshot();
  });

  test('crampedsubstack', () => {
    expect(tex2mml('\\sum_{\\crampedsubstack{x^2\\\\y^2}}')).toMatchSnapshot();
  });

  test('adjustlimits', () => {
    expect(
      tex2mml('\\adjustlimits\\lim_{n\\to\\infty} \\max_{p^2\\ge n}')
    ).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools Tagging', () => {
  test('newtagform', () => {
    expect(
      tex2mml(
        '\\newtagform{brackets}{[}{]}\\usetagform{brackets} E=mc^2\\tag{1}'
      )
    ).toMatchSnapshot();
  });

  test('newtagform styled', () => {
    expect(
      tex2mml(
        '\\newtagform{bfbrackets}[\\textbf]{[}{]}\\usetagform{bfbrackets} E=mc^2\\tag{1}'
      )
    ).toMatchSnapshot();
  });

  test('newtagform duplicate', () => {
    expectTexError('\\newtagform{a}{(}{)}\\newtagform{a}{((}{))}').toBe(
      'Duplicate tag form: a'
    );
  });

  test('newtagform empty name', () => {
    expectTexError('\\newtagform{}{(}{)}').toBe("Tag form name can't be empty");
  });

  test('usetagform undefined name', () => {
    expectTexError('\\usetagform{error}').toBe('Undefined tag form: error');
  });

  test('renewtagform', () => {
    expect(
      tex2mml(
        '\\newtagform{a}{(}{)}\\renewtagform{a}{((}{))}\\usetagform{a} E=mc^2\\tag{1}'
      )
    ).toMatchSnapshot();
  });

  test('refeq', () => {
    expect(
      tex2mml(
        '\\begin{align}E=mc^2\\label{test}\\tag*{\\textsf{A}}\\\\ \\refeq{test}\\end{align}'
      )
    ).toMatchSnapshot();
  });

  test('Tags add', () => {
    setupTex(['base', 'ams', 'mathtools'], { tags: 'ams' });
    expect(
      tex2mml('\\newtagform{bars}||\\usetagform{bars} x\\tag{1}')
    ).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools Symbols', () => {
  test('Delimiters', () => {
    expect(tex2mml('\\left\\lparen X \\right\\rparen')).toMatchSnapshot();
  });

  test('Other', () => {
    expect(tex2mml('\\ndownarrow \\nuparrow \\bigtimes_n')).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools stretchy', () => {
  test('xleftrightarrow', () => {
    expect(tex2mml('\\xleftrightarrow{x+y}')).toMatchSnapshot();
  });

  test('xLeftarrow', () => {
    expect(tex2mml('\\xLeftarrow{x+y}')).toMatchSnapshot();
  });

  test('xRightarrow', () => {
    expect(tex2mml('\\xRightarrow{x+y}')).toMatchSnapshot();
  });

  test('xLeftrightarrow', () => {
    expect(tex2mml('\\xLeftrightarrow{x+y}')).toMatchSnapshot();
  });

  test('xhookleftarrow', () => {
    expect(tex2mml('\\xhookleftarrow{x+y}')).toMatchSnapshot();
  });

  test('xhookrightarrow', () => {
    expect(tex2mml('\\xhookrightarrow{x+y}')).toMatchSnapshot();
  });

  test('xmapsto', () => {
    expect(tex2mml('\\xmapsto{x+y}')).toMatchSnapshot();
  });

  test('xrightharpoondown', () => {
    expect(tex2mml('\\xrightharpoondown{x+y}')).toMatchSnapshot();
  });

  test('xleftharpoondown', () => {
    expect(tex2mml('\\xleftharpoondown{x+y}')).toMatchSnapshot();
  });

  test('xrightleftharpoons', () => {
    expect(tex2mml('\\xrightleftharpoons{x+y}')).toMatchSnapshot();
  });

  test('xleftrightharpoons', () => {
    expect(tex2mml('\\xleftrightharpoons{x+y}')).toMatchSnapshot();
  });

  test('xrightharpoonup', () => {
    expect(tex2mml('\\xrightharpoonup{x+y}')).toMatchSnapshot();
  });

  test('xleftharpoonup', () => {
    expect(tex2mml('\\xleftharpoonup{x+y}')).toMatchSnapshot();
  });

  test('xlongleftarrow', () => {
    expect(tex2mml('\\xlongleftarrow{x+y}')).toMatchSnapshot();
  });

  test('xLongleftarrow', () => {
    expect(tex2mml('\\xLongleftarrow{x+y}')).toMatchSnapshot();
  });

  test('xlongrightarrow', () => {
    expect(tex2mml('\\xlongrightarrow{x+y}')).toMatchSnapshot();
  });

  test('xLongrightarrow', () => {
    expect(tex2mml('\\xLongrightarrow{x+y}')).toMatchSnapshot();
  });

  test('underbracket', () => {
    expect(
      tex2mml('\\underbracket{x+y+z}_{\\text{$n$ times}}')
    ).toMatchSnapshot();
  });

  test('underbracket thickness', () => {
    expect(
      tex2mml('\\underbracket[3px]{x+y+z}_{\\text{$n$ times}}')
    ).toMatchSnapshot();
  });

  test('underbracket height', () => {
    expect(
      tex2mml('\\underbracket[][10px]{x+y+z}_{\\text{$n$ times}}')
    ).toMatchSnapshot();
  });

  test('underbracket thickness height', () => {
    expect(
      tex2mml('\\underbracket[3px][10px]{x+y+z}_{\\text{$n$ times}}')
    ).toMatchSnapshot();
  });

  test('overbracket', () => {
    expect(
      tex2mml('\\overbracket{x+y+z}^{\\text{$n$ times}}')
    ).toMatchSnapshot();
  });

  test('overbracket thickness', () => {
    expect(
      tex2mml('\\overbracket[3px]{x+y+z}^{\\text{$n$ times}}')
    ).toMatchSnapshot();
  });

  test('overbracket height', () => {
    expect(
      tex2mml('\\overbracket[][10px]{x+y+z}^{\\text{$n$ times}}')
    ).toMatchSnapshot();
  });

  test('overbracket thickness height', () => {
    expect(
      tex2mml('\\overbracket[3px][10px]{x+y+z}^{\\text{$n$ times}}')
    ).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools Matrix Environments', () => {
  test('matrix*', () => {
    expect(
      tex2mml('\\begin{matrix*} -1 & 3 \\\\ 2 & -4 \\end{matrix*}')
    ).toMatchSnapshot();
  });

  test('matrix*[l]', () => {
    expect(
      tex2mml('\\begin{matrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{matrix*}')
    ).toMatchSnapshot();
  });

  test('matrix*[c]', () => {
    expect(
      tex2mml('\\begin{matrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{matrix*}')
    ).toMatchSnapshot();
  });

  test('matrix*[r]', () => {
    expect(
      tex2mml('\\begin{matrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{matrix*}')
    ).toMatchSnapshot();
  });

  test('pmatrix*', () => {
    expect(
      tex2mml('\\begin{pmatrix*} -1 & 3 \\\\ 2 & -4 \\end{pmatrix*}')
    ).toMatchSnapshot();
  });

  test('pmatrix*[l]', () => {
    expect(
      tex2mml('\\begin{pmatrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{pmatrix*}')
    ).toMatchSnapshot();
  });

  test('pmatrix*[c]', () => {
    expect(
      tex2mml('\\begin{pmatrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{pmatrix*}')
    ).toMatchSnapshot();
  });

  test('pmatrix*[r]', () => {
    expect(
      tex2mml('\\begin{pmatrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{pmatrix*}')
    ).toMatchSnapshot();
  });

  test('bmatrix*', () => {
    expect(
      tex2mml('\\begin{bmatrix*} -1 & 3 \\\\ 2 & -4 \\end{bmatrix*}')
    ).toMatchSnapshot();
  });

  test('bmatrix*[l]', () => {
    expect(
      tex2mml('\\begin{bmatrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{bmatrix*}')
    ).toMatchSnapshot();
  });

  test('bmatrix*[c]', () => {
    expect(
      tex2mml('\\begin{bmatrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{bmatrix*}')
    ).toMatchSnapshot();
  });

  test('bmatrix*[r]', () => {
    expect(
      tex2mml('\\begin{bmatrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{bmatrix*}')
    ).toMatchSnapshot();
  });

  test('Bmatrix*', () => {
    expect(
      tex2mml('\\begin{Bmatrix*} -1 & 3 \\\\ 2 & -4 \\end{Bmatrix*}')
    ).toMatchSnapshot();
  });

  test('Bmatrix*[l]', () => {
    expect(
      tex2mml('\\begin{Bmatrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{Bmatrix*}')
    ).toMatchSnapshot();
  });

  test('Bmatrix*[c]', () => {
    expect(
      tex2mml('\\begin{Bmatrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{Bmatrix*}')
    ).toMatchSnapshot();
  });

  test('Bmatrix*[r]', () => {
    expect(
      tex2mml('\\begin{Bmatrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{Bmatrix*}')
    ).toMatchSnapshot();
  });

  test('vmatrix*', () => {
    expect(
      tex2mml('\\begin{vmatrix*} -1 & 3 \\\\ 2 & -4 \\end{vmatrix*}')
    ).toMatchSnapshot();
  });

  test('vmatrix*[l]', () => {
    expect(
      tex2mml('\\begin{vmatrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{vmatrix*}')
    ).toMatchSnapshot();
  });

  test('vmatrix*[c]', () => {
    expect(
      tex2mml('\\begin{vmatrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{vmatrix*}')
    ).toMatchSnapshot();
  });

  test('vmatrix*[r]', () => {
    expect(
      tex2mml('\\begin{vmatrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{vmatrix*}')
    ).toMatchSnapshot();
  });

  test('Vmatrix*', () => {
    expect(
      tex2mml('\\begin{Vmatrix*} -1 & 3 \\\\ 2 & -4 \\end{Vmatrix*}')
    ).toMatchSnapshot();
  });

  test('Vmatrix*[l]', () => {
    expect(
      tex2mml('\\begin{Vmatrix*}[l] -1 & 3 \\\\ 2 & -4 \\end{Vmatrix*}')
    ).toMatchSnapshot();
  });

  test('Vmatrix*[c]', () => {
    expect(
      tex2mml('\\begin{Vmatrix*}[c] -1 & 3 \\\\ 2 & -4 \\end{Vmatrix*}')
    ).toMatchSnapshot();
  });

  test('Vmatrix*[r]', () => {
    expect(
      tex2mml('\\begin{Vmatrix*}[r] -1 & 3 \\\\ 2 & -4 \\end{Vmatrix*}')
    ).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools Small Matrix Environments', () => {
  test('smallmatrix*', () => {
    expect(
      tex2mml('\\begin{smallmatrix*} -a & b \\\\ c & -d \\end{smallmatrix*}')
    ).toMatchSnapshot();
  });

  test('smallmatrix*[l]', () => {
    expect(
      tex2mml('\\begin{smallmatrix*}[l] -a & b \\\\ c & -d \\end{smallmatrix*}')
    ).toMatchSnapshot();
  });

  test('smallmatrix*[c]', () => {
    expect(
      tex2mml('\\begin{smallmatrix*}[c] -a & b \\\\ c & -d \\end{smallmatrix*}')
    ).toMatchSnapshot();
  });

  test('smallmatrix*[r]', () => {
    expect(
      tex2mml('\\begin{smallmatrix*}[r] -a & b \\\\ c & -d \\end{smallmatrix*}')
    ).toMatchSnapshot();
  });

  test('psmallmatrix*', () => {
    expect(
      tex2mml('\\begin{psmallmatrix*} -a & b \\\\ c & -d \\end{psmallmatrix*}')
    ).toMatchSnapshot();
  });

  test('psmallmatrix*[l]', () => {
    expect(
      tex2mml(
        '\\begin{psmallmatrix*}[l] -a & b \\\\ c & -d \\end{psmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('psmallmatrix*[c]', () => {
    expect(
      tex2mml(
        '\\begin{psmallmatrix*}[c] -a & b \\\\ c & -d \\end{psmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('psmallmatrix*[r]', () => {
    expect(
      tex2mml(
        '\\begin{psmallmatrix*}[r] -a & b \\\\ c & -d \\end{psmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('bsmallmatrix*', () => {
    expect(
      tex2mml('\\begin{bsmallmatrix*} -a & b \\\\ c & -d \\end{bsmallmatrix*}')
    ).toMatchSnapshot();
  });

  test('bsmallmatrix*[l]', () => {
    expect(
      tex2mml(
        '\\begin{bsmallmatrix*}[l] -a & b \\\\ c & -d \\end{bsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('bsmallmatrix*[c]', () => {
    expect(
      tex2mml(
        '\\begin{bsmallmatrix*}[c] -a & b \\\\ c & -d \\end{bsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('bsmallmatrix*[r]', () => {
    expect(
      tex2mml(
        '\\begin{bsmallmatrix*}[r] -a & b \\\\ c & -d \\end{bsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('Bsmallmatrix*', () => {
    expect(
      tex2mml('\\begin{Bsmallmatrix*} -a & b \\\\ c & -d \\end{Bsmallmatrix*}')
    ).toMatchSnapshot();
  });

  test('Bsmallmatrix*[l]', () => {
    expect(
      tex2mml(
        '\\begin{Bsmallmatrix*}[l] -a & b \\\\ c & -d \\end{Bsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('Bsmallmatrix*[c]', () => {
    expect(
      tex2mml(
        '\\begin{Bsmallmatrix*}[c] -a & b \\\\ c & -d \\end{Bsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('Bsmallmatrix*[r]', () => {
    expect(
      tex2mml(
        '\\begin{Bsmallmatrix*}[r] -a & b \\\\ c & -d \\end{Bsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('vsmallmatrix*', () => {
    expect(
      tex2mml('\\begin{vsmallmatrix*} -a & b \\\\ c & -d \\end{vsmallmatrix*}')
    ).toMatchSnapshot();
  });

  test('vsmallmatrix*[l]', () => {
    expect(
      tex2mml(
        '\\begin{vsmallmatrix*}[l] -a & b \\\\ c & -d \\end{vsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('vsmallmatrix*[c]', () => {
    expect(
      tex2mml(
        '\\begin{vsmallmatrix*}[c] -a & b \\\\ c & -d \\end{vsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('vsmallmatrix*[r]', () => {
    expect(
      tex2mml(
        '\\begin{vsmallmatrix*}[r] -a & b \\\\ c & -d \\end{vsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('Vsmallmatrix*', () => {
    expect(
      tex2mml('\\begin{Vsmallmatrix*} -a & b \\\\ c & -d \\end{Vsmallmatrix*}')
    ).toMatchSnapshot();
  });

  test('Vsmallmatrix*[l]', () => {
    expect(
      tex2mml(
        '\\begin{Vsmallmatrix*}[l] -a & b \\\\ c & -d \\end{Vsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('Vsmallmatrix*[c]', () => {
    expect(
      tex2mml(
        '\\begin{Vsmallmatrix*}[c] -a & b \\\\ c & -d \\end{Vsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('Vsmallmatrix*[r]', () => {
    expect(
      tex2mml(
        '\\begin{Vsmallmatrix*}[r] -a & b \\\\ c & -d \\end{Vsmallmatrix*}'
      )
    ).toMatchSnapshot();
  });

  test('smallmatrix', () => {
    expect(
      tex2mml('\\begin{smallmatrix} -a & b \\\\ c & -d \\end{smallmatrix}')
    ).toMatchSnapshot();
  });

  test('smallmatrix', () => {
    expect(
      tex2mml('\\begin{smallmatrix} -a & b \\\\ c & -d \\end{smallmatrix}')
    ).toMatchSnapshot();
  });

  test('psmallmatrix', () => {
    expect(
      tex2mml('\\begin{psmallmatrix} -a & b \\\\ c & -d \\end{psmallmatrix}')
    ).toMatchSnapshot();
  });

  test('bsmallmatrix', () => {
    expect(
      tex2mml('\\begin{bsmallmatrix} -a & b \\\\ c & -d \\end{bsmallmatrix}')
    ).toMatchSnapshot();
  });

  test('Bsmallmatrix', () => {
    expect(
      tex2mml('\\begin{Bsmallmatrix} -a & b \\\\ c & -d \\end{Bsmallmatrix}')
    ).toMatchSnapshot();
  });

  test('vsmallmatrix', () => {
    expect(
      tex2mml('\\begin{vsmallmatrix} -a & b \\\\ c & -d \\end{vsmallmatrix}')
    ).toMatchSnapshot();
  });

  test('Vsmallmatrix', () => {
    expect(
      tex2mml('\\begin{Vsmallmatrix} -a & b \\\\ c & -d \\end{Vsmallmatrix}')
    ).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools More Environments', () => {
  test('multlined', () => {
    expect(
      tex2mml(
        'A = \\begin{multlined}[t]\\framebox[4cm]{first}\\\\\\framebox[4cm]{last}\\end{multlined} B'
      )
    ).toMatchSnapshot();
  });

  test('multlined [b] width', () => {
    expect(
      tex2mml(
        '\\begin{multlined}[b][7cm]\\framebox[4cm]{first}\\\\\\framebox[4cm]{last}\\end{multlined} = B'
      )
    ).toMatchSnapshot();
  });

  test('multlined [c] width', () => {
    expect(
      tex2mml(
        '\\begin{multlined}[c][7cm]\\framebox[4cm]{first}\\\\\\framebox[4cm]{last}\\end{multlined} = B'
      )
    ).toMatchSnapshot();
  });

  test('multlined [t] width', () => {
    expect(
      tex2mml(
        '\\begin{multlined}[t][7cm]\\framebox[4cm]{first}\\\\\\framebox[4cm]{last}\\end{multlined} = B'
      )
    ).toMatchSnapshot();
  });

  test('multlined width', () => {
    expect(
      tex2mml(
        '\\begin{multlined}[7cm]\\framebox[4cm]{first}\\\\\\framebox[4cm]{last}\\end{multlined}'
      )
    ).toMatchSnapshot();
  });

  test('multlined shoved', () => {
    expect(
      tex2mml(
        [
          '\\begin{multlined}[c][7cm]',
          '\\framebox[4cm]{first} \\\\',
          '\\shoveright{\\framebox[4cm]{second}} \\\\',
          '\\shoveleft{\\framebox[4cm]{third}} \\\\',
          '\\framebox[4cm]{last}',
          '\\end{multlined}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('multlined shoved distance', () => {
    expect(
      tex2mml(
        [
          '\\begin{multlined}[c][7cm]',
          '\\framebox[4cm]{first} \\\\',
          '\\shoveright[1cm]{\\framebox[4cm]{second}} \\\\',
          '\\shoveleft[1cm]{\\framebox[4cm]{third}} \\\\',
          '\\framebox[4cm]{last}',
          '\\end{multlined}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('shoved outside multline', () => {
    expectTexError('\\shoveleft').toBe(
      '\\shoveleft can only appear within the multline or multlined environments'
    );
  });

  test('multlined shoved misplaced', () => {
    expectTexError(
      [
        '\\begin{multlined}[c][7cm]',
        '\\framebox[4cm]{first}\\shoveleft \\\\',
        '\\end{multlined}',
      ].join('')
    ).toBe('\\shoveleft must come at the beginning of the line');
  });

  test('multlined bad width', () => {
    expectTexError('\\begin{multlined}[abc] \\end{multlined}').toBe(
      'Width for \\begin{multlined} must be a dimension'
    );
  });

  test('multlined empty row', () => {
    expect(
      tex2mml('\\begin{multlined} a \\\\ \\end{multlined}')
    ).toMatchSnapshot();
  });

  test('dcases', () => {
    expect(
      tex2mml('\\begin{dcases} 1 & x>0 \\\\ -1 & x\\le 0 \\end{dcases}')
    ).toMatchSnapshot();
  });

  test('dcases*', () => {
    expect(
      tex2mml(
        '\\begin{dcases*} 1 & if $x>0$ \\\\ -1 & otherwise \\end{dcases*}'
      )
    ).toMatchSnapshot();
  });

  test('rcases', () => {
    expect(
      tex2mml('\\begin{rcases} 1 & x>0 \\\\ -1 & x\\le 0 \\end{rcases}')
    ).toMatchSnapshot();
  });

  test('rcases*', () => {
    expect(
      tex2mml(
        '\\begin{rcases*} 1 & if $x>0$ \\\\ -1 & otherwise \\end{rcases*}'
      )
    ).toMatchSnapshot();
  });

  test('drcases', () => {
    expect(
      tex2mml('\\begin{drcases} 1 & x>0 \\\\ -1 & x\\le 0 \\end{drcases}')
    ).toMatchSnapshot();
  });

  test('drcases*', () => {
    expect(
      tex2mml(
        '\\begin{drcases*} 1 & if $x>0$ \\\\ -1 & otherwise \\end{drcases*}'
      )
    ).toMatchSnapshot();
  });

  test('cases*', () => {
    expect(
      tex2mml('\\begin{cases*} 1 & if $x>0$ \\\\ -1 & otherwise \\end{cases*}')
    ).toMatchSnapshot();
  });

  test('MoveEqLeft', () => {
    expect(
      tex2mml(
        [
          '\\begin{align*}',
          '\\MoveEqLeft \\framebox[10cm][c]{Long first line} \\\\',
          '  & = \\framebox[6cm][c]{ \\vphantom{g} 2nd line} \\\\',
          '  & \\leq \\dots',
          '\\end{align*}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('Spreadlines', () => {
    expect(
      tex2mml(
        [
          '\\begin{spreadlines}{20pt}',
          '\\begin{gather}',
          'a=b\\\\',
          'c=d',
          '\\end{gather}',
          '\\end{spreadlines}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('Spreadlines no environment', () => {
    expect(
      tex2mml(
        ['\\begin{spreadlines}{20pt}', 'a=b', '\\end{spreadlines}'].join('')
      )
    ).toMatchSnapshot();
  });

  test('Spreadlines multiple environments', () => {
    expect(
      tex2mml(
        [
          '\\begin{spreadlines}{20pt}',
          '\\begin{gather}',
          'a=b\\\\',
          'c=d',
          '\\end{gather}',
          '\\\\',
          '\\begin{gather}',
          'x=y\\\\',
          'z',
          '\\end{gather}',
          '\\end{spreadlines}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('lgathered', () => {
    expect(
      tex2mml('\\begin{lgathered} a+b+c \\\\ d \\end{lgathered}')
    ).toMatchSnapshot();
  });

  test('rgathered', () => {
    expect(
      tex2mml('\\begin{rgathered} a+b+c \\\\ d \\end{rgathered}')
    ).toMatchSnapshot();
  });

  test('ArrowBetweenLines', () => {
    expect(
      tex2mml(
        [
          '\\begin{alignat*}{2}',
          '&& \\framebox[1.5cm]{} &= \\framebox[3cm]{} \\\\',
          '\\ArrowBetweenLines',
          '&& \\framebox[1.5cm]{} &= \\framebox[2cm]{}',
          '\\end{alignat*}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('ArrowBetweenLines*', () => {
    expect(
      tex2mml(
        [
          '\\begin{alignat*}{2}',
          '&& \\framebox[1.5cm]{} &= \\framebox[3cm]{} &&\\\\',
          '\\ArrowBetweenLines*[\\Downarrow]',
          '&& \\framebox[1.5cm]{} &= \\framebox[2cm]{} &&',
          '\\end{alignat*}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('ArrowBetweenLines misplaced', () => {
    expectTexError(
      '\\begin{aligned} a \\ArrowBetweenLines \\end{aligned}'
    ).toBe('\\ArrowBetweenLines must be on a row by itself');
  });

  test('ArrowBetweenLines error', () => {
    expectTexError('\\ArrowBetweenLines').toBe(
      '\\ArrowBetweenLines can only be used in aligment environments'
    );
  });

  test('vdotswithin smallvdotswithin', () => {
    expect(
      tex2mml(
        [
          '\\begin{align*}',
          'a &= b \\\\',
          '& \\vdotswithin{=} \\\\',
          '& = c \\\\',
          '\\shortvdotswithin{=}',
          '& = d',
          '\\end{align*}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('smallvdotswithin star', () => {
    expect(
      tex2mml(
        [
          '\\begin{aligned}',
          'A&+ B \\\\',
          '&\\shortvdotswithin*{+}',
          'C &+ D',
          '\\end{aligned}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('vdotswithin smallvdotswithin', () => {
    expect(
      tex2mml(
        [
          '\\begin{alignat*}{3}',
          'A&+ B &&= C &&+ D \\\\',
          '\\MTFlushSpaceAbove',
          '&\\vdotswithin{+} &&&& \\vdotswithin{+}',
          '\\MTFlushSpaceBelow ',
          'C &+ D &&= Y &&+K',
          '\\end{alignat*}',
        ].join('')
      )
    ).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools Paired Delimiters', () => {
  test('DeclarePairedDelimiter', () => {
    expect(
      tex2mml(
        '\\DeclarePairedDelimiter\\abs{\\lvert}{\\rvert} \\abs{\\frac{a}{b}}'
      )
    ).toMatchSnapshot();
  });

  test('DeclarePairedDelimiter star', () => {
    expect(
      tex2mml(
        '\\DeclarePairedDelimiter\\abs{\\lvert}{\\rvert} \\abs*{\\frac{a}{b}}'
      )
    ).toMatchSnapshot();
  });

  test('DeclarePairedDelimiter size', () => {
    expect(
      tex2mml(
        '\\DeclarePairedDelimiter\\abs{\\lvert}{\\rvert} \\abs[\\Bigg]{\\frac{a}{b}}'
      )
    ).toMatchSnapshot();
  });

  test('DeclarePairedDelimiter duplicate', () => {
    expectTexError(
      '\\DeclarePairedDelimiter\\abs{\\lvert}{\\rvert} \\DeclarePairedDelimiter\\abs{|}{|}'
    ).toBe('Command \\abs already defined');
  });

  test('DeclarePairedDelimiterX', () => {
    expect(
      tex2mml('\\DeclarePairedDelimiterX\\x[1]{\\lvert}{\\rvert}{a#1b} \\x{X}')
    ).toMatchSnapshot();
  });

  test('DeclarePairedDelimiterXPP', () => {
    expect(
      tex2mml(
        '\\DeclarePairedDelimiterXPP\\x[1]{A}{\\lvert}{\\rvert}{B}{a#1b} \\x{X}'
      )
    ).toMatchSnapshot();
  });

  test('DeclarePairedDelimiters Backward Compatibility', () => {
    expect(
      tex2mml(
        [
          '\\DeclarePairedDelimiters\\a{|}{|}',
          '\\DeclarePairedDelimitersX\\b[1]{|}{|}{#1}',
          '\\DeclarePairedDelimitersXPP\\c[1]{A}{|}{|}{B}{#1}',
        ].join('')
      )
    ).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools Boxed Equations', () => {
  test('Aboxed', () => {
    expect(
      tex2mml('\\begin{align*}\\Aboxed{ a & = b} \\\\ & = c \\end{align*}')
    ).toMatchSnapshot();
  });

  test('Aboxed error', () => {
    expectTexError('\\Aboxed{ a & = b}').toBe(
      '\\Aboxed can only be used in aligment environments'
    );
  });

  test('Aboxed odd column', () => {
    expect(
      tex2mml('\\begin{aligned} & \\Aboxed{ a & = b} \\end{aligned}')
    ).toMatchSnapshot();
  });

  test('MakeAboxedCommand', () => {
    expect(
      tex2mml(
        '\\MakeAboxedCommand\\Afbox\\fbox \\begin{align}\\Afbox{a &= b}\\end{align}'
      )
    ).toMatchSnapshot();
  });

  test('MakeAboxedCommand star', () => {
    setupTex(['base', 'ams', 'newcommand', 'bbox', 'mathtools']);
    expect(
      tex2mml(
        '\\def\\mybox#1{\\bbox[yellow, 5px, border:2px solid]{#1}}\\MakeAboxedCommand*\\Afbox\\mybox \\begin{align}\\Afbox{a &= b}\\end{align}'
      )
    ).toMatchSnapshot();
  });

  test('MakeAboxedCommand no cs', () => {
    expectTexError('\\MakeAboxedCommand x').toBe(
      '\\MakeAboxedCommand must be followed by a control sequence'
    );
  });

  test('MakeAboxedCommand no box', () => {
    expectTexError('\\MakeAboxedCommand\\x x').toBe(
      '\\MakeAboxedCommand\\x must be followed by a control sequence'
    );
  });

  test('MakeAboxedCommand redefined', () => {
    expectTexError(
      '\\MakeAboxedCommand*\\x\\boxed \\MakeAboxedCommand\\x\\fbox'
    ).toBe('\\x is already defined');
  });
});

/**********************************************************************************/

describe('Mathtools Centered Colons', () => {
  test(':=', () => {
    expect(tex2mml('a := b')).toMatchSnapshot();
  });

  test('vcentercolon', () => {
    expect(tex2mml('a \\vcentercolon= b')).toMatchSnapshot();
  });

  test('ordinarycolon', () => {
    expect(tex2mml('a \\ordinarycolon= b')).toMatchSnapshot();
  });

  test('coloneq', () => {
    expect(tex2mml('a \\coloneq b')).toMatchSnapshot();
  });

  test('Coloneq', () => {
    expect(tex2mml('a \\Coloneq b')).toMatchSnapshot();
  });

  test('coloneqq', () => {
    expect(tex2mml('a \\coloneqq b')).toMatchSnapshot();
  });

  test('Coloneqq', () => {
    expect(tex2mml('a \\Coloneqq b')).toMatchSnapshot();
  });

  test('eqqcolon', () => {
    expect(tex2mml('a \\eqqcolon b')).toMatchSnapshot();
  });

  test('Eqqcolon', () => {
    expect(tex2mml('a \\Eqqcolon b')).toMatchSnapshot();
  });

  test('eqcolon', () => {
    expect(tex2mml('a \\eqcolon b')).toMatchSnapshot();
  });

  test('Eqcolon', () => {
    expect(tex2mml('a \\Eqcolon b')).toMatchSnapshot();
  });

  test('colonapprox', () => {
    expect(tex2mml('a \\colonapprox b')).toMatchSnapshot();
  });

  test('Colonapprox', () => {
    expect(tex2mml('a \\Colonapprox b')).toMatchSnapshot();
  });

  test('colonsim', () => {
    expect(tex2mml('a \\colonsim b')).toMatchSnapshot();
  });

  test('Colonsim', () => {
    expect(tex2mml('a \\Colonsim b')).toMatchSnapshot();
  });

  test('dblcolon', () => {
    expect(tex2mml('a \\dblcolon b')).toMatchSnapshot();
  });

  test('approxcolon', () => {
    expect(tex2mml('a \\approxcolon b')).toMatchSnapshot();
  });

  test('Approxcolon', () => {
    expect(tex2mml('a \\Approxcolon b')).toMatchSnapshot();
  });

  test('simcolon', () => {
    expect(tex2mml('a \\simcolon b')).toMatchSnapshot();
  });

  test('Simcolon', () => {
    expect(tex2mml('a \\Simcolon b')).toMatchSnapshot();
  });

  test('colondash', () => {
    expect(tex2mml('a \\colondash b')).toMatchSnapshot();
  });

  test('Colondash', () => {
    expect(tex2mml('a \\Colondash b')).toMatchSnapshot();
  });

  test('dashcolon', () => {
    expect(tex2mml('a \\dashcolon b')).toMatchSnapshot();
  });

  test('Dashcolon', () => {
    expect(tex2mml('a \\Dashcolon b')).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools Prescripts', () => {
  test('prescripts sub sup', () => {
    expect(tex2mml('\\prescript{a}{b}{X}^{c}_{d}')).toMatchSnapshot();
  });

  test('prescripts sub', () => {
    expect(tex2mml('\\prescript{a}{}{X}^{c}_{d}')).toMatchSnapshot();
  });

  test('prescripts sup', () => {
    expect(tex2mml('\\prescript{}{b}{X}^{c}_{d}')).toMatchSnapshot();
  });

  test('prescripts no post sub', () => {
    expect(tex2mml('\\prescript{}{b}{X}^{c}')).toMatchSnapshot();
  });

  test('prescripts no post sup', () => {
    expect(tex2mml('\\prescript{}{b}{X}_{d}')).toMatchSnapshot();
  });

  test('prescripts no postscripts', () => {
    expect(tex2mml('\\prescript{a}{b}{X}')).toMatchSnapshot();
  });

  test('prescripts empty', () => {
    expect(tex2mml('\\prescript{\\mathbf{}}{}{X}^{c}_{d}')).toMatchSnapshot();
  });

  test('prescripts empty no postscripts', () => {
    expect(tex2mml('\\prescript{\\mathbf{}}{}{X}')).toMatchSnapshot();
  });

  test('prescript filter ingores others', () => {
    expect(tex2mml('\\sideset{^a}{C}{_b}')).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools Split Fractions', () => {
  test('splitfrac', () => {
    expect(
      tex2mml(
        '\\frac{\\splitfrac{x + y}{+ x + y}}{z}=\\frac{\\splitdfrac{x + y}{+ x + y}}{z}'
      )
    ).toMatchSnapshot();
  });

  test('splitfrac nested', () => {
    expect(
      tex2mml(
        '\\frac{\\splitfrac{x + x + z}{\\splitfrac{\\mathstrut x + y + z}{+ x + y + z}}}{z}'
      )
    ).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools struts', () => {
  test('xmathstrut', () => {
    expect(tex2mml('\\xmathstrut{0.1}')).toMatchSnapshot();
  });

  test('xmathstrut', () => {
    expect(tex2mml('\\xmathstrut[0.2]{0.1}')).toMatchSnapshot();
  });

  test('xmathstrut error', () => {
    expectTexError('\\xmathstrut{abc}').toBe(
      'Argument to \\xmathstrut is not a number'
    );
  });
});

/**********************************************************************************/

describe('Mathtools setoptions', () => {
  test('multlined gap and pos', () => {
    expect(
      tex2mml(
        [
          '\\mathtoolsset{multlined-gap=2em,multlined-pos=b}',
          'a+\\begin{multlined} a \\\\ b \\end{multlined}+b',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('multlined-pos override', () => {
    expect(
      tex2mml(
        [
          '\\mathtoolsset{multlined-gap=2em,multlined-pos=b}',
          'a+\\begin{multlined}[b] a \\\\ b \\end{multlined}+b',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('multlined skips', () => {
    expect(
      tex2mml(
        [
          '\\mathtoolsset{multlined-gap=2em,firstline-afterskip=3em,lastline-preskip=1em}',
          '\\begin{multlined}[b] a \\\\ b \\end{multlined}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('multlined width', () => {
    expect(
      tex2mml(
        [
          '\\mathtoolsset{multlined-width=5em}',
          '\\begin{multlined} a \\\\ b \\end{multlined}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('smallmatrix-align', () => {
    expect(
      tex2mml(
        [
          '\\mathtoolsset{smallmatrix-align=r}',
          '\\begin{psmallmatrix*} a \\\\ -b \\end{psmallmatrix*}',
        ].join('')
      )
    ).toMatchSnapshot();
  });

  test('contercolon', () => {
    expect(
      tex2mml('\\mathtoolsset{centercolon=true} a := b')
    ).toMatchSnapshot();
  });

  test('contercolon-offset', () => {
    expect(
      tex2mml('\\mathtoolsset{centercolon-offset=.6em} a \\vcentercolon= b')
    ).toMatchSnapshot();
  });

  test('thincolor dx and dw', () => {
    expect(
      tex2mml(
        '\\mathtoolsset{thincolon-dx=-.06em,thincolon-dw=.12em} a \\coloneq b'
      )
    ).toMatchSnapshot();
  });

  test('use-unicode', () => {
    expect(
      tex2mml(
        '\\mathtoolsset{use-unicode=true}\\coloneq\\Coloneqq\\coloneq\\Coloneqq\\eqqcolon\\eqcolon\\dblcolon\\dashcolon'
      )
    ).toMatchSnapshot();
  });

  test('prescript formats', () => {
    expect(
      tex2mml(
        [
          '\\mathtoolsset{prescript-sub-format=\\mathbf,prescript-sup-format=\\mathsf}',
          '\\mathtoolsset{prescript-arg-format=\\mathfrak}',
          '\\prescript{a}{b}{X}^{c}_{d}',
        ].join('')
      )
    ).toMatchSnapshot();
  });
});

/**********************************************************************************/

describe('Mathtools options', () => {
  beforeEach(() => {});

  test('setoptions', () => {
    setupTex(['base', 'mathtools'], {
      mathtools: { 'allow-mathtoolsset': false },
    });
    expectTexError('\\mathtoolsset{use-unicode=true}').toBe(
      '\\mathtoolsset is disabled'
    );
  });

  test('tagforms', () => {
    setupTex(['base', 'ams', 'mathtools'], {
      mathtools: {
        tagforms: {
          bold: ['[[', ']]', '\\mathbf'],
        },
      },
    });
    expect(tex2mml('\\usetagform{bold} a=b\\tag{x}')).toMatchSnapshot();
  });

  test('tagforms error', () => {
    expect(
      trapErrors(() => {
        setupTex(['base', 'ams', 'mathtools'], {
          mathtools: {
            tagforms: {
              bold: ['[[', ']]', '\\mathbf', 'error'],
            },
          },
        });
      })
    ).toBe(
      'The tag form definition for "bold" should be an array of three strings'
    );
  });

  test('tagforms reset', () => {
    setupTex(['base', 'ams', 'mathtools'], {
      mathtools: {
        tagforms: {
          bold: ['[[', ']]', '\\mathbf'],
        },
      },
    });
    expect(
      tex2mml('\\usetagform{bold} \\usetagform{} a\\tag{1}')
    ).toMatchSnapshot();
  });

  test('non-ams tags', () => {
    class myTags extends AbstractTags {
      formatTag(tag: string) {
        return ['[[', tag, ']]'];
      }
    }
    Configuration.create('mytags', {
      [ConfigurationType.TAGS]: { mytags: myTags },
      [ConfigurationType.CONFIG]: (config: any, jax: any) => {
        jax.parseOptions.options.tags = 'mytags';
        jax.constructor.tags(jax.parseOptions, config);
      },
    });
    setupTex(['base', 'ams', 'mathtools', ['mytags', 10]], { tags: 'ams' });
    expectTexError('\\newtagform{a}()').toBe(
      '\\newtagform can only be used with ams or mathtools tags'
    );
    expectTexError('\\usetagform{bold}').toBe(
      '\\usetagform can only be used with ams or mathtools tags'
    );
  });

  test('pairedDelimiters', () => {
    setupTex(['base', 'ams', 'mathtools'], {
      mathtools: {
        pairedDelimiters: {
          aaa: ['[', ']'],
          bbb: ['|', '|', '[#1]', 1],
          ccc: ['\\{', '\\}', '[#1]', 1, '\\Rightarrow', '\\Leftarrow'],
        },
      },
    });
    expect(tex2mml('\\aaa{x} \\aaa*{x} \\aaa[\\bigg]{x}')).toMatchSnapshot();
    expect(tex2mml('\\bbb{x} \\bbb*{x} \\bbb[\\bigg]{x}')).toMatchSnapshot();
    expect(tex2mml('\\ccc{x} \\ccc*{x} \\ccc[\\bigg]{x}')).toMatchSnapshot();
  });

  test('mathtoolsset', () => {
    setupTex(['base', 'ams', 'mathtools']);
    expect(
      tex2mml(
        '\\mathtoolsset{multlined-pos={}}\\begin{multlined} a \\end{multlined}'
      )
    ).toMatchSnapshot();
  });

  test('mathtoolsset legacycolonsymbols', () => {
    setupTex(['base', 'ams', 'mathtools']);
    expect(
      tex2mml(
        '\\mathtoolsset{legacycolonsymbols}\\coloneq\\Coloneq\\eqcolon\\Eqcolon'
      )
    ).toMatchSnapshot();
  });

  test('legacycolonsymbols option', () => {
    setupTex(['base', 'ams', 'mathtools'], {
      mathtools: { legacycolonsymbols: true },
    });
    expect(tex2mml('\\coloneq\\Coloneq\\eqcolon\\Eqcolon')).toMatchSnapshot();
    expect(
      tex2mml(
        '\\mathtoolsset{legacycolonsymbols=false}\\coloneq\\Coloneq\\eqcolon\\Eqcolon'
      )
    ).toMatchSnapshot();
  });
});

/**********************************************************************************/

afterAll(() => getTokens('mathtools'));
