text: // @flow strict
  
  import { describe, it } from 'mocha';
  
  import { NoUndefinedVariablesRule } from '../rules/NoUndefinedVariablesRule';
  
  import { expectValidationErrors } from './harness';
  
  function expectErrors(queryStr) {
    return expectValidationErrors(NoUndefinedVariablesRule, queryStr);
  }
  
  function expectValid(queryStr) {
    expectErrors(queryStr).to.deep.equal([]);
  }
  
  describe('Validate: No undefined variables', () => {
    it('all variables defined', () => {
      expectValid(`
        query Foo($a: String, $b: String, $c: String) {
          field(a: $a, b: $b, c: $c)
        }
      `);
    });
  
    it('all variables deeply defined', () => {
      expectValid(`
        query Foo($a: String, $b: String, $c: String) {
          field(a: $a) {
            field(b: $b) {
              field(c: $c)
            }
          }
        }
      `);
    });
  
    it('all variables deeply in inline fragments defined', () => {
      expectValid(`
        query Foo($a: String, $b: String, $c: String) {
          ... on Type {
            field(a: $a) {
              field(b: $b) {
                ... on Type {
                  field(c: $c)
                }
              }
            }
          }
        }
      `);
    });
  
    it('all variables in fragments deeply defined', () => {
      expectValid(`
        query Foo($a: String, $b: String, $c: String) {
          ...FragA
        }
        fragment FragA on Type {
          field(a: $a) {
            ...FragB
          }
        }
        fragment FragB on Type {
          field(b: $b) {
            ...FragC
          }
        }
        fragment FragC on Type {
          field(c: $c)
        }
      `);
    });
  
    it('variable within single fragment defined in multiple operations', () => {
      expectValid(`
        query Foo($a: String) {
          ...FragA
        }
        query Bar($a: String) {
          ...FragA
        }
        fragment FragA on Type {
          field(a: $a)
        }
      `);
    });
  
    it('variable within fragments defined in operations', () => {
      expectValid(`
        query Foo($a: String) {
          ...FragA
        }
        query Bar($b: String) {
          ...FragB
        }
        fragment FragA on Type {
          field(a: $a)
        }
        fragment FragB on Type {
          field(b: $b)
        }
      `);
    });
  
    it('variable within recursive fragment defined', () => {
      expectValid(`
        query Foo($a: String) {
          ...FragA
        }
        fragment FragA on Type {
          field(a: $a) {
            ...FragA
          }
        }
      `);
    });
  
    it('variable not defined', () => {
      expectErrors(`
        query Foo($a: String, $b: String, $c: String) {
          field(a: $a, b: $b, c: $c, d: $d)
        }
      `).to.deep.equal([
        {
          message: 'Variable "$d" is not defined by operation "Foo".',
          locations: [
            { line: 3, column: 39 },
            { line: 2, column: 7 },
          ],
        },
      ]);
    });
  
    it('variable not defined by un-named query', () => {
      expectErrors(`
        {
          field(a: $a)
        }
      `).to.deep.equal([
        {
          message: 'Variable "$a" is not defined.',
          locations: [
            { line: 3, column: 18 },
            { line: 2, column: 7 },
          ],
        },
      ]);
    });
  
    it('multiple variables not defined', () => {
      expectErrors(`
        query Foo($b: String) {
          field(a: $a, b: $b, c: $c)
        }
      `).to.deep.equal([
        {
          message: 'Variable "$a" is not defined by operation "Foo".',
          locations: [
            { line: 3, column: 18 },
            { line: 2, column: 7 },
          ],
        },
        {
          message: 'Variable "$c" is not defined by operation "Foo".',
          locations: [
            { line: 3, column: 32 },
            { line: 2, column: 7 },
          ],
        },
      ]);
    });
  
    it('variable in fragment not defined by un-named query', () => {
      expectErrors(`
        {
          ...FragA
        }
        fragment FragA on Type {
          field(a: $a)
        }
      `).to.deep.equal([
        {
          message: 'Variable "$a" is not defined.',
          locations: [
            { line: 6, column: 18 },
            { line: 2, column: 7 },
          ],
        },
      ]);
    });
  
    it('variable in fragment not defined by operation', () => {
      expectErrors(`
        query Foo($a: String, $b: String) {
          ...FragA
        }
        fragment FragA on Type {
          field(a: $a) {
            ...FragB
          }
        }
        fragment FragB on Type {
          field(b: $b) {
            ...FragC
          }
        }
        fragment FragC on Type {
          field(c: $c)
        }
      `).to.deep.equal([
        {
          message: 'Variable "$c" is not defined by operation "Foo".',
          locations: [
            { line: 16, column: 18 },
            { line: 2, column: 7 },
          ],
        },
      ]);
    });
  
    it('multiple variables in fragments not defined', () => {
      expectErrors(`
        query Foo($b: String) {
          ...FragA
        }
        fragment FragA on Type {
          field(a: $a) {
            ...FragB
          }
        }
        fragment FragB on Type {
          field(b: $b) {
            ...FragC
          }
        }
        fragment FragC on Type {
          field(c: $c)
        }
      `).to.deep.equal([
        {
          message: 'Variable "$a" is not defined by operation "Foo".',
          locations: [
            { line: 6, column: 18 },
            { line: 2, column: 7 },
          ],
        },
        {
          message: 'Variable "$c" is not defined by operation "Foo".',
          locations: [
            { line: 16, column: 18 },
            { line: 2, column: 7 },
          ],
        },
      ]);
    });
  
    it('single variable in fragment not defined by multiple operations', () => {
      expectErrors(`
        query Foo($a: String) {
          ...FragAB
        }
        query Bar($a: String) {
          ...FragAB
        }
        fragment FragAB on Type {
          field(a: $a, b: $b)
        }
      `).to.deep.equal([
        {
          message: 'Variable "$b" is not defined by operation "Foo".',
          locations: [
            { line: 9, column: 25 },
            { line: 2, column: 7 },
          ],
        },
        {
          message: 'Variable "$b" is not defined by operation "Bar".',
          locations: [
            { line: 9, column: 25 },
            { line: 5, column: 7 },
          ],
        },
      ]);
    });
  
    it('variables in fragment not defined by multiple operations', () => {
      expectErrors(`
        query Foo($b: String) {
          ...FragAB
        }
        query Bar($a: String) {
          ...FragAB
        }
        fragment FragAB on Type {
          field(a: $a, b: $b)
        }
      `).to.deep.equal([
        {
          message: 'Variable "$a" is not defined by operation "Foo".',
          locations: [
            { line: 9, column: 18 },
            { line: 2, column: 7 },
          ],
        },
        {
          message: 'Variable "$b" is not defined by operation "Bar".',
          locations: [
            { line: 9, column: 25 },
            { line: 5, column: 7 },
          ],
        },
      ]);
    });
  
    it('variable in fragment used by other operation', () => {
      expectErrors(`
        query Foo($b: String) {
          ...FragA
        }
        query Bar($a: String) {
          ...FragB
        }
        fragment FragA on Type {
          field(a: $a)
        }
        fragment FragB on Type {
          field(b: $b)
        }
      `).to.deep.equal([
        {
          message: 'Variable "$a" is not defined by operation "Foo".',
          locations: [
            { line: 9, column: 18 },
            { line: 2, column: 7 },
          ],
        },
        {
          message: 'Variable "$b" is not defined by operation "Bar".',
          locations: [
            { line: 12, column: 18 },
            { line: 5, column: 7 },
          ],
        },
      ]);
    });
  
    it('multiple undefined variables produce multiple errors', () => {
      expectErrors(`
        query Foo($b: String) {
          ...FragAB
        }
        query Bar($a: String) {
          ...FragAB
        }
        fragment FragAB on Type {
          field1(a: $a, b: $b)
          ...FragC
          field3(a: $a, b: $b)
        }
        fragment FragC on Type {
          field2(c: $c)
        }
      `).to.deep.equal([
        {
          message: 'Variable "$a" is not defined by operation "Foo".',
          locations: [
            { line: 9, column: 19 },
            { line: 2, column: 7 },
          ],
        },
        {
          message: 'Variable "$a" is not defined by operation "Foo".',
          locations: [
            { line: 11, column: 19 },
            { line: 2, column: 7 },
          ],
        },
        {
          message: 'Variable "$c" is not defined by operation "Foo".',
          locations: [
            { line: 14, column: 19 },
            { line: 2, column: 7 },
          ],
        },
        {
          message: 'Variable "$b" is not defined by operation "Bar".',
          locations: [
            { line: 9, column: 26 },
            { line: 5, column: 7 },
          ],
        },
        {
          message: 'Variable "$b" is not defined by operation "Bar".',
          locations: [
            { line: 11, column: 26 },
            { line: 5, column: 7 },
          ],
        },
        {
          message: 'Variable "$c" is not defined by operation "Bar".',
          locations: [
            { line: 14, column: 19 },
            { line: 5, column: 7 },
          ],
        },
      ]);
    });
  });
  