Getting Started with UI Tests
UI tests in this framework validate web application functionality through browser automation. They use the Page Object Model pattern to create maintainable, readable tests.
Basic Test Structure
Every UI test follows this structure:
import LoginPage from "../../support/pages/LoginPage" ;
import HomePage from "../../support/pages/HomePage" ;
let loginPage : LoginPage
let homePage : HomePage
describe ( 'Feature Name' , () => {
beforeEach (() => {
// Setup: Load fixtures and initialize page objects
cy . fixture ( 'users' ). as ( 'userData' )
loginPage = new LoginPage ()
homePage = new HomePage ()
cy . visit ( '/' )
});
it ( 'Should perform expected behavior' , () => {
// Test implementation
});
});
Key Components
Import Page Objects
Import the page object classes you’ll use in your tests.
Declare Variables
Declare variables for page object instances at the describe block level.
Setup in beforeEach
Initialize page objects and load fixtures before each test.
Write Test Cases
Use page object methods to write readable test scenarios.
Complete Login Test Example
Here’s a real test from the framework:
/// < reference types = "cypress" />
import LoginPage from "../../support/pages/LoginPage" ;
import HomePage from "../../support/pages/HomePage" ;
import Navbar from "../../support/components/Navbar" ;
let loginPage : LoginPage
let homePage : HomePage
let navbar : Navbar
describe ( 'User Authentication' , () => {
beforeEach (() => {
cy . fixture ( 'users' ). as ( 'userData' )
cy . fixture ( 'loginPageData' ). as ( 'loginData' )
loginPage = new LoginPage ()
homePage = new HomePage ()
navbar = new Navbar ()
cy . visit ( '/' )
});
it ( 'Should log in successfully with valid credentials' , () => {
cy . get ( '@userData' ). then (( user : any ) => {
loginPage . verifyLoginMainPage ()
loginPage . typeUsername ( user . standard )
loginPage . typePassword ( user . password )
loginPage . clickOnLoginButton ()
homePage . verifyHomePageIsVisible ()
navbar . userLogout ()
loginPage . verifyLoginMainPage ()
})
});
it ( 'Should not log in with a locked user credentials' , () => {
cy . get ( '@userData' ). then (( user : any ) => {
cy . get ( '@loginData' ). then (( login : any ) => {
loginPage . verifyLoginMainPage ()
loginPage . typeUsername ( user . locked )
loginPage . typePassword ( user . password )
loginPage . clickOnLoginButton ()
loginPage . verifyErrorMessageIsShown
loginPage . verifyErrorMessage ( login . errorMsg )
loginPage . closeErrorMessage ()
loginPage . verifyErrorMessageIsHidden ()
})
})
});
});
What This Test Does
Setup Loads user credentials and login messages from fixtures, initializes page objects
Navigation Visits the base URL (configured in cypress.config.ui.ts)
Actions Uses page object methods to interact with the UI
Assertions Verifies expected outcomes using page object verification methods
Testing with Fixtures
Fixtures provide test data without hardcoding:
{
"standard" : "standard_user" ,
"locked" : "locked_out_user" ,
"problem" : "problem_user" ,
"password" : "secret_sauce"
}
Using fixtures in tests:
beforeEach (() => {
cy . fixture ( 'users' ). as ( 'userData' )
})
it ( 'Should login' , () => {
cy . get ( '@userData' ). then (( user : any ) => {
loginPage . typeUsername ( user . standard )
loginPage . typePassword ( user . password )
loginPage . clickOnLoginButton ()
})
})
Using cy.fixture() with as() creates an alias that’s available throughout the test. Access it with cy.get('@aliasName').
Home Page Testing Example
Here’s how to test more complex UI interactions:
import LoginPage from "../../support/pages/LoginPage" ;
import HomePage from "../../support/pages/HomePage" ;
let loginPage : LoginPage
let homePage : HomePage
describe ( 'Home Page Assertions and Verifications' , () => {
beforeEach (() => {
cy . fixture ( 'users' ). as ( 'userData' )
loginPage = new LoginPage ()
homePage = new HomePage ()
cy . visit ( '/' )
// Login before each test
cy . get ( '@userData' ). then (( user : any ) => {
loginPage . userLogin ( user . standard , user . password )
})
homePage . verifyHomePageIsVisible ()
});
it ( 'Should verify the A to Z selection' , () => {
homePage . selectFilterAtoZ ()
homePage . verifyFilterAtoZ ()
});
it ( 'Should verify the Z to A selection' , () => {
homePage . selectFilterZtoA ()
homePage . verifyFilterZtoA ()
});
it ( 'Should verify the price low to high selection' , () => {
homePage . selectFilterPriceLowtoHigh ()
homePage . verifyFilterPriceLowtoHigh ()
});
});
Testing Workflow
Authentication Hook
The beforeEach hook logs in before each test, ensuring tests start from the home page.
Action and Verification
Each test performs an action (select filter) then verifies the result.
Test Isolation
Each test runs independently - the beforeEach ensures a clean state.
Common UI Test Patterns
it ( 'Should submit form with valid data' , () => {
cy . get ( '@formData' ). then (( data : any ) => {
formPage . fillField ( 'name' , data . name )
formPage . fillField ( 'email' , data . email )
formPage . submitForm ()
formPage . verifySuccessMessage ()
})
})
Pattern 2: Navigation Flow
it ( 'Should navigate through checkout process' , () => {
homePage . addProductToCart ( 0 )
homePage . goToCart ()
cartPage . verifyProductInCart ()
cartPage . proceedToCheckout ()
checkoutPage . verifyCheckoutPageVisible ()
})
Pattern 3: Error Validation
it ( 'Should show error for invalid input' , () => {
loginPage . typeUsername ( 'invalid' )
loginPage . typePassword ( 'wrong' )
loginPage . clickOnLoginButton ()
loginPage . verifyErrorMessageIsShown ()
loginPage . verifyErrorMessage ( 'Invalid credentials' )
})
Pattern 4: State Verification
it ( 'Should persist cart items after logout' , () => {
homePage . addProductToCart ( 0 )
homePage . addProductToCart ( 1 )
navbar . userLogout ()
loginPage . userLogin ( user . standard , user . password )
homePage . verifyCartCount ( 2 )
})
Using Component Objects
For reusable UI components like navigation bars:
import Navbar from "../../support/components/Navbar" ;
let navbar : Navbar
beforeEach (() => {
navbar = new Navbar ()
})
it ( 'Should logout successfully' , () => {
navbar . userLogout ()
loginPage . verifyLoginMainPage ()
})
Component objects work like page objects but represent reusable UI components that appear across multiple pages.
Advanced Testing Techniques
Testing Dynamic Content
it ( 'Should verify sorted product names' , () => {
homePage . selectFilterAtoZ ()
cy . get ( '[data-test="inventory-item"]' ). then (( $els ) => {
const names = [ ... $els ]. map ( el => el . innerText . trim ())
const sorted = [ ... names ]. sort (( a , b ) => a . localeCompare ( b ))
expect ( names ). to . deep . equal ( sorted )
})
})
Testing with Multiple Data Sets
it ( 'Should login with multiple valid users' , () => {
const users = [ 'standard' , 'problem' , 'performance' , 'visual' ]
cy . get ( '@userData' ). then (( user : any ) => {
users . forEach ( userType => {
loginPage . typeUsername ( user [ userType ])
loginPage . typePassword ( user . password )
loginPage . clickOnLoginButton ()
homePage . verifyHomePageIsVisible ()
navbar . userLogout ()
})
})
})
Custom Commands in Tests
If you have custom commands defined in support/commands.ts:
// In support/commands.ts
Cypress . Commands . add ( 'login' , ( username : string , password : string ) => {
cy . get ( '#user-name' ). type ( username )
cy . get ( '#password' ). type ( password )
cy . get ( '#login-button' ). click ()
})
// In test file
it ( 'Should login using custom command' , () => {
cy . login ( 'standard_user' , 'secret_sauce' )
homePage . verifyHomePageIsVisible ()
})
Best Practices
Use Page Objects for all UI interactions
Never use cy.get() directly in tests - always go through page objects: // ✅ Good
loginPage . typeUsername ( 'user' )
// ❌ Bad
cy . get ( '#user-name' ). type ( 'user' )
Each test should be able to run alone: // ✅ Good - each test sets up its own state
beforeEach (() => {
cy . visit ( '/' )
loginPage . userLogin ( user . standard , user . password )
})
// ❌ Bad - tests depend on order
it ( 'Login' , () => { /* login */ })
it ( 'Add to cart' , () => { /* assumes logged in */ })
Use descriptive test names
Test names should describe behavior, not implementation: // ✅ Good
it ( 'Should display error for locked user credentials' , () => {})
// ❌ Bad
it ( 'Test login with locked user' , () => {})
Avoid hardcoding test data
Always use fixtures for test data: // ✅ Good
cy . fixture ( 'users' ). then (( user ) => {
loginPage . typeUsername ( user . standard )
})
// ❌ Bad
loginPage . typeUsername ( 'standard_user' )
Debugging UI Tests
it ( 'Should debug test flow' , () => {
loginPage . typeUsername ( 'user' )
cy . pause () // Pause test execution
loginPage . typePassword ( 'pass' )
cy . debug () // Log debug info to console
loginPage . clickOnLoginButton ()
})
Using .then() for Debugging
cy . get ( '[data-test="inventory-item"]' )
. then (( $el ) => {
console . log ( 'Element:' , $el )
console . log ( 'Text:' , $el . text ())
})
Use Cypress Test Runner’s time-travel feature to hover over commands and see the application state at that point in time.
Running UI Tests
Interactive Mode
Headless Mode
Specific Browser
Single Test File
Next Steps
Creating Page Objects Build your own page object classes
Using Fixtures Master test data management
Running Tests Learn all test execution options
Test Organization Structure your test suite effectively