Bump graphql_parser -> 1.1.0
This commit is contained in:
parent
5b0062b657
commit
84e204267b
18 changed files with 166 additions and 220 deletions
7
.idea/runConfigurations/tests_in_value_test_dart.xml
Normal file
7
.idea/runConfigurations/tests_in_value_test_dart.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="tests in value_test.dart" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/graphql_parser/test/value_test.dart" />
|
||||
<option name="testRunnerOptions" value="-j4" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
9
graphql_parser/CHANGELOG.md
Normal file
9
graphql_parser/CHANGELOG.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# 1.1.0
|
||||
* Removed `GraphQLVisitor`.
|
||||
* Enable parsing operations without an explicit
|
||||
name.
|
||||
* Parse `null`.
|
||||
* Completely ignore commas.
|
||||
* Ignore Unicode BOM, as per the spec.
|
||||
* Parse object values.
|
||||
* Parse enum values.
|
|
@ -1,8 +1,8 @@
|
|||
# graphql_parser
|
||||
[![Pub](https://img.shields.io/pub/v/graphql_parser.svg)](https://pub.dartlang.org/packages/graphql_parser)
|
||||
[![build status](https://travis-ci.org/thosakwe/graphql_parser.svg)](https://travis-ci.org/thosakwe/graphql_parser)
|
||||
[![build status](https://travis-ci.org/angel-dart/graphql.svg)](https://travis-ci.org/angel-dart/graphql)
|
||||
|
||||
Parses GraphQL queries and schemas. Also includes a `GraphQLVisitor` class.
|
||||
Parses GraphQL queries and schemas.
|
||||
|
||||
*This library is merely a parser/visitor*. Any sort of actual GraphQL API functionality must be implemented by you,
|
||||
or by a third-party package.
|
||||
|
@ -24,6 +24,9 @@ dependencies:
|
|||
The AST featured in this library is directly based off this ANTLR4 grammar created by Joseph T. McBride:
|
||||
https://github.com/antlr/grammars-v4/blob/master/graphql/GraphQL.g4
|
||||
|
||||
It has since been updated to reflect upon the grammar in the official GraphQL
|
||||
specification.
|
||||
|
||||
```dart
|
||||
import 'package:graphql_parser/graphql_parser.dart';
|
||||
|
||||
|
@ -31,6 +34,10 @@ doSomething(String text) {
|
|||
var tokens = scan(text);
|
||||
var parser = new Parser(tokens);
|
||||
|
||||
if (parser.errors.isNotEmpty) {
|
||||
// Handle errors...
|
||||
}
|
||||
|
||||
// Parse the GraphQL document using recursive descent
|
||||
var doc = parser.parseDocument();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:graphql_parser/graphql_parser.dart';
|
||||
|
||||
final String INPUT = '''
|
||||
final String text = '''
|
||||
{
|
||||
project(name: "GraphQL") {
|
||||
tagline
|
||||
|
@ -10,7 +10,7 @@ final String INPUT = '''
|
|||
.trim();
|
||||
|
||||
main() {
|
||||
var tokens = scan(INPUT);
|
||||
var tokens = scan(text);
|
||||
var parser = new Parser(tokens);
|
||||
var doc = parser.parseDocument();
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
import 'package:graphql_parser/graphql_parser.dart';
|
||||
import 'package:graphql_parser/visitor.dart';
|
||||
|
||||
const String QUERY = '''
|
||||
{
|
||||
foo,
|
||||
baz: bar
|
||||
}
|
||||
''';
|
||||
|
||||
const Map<String, dynamic> DATA = const {
|
||||
'foo': 'hello',
|
||||
'bar': 'world',
|
||||
'quux': 'extraneous'
|
||||
};
|
||||
|
||||
main() {
|
||||
// Highly-simplified querying example...
|
||||
var result = new MapQuerier(DATA).execute(QUERY);
|
||||
print(result); // { foo: hello, baz: world }
|
||||
print(result['foo']); // hello
|
||||
print(result['baz']); // world
|
||||
}
|
||||
|
||||
class MapQuerier extends GraphQLVisitor {
|
||||
final Map<String, dynamic> data;
|
||||
final Map<String, dynamic> result = {};
|
||||
|
||||
MapQuerier(this.data);
|
||||
|
||||
Map<String, dynamic> execute(String query) {
|
||||
var doc = new Parser(scan(query)).parseDocument();
|
||||
visitDocument(doc);
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
visitField(FieldContext ctx) {
|
||||
String realName, alias;
|
||||
if (ctx.fieldName.alias == null)
|
||||
realName = alias = ctx.fieldName.name;
|
||||
else {
|
||||
realName = ctx.fieldName.alias.name;
|
||||
alias = ctx.fieldName.alias.alias;
|
||||
}
|
||||
|
||||
// Set output field...
|
||||
result[alias] = data[realName];
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
import 'graphql_parser.dart';
|
||||
|
||||
class GraphQLVisitor {
|
||||
visitDocument(DocumentContext ctx) {
|
||||
ctx.definitions.forEach(visitDefinition);
|
||||
}
|
||||
|
||||
visitDefinition(DefinitionContext ctx) {
|
||||
if (ctx is OperationDefinitionContext)
|
||||
visitOperationDefinition(ctx);
|
||||
else if (ctx is FragmentDefinitionContext) visitFragmentDefinition(ctx);
|
||||
}
|
||||
|
||||
visitOperationDefinition(OperationDefinitionContext ctx) {
|
||||
if (ctx.variableDefinitions != null)
|
||||
visitVariableDefinitions(ctx.variableDefinitions);
|
||||
ctx.directives.forEach(visitDirective);
|
||||
visitSelectionSet(ctx.selectionSet);
|
||||
}
|
||||
|
||||
visitFragmentDefinition(FragmentDefinitionContext ctx) {
|
||||
visitTypeCondition(ctx.typeCondition);
|
||||
ctx.directives.forEach(visitDirective);
|
||||
visitSelectionSet(ctx.selectionSet);
|
||||
}
|
||||
|
||||
visitSelectionSet(SelectionSetContext ctx) {
|
||||
ctx.selections.forEach(visitSelection);
|
||||
}
|
||||
|
||||
visitSelection(SelectionContext ctx) {
|
||||
if (ctx.field != null) visitField(ctx.field);
|
||||
if (ctx.fragmentSpread != null) visitFragmentSpread(ctx.fragmentSpread);
|
||||
if (ctx.inlineFragment != null) visitInlineFragment(ctx.inlineFragment);
|
||||
}
|
||||
|
||||
visitInlineFragment(InlineFragmentContext ctx) {
|
||||
visitTypeCondition(ctx.typeCondition);
|
||||
ctx.directives.forEach(visitDirective);
|
||||
visitSelectionSet(ctx.selectionSet);
|
||||
}
|
||||
|
||||
visitFragmentSpread(FragmentSpreadContext ctx) {
|
||||
ctx.directives.forEach(visitDirective);
|
||||
}
|
||||
|
||||
visitField(FieldContext ctx) {
|
||||
visitFieldName(ctx.fieldName);
|
||||
ctx.arguments.forEach(visitArgument);
|
||||
ctx.directives.forEach(visitDirective);
|
||||
if (ctx.selectionSet != null) ;
|
||||
visitSelectionSet(ctx.selectionSet);
|
||||
}
|
||||
|
||||
visitFieldName(FieldNameContext ctx) {
|
||||
if (ctx.alias != null) visitAlias(ctx.alias);
|
||||
}
|
||||
|
||||
visitAlias(AliasContext ctx) {}
|
||||
|
||||
visitDirective(DirectiveContext ctx) {
|
||||
if (ctx.valueOrVariable != null) visitValueOrVariable(ctx.valueOrVariable);
|
||||
if (ctx.argument != null) visitArgument(ctx.argument);
|
||||
}
|
||||
|
||||
visitArgument(ArgumentContext ctx) {
|
||||
visitValueOrVariable(ctx.valueOrVariable);
|
||||
}
|
||||
|
||||
visitVariableDefinitions(VariableDefinitionsContext ctx) {
|
||||
ctx.variableDefinitions.forEach(visitVariableDefinition);
|
||||
}
|
||||
|
||||
visitVariableDefinition(VariableDefinitionContext ctx) {
|
||||
visitVariable(ctx.variable);
|
||||
visitType(ctx.type);
|
||||
if (ctx.defaultValue != null) visitDefaultValue(ctx.defaultValue);
|
||||
}
|
||||
|
||||
visitVariable(VariableContext ctx) {}
|
||||
|
||||
visitValueOrVariable(ValueOrVariableContext ctx) {
|
||||
if (ctx.variable != null) visitVariable(ctx.variable);
|
||||
if (ctx.value != null) visitValue(ctx.value);
|
||||
}
|
||||
|
||||
visitDefaultValue(DefaultValueContext ctx) {
|
||||
visitValue(ctx.value);
|
||||
}
|
||||
|
||||
visitValue(ValueContext ctx) {
|
||||
if (ctx is StringValueContext)
|
||||
visitStringValue(ctx);
|
||||
else if (ctx is NumberValueContext)
|
||||
visitNumberValue(ctx);
|
||||
else if (ctx is BooleanValueContext)
|
||||
visitBooleanValue(ctx);
|
||||
else if (ctx is ListValueContext) visitArrayValue(ctx);
|
||||
}
|
||||
|
||||
visitStringValue(StringValueContext ctx) {}
|
||||
|
||||
visitBooleanValue(BooleanValueContext ctx) {}
|
||||
|
||||
visitNumberValue(NumberValueContext ctx) {}
|
||||
|
||||
visitArrayValue(ListValueContext ctx) {
|
||||
ctx.values.forEach(visitValue);
|
||||
}
|
||||
|
||||
visitTypeCondition(TypeConditionContext ctx) {
|
||||
visitTypeName(ctx.typeName);
|
||||
}
|
||||
|
||||
visitType(TypeContext ctx) {
|
||||
if (ctx.typeName != null) visitTypeName(ctx.typeName);
|
||||
if (ctx.listType != null) visitListType(ctx.listType);
|
||||
}
|
||||
|
||||
visitListType(ListTypeContext ctx) {
|
||||
visitType(ctx.type);
|
||||
}
|
||||
|
||||
visitTypeName(TypeNameContext ctx) {}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
name: graphql_parser
|
||||
version: 1.0.0+1
|
||||
version: 1.1.0
|
||||
description: Parses GraphQL queries and schemas.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/thosakwe/graphql_parser
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:graphql_parser/graphql_parser.dart';
|
||||
import 'package:matcher/matcher.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common.dart';
|
||||
|
||||
main() {
|
||||
|
@ -10,13 +11,24 @@ main() {
|
|||
});
|
||||
|
||||
test('exception', () {
|
||||
expect(() => parseArgument('foo'), throwsSyntaxError);
|
||||
expect(() => parseArgument('foo:'), throwsSyntaxError);
|
||||
expect(() => parseArgumentList(r'(foo: $bar'), throwsSyntaxError);
|
||||
var isSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseArgument();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse argument');
|
||||
|
||||
var isSyntaxErrorOnArguments = predicate((x) {
|
||||
var parser = parse(x.toString())..parseArguments();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse arguments');
|
||||
|
||||
expect('foo', isSyntaxError);
|
||||
expect('foo:', isSyntaxError);
|
||||
expect(r'(foo: $bar', isSyntaxErrorOnArguments);
|
||||
});
|
||||
}
|
||||
|
||||
ArgumentContext parseArgument(String text) => parse(text).parseArgument();
|
||||
|
||||
List<ArgumentContext> parseArgumentList(String text) =>
|
||||
parse(text).parseArguments();
|
||||
|
||||
|
|
|
@ -3,6 +3,3 @@ import 'package:matcher/matcher.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
Parser parse(String text) => new Parser(scan(text));
|
||||
|
||||
final Matcher throwsSyntaxError =
|
||||
throwsA(predicate((x) => x is SyntaxError, 'is a syntax error'));
|
||||
|
|
|
@ -21,11 +21,16 @@ main() {
|
|||
});
|
||||
|
||||
test('exceptions', () {
|
||||
expect(() => parseDirective('@'), throwsSyntaxError);
|
||||
expect(() => parseDirective('@foo:'), throwsSyntaxError);
|
||||
expect(() => parseDirective('@foo ('), throwsSyntaxError);
|
||||
expect(() => parseDirective('@foo (bar: 2'), throwsSyntaxError);
|
||||
expect(() => parseDirective('@foo ()'), throwsSyntaxError);
|
||||
var isSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseDirective();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse directive');
|
||||
|
||||
expect('@', isSyntaxError);
|
||||
expect('@foo:', isSyntaxError);
|
||||
expect('@foo (', isSyntaxError);
|
||||
expect('@foo (bar: 2', isSyntaxError);
|
||||
expect('@foo (', isSyntaxError);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:graphql_parser/graphql_parser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common.dart';
|
||||
import 'directive_test.dart';
|
||||
import 'field_test.dart';
|
||||
|
@ -34,14 +35,15 @@ main() {
|
|||
});
|
||||
|
||||
test('fragment exceptions', () {
|
||||
expect(
|
||||
() => parse('fragment').parseFragmentDefinition(), throwsSyntaxError);
|
||||
expect(() => parse('fragment foo').parseFragmentDefinition(),
|
||||
throwsSyntaxError);
|
||||
expect(() => parse('fragment foo on').parseFragmentDefinition(),
|
||||
throwsSyntaxError);
|
||||
expect(() => parse('fragment foo on bar').parseFragmentDefinition(),
|
||||
throwsSyntaxError);
|
||||
var throwsSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseFragmentDefinition();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse fragment definition');
|
||||
|
||||
expect('fragment', throwsSyntaxError);
|
||||
expect('fragment foo', throwsSyntaxError);
|
||||
expect('fragment foo on', throwsSyntaxError);
|
||||
expect('fragment foo on bar', throwsSyntaxError);
|
||||
});
|
||||
|
||||
group('operation', () {
|
||||
|
@ -59,6 +61,20 @@ main() {
|
|||
]));
|
||||
});
|
||||
|
||||
test('mutation', () {
|
||||
var op = parse('mutation {foo, bar: baz}').parseOperationDefinition();
|
||||
expect(op.variableDefinitions, isNull);
|
||||
expect(op.isQuery, isFalse);
|
||||
expect(op.isMutation, isTrue);
|
||||
expect(op.name, isNull);
|
||||
expect(
|
||||
op.selectionSet,
|
||||
isSelectionSet([
|
||||
isField(fieldName: isFieldName('foo')),
|
||||
isField(fieldName: isFieldName('bar', alias: 'baz'))
|
||||
]));
|
||||
});
|
||||
|
||||
test('with operation type', () {
|
||||
var doc =
|
||||
parse(r'query foo ($one: [int] = 2) @foo @bar: 2 {foo, bar: baz}')
|
||||
|
@ -91,10 +107,13 @@ main() {
|
|||
});
|
||||
|
||||
test('exceptions', () {
|
||||
expect(
|
||||
() => parse('query').parseOperationDefinition(), throwsSyntaxError);
|
||||
expect(() => parse('query foo()').parseOperationDefinition(),
|
||||
throwsSyntaxError);
|
||||
var throwsSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseOperationDefinition();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse operation definition');
|
||||
|
||||
expect('query', throwsSyntaxError);
|
||||
expect('query foo()', throwsSyntaxError);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import 'package:graphql_parser/graphql_parser.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'common.dart';
|
||||
|
||||
import 'argument_test.dart';
|
||||
import 'common.dart';
|
||||
import 'directive_test.dart';
|
||||
import 'fragment_spread_test.dart';
|
||||
import 'selection_set_test.dart';
|
||||
|
@ -16,7 +17,12 @@ main() {
|
|||
expect('foo: bar', isFieldName('foo', alias: 'bar'));
|
||||
});
|
||||
test('exceptions', () {
|
||||
expect(() => parseFieldName('foo:'), throwsSyntaxError);
|
||||
var throwsSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseFieldName();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse field name');
|
||||
|
||||
expect('foo:', throwsSyntaxError);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -31,10 +31,14 @@ main() {
|
|||
});
|
||||
|
||||
test('exceptions', () {
|
||||
expect(() => parseInlineFragment('... on foo'), throwsSyntaxError);
|
||||
expect(() => parseInlineFragment('... on foo @bar'), throwsSyntaxError);
|
||||
expect(() => parseInlineFragment('... on'), throwsSyntaxError);
|
||||
expect(() => parseInlineFragment('...'), throwsSyntaxError);
|
||||
var throwsSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseInlineFragment();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse inline fragment');
|
||||
expect('... on foo', throwsSyntaxError);
|
||||
expect('... on foo @bar', throwsSyntaxError);
|
||||
expect('... on', throwsSyntaxError);
|
||||
expect('...', throwsSyntaxError);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,12 @@ main() {
|
|||
});
|
||||
|
||||
test('exceptions', () {
|
||||
expect(() => parseSelectionSet('{foo,bar,baz'), throwsSyntaxError);
|
||||
var throwsSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseSelectionSet();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse selection set');
|
||||
|
||||
expect('{foo,bar,baz', throwsSyntaxError);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -35,8 +35,13 @@ main() {
|
|||
});
|
||||
|
||||
test('exceptions', () {
|
||||
expect(() => parseType('[foo'), throwsSyntaxError);
|
||||
expect(() => parseType('['), throwsSyntaxError);
|
||||
var throwsSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseType();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse type');
|
||||
|
||||
expect('[foo', throwsSyntaxError);
|
||||
expect('[', throwsSyntaxError);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'dart:math' as math;
|
||||
|
||||
import 'package:graphql_parser/graphql_parser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common.dart';
|
||||
|
||||
main() {
|
||||
|
@ -40,12 +42,38 @@ main() {
|
|||
expect('"\\u0123\\u4567"', isValue('\u0123\u4567'));
|
||||
});
|
||||
|
||||
test('block string', () {
|
||||
expect('""""""', isValue(''));
|
||||
expect('"""abc"""', isValue('abc'));
|
||||
expect('"""\n\n\nabc\n\n\n"""', isValue('abc'));
|
||||
});
|
||||
|
||||
test('object', () {
|
||||
expect('{}', isValue(<String, dynamic>{}));
|
||||
expect('{a: 2}', isValue(<String, dynamic>{'a': 2}));
|
||||
expect('{a: 2, b: "c"}', isValue(<String, dynamic>{'a': 2, 'b': 'c'}));
|
||||
});
|
||||
|
||||
test('null', () {
|
||||
expect('null', isValue(null));
|
||||
});
|
||||
|
||||
test('enum', () {
|
||||
expect('FOO_BAR', isValue('FOO_BAR'));
|
||||
});
|
||||
|
||||
test('exceptions', () {
|
||||
expect(() => parseValue('[1'), throwsSyntaxError);
|
||||
var throwsSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseValue();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse value');
|
||||
|
||||
expect('[1', throwsSyntaxError);
|
||||
});
|
||||
}
|
||||
|
||||
ValueContext parseValue(String text) => parse(text).parseValue();
|
||||
|
||||
Matcher isValue(value) => new _IsValue(value);
|
||||
|
||||
class _IsValue extends Matcher {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:graphql_parser/graphql_parser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common.dart';
|
||||
import 'type_test.dart';
|
||||
import 'value_test.dart';
|
||||
|
@ -18,11 +19,21 @@ main() {
|
|||
});
|
||||
|
||||
test('exceptions', () {
|
||||
expect(() => parseVariableDefinition(r'$foo'), throwsSyntaxError);
|
||||
expect(() => parseVariableDefinition(r'$foo:'), throwsSyntaxError);
|
||||
expect(() => parseVariableDefinition(r'$foo: int ='), throwsSyntaxError);
|
||||
expect(() => parse(r'($foo: int = 2').parseVariableDefinitions(),
|
||||
throwsSyntaxError);
|
||||
var throwsSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseVariableDefinition();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse variable definition');
|
||||
|
||||
var throwsSyntaxErrorOnDefinitions = predicate((x) {
|
||||
var parser = parse(x.toString())..parseVariableDefinitions();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse variable definitions');
|
||||
|
||||
expect(r'$foo', throwsSyntaxError);
|
||||
expect(r'$foo:', throwsSyntaxError);
|
||||
expect(r'$foo: int =', throwsSyntaxError);
|
||||
|
||||
expect(r'($foo: int = 2', throwsSyntaxErrorOnDefinitions);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import 'common.dart';
|
||||
|
||||
main() {
|
||||
|
@ -12,7 +13,12 @@ main() {
|
|||
});
|
||||
|
||||
test('exceptions', () {
|
||||
expect(() => parse(r'$').parseVariable(), throwsSyntaxError);
|
||||
var throwsSyntaxError = predicate((x) {
|
||||
var parser = parse(x.toString())..parseVariable();
|
||||
return parser.errors.isNotEmpty;
|
||||
}, 'fails to parse variable');
|
||||
|
||||
expect(r'$', throwsSyntaxError);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue