Skip to content

Commit

Permalink
Fix microsoft#752 do not try to Enter OTC if there is not Secrect Key
Browse files Browse the repository at this point in the history
  • Loading branch information
AngelRodriguez8008 committed Feb 10, 2020
1 parent 0bb508d commit 128d835
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 140 deletions.
190 changes: 96 additions & 94 deletions Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,37 +67,23 @@ internal BrowserCommandResult<bool> InitializeModes()
public string[] OnlineDomains { get; set; }

#region PageWaits

internal void WaitForLoginPage()
{
IWebDriver driver = this.Browser.Driver;

driver.WaitUntilVisible(By.XPath(Elements.Xpath[Reference.Login.CrmMainPage])
, TimeSpan.FromSeconds(60),
e =>
{
//determine if we landed on the Unified Client Main page
if (driver.HasElement(By.XPath(Elements.Xpath[Reference.Login.CrmUCIMainPage])))
{
driver.WaitForPageToLoad();
driver.WaitForTransaction();
}
else //else we landed on the Web Client main page or app picker page
SwitchToDefaultContent(driver);
},
"Login page failed."
);
}

internal void WaitForMainPage()
{
IWebDriver driver = this.Browser.Driver;
driver.WaitUntilVisible(By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]));
driver.WaitForPageToLoad();
if (driver.HasElement(By.XPath(Elements.Xpath[Reference.Login.CrmUCIMainPage])))
{
driver.WaitForTransaction();
}
internal bool WaitForMainPage(TimeSpan timeout, string errorMessage)
=> WaitForMainPage(timeout, null, () => Browser.Driver.Throw(errorMessage));

internal bool WaitForMainPage(TimeSpan? timeout = null, Action<IWebElement> successCallback = null, Action failureCallback = null)
{
IWebDriver driver = Browser.Driver;
timeout = timeout ?? Constants.DefaultTimeout;
successCallback = successCallback ?? (
_ => {
bool isUCI = driver.HasElement(By.XPath(Elements.Xpath[Reference.Login.CrmUCIMainPage]));
if (isUCI)
driver.WaitForTransaction();
});

var xpathToMainPage = By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]);
var element = driver.WaitUntilVisible(xpathToMainPage, timeout, successCallback, failureCallback);
return element != null;
}

#endregion
Expand Down Expand Up @@ -133,7 +119,7 @@ private LoginResult Login(IWebDriver driver, Uri uri, SecureString username, Sec
bool success = EnterUserName(driver, username);
if (!success)
{
var isUserAlreadyLogged = IsUserAlreadyLogged(driver);
var isUserAlreadyLogged = IsUserAlreadyLogged();
if (isUserAlreadyLogged)
{
SwitchToDefaultContent(driver);
Expand Down Expand Up @@ -166,21 +152,23 @@ private LoginResult Login(IWebDriver driver, Uri uri, SecureString username, Sec
ThinkTime(1000);
}

EnterOneTimeCode(driver, mfaSecrectKey);

ClickStaySignedIn(driver);

ThinkTime(1000);
int attempts = 0;
bool entered;
do
{
entered = EnterOneTimeCode(driver, mfaSecrectKey);
success = ClickStaySignedIn(driver) || IsUserAlreadyLogged();
attempts++;
}
while (!success && attempts <= Constants.DefaultRetryAttempts); // retry to enter the otc-code, if its fail & it is requested again

if (entered && !success)
throw driver.Throw("Somethig got wrong entering the OTC. Please check the MFA-SecrectKey in configuration.");

return LoginResult.Success;
return success ? LoginResult.Success : LoginResult.Failure;
}

private static bool IsUserAlreadyLogged(IWebDriver driver)
{
var xpathToMainPage = By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]);
bool result = driver.HasElement(xpathToMainPage);
return result;
}
private bool IsUserAlreadyLogged() => WaitForMainPage(2.Seconds());

private static string GenerateOneTimeCode(SecureString mfaSecrectKey)
{
Expand Down Expand Up @@ -214,41 +202,39 @@ private static void EnterPassword(IWebDriver driver, SecureString password)
input.Submit();
}

private void EnterOneTimeCode(IWebDriver driver, SecureString mfaSecrectKey)
private bool EnterOneTimeCode(IWebDriver driver, SecureString mfaSecrectKey)
{
int attempts = 0;
while (true)
try
{
try
{
IWebElement input = GetOtcInput(driver);
if (input != null)
{
var oneTimeCode = GenerateOneTimeCode(mfaSecrectKey);
input.SendKeys(oneTimeCode);
input.Submit();
return;
}
}
catch (Exception e)
{
Trace.TraceInformation($"An Error ocur entering OTC. Attempt {attempts} of {Constants.DefaultRetryAttempts}. Exception: {e}");
if (attempts >= Constants.DefaultRetryAttempts)
throw;
}
IWebElement input = GetOtcInput(driver); // wait for the dialog, even if key is null, to print the right error
if (input == null)
return true;

attempts++;
ThinkTime(Constants.DefaultRetryDelay);
if (mfaSecrectKey == null)
throw driver.Throw("The application is wait for the OTC but your MFA-SecrectKey is not set. Please check your configuration.");

var oneTimeCode = GenerateOneTimeCode(mfaSecrectKey);
SetInputValue(driver, input, oneTimeCode, 1.Seconds());
input.Submit();
return true; // input found & code was entered
}
catch (Exception e)
{
var message = $"An Error occur entering OTC. Exception: {e.Message}";
Trace.TraceInformation(message);
throw driver.Throw<InvalidOperationException>(message, e);
}
}


