diff --git a/.idea/runConfigurations/objects_in_equality_test_dart.xml b/.idea/runConfigurations/objects_in_equality_test_dart.xml new file mode 100644 index 00000000..47dbf8c5 --- /dev/null +++ b/.idea/runConfigurations/objects_in_equality_test_dart.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/tests_in_graphql_schema.xml b/.idea/runConfigurations/tests_in_graphql_schema.xml new file mode 100644 index 00000000..b3d4f5e6 --- /dev/null +++ b/.idea/runConfigurations/tests_in_graphql_schema.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/graphql_schema/lib/src/field.dart b/graphql_schema/lib/src/field.dart index 053b4604..4132e68b 100644 --- a/graphql_schema/lib/src/field.dart +++ b/graphql_schema/lib/src/field.dart @@ -17,8 +17,8 @@ class GraphQLObjectField { this.deprecationReason, this.description}) { assert(type != null, 'GraphQL fields must specify a `type`.'); - assert( - resolve != null, 'GraphQL fields must specify a `resolve` callback.'); +// assert( +// resolve != null, 'GraphQL fields must specify a `resolve` callback.'); this.inputs.addAll(arguments ?? []); } diff --git a/graphql_schema/lib/src/gen.dart b/graphql_schema/lib/src/gen.dart index d8044e65..8a2474e5 100644 --- a/graphql_schema/lib/src/gen.dart +++ b/graphql_schema/lib/src/gen.dart @@ -24,7 +24,7 @@ GraphQLObjectField field( String deprecationReason, String description}) { return new GraphQLObjectField(name, type, arguments: inputs, - resolve: resolve ?? (_, __) => null, + resolve: resolve, description: description, deprecationReason: deprecationReason); } diff --git a/graphql_schema/lib/src/union.dart b/graphql_schema/lib/src/union.dart index 129f985b..5253ac27 100644 --- a/graphql_schema/lib/src/union.dart +++ b/graphql_schema/lib/src/union.dart @@ -34,7 +34,9 @@ class GraphQLUnionType Map serialize(Map value) { for (var type in possibleTypes) { try { - return type.serialize(value); + if (type.validate('@root', value).successful) { + return type.serialize(value); + } } catch (_) {} } diff --git a/graphql_schema/test/equality_test.dart b/graphql_schema/test/equality_test.dart new file mode 100644 index 00000000..a5fb2e70 --- /dev/null +++ b/graphql_schema/test/equality_test.dart @@ -0,0 +1,113 @@ +import 'package:graphql_schema/graphql_schema.dart'; +import 'package:test/test.dart'; + +/// Note: this doesn't test for scalar types, which are final, and therefore use built-in equality. +void main() { + group('equality', () { + test('enums', () { + expect(enumTypeFromStrings('A', ['B', 'C']), + enumTypeFromStrings('A', ['B', 'C'])); + expect(enumTypeFromStrings('A', ['B', 'C']), + isNot(enumTypeFromStrings('B', ['B', 'C']))); + }); + + test('objects', () { + expect( + objectType('B', fields: [ + field('b', graphQLString.nonNullable()), + ]), + objectType('B', fields: [ + field('b', graphQLString.nonNullable()), + ]), + ); + + expect( + objectType('B', fields: [ + field('b', graphQLString.nonNullable()), + ]), + isNot(objectType('BD', fields: [ + field('b', graphQLString.nonNullable()), + ])), + ); + + expect( + objectType('B', fields: [ + field('b', graphQLString.nonNullable()), + ]), + isNot(objectType('B', fields: [ + field('ba', graphQLString.nonNullable()), + ])), + ); + + expect( + objectType('B', fields: [ + field('b', graphQLString.nonNullable()), + ]), + isNot(objectType('B', fields: [ + field('a', graphQLFloat.nonNullable()), + ])), + ); + }); + + test('input type', () {}); + + test('union type', () { + expect( + new GraphQLUnionType('A', [ + objectType('B', fields: [ + field('b', graphQLString.nonNullable()), + ]), + objectType('C', fields: [ + field('c', graphQLString.nonNullable()), + ]), + ]), + new GraphQLUnionType('A', [ + objectType('B', fields: [ + field('b', graphQLString.nonNullable()), + ]), + objectType('C', fields: [ + field('c', graphQLString.nonNullable()), + ]), + ]), + ); + + expect( + new GraphQLUnionType('A', [ + objectType('B', fields: [ + field('b', graphQLString.nonNullable()), + ]), + objectType('C', fields: [ + field('c', graphQLString.nonNullable()), + ]), + ]), + isNot(new GraphQLUnionType('AA', [ + objectType('B', fields: [ + field('b', graphQLString.nonNullable()), + ]), + objectType('C', fields: [ + field('c', graphQLString.nonNullable()), + ]), + ])), + ); + + expect( + new GraphQLUnionType('A', [ + objectType('BB', fields: [ + field('b', graphQLString.nonNullable()), + ]), + objectType('C', fields: [ + field('c', graphQLString.nonNullable()), + ]), + ]), + isNot(new GraphQLUnionType('AA', [ + objectType('BDD', fields: [ + field('b', graphQLString.nonNullable()), + ]), + objectType('C', fields: [ + field('c', graphQLString.nonNullable()), + ]), + ])), + ); + }); + }); +} diff --git a/graphql_schema/test/inheritance_test.dart b/graphql_schema/test/inheritance_test.dart new file mode 100644 index 00000000..0157bcfa --- /dev/null +++ b/graphql_schema/test/inheritance_test.dart @@ -0,0 +1,67 @@ +import 'package:graphql_schema/graphql_schema.dart'; +import 'package:test/test.dart'; + +void main() { + group('interface', () { + var a = objectType( + 'A', + isInterface: true, + fields: [ + field('text', graphQLString.nonNullable()), + ], + ); + + var b = objectType( + 'B', + isInterface: true, + interfaces: [a], + fields: [ + field('text', graphQLString.nonNullable()), + ], + ); + + var c = objectType( + 'C', + isInterface: true, + interfaces: [b], + fields: [ + field('text', graphQLString.nonNullable()), + ], + ); + + test('child implements parent', () { + expect(b.isImplementationOf(a), true); + expect(c.isImplementationOf(b), true); + }); + + test('parent does not implement child', () { + expect(a.isImplementationOf(b), false); + }); + + test('child interfaces contains parent', () { + expect(b.interfaces, contains(a)); + expect(c.interfaces, contains(b)); + }); + + test('parent possibleTypes contains child', () { + expect(a.possibleTypes, contains(b)); + expect(b.possibleTypes, contains(c)); + }); + + test('grandchild implements grandparent', () { + expect(c.isImplementationOf(a), true); + }); + + test('grandparent does not implement grandchild', () { + expect(a.isImplementationOf(c), false); + }); + + test('grandchild interfaces contains grandparent', () { + expect(c.interfaces, contains(a)); + }); + + test('grandparent possibleTypes contains grandchild', () { + expect(a.possibleTypes, contains(c)); + }); + }); +} \ No newline at end of file diff --git a/graphql_schema/test/serialize_test.dart b/graphql_schema/test/serialize_test.dart index c3c0efe7..5725df02 100644 --- a/graphql_schema/test/serialize_test.dart +++ b/graphql_schema/test/serialize_test.dart @@ -1,11 +1,36 @@ import 'package:graphql_schema/graphql_schema.dart'; import 'package:test/test.dart'; + import 'common.dart'; main() { - test('scalar', () { - expect(graphQLString.serialize('a'), 'a'); + test('int', () { + expect(graphQLInt.serialize(23), 23); + }); + test('float', () { + expect(graphQLFloat.serialize(23.0), 23.0); + }); + + test('bool', () { + expect(graphQLBoolean.serialize(true), true); + }); + + test('string', () { + expect(graphQLString.serialize('a'), 'a'); + }); + + test('enum', () { + var response = enumTypeFromStrings('Response', ['YES', 'NO']); + expect(response.serialize('YES'), 'YES'); + }); + + test('enum only serializes correct values', () { + var response = enumTypeFromStrings('Response', ['YES', 'NO']); + expect(() => response.serialize('MAYBE'), throwsStateError); + }); + + test('date', () { var now = new DateTime.now(); expect(graphQLDate.serialize(now), now.toIso8601String()); }); @@ -19,6 +44,21 @@ main() { [today.toIso8601String(), tomorrow.toIso8601String()]); }); + group('input object', () { + var type = inputObjectType( + 'Foo', + inputFields: [ + inputField('bar', graphQLString.nonNullable()), + inputField('baz', graphQLFloat.nonNullable()), + ], + ); + + test('serializes valid input', () { + expect( + type.serialize({'bar': 'a', 'baz': 2.0}), {'bar': 'a', 'baz': 2.0}); + }); + }); + test('object', () { var catchDate = new DateTime.now(); @@ -28,6 +68,37 @@ main() { {'species': 'Pikachu', 'catch_date': catchDate.toIso8601String()}); }); + test('union type lets any of its types serialize', () { + var typeType = enumTypeFromStrings('Type', [ + 'FIRE', + 'WATER', + 'GRASS', + ]); + + var pokemonType = objectType('Pokémon', fields: [ + field( + 'name', + graphQLString.nonNullable(), + ), + field( + 'type', + typeType, + ), + ]); + + var digimonType = objectType( + 'Digimon', + fields: [ + field('size', graphQLFloat.nonNullable()), + ], + ); + + var u = new GraphQLUnionType('Monster', [pokemonType, digimonType]); + + expect(u.serialize({'size': 10.0}), {'size': 10.0}); + expect(u.serialize({'name': 'Charmander', 'type': 'FIRE'}), {'name': 'Charmander', 'type': 'FIRE'}); + }); + test('nested object', () { var pikachuDate = new DateTime.now(), charizardDate = pikachuDate.subtract(new Duration(days: 10)); diff --git a/graphql_schema/test/validation_test.dart b/graphql_schema/test/validation_test.dart index f8810d2d..d858a75a 100644 --- a/graphql_schema/test/validation_test.dart +++ b/graphql_schema/test/validation_test.dart @@ -27,6 +27,10 @@ void main() { var throwsATypeError = throwsA(predicate((x) => x is TypeError, 'is a type error')); + test('object accepts valid input', () { + expect({'name': 'Charmander', 'type': 'FIRE'}, isValidPokemon); + }); + test('mismatched scalar type', () { expect(() => pokemonType.validate('@root', {'name': 24}), throwsATypeError); }); @@ -50,4 +54,49 @@ void main() { test('enum rejects invalid value', () { expect(typeType.validate('@root', 'POISON').successful, false); }); + + group('union type', () { + var digimonType = objectType( + 'Digimon', + fields: [ + field('size', graphQLFloat.nonNullable()), + ], + ); + + var u = new GraphQLUnionType('Monster', [pokemonType, digimonType]); + + test('any of its types returns valid', () { + expect(u.validate('@root', {'size': 32.0}).successful, true); + expect( + u.validate( + '@root', {'name': 'Charmander', 'type': 'FIRE'}).successful, + true); + }); + }); + + group('input object', () { + var type = inputObjectType( + 'Foo', + inputFields: [ + inputField('bar', graphQLString.nonNullable()), + inputField('baz', graphQLFloat.nonNullable()), + ], + ); + + test('accept valid input', () { + expect(type.validate('@root', {'bar': 'a', 'baz': 2.0}).value, + {'bar': 'a', 'baz': 2.0}); + }); + + test('error on missing non-null fields', () { + expect(type.validate('@root', {'bar': 'a'}).successful, false); + }); + + test('error on unrecognized fields', () { + expect( + type.validate( + '@root', {'bar': 'a', 'baz': 2.0, 'franken': 'stein'}).successful, + false); + }); + }); }