sling_gql_test 1.1.1
sling_gql_test: ^1.1.1 copied to clipboard
Test helpers for sling_gql: an in-memory GraphQL server that answers the client's own documents from plain Dart data, and pumpUntilSettled for widget tests.
example/sling_gql_test_example.dart
// A widget test with sling_gql_test: the server answers from plain maps,
// `pumpUntilSettled` waits for the round trip, `server.requests` is the log.
//
// In a real app the `Query` / `User` / `Mutation` classes at the bottom are
// generated by `sling_gql_gen`; they are written out here so the example runs
// on its own (`flutter test example/`).
//
// type Query { me: User! user(id: ID!): User }
// type User { id: ID! name: String! age: Int }
// type Mutation { rename(id: ID!, name: String!): User! }
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:sling_gql/sling_gql.dart';
import 'package:sling_gql_test/sling_gql_test.dart';
void main() {
testWidgets('shows the viewer, renames them', (tester) async {
final ada = {'__typename': 'User', 'id': '1', 'name': 'Ada', 'age': 36};
final server = MockGraphQLServer(
query: {'me': ada, 'user': (args) => args['id'] == '1' ? ada : null},
mutation: {'rename': (args) => ada..['name'] = args['name']},
);
final client = server.client(Query.root);
await tester.pumpWidget(
SlingScope<Query>(
client: client,
schema: slingSchema,
child: const Directionality(
textDirection: TextDirection.ltr,
child: ProfileName(),
),
),
);
expect(find.text('loading'), findsOneWidget);
await tester.pumpUntilSettled(client);
expect(find.text('Ada'), findsOneWidget);
expect(server.requests, hasLength(1));
expect(server.lastRequest.selects('me.name'), isTrue);
// The fields read in the body are the mutation's selection.
final renamed = client.mutate(
(m) => m.rename(id: '1', name: 'Grace')?.name,
);
await tester.pumpUntilSettled(client);
expect(await renamed, 'Grace');
expect(find.text('Grace'), findsOneWidget, reason: 'User:1 was notified');
expect(server.lastRequest.type, 'mutation');
});
}
class ProfileName extends StatelessWidget {
const ProfileName({super.key});
@override
Widget build(BuildContext context) => QueryBuilder<Query>(
builder: (context, query, state) => Text(query.me.name ?? 'loading'),
);
}
// --- What sling_gql_gen would generate ---------------------------------------
class Query extends Accessor {
Query(super.recorder, super.selection, super.path);
Query.root(Recorder r) : super(r, r.root, const []);
User get me => object('me', User.new, keyed: true)!;
User? user({required String id}) =>
object('user', User.new, args: {'id': Arg('ID!', id)}, lookup: 'User');
}
class User extends Accessor {
User(super.recorder, super.selection, super.path);
String? get id => scalar<String>('id');
String? get name => scalar<String>('name');
set name(String? v) => write('name', v);
int? get age => scalar<int>('age');
}
class Mutation extends Accessor {
Mutation(super.recorder, super.selection, super.path);
Mutation.root(Recorder r) : super(r, r.root, const []);
User? rename({required String id, required String name}) => object(
'rename',
User.new,
args: {'id': Arg('ID!', id), 'name': Arg('String!', name)},
keyed: true,
);
}
extension SlingMutations on SlingClient<Query> {
Future<T> mutate<T>(T Function(Mutation m) body) =>
mutateWith(Mutation.root, body);
}
const slingSchema = SlingSchema<Query, Mutation>(
query: Query.root,
mutation: Mutation.root,
);