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

Lab consistency changes, Fixes AB#3043333 #2517

Merged
merged 22 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2558d08
add changes to allow retries in various places of lab api client:
fadidurah Oct 2, 2024
a265375
Merge branch 'dev' into fadi/lab-cons
fadidurah Oct 8, 2024
e8ab16b
Merge branch 'dev' of https://github.com/AzureAD/microsoft-authentica…
fadidurah Oct 11, 2024
e99501e
default retry for tests, update ui handling for Teams, Outlook
fadidurah Oct 11, 2024
5ec29bd
Merge branch 'fadi/lab-cons' of https://github.com/AzureAD/microsoft-…
fadidurah Oct 11, 2024
b72e954
Update LabApiUtilities/src/main/com/microsoft/identity/labapi/utiliti…
fadidurah Oct 11, 2024
943df47
Update labapi/src/main/java/com/microsoft/identity/internal/test/laba…
fadidurah Oct 11, 2024
0601fec
Update labapi/src/main/java/com/microsoft/identity/internal/test/laba…
fadidurah Oct 11, 2024
786964f
Update labapi/src/main/java/com/microsoft/identity/internal/test/laba…
fadidurah Oct 11, 2024
1d688a2
Update labapi/src/main/java/com/microsoft/identity/internal/test/laba…
fadidurah Oct 11, 2024
7c9a27d
Merge branch 'fadi/lab-cons' of https://github.com/AzureAD/microsoft-…
fadidurah Oct 11, 2024
7ce3097
comments
fadidurah Oct 11, 2024
35c03c1
comment
fadidurah Oct 11, 2024
af5c19e
Merge branch 'dev' of https://github.com/AzureAD/microsoft-authentica…
fadidurah Oct 15, 2024
a1928b7
more tweaks
fadidurah Oct 17, 2024
5d963e3
remove apiClient retry
fadidurah Oct 17, 2024
a73128c
revert more changes
fadidurah Oct 17, 2024
f3786c0
add retry to fetch user
fadidurah Oct 17, 2024
5bb89bc
add retry to fetch user
fadidurah Oct 17, 2024
140ae40
add retry to fetch user
fadidurah Oct 17, 2024
854081d
tweak teams ui handle
fadidurah Oct 18, 2024
3430bb0
teams
fadidurah Oct 21, 2024
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 @@ -34,6 +34,8 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.concurrent.TimeUnit;

import lombok.NonNull;

