Skip to content

Commit

Permalink
Clean up page objects (#63)
Browse files Browse the repository at this point in the history
When we first started this project we were also new to Cypress. We added an initial implementation of page objects and got some basic scenarios working.

Since then our understanding Cypress has improved plus our newest page objects are not consistent with how we did the original ones. We've also brought the concept of menus in.

Finally, we're using our page objects inconsistently. Some scenarios depend on them. Others like the legacy tests ignore them completely.

Updating the scenarios to be consistent is for another change. But this one focuses on getting the page objects sorted in readiness. We'll get them consistent and reduce some duplication by introducing some base page objects.

** Notes

* Update deprecated use of cy.server()
Cypress replaced this with `cy.intercept()` in its latest versions. Plus it's what we use elsewhere in suite.

* Fix transactions feature cust reference
We think the previous reference was just something we had found in all our AWS environments. We now use a reference that we know is included in our test import files.

* Add a BasePage and extend all from it
We start by creating a `BasePage` which we then extend all other pages from. It takes over duties for the `mainHeading()`. We also move to some conventions.

- elements will feature their type in the name, for example, `cancelButton`
- rather than refer to the main submit button on each page by its display name we go with one that is consistent. This also means we can declare it once in our `BasePage`
- we expect all pages to provide a `confirm()` method. This will replace checks on `mainHeading()` littered throughout the test to confirm we're in the right place. Our convention now will be to ask the page object to confirm we're where we are supposed to be

These changes mean a number of edits to the steps. Along the way whilst retesting we found some broken elements. So, scattered throughout the changes will be ones related to fixing a test rather than the switch to a base page.

* Add BaseAppPage

The base app page represents what you see once you have authenticated. This means the various menus. The intention is to access the menus through the page object that represents where you are rather than referencing menus separartly in the steps. We create a base class for it to remove duplication. We want to try and get referencing them out of the steps because we think it will help make them cleaner and clearer.

* Add annual billing menu
Whilst working on a scenario spotted that we were accessing the annual billing menu but had yet to wrap it in a 'menu'.

* Move alert checking to a Cypress command

Went with this option for the reasons set out in the method comments. Again, we're holding off touching the legacy tests and will deal with them separately.
  • Loading branch information
Cruikshanks authored Mar 27, 2022
1 parent cf48d75 commit 25c2c98
Show file tree
Hide file tree
Showing 32 changed files with 368 additions and 331 deletions.
14 changes: 7 additions & 7 deletions cypress/integration/authorisation/authorisation_steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@ import { And, When, Then, But } from 'cypress-cucumber-preprocessor/steps'
import TransactionsPage from '../../pages/transactions_page'

When('I see the transactions page', () => {
TransactionsPage.mainHeading().contains('Transactions to be billed')
TransactionsPage.confirm()
})

Then('I should see the admin menu', () => {
TransactionsPage.adminMenu().should('be.visible')
TransactionsPage.adminMenu.menuLink().should('be.visible')
})

But('I should not see the admin menu', () => {
TransactionsPage.adminMenu().should('not.exist')
TransactionsPage.adminMenu.menuLink().should('not.exist')
})

Then('I should see the billing menu', () => {
TransactionsPage.billingMenu().should('be.visible')
TransactionsPage.annualBillingMenu.menuLink().should('be.visible')
})

But('I should not see the billing menu', () => {
TransactionsPage.billingMenu().should('not.exist')
TransactionsPage.annualBillingMenu.menuLink().should('not.exist')
})

Then('I should see the transactions menu', () => {
TransactionsPage.transactionMenu().should('be.visible')
TransactionsPage.transactionsMenu.menuLink().should('be.visible')
})

And('I should see download transactions', () => {
Expand All @@ -35,5 +35,5 @@ But('I should not see download transactions', () => {

Then('I should only see the {string} regime', (regime) => {
cy.get('.navbar-text').contains(regime, { matchCase: false })
TransactionsPage.regimeMenu().should('not.exist')
TransactionsPage.regimeMenu.menuLink().should('not.exist')
})
92 changes: 40 additions & 52 deletions cypress/integration/common/email.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import AddUserPage from '../../pages/add_user_page'
import EditUserPage from '../../pages/edit_user_page'
import ForgotPasswordPage from '../../pages/forgot_password_page'
import LastEmailPage from '../../pages/last_email_page'
import MainMenu from '../../pages/menus/main_menu'
import ResendUnlockPage from '../../pages/resend_unlock_page'
import SignInPage from '../../pages/sign_in_page'
import TransactionsPage from '../../pages/transactions_page'
import UsersPage from '../../pages/users_page'

Given('I am a new user', () => {
Expand All @@ -32,21 +32,19 @@ Given('I am an existing user', () => {
When('a new account is created for me', () => {
cy.signIn(Cypress.config().users.admin.email)

MainMenu.admin.getOption('User Management', '').click()
TransactionsPage.adminMenu.getOption('User Management', '').click()

UsersPage.addUserAccount().click()
UsersPage.addUserAccountButton().click()

cy.get('@user').then((user) => {
AddUserPage.email().type(user.email)
AddUserPage.firstName().type(user.firstName)
AddUserPage.lastName().type(user.lastName)
AddUserPage.emailInput().type(user.email)
AddUserPage.firstNameInput().type(user.firstName)
AddUserPage.lastNameInput().type(user.lastName)

AddUserPage.regimeAccess('Waste').click()
AddUserPage.addAndInviteUser().click()

cy.get('.col > .alert')
.should('contain.text', 'User account created')
AddUserPage.regimeAccessCheckbox('Waste').click()
AddUserPage.submitButton().click()
})
cy.alertShouldContain('User account created')
})

And('I accept the invitation', () => {
Expand All @@ -59,11 +57,11 @@ And('I accept the invitation', () => {
const link = LastEmailPage.extractInvitationLink(lastEmail.last_email.body)

cy.visit(link).then(() => {
AcceptInvitePage.mainHeading().should('contain', 'Set a password')
AcceptInvitePage.confirm()

AcceptInvitePage.password().type(Cypress.env('PASSWORD'), { log: false })
AcceptInvitePage.passwordConfirmation().type(Cypress.env('PASSWORD'), { log: false })
AcceptInvitePage.setPassword().click()
AcceptInvitePage.passwordInput().type(Cypress.env('PASSWORD'), { log: false })
AcceptInvitePage.passwordConfirmationInput().type(Cypress.env('PASSWORD'), { log: false })
AcceptInvitePage.submitButton().click()
})
})
})
Expand All @@ -72,19 +70,19 @@ And('I accept the invitation', () => {
Then('I will be signed in with my new account', () => {
cy.get('@user').then((user) => {
const username = `${user.firstName} ${user.lastName}`
MainMenu.user.menuLink().should('contain', username)
TransactionsPage.userMenu.menuLink().should('contain', username)
})
})

And('I incorrectly enter my password 5 times', () => {
cy.get('@user').then((user) => {
SignInPage.email().type(user.email)
SignInPage.emailInput().type(user.email)

for (let i = 0; i < 5; i++) {
SignInPage.password().clear()
SignInPage.password().type(generateStringHelper())
SignInPage.passwordInput().clear()
SignInPage.passwordInput().type(generateStringHelper())

SignInPage.logIn().click()
SignInPage.submitButton().click()
}
})
})
Expand All @@ -93,18 +91,16 @@ And('I have forgotten my password', () => {
SignInPage.visit()
SignInPage.forgotPasswordLink().click()

ForgotPasswordPage.mainHeading().should('contain', 'Forgot your password?')
ForgotPasswordPage.confirm()

cy.get('@user').then((user) => {
ForgotPasswordPage.email().type(user.email)
ForgotPasswordPage.sendMeResetPasswordInstructions().click()
ForgotPasswordPage.emailInput().type(user.email)
ForgotPasswordPage.submitButton().click()
})

cy.get('.col > .alert')
.should(
'contain.text',
'If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes.'
)
cy.alertShouldContain(
'If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes.'
)
})

When('I follow the link to unlock my account', () => {
Expand All @@ -125,17 +121,15 @@ And('request another unlock email', () => {
ResendUnlockPage.confirm()

cy.get('@user').then((user) => {
ResendUnlockPage.email().type(user.email)
ResendUnlockPage.resendUnlockInstructions().click()
ResendUnlockPage.emailInput().type(user.email)
ResendUnlockPage.submitButton().click()
})
})

Then('I will see confirmation my account is unlocked', () => {
cy.get('.col > .alert')
.should(
'contain.text',
'Your account has been unlocked successfully. Please sign in to continue.'
)
cy.alertShouldContain(
'Your account has been unlocked successfully. Please sign in to continue.'
)
})

But('I miss the first invitation email', () => {
Expand All @@ -153,7 +147,7 @@ But('I miss the first invitation email', () => {

But('I miss the first unlock email', () => {
cy.get('@user').then((user) => {
LastEmailPage.lastEmail([user.email, 'Your account has been locked'])
LastEmailPage.lastEmail([`Hello ${user.firstName} ${user.lastName}`, 'account has been locked'])

cy.get('@lastEmail').then((lastEmail) => {
const link = LastEmailPage.extractUnlockAccountLink(lastEmail.last_email.body)
Expand All @@ -164,18 +158,18 @@ But('I miss the first unlock email', () => {
})

And('request another invitation email', () => {
MainMenu.admin.getOption('User Management', '').click()
TransactionsPage.adminMenu.getOption('User Management', '').click()

cy.get('@user').then((user) => {
UsersPage.searchName().type(user.lastName)
UsersPage.search().click()
UsersPage.searchNameInput().type(user.lastName)
UsersPage.submitButton().click()

UsersPage.searchResults().each((element, index) => {
UsersPage.searchResultsTable().each((element, index) => {
cy.get(`.table-responsive > tbody > tr:nth-child(${index + 1}) td`).eq(2).invoke('text').then((email) => {
if (email === user.email) {
cy.get(`.table-responsive > tbody > tr:nth-child(${index + 1})`).invoke('attr', 'id').then((id) => {
UsersPage.searchResultEdit(id).click()
EditUserPage.resendInvite().click()
UsersPage.searchResultEditButton(id).click()
EditUserPage.resendInviteButton().click()
})
}
})
Expand All @@ -184,17 +178,11 @@ And('request another invitation email', () => {
})

Then('the TCM will confirm the user has been reinvited', () => {
cy.get('.col > .alert')
.should(
'contain.text',
'User reinvited'
)
cy.alertShouldContain('User reinvited')
})

Then('I will see confirmation an unlock email has been sent', () => {
cy.get('.col > .alert')
.should(
'contain.text',
'If your account exists, you will receive an email with instructions for how to unlock it in a few minutes.'
)
cy.alertShouldContain(
'If your account exists, you will receive an email with instructions for how to unlock it in a few minutes.'
)
})
6 changes: 3 additions & 3 deletions cypress/integration/common/sign_in.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import SignInPage from '../../pages/sign_in_page'

Given('I sign in as the {string} user', (user) => {
SignInPage.visit()
SignInPage.email().type(Cypress.config().users[user].email)
SignInPage.password().type(Cypress.env('PASSWORD'))
SignInPage.emailInput().type(Cypress.config().users[user].email)
SignInPage.passwordInput().type(Cypress.env('PASSWORD'))

SignInPage.logIn().click()
SignInPage.submitButton().click()
})
10 changes: 10 additions & 0 deletions cypress/integration/common/transactions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { When } from 'cypress-cucumber-preprocessor/steps'
import TransactionsPage from '../../pages/transactions_page'

When('I select the {string} regime', (regimeName) => {
cy.task('regime', regimeName).then((regime) => {
cy.wrap(regime).as('regime')

TransactionsPage.regimeMenu.getOption(regime.name).click()
})
})
26 changes: 10 additions & 16 deletions cypress/integration/email/expired_tokens/expired_tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,16 @@ And('request another reset password email', () => {
SignInPage.visit()
SignInPage.forgotPasswordLink().click()

ForgotPasswordPage.mainHeading().should('contain', 'Forgot your password?')
ForgotPasswordPage.confirm()

cy.get('@user').then((user) => {
ForgotPasswordPage.email().type(user.email)
ForgotPasswordPage.sendMeResetPasswordInstructions().click()
ForgotPasswordPage.emailInput().type(user.email)
ForgotPasswordPage.submitButton().click()
})

cy.get('.col > .alert')
.should(
'contain.text',
'If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes.'
)
cy.alertShouldContain(
'If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes.'
)
})

And('I try to accept the first invitation email', () => {
Expand All @@ -55,20 +53,16 @@ When('I try to accept the first reset password email', () => {
cy.visit(firstLink).then(() => {
ChangePasswordPage.confirm()

ChangePasswordPage.password().type(Cypress.env('PASSWORD'), { log: false })
ChangePasswordPage.passwordConfirmation().type(Cypress.env('PASSWORD'), { log: false })
ChangePasswordPage.changeMyPassword().click()
ChangePasswordPage.passwordInput().type(Cypress.env('PASSWORD'), { log: false })
ChangePasswordPage.passwordConfirmationInput().type(Cypress.env('PASSWORD'), { log: false })
ChangePasswordPage.submitButton().click()
})
})
})

Then('the TCM will confuse me and not be helpful', () => {
cy.log('User redirected to the sign in page with no message about token being invalid')
cy.get('.col > .alert')
.should(
'contain.text',
'You need to sign in before continuing'
)
cy.alertShouldContain('You need to sign in before continuing')
cy.url().should('include', '/auth/sign_in')
})

Expand Down
8 changes: 4 additions & 4 deletions cypress/integration/email/general/general_steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ When('I follow the link to reset my password', () => {
const link = LastEmailPage.extractResetPasswordLink(lastEmail.last_email.body)

cy.visit(link).then(() => {
ChangePasswordPage.mainHeading().should('contain', 'Change your password')
ChangePasswordPage.confirm()

ChangePasswordPage.password().type(Cypress.env('PASSWORD'), { log: false })
ChangePasswordPage.passwordConfirmation().type(Cypress.env('PASSWORD'), { log: false })
ChangePasswordPage.changeMyPassword().click()
ChangePasswordPage.passwordInput().type(Cypress.env('PASSWORD'), { log: false })
ChangePasswordPage.passwordConfirmationInput().type(Cypress.env('PASSWORD'), { log: false })
ChangePasswordPage.submitButton().click()
})
})
})
Expand Down
13 changes: 3 additions & 10 deletions cypress/integration/export_data/export_data.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import { When, Then, And } from 'cypress-cucumber-preprocessor/steps'
import { Then, And } from 'cypress-cucumber-preprocessor/steps'
import ExportDataPage from '../../pages/export_data_page'
import TransactionsPage from '../../pages/transactions_page'

When('I select the {string} regime', (regime) => {
TransactionsPage.regimeMenu().click()
TransactionsPage.regimeMenuItem(regime).click()
})

And('I proceed to view file download details', () => {
TransactionsPage.transactionMenu().click()
TransactionsPage.downloadTransactionDataMenuItem().click()
TransactionsPage.transactionsMenu.getOption('Download Transaction Data').click()
})

Then('I can view the Data Protection Notice', () => {
ExportDataPage.dataProtectionNotice().should('be.visible')
})

And('I can download transaction data', () => {
ExportDataPage.downloadBtn().should('have.attr', 'href', '/regimes/pas/data_export/download')
// DownloadTransactionFilePage.downloadBtn().click()
ExportDataPage.downloadButton().should('have.attr', 'href', '/regimes/pas/data_export/download')
})
6 changes: 3 additions & 3 deletions cypress/integration/legacy/cfd/cfd_steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ Before(() => {

Given('I sign in as the {word} user', (regime) => {
SignInPage.visit()
SignInPage.email().type(Cypress.config().users[regime].email)
SignInPage.password().type(Cypress.env('PASSWORD'))
SignInPage.emailInput().type(Cypress.config().users[regime].email)
SignInPage.passwordInput().type(Cypress.env('PASSWORD'))

SignInPage.logIn().click()
SignInPage.submitButton().click()
})

Then('the user menu is visible', () => {
Expand Down
6 changes: 3 additions & 3 deletions cypress/integration/legacy/pas/pas_steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ Before(() => {

Given('I sign in as the {word} user', (regime) => {
SignInPage.visit()
SignInPage.email().type(Cypress.config().users[regime].email)
SignInPage.password().type(Cypress.env('PASSWORD'))
SignInPage.emailInput().type(Cypress.config().users[regime].email)
SignInPage.passwordInput().type(Cypress.env('PASSWORD'))

SignInPage.logIn().click()
SignInPage.submitButton().click()
})

Then('the user menu is visible', () => {
Expand Down
6 changes: 3 additions & 3 deletions cypress/integration/legacy/wml/wml_steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ Before(() => {

Given('I sign in as the {word} user', (regime) => {
SignInPage.visit()
SignInPage.email().type(Cypress.config().users[regime].email)
SignInPage.password().type(Cypress.env('PASSWORD'))
SignInPage.emailInput().type(Cypress.config().users[regime].email)
SignInPage.passwordInput().type(Cypress.env('PASSWORD'))

SignInPage.logIn().click()
SignInPage.submitButton().click()
})

Then('the user menu is visible', () => {
Expand Down
Loading

0 comments on commit 25c2c98

Please sign in to comment.