3axap4eHko/xast

View on GitHub
src/__tests__/variable.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import {
  Lexer,
  addDefaultTokenizers,
  Parser,
  parsers,
  TokenKind,
  NodeParser,
  Node,
} from '../index';

const KEYWORD = 'variable';

type ValueNode = parsers.BooleanValueNode | parsers.NullValueNode | parsers.StringValueNode | parsers.NumberValueNode;

export interface VariableNode extends Node {
  readonly kind: 'VARIABLE';
  readonly name: parsers.NameNode;
  readonly value: ValueNode;
}
const getValue = (parser: Parser): ValueNode => {
  const token = parser.lexer.token;
  parser.advanceLexer();
  switch (token.kind) {
    case TokenKind.NUMBER:
      return parser.node<parsers.NumberValueNode>(token, {
        kind: 'NUMBER',
        value: token.value,
      });
    case TokenKind.STRING:
    case TokenKind.STRING_EXPR:
      return parser.node<parsers.StringValueNode>(token, {
        kind: 'STRING',
        value: token.value,
      });
    case TokenKind.NAME:
      switch (token.value) {
        case 'true':
          return parser.node<parsers.BooleanValueNode>(token, {
            kind: 'BOOLEAN',
            value: true,
          });
        case 'false':
          return parser.node<parsers.BooleanValueNode>(token, {
            kind: 'BOOLEAN',
            value: false,
          });
        case 'null':
          return parser.node<parsers.NullValueNode>(token, { kind: 'NULL' });
        default:
          throw parser.unexpected(token);
      }
  }
  throw parser.unexpected(token);
}

export const variableParser: NodeParser<VariableNode> = {
  kind: 'VARIABLE',
  trigger: {
    kind: TokenKind.NAME,
    keyword: KEYWORD,
  },
  parse(parser: Parser): VariableNode | undefined {
    const token = parser.lexer.token;
    parser.expectKeyword(KEYWORD);
    const name = parser.expectParse(parsers.nameParser);
    parser.expectToken(TokenKind.EQUALS);
    const value = getValue(parser);
    parser.expectToken(TokenKind.SEMICOLON);

    return parser.node<VariableNode>(token, {
      kind: 'VARIABLE',
      name,
      value,
    });
  },

}

const expectVariable = (name: string, type: string, value?: string | boolean) => {
  return expect.objectContaining({
    kind: 'VARIABLE',
    name: expect.objectContaining({
      kind: 'NAME',
      value: name,
    }),
    value: expect.objectContaining({
      kind: type,
      ...(typeof value === 'undefined' ? {} : { value }),
    }),
  });
}

describe('Variable test suite', () => {
  it.each([
    { schema: 'variable BOOLEAN_TRUE = true;', name: 'BOOLEAN_TRUE', type: 'BOOLEAN', value: true },
    { schema: 'variable BOOLEAN_FALSE = false;', name: 'BOOLEAN_FALSE', type: 'BOOLEAN', value: false },
    { schema: 'variable NULL = null;', name: 'NULL', type: 'NULL' },
    { schema: 'variable STRING = "string";', name: 'STRING', type: 'STRING', value: 'string' },
    { schema: 'variable INT_NUMBER = 12;', name: 'INT_NUMBER', type: 'NUMBER', value: '12' },
    { schema: 'variable FLOAT_NUMBER = 12.3;', name: 'FLOAT_NUMBER', type: 'NUMBER', value: '12.3' },
  ])('Should parse variable $variable of type $type', ({ schema, name, type, value }) => {
    const lexer = new Lexer(schema);
    addDefaultTokenizers(lexer);

    const parser = new Parser(lexer, { enableLocation: true });
    parser.add(variableParser);

    const result = parser.parseSchema();
    expect(result).toMatchObject({
      kind: 'SCHEMA',
      definitions: [
        expectVariable(name, type, value)
      ]
    });
  });
});