Expand All @@ -45,6 +47,8 @@ public class LabApiAuthenticationClient implements IAccessTokenSupplier {
private final static String AUTHORITY = "https://login.microsoftonline.com/" + TENANT_ID;
private final static String KEYSTORE_TYPE = "Windows-MY";
private final static String KEYSTORE_PROVIDER = "SunMSCAPI";
private final int DEFAULT_ACCESS_TOKEN_RETRIES = 2;
private final int ATTEMPT_RETRY_WAIT = 3;
private final String mLabCredential;
private final String mLabCertPassword;
private final String mScope;
Expand Down Expand Up @@ -72,6 +76,51 @@ public LabApiAuthenticationClient(@NonNull final String labSecret, final String

@Override
public String getAccessToken() throws LabApiException {
return getAccessToken(DEFAULT_ACCESS_TOKEN_RETRIES);
}

/**
* Attempt to acquire an access token. Accepts a parameter to denote number of retries
* @param retries how many times to attempt acquire access token before returning a failure.
* @return an access token for Lab API
* @throws LabApiException exception given back by Lab API
*/
public String getAccessToken(final int retries) throws LabApiException {
fadidurah marked this conversation as resolved.
Show resolved Hide resolved

// Do this in a loop, if we get an exception or null result, try again
for (int i = 1; i <= retries; i++) {
System.out.printf(Locale.ENGLISH, "getAccessToken attempt #%d%n", i);

try {
final String result = getAccessTokenInternal();
if (result != null) {
return result;
}
} catch (final LabApiException labApiException) {
if (i < retries) {
System.out.printf(
Locale.ENGLISH,
"getAccessToken attempt #%d%n failed: %s", i,
labApiException
);

try {
Thread.sleep(TimeUnit.SECONDS.toMillis(ATTEMPT_RETRY_WAIT));
} catch (final InterruptedException e) {
e.printStackTrace();
}
} else {
// last attempt, just throw the exception back
throw labApiException;
}
}
}

// Retries exhausted, no error, but still a null result
return null;
}

private String getAccessTokenInternal() throws LabApiException {
final IConfidentialAuthClient confidentialAuthClient = new Msal4jAuthClient();
final TokenParameters tokenParameters = TokenParameters.builder()
.clientId(mClientId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public class LabClient implements ILabClient {

private final LabApiAuthenticationClient mLabApiAuthenticationClient;
private final long PASSWORD_RESET_WAIT_DURATION = TimeUnit.SECONDS.toMillis(65);
private final long LAB_API_RETRY_WAIT = TimeUnit.SECONDS.toMillis(2);
private final long LAB_API_RETRY_WAIT = TimeUnit.SECONDS.toMillis(5);

/**
* Temp users API provided by Lab team can often take more than 10 seconds to return...hence, we
Expand All @@ -70,6 +70,26 @@ public class LabClient implements ILabClient {

@Override
public ILabAccount getLabAccount(@NonNull final LabQuery labQuery) throws LabApiException {
// Adding a second attempt here, api sometimes fails to fetch the user.
try {
return getLabAccountInternalWithQuery(labQuery);
} catch (final Exception e){
// Seems new Lab API may fail temp user creation, without throwing a lab exception
// (we may get a non-error response, with a null temp user field)
// So, we will make this exception handling generic, and retry in all failure cases

// Wait for a bit
try {
Thread.sleep(LAB_API_RETRY_WAIT);
} catch (final InterruptedException e2) {
e2.printStackTrace();
}

return getLabAccountInternalWithQuery(labQuery);
}
}

private ILabAccount getLabAccountInternalWithQuery(@NonNull final LabQuery labQuery) throws LabApiException {
final List<ConfigInfo> configInfos = fetchConfigsFromLab(labQuery);
// for each query, lab actually returns a list of accounts..all of which fit the criteria..
// usually we only need one such account, and hence over here we are just picking the first
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.*;
import java.util.Map.Entry;
Expand Down Expand Up @@ -828,8 +826,8 @@ public <T> ApiResponse<T> execute(Call call, Type returnType) throws ApiExceptio
Response response = call.execute();
T data = handleResponse(response, returnType);
return new ApiResponse<T>(response.code(), response.headers().toMultimap(), data);
} catch (IOException e) {
throw new ApiException(e);
} catch (final IOException exception) {
throw new ApiException(exception);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
import androidx.annotation.Nullable;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiObjectNotFoundException;
import androidx.test.uiautomator.UiSelector;

import com.microsoft.identity.client.ui.automation.broker.ITestBroker;
import com.microsoft.identity.client.ui.automation.browser.IBrowser;
import com.microsoft.identity.client.ui.automation.installer.LocalApkInstaller;
import com.microsoft.identity.client.ui.automation.interaction.microsoftsts.MicrosoftStsPromptHandler;
import com.microsoft.identity.client.ui.automation.interaction.microsoftsts.MicrosoftStsPromptHandlerParameters;
import com.microsoft.identity.client.ui.automation.logging.Logger;
import com.microsoft.identity.client.ui.automation.utils.CommonUtils;
import com.microsoft.identity.client.ui.automation.utils.UiAutomatorUtils;

import org.junit.Assert;
Expand Down Expand Up @@ -118,12 +120,14 @@ public void signInSilentlyWithSingleAccountFragment(@Nullable final IBrowser bro
@NonNull final ITestBroker broker,
final boolean shouldHandleBrowserFirstRun) {
Logger.i(TAG, "Signing in into Azure Sample App with Single Account Mode Fragment..");
// Click Sign In in Single Account Fragment
UiAutomatorUtils.handleButtonClick("com.azuresamples.msalandroidapp:id/btn_signIn");

if (broker == null && browser != null && shouldHandleBrowserFirstRun) {
// handle browser first run as applicable
((IApp) browser).handleFirstRun();
try {
// Click Sign In in Single Account Fragment
UiAutomatorUtils.obtainUiObjectWithUiSelector(new UiSelector().resourceId(
"com.azuresamples.msalandroidapp:id/btn_signIn").clickable(true),
CommonUtils.FIND_UI_ELEMENT_TIMEOUT).click();
} catch (UiObjectNotFoundException exception) {
throw new AssertionError(exception);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,20 @@ public void addFirstAccount(@NonNull final String username,
@Override
public void onAccountAdded() {
Logger.i(TAG, "Handling UI after account is added on the App..");

// Sometime Outlook asks user to choose account type after entering password. I'm not sure what
// causes this UI to pop up, but adding a safe check here to choose Office account
UiAutomatorUtils.handleButtonClickSafely("com.microsoft.office.outlook:id/btn_add_account_o365_rest", CommonUtils.FIND_UI_ELEMENT_TIMEOUT_SHORT);

// Make sure we are on add another account (shows up after an account is added)
final UiObject addAnotherAccountScreen = UiAutomatorUtils.obtainUiObjectWithText("Add another account");

Assert.assertTrue(
"Add another account screen doesn't appear in Outlook.",
addAnotherAccountScreen.waitForExists(FIND_UI_ELEMENT_TIMEOUT_LONG)
"Add another account screen doesn't appear in Outlook.", addAnotherAccountScreen.exists()
);

// click may be later
UiAutomatorUtils.handleButtonClick("com.microsoft.office.outlook:id/bottom_flow_navigation_start_button");

}

@Override
Expand All @@ -116,6 +120,9 @@ public void addAnotherAccount(final String username,
@Override
public void confirmAccount(@NonNull final String username) {
Logger.i(TAG, "Confirming account with supplied username is signed in..");

handleIntroDialogueAfterSignIn();

// Click the account drawer
UiAutomatorUtils.handleButtonClick("com.microsoft.office.outlook:id/account_button", FIND_UI_ELEMENT_TIMEOUT_LONG);

Expand All @@ -127,6 +134,10 @@ public void confirmAccount(@NonNull final String username) {
);
}

private void handleIntroDialogueAfterSignIn() {
UiAutomatorUtils.handleButtonClickSafely("com.microsoft.office.outlook:id/btn_primary_button", CommonUtils.FIND_UI_ELEMENT_TIMEOUT_SHORT);
}

private void signIn(@NonNull final String username,
@NonNull final String password,
@NonNull final FirstPartyAppPromptHandlerParameters promptHandlerParameters) {
Expand All @@ -152,7 +163,7 @@ public void addExistingFirstAccount(@NonNull final String username) {
// Click start btn
UiAutomatorUtils.handleButtonClick("com.microsoft.office.outlook:id/btn_primary_button");

Assert.assertTrue("Not on Accounts found page", UiAutomatorUtils.obtainUiObjectWithExactText("Accounts found").exists());
Assert.assertTrue("Not on Accounts found page", UiAutomatorUtils.obtainUiObjectWithExactText("Accounts found", CommonUtils.FIND_UI_ELEMENT_TIMEOUT_SHORT).exists());
Assert.assertTrue("Couldn't find account:" + username, UiAutomatorUtils.obtainUiObjectWithText(username).exists());

// Click Continue btn
Expand All @@ -169,6 +180,8 @@ public void addExistingFirstAccount(@NonNull final String username) {
public void signInThroughSnackBar(@NonNull final String username,
@NonNull final String password,
@NonNull final FirstPartyAppPromptHandlerParameters promptHandlerParameters) {
handleIntroDialogueAfterSignIn();

// Click SIGN IN Button in snackBar
UiAutomatorUtils.handleButtonClick("com.microsoft.office.outlook:id/snackbar_action");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
package com.microsoft.identity.client.ui.automation.app;

import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiObjectNotFoundException;
import androidx.test.uiautomator.UiSelector;
Expand Down Expand Up @@ -59,7 +61,12 @@ public TeamsApp(@NonNull final IAppInstaller appInstaller) {

@Override
public void handleFirstRun() {
// nothing needed here
if (shouldHandleFirstRun) {
UiAutomatorUtils.handleButtonClickForObjectWithTextSafely("Get started");

UiAutomatorUtils.obtainUiObjectWithResourceId("com.microsoft.teams:id/sign_in_text");
shouldHandleFirstRun = false;
}
}

@Override
Expand Down Expand Up @@ -139,16 +146,12 @@ private void signInWithEmail(@NonNull final String username,
@NonNull final String password,
@NonNull final MicrosoftStsPromptHandlerParameters promptHandlerParameters) {
Logger.i(TAG, "Sign-In on the APP..");
// Enter email in email field
UiAutomatorUtils.handleInputByClass("android.widget.EditText", username);

// Enter email in email field and press enter
UiAutomatorUtils.handleInput("com.microsoft.teams:id/edit_email_refresh", username);

// Click Sign in btn
try {
UiAutomatorUtils.handleButtonClick("com.microsoft.teams:id/sign_in_button_refresh", CommonUtils.FIND_UI_ELEMENT_TIMEOUT_SHORT);
}
catch (AssertionError e){
UiAutomatorUtils.handleButtonClick("com.microsoft.teams:id/sign_in_button", CommonUtils.FIND_UI_ELEMENT_TIMEOUT_SHORT);
}
UiAutomatorUtils.handleButtonClick("com.microsoft.teams:id/sign_in_button_refresh", CommonUtils.FIND_UI_ELEMENT_TIMEOUT_SHORT);

Logger.i(TAG, "Handle Sign-In with Email Prompt on the APP..");
// handle prompt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ public void addAnotherAccount(@NonNull final String username,
Logger.i(TAG, "Adding Another Account..");
// Click account drawer
UiAutomatorUtils.handleButtonClick("com.microsoft.office.word:id/docsui_me_image");
// Click Add or switch account
UiAutomatorUtils.handleButtonClick("com.microsoft.office.word:id/docsui_account_list_item_people_card");
// Click add account
UiAutomatorUtils.handleButtonClick("com.microsoft.office.word:id/docsui_account_list_add_account");
UiAutomatorUtils.handleButtonClickForObjectWithText("Add an account");
// sing in with supplied username/password
signIn(username, password, promptHandlerParameters);
}
Expand Down Expand Up @@ -140,17 +142,6 @@ private void signIn(@NonNull final String username,
public void confirmAccount(@NonNull final String username) {
Logger.i(TAG, "Confirming account with supplied username is signed in..");

// Had a screen for microsoft 365 pop up occasionally
final UiObject msft365Object = UiAutomatorUtils.obtainUiObjectWithText("Go Premium with Microsoft 365 Personal");
if (msft365Object.exists()) {
final UiObject skipObject = UiAutomatorUtils.obtainUiObjectWithText("SKIP FOR NOW");
try {
skipObject.click();
} catch (UiObjectNotFoundException e) {
Logger.i(TAG, "Ignoring failure to find confirm account UI");
}
}

UiAutomatorUtils.handleButtonClick("com.microsoft.office.word:id/docsui_me_image");

final UiObject testAccountLabelWord = UiAutomatorUtils.obtainUiObjectWithText(username);
Expand Down
Loading