- Verify that the login page has a form with fields for entering a username and password.
- Verify that the form has a submit button that is disabled until both fields are filled in.
- Verify that the form is submitted using HTTP POST method to the correct endpoint.
- Verify that the form is properly validated to ensure that the entered username and password match the expected format.
- Verify that the user is redirected to the appropriate page upon successful login (e.g. dashboard or home page).
- Verify that the user is shown an error message if the entered username or password is incorrect.
- Verify that the page has a link for resetting the password.
- Verify that the page has a link for creating an account.
You can use Jest and Enzyme for testing your React components and Playwright to automate browser testing.
Here is an example of how a test suite for a login page using Java, TypeScript, Playwright, and Cucumber might be structured:
- Create a new project in TypeScript, and add Playwright and Cucumber as dependencies.
- Define the feature file for the login page in Cucumber, outlining the various scenarios to be tested. For example:
Feature: Login Page
As a user,
I want to be able to login to the website
So that I can access my account
Scenario: Successful login
Given I am on the login page
When I enter my username and password
And I click the login button
Then I should be taken to the dashboard
Scenario: Incorrect login credentials
Given I am on the login page
When I enter an incorrect username and password
And I click the login button
Then I should see an error message
Scenario: Forgotten password
Given I am on the login page
When I click the “Forgot Password” link
Then I should be taken to the password reset page
Write step definitions in TypeScript that match the steps outlined in the feature file. For example:
import { Given, When, Then } from 'cucumber';
import { playwright } from 'playwright';
Given('I am on the login page', async function() {
// navigate to login page
});
When('I enter my username and password', async function() {
// enter username and password
});
When('I click the login button', async function() {
// click login button
});
Then('I should be taken to the dashboard', async function() {
// verify that user is taken to dashboard
});
Then('I should see an error message', async function() {
// verify that error message is displayed
});
step definitions with page element locators using placeholders and load the locators from a JSON file. Here’s an example of how you might structure the code:
json file:
{
"usernameInput": "#username-input",
"passwordInput": "#password-input",
"loginButton": "#login-button",
"errorMessage": "#error-message"
}
Import the JSON file into the step definition file:
import { Given, When, Then } from 'cucumber';
import { playwright, Playwright } from 'playwright';
import locators from './locators.json';
let browser: Playwright;
Given('I am on the login page', async function() {
// navigate to login page
browser = await playwright.chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://example.com/login');
});
When('I enter {string} as username and {string} as password', async function(username:string,password:string) {
// enter username and password
await page.fill(locators.usernameInput,username);
await page.fill(locators.passwordInput,password);
});
When('I click the {string} button', async function(button:string) {
// click login button
await page.click(locators[`${button}Button`]);
});
Then('I should be taken to the {string}', async function(page:string) {
// verify that user is taken to dashboard
await page.waitForNavigation();
const url = await page.url();
expect(url).to.contain(page);
});
Then('I should see an {string} message', async function(message:string) {
// verify that error message is displayed
const text = await page.textContent(locators.errorMessage);
expect(text).to.contain(message);
});
By using this approach, you can separate the locators from the test code, making it easier to maintain the tests and make updates to the locators if the page changes.
Please note that the above code is just an example, you need to adjust the selectors to match the actual html elements on your page. Also, you need to import the necessary modules, like expect
to make the assertions.
Using C# and Playwright.
using TechTalk.SpecFlow;
using Newtonsoft.Json;
using System.IO;
using PlaywrightSharp;
using System.Threading.Tasks;
namespace LoginPageTest
{
[Binding]
public class LoginSteps
{
private dynamic locators;
private IBrowser browser;
private IPage page;
public LoginSteps()
{
// Load locators from JSON file
string locatorsJson = File.ReadAllText("locators.json");
locators = JsonConvert.DeserializeObject(locatorsJson);
}
[Given(@"I am on the login page")]
public async Task GivenIAmOnTheLoginPage()
{
// open browser and navigate to login page
browser = await Playwright.CreateBrowserAsync();
page = await browser.NewContextAsync().NewPageAsync();
await page.GoToAsync("https://example.com/login");
}
[When(@"I enter (.*) as username and (.*) as password")]
public async Task WhenIEnterUsernameAndPassword(string username, string password)
{
// enter username and password
await page.FillAsync(locators.usernameInput, username);
await page.FillAsync(locators.passwordInput, password);
}
[When(@"I click the (.*) button")]
public async Task WhenIClickTheButton(string button)
{
// click login button
await page.ClickAsync(locators[button + "Button"]);
}
[Then(@"I should be taken to the (.*)")]
public async Task ThenIShouldBeTakenToThePage(string page)
{
// verify that user is taken to dashboard
await page.WaitForNavigationAsync();
Assert.IsTrue(page.Url.Contains(page));
}
[Then(@"I should see an (.*) message")]
public async Task ThenIShouldSeeAnErrorMessage(string message)
{
// verify that error message is displayed
IElement errorMessage = await page.WaitForSelectorAsync(locators.errorMessage);
Assert.IsTrue(await errorMessage.TextContentAsync().Contains(message));
}
}
}
.
A polling mechanism:
In this example, the WaitForClickableElementAsync
method waits for the element to be present and interactable on the page, while the ClickWithPollingAsync
method clicks the element after checking that it is interactable. The polling mechanism will retry the action until the timeout has been reached or the element is clickable. This can help to prevent flaky tests caused by race conditions or other timing issues.
using System.Threading;
using System.Threading.Tasks;
using PlaywrightSharp;
namespace LoginPageTest
{
public static class PlaywrightExtensions
{
public static async Task<IElement> WaitForClickableElementAsync(this IPage page, string selector, int timeout = 5000)
{
IElement element = null;
int interval = 100;
int elapsed = 0;
while (elapsed < timeout)
{
try
{
element = await page.QuerySelectorAsync(selector);
if (element != null && await element.IsInteractableAsync())
{
return element;
}
}
catch (PlaywrightSharpException)
{
// ignore exceptions
}
await Task.Delay(interval);
elapsed += interval;
}
throw new PlaywrightSharpException($"Timed out waiting for element with selector '{selector}' to be clickable");
}
public static async Task ClickWithPollingAsync(this IElement element, int timeout = 5000)
{
int interval = 100;
int elapsed = 0;
while (elapsed < timeout)
{
try
{
if (await element.IsInteractableAsync())
{
await element.ClickAsync();
return;
}
}
catch (PlaywrightSharpException)
{
// ignore exceptions
}
await Task.Delay(interval);
elapsed += interval;
}
throw new PlaywrightSharpException($"Timed out waiting for element with selector '{element.Selector}' to be clickable");
}
}
[Binding]
public class LoginSteps
{
// ...
[When(@"I click the (.*) button")]
public async Task WhenIClickTheButton(string button)
{
// Wait for the login button to be clickable, then click it
IElement loginButton = await page.WaitForClickableElementAsync(locators[button + "Button"]);
await loginButton.ClickWithPollingAsync();
}
// ...
}
}
C# and selenium
here's an example of how you can implement a polling mechanism in C# to click on elements and prevent flaky tests using Selenium:
In this example, the WaitForClickableElement method waits for the element to be present and interactable on the page, while the ClickWithPolling method clicks the element after checking that it is interactable. The polling mechanism will retry the action until the timeout has been reached or the element is clickable. This can help to prevent flaky tests caused by race conditions or other timing issues.
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System;
namespace LoginPageTest
{
public static class WebDriverExtensions
{
public static IWebElement WaitForClickableElement(this IWebDriver driver, By locator, int timeout = 5000)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromMilliseconds(timeout));
wait.IgnoreExceptionTypes(typeof(StaleElementReferenceException), typeof(NoSuchElementException));
return wait.Until(ExpectedConditions.ElementToBeClickable(locator));
}
public static void ClickWithPolling(this IWebElement element, int timeout = 5000)
{
int interval = 100;
int elapsed = 0;
while (elapsed < timeout)
{
try
{
if (element.Enabled && element.Displayed)
{
element.Click();
return;
}
}
catch (StaleElementReferenceException)
{
// ignore exceptions
}
System.Threading.Thread.Sleep(interval);
elapsed += interval;
}
throw new NoSuchElementException($"Timed out waiting for element with locator '{element.ToString()}' to be clickable");
}
}
[Binding]
public class LoginSteps
{
private IWebDriver driver;
public LoginSteps(IWebDriver driver)
{
this.driver = driver;
}
[When(@"I click the (.*) button")]
public void WhenIClickTheButton(string button)
{
// Wait for the login button to be clickable, then click it
IWebElement loginButton = driver.WaitForClickableElement(locators[button + "Button"]);
loginButton.ClickWithPolling();
}
}
}