While running a UI-test for a login flow, the user might already be logged in. In case the user is logged in, a logout needs to be performed first.
In this case, I have to wait for either a sign in or sign out button to appear, but I don't know which will appear. I would like to simply wait for one or the other to show up, and then perform an action based on which returns first.
I simply added a silent fail on attempting to log out. If the operation times out, the script just keeps going to the log in part.
Attempting to log out and letting it fail if a timeout happens:
try {
const profileIconLocator = By.css('.frontpage-menu-item-icon-profile');
await browser.wait(Until.elementIsVisible(profileIconLocator));
const profileIcon = await browser.findElement(profileIconLocator);
assert.ok(await profileIcon.isDisplayed(), 'Ordbog profile icon button is visible');
await profileIcon.click();
const logOutLinkLocator = By.css('.frontpage-menu-dropdown-tools-item:nth-child(3)');
await browser.wait(Until.elementIsVisible(logOutLinkLocator));
const logOutLink = await browser.findElement(logOutLinkLocator);
assert.ok(await logOutLink.isDisplayed(), 'Log out link is visible');
await logOutLink.click();
} catch (error) {
console.log("LogOutStep: Failed to log out before logging in", error);
}
Then try to log in assuming that the test-user is logged out:
let signInButtonLocator = By.css('.frontpage-menu-item-icon-signin');
await browser.wait(Until.elementIsVisible(signInButtonLocator));
const signInButton = await browser.findElement(signInButtonLocator);
assert.ok(await signInButton.isDisplayed(), 'Sign in button is visible');
await signInButton.click();
I expect the user to always be logged out before logging in, but it's not ideal for the UI-test to have to wait for a timeout in the first logout attempt for every test user.
The ideal result would be that I can await for either of the two async calls to return, cancel the other upon return, and then execute a function like such:
// Wait for this to execute first(log out button)
const profileIconLocator = By.css('.frontpage-menu-item-icon-profile');
await browser.wait(Until.elementIsVisible(profileIconLocator));
// or this (sign in button)
let signInButtonLocator = By.css('.frontpage-menu-item-icon-signin');
await browser.wait(Until.elementIsVisible(signInButtonLocator));
// If sign in button shows first, run login
// If sign out button shows first, perform log out, then login again.
I ended up solving it differently, by using a multiquery selector that waits for the first instance of one or the other CSS class. So here I wait for either the login button or log out button to appear first, and then act based on the class of the element that appeared:
const profileIconOrSignInButtonLocator = By.css('.frontpage-menu-item-icon-profile, .frontpage-menu-item-icon-signin');
await browser.wait(Until.elementIsVisible(profileIconOrSignInButtonLocator));
const loginOrLogoutButton = await browser.findElement(profileIconOrSignInButtonLocator);
var elementClass = await loginOrLogoutButton.getAttribute('class');
if (elementClass === 'frontpage-menu-item-icon frontpage-menu-item-icon-signin') {
console.log("Simply log in");
await loginFrontPage(browser);
} else { // If this happens we're already logged in, and need to log out first.
console.log("Log out and then log in");
await logoutFrontPage(browser);
await loginFrontPage(browser);
}