private static IWebElement GetOtcInput(IWebDriver driver)
=> driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.Login.OneTimeCode]), TimeSpan.FromSeconds(2));

private static void ClickStaySignedIn(IWebDriver driver)
private static bool ClickStaySignedIn(IWebDriver driver)
{
var xpath = By.XPath(Elements.Xpath[Reference.Login.StaySignedIn]);
driver.ClickWhenAvailable(xpath, TimeSpan.FromSeconds(10));
var element = driver.ClickIfVisible(xpath, 5.Seconds());
return element != null;
}

private static void SwitchToDefaultContent(IWebDriver driver)
Expand All @@ -272,7 +258,22 @@ internal BrowserCommandResult<LoginResult> PassThroughLogin(Uri uri)
{
driver.Navigate().GoToUrl(uri);
WaitForLoginPage();
WaitForMainPage(60.Seconds(),
_ =>
{
//determine if we landed on the Unified Client Main page
var isUCI = driver.HasElement(By.XPath(Elements.Xpath[Reference.Login.CrmUCIMainPage]));
if (isUCI)
{
driver.WaitForPageToLoad();
driver.WaitForTransaction();
}
else
//else we landed on the Web Client main page or app picker page
SwitchToDefaultContent(driver);
},
() => driver.Throw("Load Main Page Fail.")
);
return LoginResult.Success;
});
Expand All @@ -294,7 +295,7 @@ public void ADFSLoginAction(LoginRedirectEventArgs args)
//Insert any additional code as required for the SSO scenario

//Wait for CRM Page to load
driver.WaitUntilVisible(By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]), TimeSpan.FromSeconds(60), "Login page failed.");
WaitForMainPage(TimeSpan.FromSeconds(60), "Login page failed.");
SwitchToMainFrame(driver);
}

Expand Down Expand Up @@ -324,7 +325,7 @@ public void MSFTLoginAction(LoginRedirectEventArgs args)
//Insert any additional code as required for the SSO scenario

//Wait for CRM Page to load
driver.WaitUntilVisible(By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]), TimeSpan.FromSeconds(60), "Login page failed.");
WaitForMainPage(TimeSpan.FromSeconds(60), "Login page failed.");
SwitchToMainFrame(driver);
}

