Skip to content

Commit

Permalink
MAN-19: Optimize Playwright Tests to Reduce Redundancy in Delius Veri…
Browse files Browse the repository at this point in the history
…fication and Add Tests for Personal Details Match in MAS and Delius (#153)

* MAN-19: Optimize Playwright Tests to Reduce Redundancy in Delius Verification and Add Tests for Personal Details Match in MAS and Delius

* MAN-19: Optimize Playwright Tests to Reduce Redundancy in Delius Verification and Add Tests for Personal Details Match in MAS and Delius

* MAN-19: Optimize Playwright Tests to Reduce Redundancy in Delius Verification and Add Tests for Personal Details Match in MAS and Delius

* MAN-19: Optimize Playwright Tests to Reduce Redundancy in Delius Verification and Add Tests for Personal Details Match in MAS and Delius

* Regenerate package-lock.json after deleting node_modules

* Align package versions with the repository

* Revereted package-lock.json
  • Loading branch information
smalepati99 authored Sep 23, 2024
1 parent 82b1bf9 commit 48821ed
Show file tree
Hide file tree
Showing 12 changed files with 328 additions and 61 deletions.
48 changes: 24 additions & 24 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,27 @@ jobs:
timeout-minutes: 60
runs-on: moj-cloud-platform
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install
- name: Add config
run: cp e2e_tests/.env.example e2e_tests/.env
- name: Run Playwright tests
run: npm run e2e-test
env:
DELIUS_USERNAME: ${{ secrets.E2E_DELIUS_USERNAME }}
DELIUS_PASSWORD: ${{ secrets.E2E_DELIUS_PASSWORD }}
- name: Publish HTML report
if: always()
uses: JamesIves/github-pages-deploy-action@920cbb300dcd3f0568dbc42700c61e2fd9e6139c # v4
with:
folder: playwright-report
target-folder: playwright-report/${{ github.ref_name }}/${{ github.run_id }}/${{ github.run_attempt }}
- name: Output HTML report URL
if: always()
run: echo '[🎭 Playwright HTML Report](https://ministryofjustice.github.io/hmpps-probation-integration-e2e-tests/playwright-report/${{ github.ref_name }}/${{ github.run_id }}/${{ github.run_attempt }})' | tee -a "$GITHUB_STEP_SUMMARY"
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install
- name: Add config
run: cp e2e_tests/.env.example e2e_tests/.env
- name: Run Playwright tests
run: npm run e2e-test
env:
DELIUS_USERNAME: ${{ secrets.E2E_DELIUS_USERNAME }}
DELIUS_PASSWORD: ${{ secrets.E2E_DELIUS_PASSWORD }}
- name: Publish HTML report
if: always()
uses: JamesIves/github-pages-deploy-action@920cbb300dcd3f0568dbc42700c61e2fd9e6139c # v4
with:
folder: playwright-report
target-folder: playwright-report/${{ github.ref_name }}/${{ github.run_id }}/${{ github.run_attempt }}
- name: Output HTML report URL
if: always()
run: echo '[🎭 Playwright HTML Report](https://ministryofjustice.github.io/hmpps-probation-integration-e2e-tests/playwright-report/${{ github.ref_name }}/${{ github.run_id }}/${{ github.run_attempt }})' | tee -a "$GITHUB_STEP_SUMMARY"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,4 @@ npm run e2e-test:debug
### Dependency Checks

The template project has implemented some scheduled checks to ensure that key dependencies are kept up to date.
If these are not desired in the cloned project, remove references to `check_outdated` job from `.circleci/config.yml`
If these are not desired in the cloned project, remove references to `check_outdated` job from `.circleci/config.yml`
2 changes: 1 addition & 1 deletion e2e_tests/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ MANAGE_A_SUPERVISION_URL=https://manage-a-supervision-dev.hmpps.service.justice.

# Credentials
#DELIUS_USERNAME=example
#DELIUS_PASSWORD=example
#DELIUS_PASSWORD=example
29 changes: 29 additions & 0 deletions e2e_tests/steps/delius/add-address/addAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Page } from '@playwright/test'
import {
Address,
buildAddress,
createAddress,
} from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/delius/address/create-address'

const addAddress = async (
page: Page,
crn: string,
type: string,
tableId: string,
): Promise<{ type: string; address: Address }> => {
const addressType = type || 'Main' // Default to 'Main' if type is not provided
const address = buildAddress(addressType)
await createAddress(page, crn, address)
const fetchedAddressType = await getTypeColumnValue(page, tableId)
return { type: fetchedAddressType, address }
}

const getTypeColumnValue = async (page: Page, tableId: string): Promise<string> => {
const typeColumnIndex = await page.locator(`#${tableId} thead th:has-text("Type")`).evaluate(header => {
const headers = Array.from(header.parentElement.children)
return headers.indexOf(header) + 1
})
return page.locator(`#${tableId} tbody tr:first-child td:nth-child(${typeColumnIndex})`).innerText()
}

export default addAddress
31 changes: 31 additions & 0 deletions e2e_tests/steps/delius/add-personal-details/addPersonalDetails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { faker } from '@faker-js/faker/locale/en_GB'
import { Page, expect } from '@playwright/test'
import { findOffenderByCRN } from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/delius/offender/find-offender'
import { retryOnError } from '../utils/utils'

interface PersonalDetails {
mobileNumber: string
telephoneNumber: string
email: string
}

const addPersonalDetails = async (page: Page, crn: string): Promise<PersonalDetails> => {
return retryOnError(async innerPage => {
await findOffenderByCRN(innerPage, crn)
await innerPage.locator('#navigation-include\\:linkNavigation2OffenderIndex').click()
await innerPage.getByRole('link', { name: 'Personal Details' }).click()
await innerPage.getByRole('button', { name: 'Update' }).click()
await expect(innerPage.locator('#content > h1')).toContainText('Update Personal Details')
await innerPage.getByLabel('Mobile Number:').fill(`07${faker.string.numeric(9)}`)
await innerPage.getByLabel('Telephone Number:').fill(faker.phone.number())
await innerPage.getByLabel('Email address:').fill(faker.internet.email())
await innerPage.getByRole('button', { name: 'Save' }).click()
await expect(innerPage.locator('#content > h1')).toContainText('Personal Details')
const mobileNumber = await innerPage.locator('#Mobile\\:outputText').textContent()
const telephoneNumber = await innerPage.locator('#Telephone\\:outputText').textContent()
const email = await innerPage.locator('#Email\\:outputText').textContent()
return { mobileNumber, telephoneNumber, email }
}, page)
}

export default addPersonalDetails
16 changes: 16 additions & 0 deletions e2e_tests/steps/delius/create-offender/createOffender.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Page } from '@playwright/test'
import { login as loginToDelius } from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/delius/login'
import {
deliusPerson,
Person,
} from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/delius/utils/person'
import { createOffender } from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/delius/offender/create-offender'

const loginDeliusAndCreateOffender = async (page: Page): Promise<[Person, string]> => {
await loginToDelius(page)
const person = deliusPerson()
const crn = await createOffender(page, { person })
return [person, crn]
}

export default loginDeliusAndCreateOffender
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Page, expect } from '@playwright/test'
import { faker } from '@faker-js/faker/locale/en_GB'
import { selectOption } from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/delius/utils/inputs'
import { Address } from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/delius/address/create-address'
import { findOffenderByCRN } from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/delius/offender/find-offender'
import { formatDate, retryOnError } from '../utils/utils'

export const createPersonalContact = async (page: Page, crn: string, options: Address) => {
return retryOnError(async innerPage => {
await findOffenderByCRN(innerPage, crn) // Use innerPage instead of page
await innerPage.locator('#navigation-include\\:linkNavigation2OffenderIndex').click()
const firstName = faker.person.firstName()
const lastName = faker.person.lastName()
const contactName = `${firstName} ${lastName}`
const contactPhoneNumber = `07${faker.string.numeric(9)}`
const contactEmail = faker.internet.email()
const notes = `Added Emergency contact on ${formatDate(new Date())}`
await innerPage.locator('#navigation-include\\:linkNavigation3PersonalContact').click()
await expect(innerPage).toHaveTitle('Personal Contacts')
await innerPage.locator('[value="Add Personal Contact"]').click()
await expect(innerPage).toHaveTitle('Add Personal Contact')
await selectOption(innerPage, '#Relationship\\:selectOneMenu', 'Emergency Contact')
await innerPage.locator('#RelationshipToOffender\\:inputText').fill('Friend')
await innerPage.locator('#FirstName\\:inputText').fill(firstName)
await innerPage.locator('#Surname\\:inputText').fill(lastName)
await innerPage.locator('#MobileNumber\\:inputText').fill(contactPhoneNumber)
await innerPage.locator('#EmailAddress\\:inputText').fill(contactEmail)
await innerPage.locator('#Notes\\:notesField').fill(notes)
await innerPage.locator('#HouseNumber\\:inputText').fill(options.buildingNumber)
await innerPage.locator('#StreetName\\:inputText').fill(options.street)
await innerPage.locator('#TownCity\\:inputText').fill(options.cityName)
await innerPage.locator('#County\\:inputText').fill(options.county)
await innerPage.locator('#Postcode\\:inputText').fill(options.zipCode)
await innerPage.locator('[value="Save"]').click()
await expect(innerPage).toHaveTitle('Personal Contacts')

return {
contactName,
contactPhoneNumber,
contactEmail,
}
}, page) // Pass the page object to retryOnError
}

