diff --git a/src/client/testing/page-objects/index.ts b/src/client/testing/page-objects/index.ts index 8346ad1b3..840af931c 100644 --- a/src/client/testing/page-objects/index.ts +++ b/src/client/testing/page-objects/index.ts @@ -8,6 +8,7 @@ import SiteSelectionPage from './site-selection'; import SitePage from './site'; import NotAuthorizedPage from './unauthorized'; import UserManagementPage from './user-management'; +import ServiceNotFoundPage from './service-not-found'; import EditAccessNeedsPage from './change-site-details-pages/edit-access-need'; import EditInformationForCitizensPage from './change-site-details-pages/edit-citizen-information'; @@ -62,4 +63,5 @@ export { EditAvailabilityConfirmedPage, MonthViewAvailabilityPage, WeekViewAvailabilityPage, + ServiceNotFoundPage, }; diff --git a/src/client/testing/page-objects/service-not-found.ts b/src/client/testing/page-objects/service-not-found.ts new file mode 100644 index 000000000..546d007ca --- /dev/null +++ b/src/client/testing/page-objects/service-not-found.ts @@ -0,0 +1,13 @@ +import { type Locator, type Page } from '@playwright/test'; +import RootPage from './root'; + +export default class ServiceNotFoundPage extends RootPage { + readonly title: Locator; + + constructor(page: Page) { + super(page); + this.title = page.getByRole('heading', { + name: 'Sorry, there is a problem with this service', + }); + } +} diff --git a/src/client/testing/tests/change-availability/cancel-date-range.spec.ts b/src/client/testing/tests/change-availability/cancel-date-range.spec.ts index 9ad30014f..a7524e34b 100644 --- a/src/client/testing/tests/change-availability/cancel-date-range.spec.ts +++ b/src/client/testing/tests/change-availability/cancel-date-range.spec.ts @@ -1,4 +1,8 @@ -import { OAuthLoginPage, RootPage } from '@testing-page-objects'; +import { + OAuthLoginPage, + RootPage, + ServiceNotFoundPage, +} from '@testing-page-objects'; import { Site } from '@types'; import { test, expect, overrideFeatureFlag } from '../../fixtures'; @@ -32,25 +36,23 @@ test.describe.configure({ mode: 'serial' }); await rootPage.pageContentLogInButton.click(); await oAuthPage.signIn(); - // await page.goto('/manage-your-appointments/sites'); await page.waitForURL(`/manage-your-appointments/sites`); await page .getByRole('link', { name: 'View Church Lane Pharmacy' }) .click(); + await page.waitForURL(`/manage-your-appointments/site/${site.id}`); await page .getByRole('link', { name: 'View availability and manage' }) .click(); - await page.waitForURL( `/manage-your-appointments/site/${site.id}/view-availability`, ); }); test('Cancel a date range monthly page', async ({ page }) => { + const serviceNotFoundPage = new ServiceNotFoundPage(page); if (!CancelADateRangeFlagEnabled) { - expect( - page.getByRole('button', { name: 'Change availability' }), - ).not.toBeVisible(); + await expect(serviceNotFoundPage.title).not.toBeVisible(); return; } @@ -58,18 +60,15 @@ test.describe.configure({ mode: 'serial' }); page.getByRole('button', { name: 'Change availability' }), ).toBeVisible(); await page.getByRole('button', { name: 'Change availability' }).click(); - await expect(page).toHaveURL(/.*\/change-availability/, { - timeout: 15000, - }); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); await expect( page.getByRole('link', { name: 'Back', exact: true }), ).toBeVisible(); - await page - .getByRole('link', { name: 'Back', exact: true }) - .click({ delay: 100 }); - await expect(page).toHaveURL( + await page.getByRole('link', { name: 'Back', exact: true }).click(); + await page.waitForURL( `/manage-your-appointments/site/${site.id}/view-availability`, - { timeout: 15000 }, ); }); @@ -81,10 +80,9 @@ test.describe.configure({ mode: 'serial' }); .getByRole('link') .click(); + const serviceNotFoundPage = new ServiceNotFoundPage(page); if (!CancelADateRangeFlagEnabled) { - expect( - page.getByRole('button', { name: 'Change availability' }), - ).not.toBeVisible(); + await expect(serviceNotFoundPage.title).not.toBeVisible(); return; } @@ -92,14 +90,14 @@ test.describe.configure({ mode: 'serial' }); page.getByRole('button', { name: 'Change availability' }), ).toBeVisible(); await page.getByRole('button', { name: 'Change availability' }).click(); - await expect(page).toHaveURL(/.*\/change-availability/); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); await expect( page.getByRole('link', { name: 'Back', exact: true }), ).toBeVisible(); - await page - .getByRole('link', { name: 'Back', exact: true }) - .click({ delay: 100 }); - await expect(page).toHaveURL(/.*\/view-availability(\/week)?/); + await page.getByRole('link', { name: 'Back', exact: true }).click(); + await page.waitForURL(/.*\/view-availability(\/week)?/); }); test.skip('Cancel a date range daily page', async ({ page }) => { @@ -109,22 +107,26 @@ test.describe.configure({ mode: 'serial' }); .filter({ hasText: '23 February to 1 March' }) .getByRole('link') .click(); - await expect(page).toHaveURL(/.*\/view-availability\/week/); + await page.waitForURL(/.*\/view-availability\/week/); const wednesdaySection = page .locator('li') .filter({ hasText: 'Wednesday 25 February' }); await expect( wednesdaySection.getByText(/Total appointments: [1-9]\d*/), - ).toBeVisible({ timeout: 20000 }); + ).toBeVisible(); // If the Total appointments: 0 then the "View daily appointments" link will not be visible await page .getByRole('link', { name: 'View daily appointments' }) .click(); + await page.waitForURL( + new RegExp( + `.*\/site\/${site.id}\/view-availability\/daily-appointments.*`, + ), + ); + const serviceNotFoundPage = new ServiceNotFoundPage(page); if (!CancelADateRangeFlagEnabled) { - expect( - page.getByRole('button', { name: 'Change availability' }), - ).not.toBeVisible(); + await expect(serviceNotFoundPage.title).not.toBeVisible(); return; } @@ -132,49 +134,58 @@ test.describe.configure({ mode: 'serial' }); page.getByRole('button', { name: 'Change availability' }), ).toBeVisible(); await page.getByRole('button', { name: 'Change availability' }).click(); - await expect(page).toHaveURL( + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); await expect( page.getByRole('link', { name: 'Back', exact: true }), ).toBeVisible(); - await page - .getByRole('link', { name: 'Back', exact: true }) - .click({ delay: 100 }); - await expect(page).toHaveURL( - /.*\/view-availability(\/daily-appointments)?/, - ); + await page.getByRole('link', { name: 'Back', exact: true }).click(); + await page.waitForURL(/.*\/view-availability(\/daily-appointments)?/); }); }); test.describe('Select Dates To Cancel', () => { test.beforeEach(async ({ page, getTestSite }) => { - site = getTestSite(1); + site = getTestSite(2); rootPage = new RootPage(page); oAuthPage = new OAuthLoginPage(page); await rootPage.goto(); await rootPage.pageContentLogInButton.click(); await oAuthPage.signIn(); + await page.waitForURL(`/manage-your-appointments/sites`); + await page + .getByRole('link', { name: 'View Church Lane Pharmacy' }) + .click(); + await page.waitForURL(`/manage-your-appointments/site/${site.id}`); + await page + .getByRole('link', { name: 'View availability and manage' }) + .click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/view-availability`, + ); }); test('Select dates to cancel error, mandatory field validation', async ({ page, }) => { + const serviceNotFoundPage = new ServiceNotFoundPage(page); if (!CancelADateRangeFlagEnabled) { - test.skip(); - - //TODO assert not found page?? - //Sorry, we could not find that page + await expect(serviceNotFoundPage.title).not.toBeVisible(); + return; } - //TODO navigate there rather than goto - await page.goto( + await page.getByRole('button', { name: 'Change availability' }).click(); + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); - await page.getByRole('button', { name: 'Continue to cancel' }).click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); + await page.getByRole('button', { name: 'Continue' }).click(); // Use locator that finds the error text specifically @@ -182,17 +193,13 @@ test.describe.configure({ mode: 'serial' }); .locator('.nhsuk-form-group') .filter({ hasText: 'Start date' }) .locator('.nhsuk-error-message'); - await expect(startDateError).toContainText('Enter a start date', { - timeout: 15000, - }); + await expect(startDateError).toContainText('Enter a start date'); const endDateError = page .locator('.nhsuk-form-group') .filter({ hasText: 'End date' }) .locator('.nhsuk-error-message'); - await expect(endDateError).toContainText('Enter an end date', { - timeout: 15000, - }); + await expect(endDateError).toContainText('Enter an end date'); await expect( page.locator('.nhsuk-u-visually-hidden').first(), @@ -202,8 +209,7 @@ test.describe.configure({ mode: 'serial' }); page.getByRole('link', { name: 'Back', exact: true }), ).toBeVisible(); await page.getByRole('link', { name: 'Back', exact: true }).click(); - - await expect(page).toHaveURL( + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); }); @@ -211,18 +217,20 @@ test.describe.configure({ mode: 'serial' }); test('Select dates to cancel error, must be in the future', async ({ page, }) => { + const serviceNotFoundPage = new ServiceNotFoundPage(page); if (!CancelADateRangeFlagEnabled) { - test.skip(); - - //TODO assert not found page?? - //Sorry, we could not find that page + await expect(serviceNotFoundPage.title).not.toBeVisible(); + return; } - await page.goto( + await page.getByRole('button', { name: 'Change availability' }).click(); + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); - await page.getByRole('button', { name: 'Continue to cancel' }).click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); // Calculate yesterday const yesterday = new Date(); @@ -253,18 +261,17 @@ test.describe.configure({ mode: 'serial' }); await expect( errorMessages.filter({ hasText: /Start date/ }), - ).toContainText(/must be in the future/i, { timeout: 15000 }); + ).toContainText(/must be in the future/i); await expect( errorMessages.filter({ hasText: /End date/ }), - ).toContainText(/must be in the future/i, { timeout: 15000 }); + ).toContainText(/must be in the future/i); await expect( page.getByRole('link', { name: 'Back', exact: true }), ).toBeVisible(); await page.getByRole('link', { name: 'Back', exact: true }).click(); - - await expect(page).toHaveURL( + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); }); @@ -272,18 +279,20 @@ test.describe.configure({ mode: 'serial' }); test('Select dates to cancel error, end date must be after the start date', async ({ page, }) => { + const serviceNotFoundPage = new ServiceNotFoundPage(page); if (!CancelADateRangeFlagEnabled) { - test.skip(); - - //TODO assert not found page?? - //Sorry, we could not find that page + await expect(serviceNotFoundPage.title).not.toBeVisible(); + return; } - await page.goto( + await page.getByRole('button', { name: 'Change availability' }).click(); + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); - await page.getByRole('button', { name: 'Continue to cancel' }).click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); // Start is 5 days from now, End is 2 days from now const now = new Date(); @@ -314,7 +323,6 @@ test.describe.configure({ mode: 'serial' }); .locator('#end-date-year') .fill(endDate.getFullYear().toString()); - // Submit await page .getByRole('button', { name: 'Continue', exact: true }) .click(); @@ -325,16 +333,13 @@ test.describe.configure({ mode: 'serial' }); .filter({ hasText: 'End date' }); await expect( errorContainer.locator('.nhsuk-error-message'), - ).toContainText(/End date must be on or after the start date/i, { - timeout: 15000, - }); + ).toContainText(/End date must be on or after the start date/i); await expect( page.getByRole('link', { name: 'Back', exact: true }), ).toBeVisible(); await page.getByRole('link', { name: 'Back', exact: true }).click(); - - await expect(page).toHaveURL( + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); }); @@ -342,18 +347,20 @@ test.describe.configure({ mode: 'serial' }); test('Select dates to cancel error within 3 months - 90 days or less', async ({ page, }) => { + const serviceNotFoundPage = new ServiceNotFoundPage(page); if (!CancelADateRangeFlagEnabled) { - test.skip(); - - //TODO assert not found page?? - //Sorry, we could not find that page + await expect(serviceNotFoundPage.title).not.toBeVisible(); + return; } - await page.goto( + await page.getByRole('button', { name: 'Change availability' }).click(); + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); - await page.getByRole('button', { name: 'Continue to cancel' }).click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); const now = new Date(); @@ -388,9 +395,7 @@ test.describe.configure({ mode: 'serial' }); await page .getByRole('button', { name: 'Continue', exact: true }) .click(); - - // No validation errors, it redirects - await expect(page).toHaveURL( + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); }); @@ -398,18 +403,20 @@ test.describe.configure({ mode: 'serial' }); test('Select dates to cancel error within 3 months - greater than 90 days', async ({ page, }) => { + const serviceNotFoundPage = new ServiceNotFoundPage(page); if (!CancelADateRangeFlagEnabled) { - test.skip(); - - //TODO assert not found page?? - //Sorry, we could not find that page + await expect(serviceNotFoundPage.title).not.toBeVisible(); + return; } - await page.goto( + await page.getByRole('button', { name: 'Change availability' }).click(); + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); - await page.getByRole('button', { name: 'Continue to cancel' }).click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); const now = new Date(); @@ -452,7 +459,7 @@ test.describe.configure({ mode: 'serial' }); await expect( startDateGroup.locator('.nhsuk-error-message'), - ).toContainText('Start date must be', { timeout: 15000 }); + ).toContainText('Start date must be'); // Target the End Date group by its specific legend const endDateGroup = page.locator('.nhsuk-form-group').filter({ @@ -461,16 +468,194 @@ test.describe.configure({ mode: 'serial' }); await expect( endDateGroup.locator('.nhsuk-error-message'), - ).toContainText('End date must be', { timeout: 15000 }); + ).toContainText('End date must be'); await expect( page.getByRole('link', { name: 'Back', exact: true }), ).toBeVisible(); await page.getByRole('link', { name: 'Back', exact: true }).click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); + }); + }); - await expect(page).toHaveURL( + test.describe('Cannot Cancel', () => { + test.beforeEach(async ({ page, getTestSite }) => { + site = getTestSite(2); + rootPage = new RootPage(page); + oAuthPage = new OAuthLoginPage(page); + + await rootPage.goto(); + await rootPage.pageContentLogInButton.click(); + await oAuthPage.signIn(); + + await page.goto('/manage-your-appointments/sites'); + await page.waitForURL(`/manage-your-appointments/sites`); + await page + .getByRole('link', { name: 'View Church Lane Pharmacy' }) + .click(); + await page.waitForURL(`/manage-your-appointments/site/${site.id}`); + await page + .getByRole('link', { name: 'View availability and manage' }) + .click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/view-availability`, + ); + }); + + test('Cannot cancel these sessions - Return to view availability', async ({ + page, + }) => { + const serviceNotFoundPage = new ServiceNotFoundPage(page); + if (!CancelADateRangeFlagEnabled) { + await expect(serviceNotFoundPage.title).not.toBeVisible(); + return; + } + + await page.getByRole('button', { name: 'Change availability' }).click(); + await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); + await expect( + page.getByRole('button', { name: 'Continue to cancel' }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Continue to cancel' }).click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); + + const now = new Date(); + + const startDate = new Date(now); + startDate.setDate(now.getDate() + 1); + + const endDate = new Date(now); + endDate.setDate(now.getDate() + 23); + + await page + .locator('#start-date-day') + .fill(startDate.getDate().toString()); + await page + .locator('#start-date-month') + .fill((startDate.getMonth() + 1).toString()); + await page + .locator('#start-date-year') + .fill(startDate.getFullYear().toString()); + + await page.locator('#end-date-day').fill(endDate.getDate().toString()); + await page + .locator('#end-date-month') + .fill((endDate.getMonth() + 1).toString()); + await page + .locator('#end-date-year') + .fill(endDate.getFullYear().toString()); + + await page + .getByRole('button', { name: 'Continue', exact: true }) + .click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); + + await page + .getByRole('button', { name: 'Return to view availability' }) + .click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/view-availability`, + ); + }); + + test('Cannot cancel these sessions - Select different dates', async ({ + page, + }) => { + const serviceNotFoundPage = new ServiceNotFoundPage(page); + if (!CancelADateRangeFlagEnabled) { + await expect(serviceNotFoundPage.title).not.toBeVisible(); + return; + } + + await page.getByRole('button', { name: 'Change availability' }).click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); + await expect( + page.getByRole('button', { name: 'Continue to cancel' }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Continue to cancel' }).click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); + + const now = new Date(); + + const startDate = new Date(now); + startDate.setDate(now.getDate() + 1); + + const endDate = new Date(now); + endDate.setDate(now.getDate() + 23); + + await page + .locator('#start-date-day') + .fill(startDate.getDate().toString()); + await page + .locator('#start-date-month') + .fill((startDate.getMonth() + 1).toString()); + await page + .locator('#start-date-year') + .fill(startDate.getFullYear().toString()); + + await page.locator('#end-date-day').fill(endDate.getDate().toString()); + await page + .locator('#end-date-month') + .fill((endDate.getMonth() + 1).toString()); + await page + .locator('#end-date-year') + .fill(endDate.getFullYear().toString()); + + await page + .getByRole('button', { name: 'Continue', exact: true }) + .click(); + + await expect( + page.getByRole('button', { + name: 'Select different dates', + exact: true, + }), + ).toBeVisible(); + + await page + .getByRole('button', { name: 'Select different dates' }) + .click(); + + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/change-availability`, + ); + + await expect( + page.getByRole('heading', { name: 'Select dates to cancel' }), + ).toBeVisible(); + + // Verify that the inputs still contain the dates previously populated + await expect(page.locator('#start-date-day')).toHaveValue( + startDate.getDate().toString(), + ); + await expect(page.locator('#start-date-month')).toHaveValue( + (startDate.getMonth() + 1).toString(), + ); + await expect(page.locator('#start-date-year')).toHaveValue( + startDate.getFullYear().toString(), + ); + + await expect(page.locator('#end-date-day')).toHaveValue( + endDate.getDate().toString(), + ); + await expect(page.locator('#end-date-month')).toHaveValue( + (endDate.getMonth() + 1).toString(), + ); + await expect(page.locator('#end-date-year')).toHaveValue( + endDate.getFullYear().toString(), + ); }); }); }); @@ -494,24 +679,36 @@ test.describe.configure({ mode: 'serial' }); }); test.beforeEach(async ({ page, getTestSite }) => { - site = getTestSite(1); + site = getTestSite(2); rootPage = new RootPage(page); oAuthPage = new OAuthLoginPage(page); await rootPage.goto(); await rootPage.pageContentLogInButton.click(); await oAuthPage.signIn(); - await page.waitForURL(`/manage-your-appointments/sites`); - }); - test('Verify number of steps when bookings exist', async ({ page }) => { - await page.goto( - `/manage-your-appointments/site/${site.id}/change-availability`, + await page.goto('/manage-your-appointments/sites'); + await page.waitForURL(`/manage-your-appointments/sites`); + await page + .getByRole('link', { name: 'View Church Lane Pharmacy' }) + .click(); + await page.waitForURL(`/manage-your-appointments/site/${site.id}`); + await page + .getByRole('link', { name: 'View availability and manage' }) + .click(); + await page.waitForURL( + `/manage-your-appointments/site/${site.id}/view-availability`, ); + await expect( + page.getByRole('button', { name: 'Change availability' }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Change availability' }).click(); await page.waitForURL( `/manage-your-appointments/site/${site.id}/change-availability`, ); + }); + test('Verify number of steps when bookings exist', async ({ page }) => { const listItems = page .locator('ol li') .filter({ hasNot: page.getByRole('button') });