Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: confirm sign in tests #1176

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import 'envs/auth_with_username.dart' as auth_with_username;
/// All envs modeled on:
/// https://github.com/aws-amplify/amplify-ui/blob/main/examples/angular/src/pages/ui/components/authenticator/
const environmentsByConfiguration = {
'ui/components/authenticator/confirm-sign-up': 'auth-with-email',
'ui/components/authenticator/sign-up-with-email': 'auth-with-email',
'ui/components/authenticator/sign-up-with-phone': 'auth-with-phone',
'ui/components/authenticator/sign-up-with-username': 'auth-with-username',
Expand All @@ -35,6 +36,7 @@ const environmentsByConfiguration = {
'ui/components/authenticator/sign-in-with-email-lambda':
'auth-with-email-lambda-signup-trigger',
'ui/components/authenticator/reset-password': 'auth-with-username',
'ui/components/authenticator/verify-user': 'auth-with-email',
};

const environments = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import 'package:amplify_api/amplify_api.dart';
import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import 'config.dart';
import 'pages/confirm_sign_up_page.dart';
import 'pages/sign_in_page.dart';
import 'pages/sign_up_page.dart';
import 'pages/test_utils.dart';
import 'utils/data_utils.dart';
import 'utils/mock_data.dart';

// This test follows the Amplify UI feature "confirm-sign-up"
// https://github.com/aws-amplify/amplify-ui/blob/main/packages/e2e/features/ui/components/authenticator/confirm-sign-up.feature

void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized()
as IntegrationTestWidgetsFlutterBinding;
// resolves issue on iOS. See: https://github.com/flutter/flutter/issues/89651
binding.deferFirstFrame();

final authenticator = MaterialApp(
home: Authenticator(
child: const Scaffold(
body: Center(
child: SignOutButton(),
),
),
),
);

group('confirm-sign-up', () {
// Given I'm running the example "ui/components/authenticator/confirm-sign-up"
setUpAll(() async {
await loadConfiguration(
'ui/components/authenticator/confirm-sign-up',
additionalConfigs: [AmplifyAPI()],
);
});

// Scenario: Confirm a new username & password with an invalid code
testWidgets(
'Confirm a new username & password with an invalid code',
(tester) async {
SignUpPage signUpPage = SignUpPage(tester: tester);
ConfirmSignUpPage confirmSignUpPage = ConfirmSignUpPage(tester: tester);
SignInPage signInPage = SignInPage(tester: tester);

await loadAuthenticator(tester: tester, authenticator: authenticator);

var username = generateEmail();
var password = generatePassword();

await signInPage.navigateToSignUp();

// When I type a new "email"
await signUpPage.enterUsername(username);

// And I type my password
await signUpPage.enterPassword(password);

// And I confirm my password
await signUpPage.enterPasswordConfirmation(password);

// And I click the "Create Account" button
await signUpPage.submitSignUp();

// And I see "Confirmation Code"
confirmSignUpPage.expectConfirmationCodeIsPresent();

// And I type an invalid confirmation code
await confirmSignUpPage.enterCode('123456');

// And I click the "Confirm" button
await confirmSignUpPage.submitConfirmSignUp();

// Then I see "Username/client id combination not found."
confirmSignUpPage.expectInvalidVerificationCode();
},
);

// Scenario: Confirm a new username & password with a valid code
testWidgets(
'Confirm a new username & password with a valid code',
(tester) async {
SignUpPage signUpPage = SignUpPage(tester: tester);
ConfirmSignUpPage confirmSignUpPage = ConfirmSignUpPage(tester: tester);
SignInPage signInPage = SignInPage(tester: tester);

await loadAuthenticator(tester: tester, authenticator: authenticator);

var username = generateEmail();
var password = generatePassword();

await signInPage.navigateToSignUp();

// When I type a new "email"
await signUpPage.enterUsername(username);

// And I type my password
await signUpPage.enterPassword(password);

// And I confirm my password
await signUpPage.enterPasswordConfirmation(password);

await subscribeToOTPCode(
onSubscriptionEstablished: () async {
// And I click the "Create Account" button
await signUpPage.submitSignUp();

// And I see "Confirmation Code"
confirmSignUpPage.expectConfirmationCodeIsPresent();
},
onCodeRecieved: (String code) async {
// And I type a valid confirmation code
await confirmSignUpPage.enterCode(code);

// And I click the "Confirm" button
await confirmSignUpPage.submitConfirmSignUp();

// Then I see "Sign out"
signInPage.expectAuthenticated();
},
);
},
);

// Scenario: User is already confirmed and then clicks Resend Code
testWidgets(
'User is already confirmed and then clicks Resend Code',
(tester) async {
// TODO: clarify requirements
},
skip: true,
);

// Scenario: Supports "One-Time Code"
testWidgets(
'Supports "One-Time Code"',
(tester) async {
// TODO: clarify requirements
},
skip: true,
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* permissions and limitations under the License.
*/

import 'dart:io';

import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:amplify_authenticator/src/keys.dart';
import 'package:amplify_authenticator/src/screens/authenticator_screen.dart';
Expand Down Expand Up @@ -105,6 +107,17 @@ abstract class AuthenticatorPage {
expect(expectCombinationNotFound, findsOneWidget);
}

/// Then I see Invalid verification code
Future<void> expectInvalidVerificationCode() async {
if (Platform.isAndroid) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It surprised me that the messages were different on iOS and Android. Seems like this is a bug. Not specific to the authenticator though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expectError('Confirmation code entered is not correct.');
} else if (Platform.isIOS) {
expectError('Invalid verification code provided, please try again.');
} else {
throw Exception('Unsupprted platform');
}
}

Future<void> selectCountryCode() async {
expect(countrySelectField, findsOneWidget);
await tester.tap(countrySelectField);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@
* permissions and limitations under the License.
*/

import 'dart:async';
import 'dart:convert';

import 'package:amplify_api/amplify_api.dart';
import 'package:amplify_flutter/amplify.dart';
import 'package:flutter_test/flutter_test.dart';

import 'types/admin_create_user_response.dart';
import 'types/confirmation_code_response.dart';
import 'types/delete_user_response.dart';

const deleteDocument = '''mutation DeleteUser(\$Username: String!) {
Expand Down Expand Up @@ -98,3 +102,62 @@ Future<AdminCreateUserResponse?> adminCreateUser(
}
});
}

Future<ConfirmationCodeTestRun> subscribeToOTPCode({
Future<void> Function()? onSubscriptionEstablished,
Future<void> Function(String code)? onCodeRecieved,
}) async {
Completer<ConfirmationCodeTestRun> codeCompleter =
Completer<ConfirmationCodeTestRun>();
String subscriptionDocument = '''subscription MyMutation {
onCreateConfirmSignUpTestRun {
id
}
}''';

String queryDocument = '''query GetCode(\$id: ID!) {
getConfirmSignUpTestRun(id: \$id) {
id
currentCode
}
}''';

Completer<void> subscriptionCompleter = Completer<void>();

final Stream<GraphQLResponse<String>> operation = Amplify.API.subscribe(
GraphQLRequest<String>(document: subscriptionDocument),
onEstablished: () {
subscriptionCompleter.complete();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you tested and this worked? When I tried this approach it hung.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It hung some (most?) of the time but did seem to work some of the time. I have await subscriptionCompleter.future commented out and instead have await Future.delayed(const Duration(seconds: 1), () {}); as a temp solution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alrighty, thanks

},
);

// TODO: subscriptionCompleter is not working correctly. Future.delayed is a temp solution
// await subscriptionCompleter.future;
await Future.delayed(const Duration(seconds: 1), () {});

operation.listen((event) async {
Map<dynamic, dynamic> parsedMap = jsonDecode(event.data);
String codeId = parsedMap['onCreateConfirmSignUpTestRun']['id'];
var codeQuery = Amplify.API.query(
request: GraphQLRequest<String>(
document: queryDocument,
variables: <String, String>{'id': codeId},
));

var response = await codeQuery.response;

ConfirmationCodeTestRun result =
ConfirmationCodeTestRun.fromJson(response.data);

if (onCodeRecieved != null) {
await onCodeRecieved(result.code!);
}

codeCompleter.complete(result);
});

if (onSubscriptionEstablished != null) {
await onSubscriptionEstablished();
}
return codeCompleter.future;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* permissions and limitations under the License.
*/

// This test follows the Amplify UI feature "sign-in-with-username"
// This test follows the Amplify UI feature "verify-user"
// https://github.com/aws-amplify/amplify-ui/blob/main/packages/e2e/features/ui/components/authenticator/verify-user.feature

import 'package:amplify_api/amplify_api.dart';
Expand Down Expand Up @@ -45,14 +45,18 @@ void main() {
);

group('verify-user', () {
// Given I'm running the example "ui/components/authenticator/sign-up-with-email"
// Given I'm running the example "ui/components/authenticator/verify-user"
setUpAll(() async {
await loadConfiguration(
'ui/components/authenticator/sign-up-with-email',
'ui/components/authenticator/verify-user',
additionalConfigs: [AmplifyAPI()],
);
});

tearDown(() async {
await Amplify.Auth.signOut();
});

// Scenario: Redirect to "Verify" page
testWidgets('Redirect to "Verify" page', (tester) async {
SignInPage signInPage = SignInPage(tester: tester);
Expand Down