export default createPersonalContact
19 changes: 19 additions & 0 deletions e2e_tests/steps/delius/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Page } from '@playwright/test'
import { DateTime } from 'luxon'

export const retryOnError = async <T>(
fn: (page: Page) => Promise<T>,
page: Page,
retryOnErrorPage = true,
): Promise<T> => {
try {
return await fn(page)
} catch (error) {
if (retryOnErrorPage && (await page.title()) === 'Error Page') {
return fn(page)
}
throw error
}
}

export const formatDate = date => DateTime.fromJSDate(date).toFormat('d MMMM yyyy')
28 changes: 28 additions & 0 deletions e2e_tests/steps/mas/personal-details/personalDetails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Page, expect, Locator } from '@playwright/test'
import { login as loginToManageMySupervision } from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/manage-a-supervision/login'

export const loginMasAndGoToPersonalDetails = async (page: Page, crn: string) => {
await loginToManageMySupervision(page)
await searchForCrn(page, crn)
await page.getByRole('link', { name: 'Personal details' }).first().click()
await expect(page.locator('h1.govuk-heading-l')).toContainText('Personal Details')
}

export const searchForCrn = async (page: Page, crn: string) => {
await page.getByRole('link', { name: 'Search' }).click()
await page.getByLabel('Find a person on probation').fill(crn)
await page.getByRole('button', { name: 'Search' }).click()
await page.locator(`[href$="${crn}"]`).click()
await expect(page).toHaveTitle('Manage a Supervision - Overview')
}

export const assertAddressDetails = async (
page: Page,
locator: Locator,
address: { buildingNumber: string; street: string; cityName: string; county: string; zipCode: string },
) => {
await expect(locator).toContainText(`${address.buildingNumber} ${address.street}`)
await expect(locator).toContainText(address.cityName)
await expect(locator).toContainText(address.county)
await expect(locator).toContainText(address.zipCode)
}
125 changes: 125 additions & 0 deletions e2e_tests/tests/verify-delius-details.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { Browser, BrowserContext, expect, Page, test } from '@playwright/test'
import * as dotenv from 'dotenv'
import { Person } from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/delius/utils/person.js'
import {
buildAddress,
Address,
} from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/delius/address/create-address'
import { login as loginToManageMySupervision } from '@ministryofjustice/hmpps-probation-integration-e2e-tests/steps/manage-a-supervision/login.js'
import loginDeliusAndCreateOffender from '../steps/delius/create-offender/createOffender'

import addPersonalDetails from '../steps/delius/add-personal-details/addPersonalDetails'
import { createPersonalContact } from '../steps/delius/create-personal-contact/createPersonalContact'
import addAddress from '../steps/delius/add-address/addAddress'
import {
assertAddressDetails,
loginMasAndGoToPersonalDetails,
searchForCrn,
} from '../steps/mas/personal-details/personalDetails'
import { formatDate } from '../steps/delius/utils/utils'

dotenv.config({ path: 'e2e_tests/.env' }) // read environment variables into process.env

const todaysDate = formatDate(new Date())

let crn: string
let person: Person
let mobileNumber: string
let telephoneNumber: string
let email: string
let mainAddressType: string
let mainAddress: Address
let addressType: string
let otherAddress: Address
let contactName: string
let contactPhoneNumber: string
let contactEmail: string
let contactAddress: Address

// Create a context and page manually for beforeAll
let browser: Browser
let context: BrowserContext
let page: Page

