I'm trying to test a payment gateway method with stripe and I have this error when i try to click the button which it said "[log] {success: false, error: o is not defined}", here is my code. I'm not sure what or how to solve this but I would really need some help on this one. Also I included with the screenshot of the problem
Main.dart
import 'dart:convert';
import 'dart:developer';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:http/http.dart' as http;
Future <void> main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: FirebaseOptions(apiKey: "AIzaSyD7Uhhiv85NCcDjoTEQ-K7T5qU4bNXci8M",
appId: "XXX", messagingSenderId: "XXX",
projectId: "itt632-56973",
));
Stripe.publishableKey = "pk_test_51LHIjvJ5bt4B4CmJ3xTspeufDqv4VojZlmhuBaVvjhUoDbJaoKziiUkFZPOTl3a4CbVcfdmnJKosItZiiweRYfvF00aOI2xKIS";
Stripe.instance.applySettings();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Demo(),
);
}
}
// 'https://us-central1-itt632-56973.cloudfunctions.net/stripePaymentIntentRequest'),
class Demo extends StatelessWidget {
const Demo({Key? key}) : super(key: key);
Future<void> initPayment(
{required String email,
required double amount,
required BuildContext context}) async {
try {
// 1. Create a payment intent on the server
final response = await http.post(
Uri.parse(
'https://us-central1-itt632-56973.cloudfunctions.net/stripePaymentIntentRequest'),
body: {
'email': email,
'amount': amount.toString(),
});
final jsonResponse = jsonDecode(response.body);
log(jsonResponse.toString());
// 2. Initialize the payment sheet
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: jsonResponse['paymentIntent'],
merchantDisplayName: 'Grocery Flutter course',
customerId: jsonResponse['customer'],
customerEphemeralKeySecret: jsonResponse['ephemeralKey'],
testEnv: true,
merchantCountryCode: 'SG',
));
await Stripe.instance.presentPaymentSheet();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Payment is successful'),
),
);
} catch (errorr) {
if (errorr is StripeException) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('An error occured ${errorr.error.localizedMessage}'),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('An error occured $errorr'),
),
);
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: const Text('Pay 50\$'),
onPressed: () async {
await initPayment(
amount: 50.0, context: context, email: 'JEFRILUQMAN#test.com');
},
)),
);
}
}
Here is the index.js function for the stripe
const functions = require("firebase-functions");
const stripe = require("stripe")("sk_test_51LHIjvJ5bt4B4CmJclvzNcSaSPICuutFIjmg3GZb1sv6fySDehLAhvHOW2hl1W7EWXYM5oqfHkHZyJDeB36rIJpf00ksfQJnWa");
exports.stripePaymentIntentRequest = functions.https.onRequest(async (req, res) => {
try {
let customerId;
//Gets the customer who's email id matches the one sent by the client
const customerList = await stripe.customers.list({
email: req.body.email,
limit: 1
});
//Checks the if the customer exists, if not creates a new customer
if (customerList.data.length !== 0) {
customerId = customerList.data[0].id;
}
else {
const customer = await stripe.customers.create({
email: req.body.email
});
customerId = customer.data.id;
}
//Creates a temporary secret key linked with the customer
const ephemeralKey = await stripe.ephemeralKeys.create(
{ customer: customerId },
{ apiVersion: '2020-08-27' }
);
//Creates a new payment intent with amount passed in from the client
const paymentIntent = await stripe.paymentIntents.create({
amount: parseInt(req.body.amount),
currency: 'usd',
customer: customerId,
})
res.status(200).send({
paymentIntent: paymentIntent.client_secret,
ephemeralKey: ephemeralKey.secret,
customer: customerId,
success: true,
})
} catch (error) {
res.status(404).send({ success: false, error: error.message })
}
});
Screenshot
Screenshot2
Related
auth_service.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:zomato_clone/common/widgets/bottom_bar.dart';
import 'package:zomato_clone/constants/error_handling.dart';
import 'package:zomato_clone/constants/global_variables.dart';
import 'package:zomato_clone/constants/utils.dart';
import 'package:zomato_clone/features/admin/screens/admin_screen.dart';
import 'package:zomato_clone/models/user.dart';
import 'package:zomato_clone/providers/user_provider.dart';
class AuthService {
// sign up user
void signUpUser({
required BuildContext context,
required String email,
required String password,
required String name,
}) async {
try {
User user = User(
id: '',
name: name,
password: password,
email: email,
address: '',
type: '',
token: '',
cart: [],
);
http.Response res = await http.post(
Uri.parse('$uri/api/signup'),
body: user.toJson(),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
);
httpErrorHandling(
response: res,
context: context,
onSuccess: () {
showSnackBar(
context,
'Account created! Login with the same credentials!',
);
},
);
} catch (e) {
showSnackBar(context, e.toString());
}
}
// sign in user
void signInUser({
required BuildContext context,
required String email,
required String password,
}) async {
try {
http.Response res = await http.post(
Uri.parse('$uri/api/signin'),
body: jsonEncode({
'email': email,
'password': password,
}),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
);
httpErrorHandling(
response: res,
context: context,
onSuccess: () async {
SharedPreferences prefs = await SharedPreferences.getInstance();
Provider.of<UserProvider>(context, listen: false).setUser(res.body);
await prefs.setString(
'x-auth-token', jsonDecode(res.body)['token']);
Navigator.pushNamedAndRemoveUntil(
context,
Provider.of<UserProvider>(context, listen: false).user.type ==
'user'
? BottomBar.routeName
: AdminScreen.routeName,
(route) => false);
});
} catch (e) {
showSnackBar(context, e.toString());
}
}
// get user data
void getUserData(
BuildContext context,
) async {
try {
var userProvider = Provider.of<UserProvider>(context, listen: false);
SharedPreferences prefs = await SharedPreferences.getInstance();
String? token = prefs.getString('x-auth-token');
if (token == null) {
prefs.setString('x-auth-token', '');
}
var tokenRes = await http.post(
Uri.parse('$uri/tokenIsValid'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'x-auth-token': token!
},
);
var response = jsonDecode(tokenRes.body);
if (response == true) {
http.Response userRes = await http.get(
Uri.parse('$uri/'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'x-auth-token': token
},
);
userProvider.setUser(userRes.body);
}
} catch (e) {
showSnackBar(context, e.toString());
}
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:provider/provider.dart';
import 'package:zomato_clone/common/widgets/bottom_bar.dart';
import 'package:zomato_clone/features/admin/screens/admin_screen.dart';
import 'package:zomato_clone/features/auth/screens/auth_screen.dart';
import 'package:zomato_clone/features/auth/services/auth_service.dart';
import 'package:zomato_clone/providers/user_provider.dart';
import 'package:zomato_clone/router.dart';
import 'package:zomato_clone/splash_screen.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();
runApp(MultiProvider(providers: [
ChangeNotifierProvider(
create: (context) => UserProvider(),
)
], child: MyApp()));
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final AuthService authService = AuthService();
#override
void initState() {
super.initState();
authService.getUserData(context);
setState(() {});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
useMaterial3: true,
primarySwatch: Colors.blue,
),
onGenerateRoute: (settings) => generateRoute(settings),
home: Provider.of<UserProvider>(context).user.email.isNotEmpty
? Provider.of<UserProvider>(context).user.type == 'user'
? const BottomBar()
: AdminScreen()
: const AuthScreen());
}
}
when i restart my app in flutter i first land on login page then go back to home page please tell me how to solve this problem.
when i tried a splashscreen it solve th problem when network is slow it goes back to loginpage then redirect to homePage help me solve this problem.
you should make getUserData to be Future and make a separate function to get the user date and make a local variable to handle loading state while fetching the data from the server and then use this variable later in home attribute
bool _loading = true ;
void prepareData()async{
await authService.getUserData(context);
_loading =false;
setState(() {});
}
#override
void initState() {
super.initState();
prepareData();
}
home: _loading?Center(child:CircularProgressIndicator()):Provider.of<UserProvider>(context).user.email.isNotEmpty
? Provider.of<UserProvider>(context).user.type == 'user'
? const BottomBar()
: AdminScreen()
: const AuthScreen());
I have been working on this Getx library for the first time and i don't know why but the initial screen function is not working for some reason. It's not showing any bug in the stack trace so i guess it's gotta be something else. has anyone any idea? Debug shows no error or warnings.
here's the code
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tutorial_two/screens/screen_home.dart';
import 'package:tutorial_two/screens/screen_login.dart';
import 'package:velocity_x/velocity_x.dart';
class AuthController extends GetxController {
static AuthController instance =
Get.find(); // auth controller instance; will be called in other widgets
// user instance
late Rx<User?> _user;
// get user data like name, email, password etc
FirebaseAuth auth = FirebaseAuth.instance; // firebase auth instance
#override
void onReady() {
super.onReady();
_user = Rx<User?>(auth.currentUser); // getting current user
_user.bindStream(
auth.userChanges(),
); //notifies app about user login and logout
ever(_user,
_initialScreen); //this funnction will make sure user gets to correct screen
}
_initialScreen(User? user) {
if (user == null) {
Get.offAll(() => const LoginScreen());
} else {
Get.offAll(() => const HomeScreen());
}
}
Future<void> register(
BuildContext context, String email, username, password) async {
try {
await auth.createUserWithEmailAndPassword(
email: email, password: password);
//await Future.delayed(const Duration(seconds: 2));
Future.delayed(
const Duration(
seconds: 1,
), () {
VxToast.show(context,
msg: "Registration Successful",
bgColor: Colors.green.shade100,
textColor: Colors.green.shade500,
textSize: 14,
position: VxToastPosition.center);
});
} catch (e) {
VxToast.show(context,
msg: "Error: $e",
bgColor: Colors.red.shade100,
textColor: Colors.red.shade500,
textSize: 14,
position: VxToastPosition.center);
}
}
Future<void> login(BuildContext context, String email, password) async {
try {
await auth.signInWithEmailAndPassword(email: email, password: password);
//await Future.delayed(const Duration(seconds: 2));
Future.delayed(
const Duration(
seconds: 1,
), () {
VxToast.show(context,
msg: "Login Successful",
bgColor: Colors.green.shade100,
textColor: Colors.green.shade500,
textSize: 14,
position: VxToastPosition.center);
});
} catch (e) {
VxToast.show(context,
msg: "Error: $e",
bgColor: Colors.red.shade100,
textColor: Colors.red.shade500,
textSize: 14,
position: VxToastPosition.center);
}
}
Future<void> logOut(BuildContext context) async {
try {
await auth.signOut();
} catch (e) {
VxToast.show(context,
msg: "Error: $e",
bgColor: Colors.red.shade100,
textColor: Colors.red.shade500,
textSize: 14,
position: VxToastPosition.center);
}
}
}
here's the main code
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tutorial_two/screens/screen_home.dart';
import 'package:tutorial_two/screens/screen_login.dart';
import 'package:tutorial_two/screens/screen_signup.dart';
import 'package:tutorial_two/screens/screen_splash.dart';
import 'package:tutorial_two/utils/auth_controller.dart';
import 'package:tutorial_two/utils/routes.dart';
import 'package:tutorial_two/widgets/themes.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
Firebase.initializeApp().then((value) {
Get.put(AuthController());
});
runApp(const TutorialTwo());
}
class TutorialTwo extends StatelessWidget {
const TutorialTwo({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
themeMode: ThemeMode.system,
theme: MyTheme.lightTheme(context),
darkTheme: MyTheme.darkTheme(context),
debugShowCheckedModeBanner: false,
home: const SplashScreen(),
routes: {
MyRoutes.homeRoute: (context) => const HomeScreen(),
MyRoutes.loginRoute: (context) => const LoginScreen(),
MyRoutes.signupRoute: (context) => const SignupScreen(),
},
);
}
}
To use Get navigation management, you must use GetMaterialApp instead of MaterialApp,
//Replace the following line
return MaterialApp(
//with this one
return GetMaterialApp(
I am struggling to to show my custom error messages during login/sign up. The error messages that are showing up are the default ones coming from Firebase. I need to display the custom error messages in the code below depending on the error code. Please assist...
I'm not sure what's still missing. Iv'e tried everything but still not winning
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import '../widgets/auth/auth_form.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AuthScreen extends StatefulWidget {
const AuthScreen({Key? key}) : super(key: key);
static const routeName = '/auth_screen';
#override
_AuthScreenState createState() => _AuthScreenState();
}
class _AuthScreenState extends State<AuthScreen> {
final _auth = FirebaseAuth.instance;
var _isLoading = false;
void _submitAuthForm(
String email,
String password,
String displayName,
String phoneNumber,
bool isLogin,
BuildContext ctx,
) async {
UserCredential authResult;
try {
setState(() {
_isLoading = true;
});
if (isLogin) {
authResult = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
} else {
authResult = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
await FirebaseFirestore.instance
.collection('userProfile')
.doc(authResult.user!.uid)
.set(
{
'displayName': displayName,
'email': email,
'phoneNumber': phoneNumber,
'uid': authResult.user!.uid,
},
);
}
} on FirebaseAuthException catch (error) {
var message = 'An error has occured.'; // default message
switch (error.code) {
case 'EMAIL_NOT_FOUND':
message = 'There is no user account with the email address provided.';
break;
case 'EMAIL_EXISTS':
message = 'The email address is already in use by another account.';
break;
case 'INVALID_PASSWORD':
message = 'Invalid password. Please enter correct password.';
break;
}
setState(() {
_isLoading = false;
});
showDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: const Text('An error occured'),
content: Text(
error.message.toString(),
),
actions: [
TextButton(
child: const Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
);
}
}
I think your problem lies in the fact that while you have defined a message variable, you are not using it inside the dialog. You are instead using error.message.toString().
showDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: const Text('An error occured'),
content: Text(
error.message.toString(), /// <-------------- HERE
),
actions: [
TextButton(
child: const Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
);
Replace that with message and it should work.
This is show type list dynamic is not a subtype of type map string dynamic" Now when i run emulator is show "type list dynamic is not a subtype of type map string dynamic" on my emulator how can i fix it ?
Now i want to create program about find api id form my emulator use text field and button when i put some number and click button is will show data or title of that ID , but I'm just beginner so if anyone thing my code is correct or not or you have some recommend pls tell me
My JSON
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response =
await http.get(Uri.parse('http://192.168.176.131:3000/api/courses/'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception1
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
Album({
required this.userId,
required this.id,
required this.title,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Future<Album> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
Widget build(BuildContext context) {
TextEditingController searchController = new TextEditingController();
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Column(children: [
Container(
child: Row(children: [
Expanded(
flex: 2,
child: TextField(
controller: searchController
)
),
Expanded(
flex: 1,
child: FlatButton(
onPressed: () {
print(
"this is the text to search for => ${searchController
.text}");
},
child: Text("Search"),
)
)
],)
),
Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
],)
),
);
}
}
JSON Code
const Joi = require('joi');
const express = require('express');
const app = express();
app.use(express.json());
const courses = [
{ userId:1, id:1, title:'course 1'},
{ userId:2, id:2, title:'course 2'},
{ userId:3, id:3, title:'course 3'},
];
app.get('/', (req,res) => {
res.send('Hello world');
});
app.get('/api/courses',(req, res) => {
res.send(courses);
});
app.post('/api/courses', (req, res) => {
const { error } = validateCourse(req.body);
if (error)return res.status(400).send(error.details[0].message);
const course = {
id: courses.length +1,
name: req.body.name
};
courses.push(course);
res.send(course);
});
app.put('/api/courses/:id', (req, res) => {
const course = courses.find(c => c.id === parseInt(req.params.id));
if (!course) return res.status(404).send('The course with the given ID was not found.');
const {error} = validateCourse(req.body); //result.eror
if (error) return res.status(400).send(error.details[0].message);
//Update course
course.name = req.body.name;
res.send(course);
//Return the updated course
});
app.delete('/api/courses/:id', (req, res) => {
const course = courses.find(c => c.id === parseInt(req.params.id));
if (!course) return res.status(404).send('The course with the given ID was not found.');
const index = courses.indexOf(course);
courses.splice(index, 1);
res.send(course);
});
function validateCourse(course){
const schema = {
name: Joi.string().min(3).required()
};
return Joi.validate(course, schema);
}
app.get('/api/courses/:id', (req, res) => {
const course = courses.find(c => c.id === parseInt(req.params.id));
if (!course) return res.status(404).send('The course with the given ID was not found.');
res.send(course);
});
const port = process.env.PORT || 3000;
//app.listen(port, () => console.log(Listening on port ${port} ..));
app.listen(3000, '0.0.0.0');
You should cast the returned value from the jsonDecode to a Map <String, dynamic>.
jsonDecode returns a dynamic value, not a Map <String, dynamic> as the Album.fromJson expects.
I'm trying to create widget tests for a flutter application using GraphQL.
What I want to do is to test the behaviour of the app which depends on the result of a GraphQL Mutation on a user action.
This is a very simple example of the app:
class FirstScreen extends StatelessWidget {
#override
Widget return Container(
child: Mutation(
options: myMutationOptions,
onCompleted: (dynamic result) {
final bool myBool = result['bool'] as bool;
if (myBool) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => SecondScreen()));
} else {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ThirdScreen()));
}
},
builder: (RunMutation runMutation, QueryResult queryResult) {
return FlatButton(
child: Text('Button'),
onPressed: () async {
await runMutation(myParameters).networkResult;
},
);
},
),
);
}
What I would like to do is to mock the result of the mutation so in my widget tests, I can test that the button redirects to the SecondScreen or ThirdScreen depending of the result myBool.
How can I do that ?
I finally managed to successfully mock a GraphQL Mutation. Here is how I did it, it is inspired from #Gpack's comment but I had to add some modifications and details to it.
To make it easy to use I created a wrapper widget GraphQLMutationMocker :
class MockClient extends Mock implements Client {
MockClient({
this.mockedResult,
this.mockedStatus = 200,
});
final Map<String, dynamic> mockedResult;
final int mockedStatus;
#override
Future<StreamedResponse> send(BaseRequest request) {
return Future<StreamedResponse>.value(
StreamedResponse(
Stream.value(utf8.encode(jsonEncode(mockedResult))),
mockedStatus,
),
);
}
}
class GraphQLMutationMocker extends StatelessWidget {
const GraphQLMutationMocker({
#required this.child,
this.mockedResult = const {},
this.mockedStatus = 200,
this.url = 'http://url',
this.storagePrefix = 'test',
});
final Widget child;
final Map<String, dynamic> mockedResult;
final int mockedStatus;
final String url;
final String storagePrefix;
#override
Widget build(BuildContext context) {
final mockClient = MockClient(
mockedResult: mockedResult,
mockedStatus: mockedStatus,
);
final httpLink = HttpLink(
uri: url,
httpClient: mockClient,
);
final graphQLClient = ValueNotifier(
GraphQLClient(
cache: InMemoryCache(storagePrefix: storagePrefix),
link: httpLink,
),
);
return GraphQLProvider(
client: graphQLClient,
child: child,
);
}
}
Then it was pretty easy to write the tests
group('Test mutation', () {
testWidgets('It should redirect to SecondScreen', (WidgetTester tester) async {
await tester.pumpWidget(GraphQLMutationMocker(
mockedResult: <String, dynamic>{
'data': {
'bool': true,
},
},
child: FirstScreen(),
));
// Click on button
await tester.tap(find.text('Button'));
await tester.pumpAndSettle();
// Check I'm on the right screen
expect(find.byType(SecondScreen), findsOneWidget);
expect(find.byType(ThirdScreen), findsNothing);
});
testWidgets('It should redirect to ThirdScreen', (WidgetTester tester) async {
await tester.pumpWidget(GraphQLMutationMocker(
mockedResult: <String, dynamic>{
'data': {
'bool': false,
},
},
child: FirstScreen(),
));
// Click on button
await tester.tap(find.text('Button'));
await tester.pumpAndSettle();
// Check I'm on the right screen
expect(find.byType(SecondScreen), findsNothing);
expect(find.byType(ThirdScreen), findsOneWidget);
});
})
Create a mock of http.Client like in the flutter docs
In your test, wrap your FirstScreen in a GraphqlProvider like so:
class MockHttpClient extends Mock implements Client {}
group('Test mutation', () {
MockHttpClient mockHttpClient;
HttpLink httpLink;
ValueNotifier<GraphQLClient> client;
setUp(() async {
mockHttpClient = MockHttpClient();
httpLink = HttpLink(
uri: 'https://unused/graphql',
httpClient: mockHttpClient,
);
client = ValueNotifier(
GraphQLClient(
cache: InMemoryCache(storagePrefix: 'test'),
link: httpLink,
),
);
});
testWidgets('redirects to SecondScreen', (WidgetTester tester) async {
when(client.send(captureAny)).thenAnswer(/* ... */);
await tester.pumpWidget(GraphQLProvider(
client: client,
child: FirstScreen(),
));
// Click on button
verify(mockHttpClient.send(any)).called(1);
// etc.
});
})