Expand All @@ -342,7 +343,7 @@ internal BrowserCommandResult<bool> OpenApp(string appName, int thinkTime = Cons
driver.SwitchTo().DefaultContent();
//Handle left hand Nav in Web Client
var success = TryToClickInAppTile(appName, driver) ||
var success = TryToClickInAppTile(appName, driver) ||
TryOpenAppFromMenu(driver, appName, AppReference.Navigation.WebAppMenuButton) ||
TryOpenAppFromMenu(driver, appName, AppReference.Navigation.UCIAppMenuButton);
Expand All @@ -361,7 +362,8 @@ private bool TryOpenAppFromMenu(IWebDriver driver, string appName, string appMen
bool found = false;
var xpathToAppMenu = By.XPath(AppElements.Xpath[appMenuButton]);
driver.WaitUntilClickable(xpathToAppMenu, TimeSpan.FromSeconds(5),
appMenu => {
appMenu =>
{
appMenu.Click(true);
OpenAppFromMenu(driver, appName);
found = true;
Expand All @@ -373,7 +375,7 @@ internal void OpenAppFromMenu(IWebDriver driver, string appName)
{
var container = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Navigation.AppMenuContainer]));
var xpathToButton = "//nav[@aria-hidden='false']//button//*[text()='[TEXT]']".Replace("[TEXT]", appName);
container.ClickWhenAvailable(By.XPath(xpathToButton),
container.ClickWhenAvailable(By.XPath(xpathToButton),
TimeSpan.FromSeconds(1),
$"App Name {appName} not found."
);
Expand Down Expand Up @@ -406,10 +408,10 @@ private static bool TryToClickInAppTile(string appName, IWebDriver driver)

var xpathToAppContainer = By.XPath(AppElements.Xpath[AppReference.Navigation.UCIAppContainer]);
var xpathToappTile = By.XPath(AppElements.Xpath[AppReference.Navigation.UCIAppTile].Replace("[NAME]", appName));

bool success = false;
driver.WaitUntilVisible(xpathToAppContainer, TimeSpan.FromSeconds(5),
appContainer => success = appContainer.ClickWhenAvailable(xpathToappTile, TimeSpan.FromSeconds(5)) != null
driver.WaitUntilVisible(xpathToAppContainer, TimeSpan.FromSeconds(5),
appContainer => success = appContainer.ClickWhenAvailable(xpathToappTile, TimeSpan.FromSeconds(5)) != null
);

return success;
Expand Down Expand Up @@ -1034,7 +1036,7 @@ private static ICollection<IWebElement> GetListItems(IWebElement container, Look
{
var name = control.Name;
var xpathToItems = By.XPath(AppElements.Xpath[AppReference.Entity.LookupFieldResultListItem].Replace("[NAME]", name));

//wait for complete the search
container.WaitUntil(d => d.FindVisible(By.XPath("//li/div/label/span"))?.Text?.Equals(control.Value, StringComparison.OrdinalIgnoreCase) == true);

Expand Down Expand Up @@ -1135,10 +1137,10 @@ internal BrowserCommandResult<List<string>> GetCommandValues(bool includeMoreCom
private static List<string> TryGetCommandValues(bool includeMoreCommandsValues, IWebDriver driver)
{
const string moreCommandsLabel = "more commands";

//Find the button in the CommandBar
IWebElement ribbon = GetRibbon(driver);

//Get the CommandBar buttons
Dictionary<string, IWebElement> commandBarItems = GetMenuItems(ribbon);
bool hasMoreCommands = commandBarItems.TryGetValue(moreCommandsLabel, out var moreCommandsButton);
Expand All @@ -1155,7 +1157,7 @@ private static List<string> TryGetCommandValues(bool includeMoreCommandsValues,
var result = GetCommandNames(commandBarItems.Values);
return result;
}

private static Dictionary<string, IWebElement> GetMenuItems(IWebElement menu)
{
var result = new Dictionary<string, IWebElement>();
Expand Down Expand Up @@ -1186,8 +1188,8 @@ private static IWebElement GetRibbon(IWebDriver driver)
var xpathCommandBarContainer = By.XPath(AppElements.Xpath[AppReference.CommandBar.Container]);
var xpathCommandBarGrid = By.XPath(AppElements.Xpath[AppReference.CommandBar.ContainerGrid]);

IWebElement ribbon =
driver.WaitUntilAvailable(xpathCommandBarContainer, 5.Seconds()) ??
IWebElement ribbon =
driver.WaitUntilAvailable(xpathCommandBarContainer, 5.Seconds()) ??
driver.WaitUntilAvailable(xpathCommandBarGrid, 5.Seconds()) ??
throw new InvalidOperationException("Unable to find the ribbon.");

Expand Down Expand Up @@ -1220,7 +1222,7 @@ private static IWebElement GetRibbon(IWebDriver driver)
continue;
var key = viewItem.Text.ToLowerString();
if(string.IsNullOrWhiteSpace(key))
if (string.IsNullOrWhiteSpace(key))
continue;
if (!result.ContainsKey(key))
Expand Down Expand Up @@ -1255,7 +1257,7 @@ internal BrowserCommandResult<bool> OpenRecord(int index, int thinkTime = Consta
{
var xpathToGrid = By.XPath(AppElements.Xpath[AppReference.Grid.Container]);
IWebElement control = driver.WaitUntilAvailable(xpathToGrid);
Func<Actions, Actions> action;
if (checkRecord)
action = e => e.Click();
Expand Down Expand Up @@ -1837,18 +1839,18 @@ internal BrowserCommandResult<bool> SetValue(string field, string value)
});
}

private void SetInputValue(IWebDriver driver, IWebElement input, string value)
private void SetInputValue(IWebDriver driver, IWebElement input, string value, TimeSpan? thinktime = null)
{
input.SendKeys(Keys.Control + "a");
input.SendKeys(Keys.Backspace);
driver.WaitForTransaction();

if (!string.IsNullOrWhiteSpace(value))
{
input.SendKeys(value, true);
driver.WaitForTransaction();
ThinkTime(3000);
}
if (string.IsNullOrWhiteSpace(value))
return;

input.SendKeys(value, true);
driver.WaitForTransaction();
ThinkTime(thinktime ?? 3.Seconds());
}

/// <summary>
Expand Down Expand Up @@ -1903,7 +1905,7 @@ internal BrowserCommandResult<bool> SetValue(LookupItem[] controls, bool clearFi
TryRemoveLookupValue(driver, fieldContainer, control);
TryToSetValue(fieldContainer, controls);
return true;
});
}
Expand Down Expand Up @@ -1962,7 +1964,7 @@ private void SetLookUpByValue(ISearchContext container, LookupItem control)

if (items.Count == 0)
throw new InvalidOperationException($"List does not contain a record with the name: {control.Value}");

int index = control.Index;
if (index >= items.Count)
throw new InvalidOperationException($"List does not contain {index + 1} records. Please provide an index value less than {items.Count} ");
Expand Down

0 comments on commit 128d835

Please sign in to comment.