test.describe('Delius Details Verification', () => {
test.beforeAll(async ({ browser: b }) => {
test.setTimeout(120000)
browser = b
context = await browser.newContext()
page = await context.newPage()

// Log in to Delius and create a new person record
;[person, crn] = await loginDeliusAndCreateOffender(page)

// Add personal details, main address, secondary address, and personal contact details
;({ mobileNumber, telephoneNumber, email } = await addPersonalDetails(page, crn))
;({ type: mainAddressType, address: mainAddress } = await addAddress(page, crn, 'Main', 'mainAddressTable'))
;({ type: addressType, address: otherAddress } = await addAddress(page, crn, 'Secondary', 'otherAddressTable'))

// Initialize contactAddress
contactAddress = buildAddress()
;({ contactName, contactPhoneNumber, contactEmail } = await createPersonalContact(page, crn, contactAddress))
})

test.afterAll(async () => {
// Close the context only after all tests have completed
await context.close()
})

test('Verify the header details', async ({ page: innerPage }) => {
// Login to MaS and search for Crn
await loginToManageMySupervision(innerPage)
await searchForCrn(innerPage, crn)

// Verify the person appears in the search results and crn, name, dob & tier matches
await expect(innerPage.getByTestId('crn')).toContainText(crn)
await expect(innerPage.getByTestId('name')).toContainText(`${person.firstName} ${person.lastName}`)
await expect(innerPage.getByTestId('headerDateOfBirthValue')).toContainText(formatDate(person.dob))
await expect(innerPage.getByTestId('tierValue').first()).toContainText('D0')
})

test('Verify that the personal details of a person in MAS match those in Delius', async ({ page: innerPage }) => {
// Login to MaS and search for Crn
await loginMasAndGoToPersonalDetails(innerPage, crn)

// Verify that the personal details match those in Delius
await Promise.all([
expect(innerPage.getByTestId('telephoneNumberValue')).toContainText(telephoneNumber),
expect(innerPage.getByTestId('mobileNumberValue')).toContainText(mobileNumber),
expect(innerPage.getByTestId('emailAddressValue')).toContainText(email),
])

// Verify that the main address details match those in Delius
await innerPage.getByText('View address details').click()
await Promise.all([
assertAddressDetails(innerPage, innerPage.getByTestId('mainAddressValue'), mainAddress),
expect(innerPage.getByTestId('addressTypeValue')).toContainText(`${mainAddressType} (verified)`),
expect(innerPage.getByTestId('mainAddressStartDateValue')).toContainText(todaysDate),
expect(innerPage.getByTestId('mainAddressNotesValue')).toContainText('Notes added for Main address'),
])

// Verify secondary address details match those in Delius
await innerPage.getByRole('link', { name: /View all addresses/ }).click()
await expect(innerPage.locator('h1')).toContainText('Addresses')
await Promise.all([
assertAddressDetails(innerPage, innerPage.getByTestId('otherAddressValue1'), otherAddress),
expect(innerPage.getByTestId('otherAddressTypeValue1')).toContainText(`${addressType} (verified)`),
expect(innerPage.getByTestId('otherAddressStartDateValue1')).toContainText(todaysDate),
expect(innerPage.getByTestId('otherAddressNotesValue1')).toContainText('Notes added for Secondary address'),
])

// Verify that the emergency contact details match those in Delius
await innerPage.getByRole('link', { name: 'Personal details' }).first().click()
await innerPage.getByRole('link', { name: `${contactName} – ` }).click()
await Promise.all([
expect(innerPage.getByTestId('contactRelationshipTypeHeaderValue')).toContainText('Emergency Contact'),
expect(innerPage.getByTestId('contactNameValue')).toContainText(contactName),
expect(innerPage.getByTestId('contactRelationshipValue')).toContainText('Friend'),
assertAddressDetails(innerPage, innerPage.getByTestId('contactAddressValue'), contactAddress),
expect(innerPage.getByTestId('contactPhoneValue')).toContainText(contactPhoneNumber),
expect(innerPage.getByTestId('contactEmailValue')).toContainText(contactEmail),
expect(innerPage.getByTestId('contactStartDateValue')).toContainText(todaysDate),
expect(innerPage.getByTestId('contactNotesValue')).toContainText(`Added Emergency contact on ${todaysDate}`),
])
})
})
32 changes: 0 additions & 32 deletions e2e_tests/tests/view-case.spec.ts

This file was deleted.

Loading

0 comments on commit 48821ed

Please sign in to comment.