diff --git a/playwright-e2e/lib/component/dsm/navigation/enums/mainMenu.enum.ts b/playwright-e2e/lib/component/dsm/navigation/enums/mainMenu.enum.ts index c1e67a82df..53c85e8440 100644 --- a/playwright-e2e/lib/component/dsm/navigation/enums/mainMenu.enum.ts +++ b/playwright-e2e/lib/component/dsm/navigation/enums/mainMenu.enum.ts @@ -1,5 +1,6 @@ export enum MainMenu { STUDY = 'Study', SELECTED_STUDY = 'Selected study', - MISCELLANEOUS = 'Miscellaneous' + MISCELLANEOUS = 'Miscellaneous', + SAMPLES = 'Samples' } diff --git a/playwright-e2e/lib/component/dsm/navigation/enums/samplesNav.enum.ts b/playwright-e2e/lib/component/dsm/navigation/enums/samplesNav.enum.ts new file mode 100644 index 0000000000..6ff51975c0 --- /dev/null +++ b/playwright-e2e/lib/component/dsm/navigation/enums/samplesNav.enum.ts @@ -0,0 +1,22 @@ +export enum SamplesMenu { + UNSENT_KITS_OVERVIEW = 'Unsent Kits Overview', + REPORT = 'Report', + SUMMARY = 'Summary', + KITS_WITHOUT_LABELS = 'Kits without Labels', + QUEUE = 'Queue', + ERROR = 'Error', + INITIAL_SCAN = 'Initial Scan', + TRACKING_SCAN = 'Tracking Scan', + FINAL_SCAN = 'Final Scan', + RGP_FINAL_SCAN = 'RGP Final Scan', + DISCARD_SAMPLE = 'Discard Sample', + SENT = 'Sent', + RECEIVED = 'Received', + SENT_RECEIVED_OVERVIEW = 'Sent/Received Overview', + DEACTIVATED = 'Deactivated', + SEARCH = 'Search', + KIT_UPLOAD = 'Kit Upload', + STOOL_SAMPLE_UPLOAD = 'Stool Sample Upload', + LABEL_SETTINGS = 'Label Settings', + CLINICAL_ORDERS = 'Clinical Orders' +} \ No newline at end of file diff --git a/playwright-e2e/lib/component/dsm/navigation/navigation.ts b/playwright-e2e/lib/component/dsm/navigation/navigation.ts index ddcc652edd..8a5c3eb6ba 100644 --- a/playwright-e2e/lib/component/dsm/navigation/navigation.ts +++ b/playwright-e2e/lib/component/dsm/navigation/navigation.ts @@ -1,15 +1,18 @@ import { Page } from '@playwright/test'; import Dropdown from 'lib/widget/dsm/dropdown'; import ParticipantListPage from 'pages/dsm/participantList-page'; +import KitUploadPage from 'pages/dsm/kitUpload-page'; import { MainMenu } from './enums/mainMenu.enum'; import { StudyNav } from './enums/studyNav.enum'; import { Study } from './enums/selectStudyNav.enum'; import { NavigationItems } from './navigation-types'; +import { SamplesMenu } from './enums/samplesNav.enum'; import {Miscellaneous} from 'lib/component/dsm/navigation/enums/miscellaneousNav.enum'; export class Navigation { private readonly navigationItems: Partial = { - study: new Map([[StudyNav.PARTICIPANT_LIST, new ParticipantListPage(this.page)]]) + study: new Map([[StudyNav.PARTICIPANT_LIST, new ParticipantListPage(this.page)]]), + samples: new Map([[SamplesMenu.KIT_UPLOAD, new KitUploadPage(this.page)]]) }; constructor(private readonly page: Page) {} @@ -23,6 +26,12 @@ export class Navigation { return (this.navigationItems.study as Map).get(studyNav) as T; } + //Select from the Samples menu in DSM + async selectFromSamples(samplesMenu: SamplesMenu): Promise { + await this.selectFrom(MainMenu.SAMPLES, samplesMenu); + return (this.navigationItems.samples as Map).get(samplesMenu) as T; + } + private async selectFrom(from: MainMenu, selection: Study | StudyNav | Miscellaneous | string): Promise { await new Dropdown(this.page, from).selectOption(selection); } diff --git a/playwright-e2e/lib/component/dsm/samples/enums/kitType.enum.ts b/playwright-e2e/lib/component/dsm/samples/enums/kitType.enum.ts new file mode 100644 index 0000000000..928658143a --- /dev/null +++ b/playwright-e2e/lib/component/dsm/samples/enums/kitType.enum.ts @@ -0,0 +1,6 @@ +//Inputting the various kit types that can be uploaded in DSM +//todo Update as needed as more kit upload tests are created +export enum KitType { + BLOOD = 'BLOOD', + BLOOD_AND_RNA = 'BLOOD & RNA' +} diff --git a/playwright-e2e/package-lock.json b/playwright-e2e/package-lock.json index a121feab19..43e244c98c 100644 --- a/playwright-e2e/package-lock.json +++ b/playwright-e2e/package-lock.json @@ -23,7 +23,7 @@ }, "devDependencies": { "@faker-js/faker": "^7.6.0", - "@playwright/test": "^1.31.1", + "@playwright/test": "^1.32.3", "@types/file-saver": "^2.0.5", "@types/mailparser": "^3.4.0", "@types/uuid": "^8.3.4", @@ -173,13 +173,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz", - "integrity": "sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==", + "version": "1.32.3", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz", + "integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==", "dev": true, "dependencies": { "@types/node": "*", - "playwright-core": "1.31.1" + "playwright-core": "1.32.3" }, "bin": { "playwright": "cli.js" @@ -3002,9 +3002,9 @@ } }, "node_modules/playwright-core": { - "version": "1.31.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.1.tgz", - "integrity": "sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==", + "version": "1.32.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.3.tgz", + "integrity": "sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg==", "dev": true, "bin": { "playwright": "cli.js" diff --git a/playwright-e2e/package.json b/playwright-e2e/package.json index 07b15ce4d2..8f31e478ff 100644 --- a/playwright-e2e/package.json +++ b/playwright-e2e/package.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@faker-js/faker": "^7.6.0", - "@playwright/test": "^1.31.1", + "@playwright/test": "^1.32.3", "@types/file-saver": "^2.0.5", "@types/mailparser": "^3.4.0", "@types/uuid": "^8.3.4", diff --git a/playwright-e2e/pages/dsm/dsm-page-base.ts b/playwright-e2e/pages/dsm/dsm-page-base.ts new file mode 100644 index 0000000000..fc42efc604 --- /dev/null +++ b/playwright-e2e/pages/dsm/dsm-page-base.ts @@ -0,0 +1,15 @@ +import { Page } from '@playwright/test'; +import PageBase from 'pages/page-base'; + +/** + * Data Study Manager (DSM) page base + */ +export abstract class DSMPageBase extends PageBase { + protected constructor(page: Page) { + const { DSM_BASE_URL } = process.env; + if (DSM_BASE_URL == null) { + throw Error(`Invalid DSM base URL: process.env.DSM_BASE_URL=${DSM_BASE_URL}`); + } + super(page, DSM_BASE_URL); + } +} \ No newline at end of file diff --git a/playwright-e2e/pages/dsm/home-page.ts b/playwright-e2e/pages/dsm/home-page.ts index 171db89b48..93e69d48e6 100644 --- a/playwright-e2e/pages/dsm/home-page.ts +++ b/playwright-e2e/pages/dsm/home-page.ts @@ -1,6 +1,6 @@ import { expect, Locator, Page } from '@playwright/test'; -enum Titles { +export enum Titles { WELCOME = 'Welcome to the DDP Study Management System', SELECTED_STUDY = 'You have selected the ', STUDY = ' study.' @@ -17,6 +17,11 @@ export default class HomePage { return this.page.locator('h2'); } + //Should return: "You have selected the {study name here} study." + public getStudySelectionText(study: string): string { + return Titles.SELECTED_STUDY + study + Titles.STUDY; + } + /* Assertions */ async assertWelcomeTitle(): Promise { await expect(this.welcomeTitle).toHaveText(Titles.WELCOME); diff --git a/playwright-e2e/pages/dsm/kitUpload-page.ts b/playwright-e2e/pages/dsm/kitUpload-page.ts new file mode 100644 index 0000000000..3e726350c0 --- /dev/null +++ b/playwright-e2e/pages/dsm/kitUpload-page.ts @@ -0,0 +1,64 @@ +import { expect, Locator, Page } from '@playwright/test'; +import { KitType } from 'lib/component/dsm/samples/enums/kitType.enum' + +export default class KitUploadPage { + private readonly PAGE_TITLE: string = 'Kit Upload'; + constructor(private readonly page: Page) {} + + public getPageTitle(): Locator { + return this.page.getByRole('heading', { name: this.PAGE_TITLE}); + } + + public getKitTypeOption(kitType: KitType): Locator { + const kitOption = this.page.locator(`//mat-checkbox/label[span[normalize-space(text()) = '${kitType}']]`); + return kitOption; + } + + //This option only appears to some study admins based on permissions - so I have a getter here + //so I can use it to verify display of option and also select option later + public getSkipAddressValidationOption(): Locator { + return this.page.locator('label').filter({ hasText: 'Skip address validation on upload' }); + } + + //todo add assertion for display of button + public getBrowseButton(): Locator { + return this.page.getByText('Browse...'); + } + + //todo add assertion for display of button + private getUploadButton(): Locator { + return this.page.getByRole('button', { name: 'Upload Kits' }); + } + + public async selectKitUploadButton(): Promise { + const kitUploadButton = this.getUploadButton(); + await kitUploadButton.click(); + } + + //todo maybe add assertion for the webelements of the popup + public getKitUploadConfirmationPopUp(): Locator { + return this.page.locator('app-modal div').filter({ hasText: 'Please select the participant for which you want to upload a new one'}).nth(3); + } + + public async selectAddNewKitForParticipantOption(guid: string, shortID: string, firstName: string, lastName: string): Promise { + const participantKit = this.page.getByText(`Participant already has a kit. '${guid}' '${shortID}' '${firstName}' '${lastName}'`); + await participantKit.check(); + } + + public async closeKitConfirmationPopUp(): Promise { + const closeButton = this.page.getByRole('button', { name: 'Close' }); + await closeButton.click(); + } + + public async confirmKitUpload(): Promise { + const updateKitsButton = this.page.getByRole('button', { name: 'Upload Kit', exact: true}); + await updateKitsButton.click(); + } + + public async assertKitUploadConfirmationMessage(): Promise { + const verficationMessage = this.page.getByRole('heading', { name: 'All participants were uploaded.'}); + await expect(verficationMessage).toBeVisible(); + } + + //to-do add screenshot verification for the kit upload instructions +} diff --git a/playwright-e2e/tests/rgp/kit-upload-blood-and-rna.spec.ts b/playwright-e2e/tests/rgp/kit-upload-blood-and-rna.spec.ts new file mode 100644 index 0000000000..cd23561e22 --- /dev/null +++ b/playwright-e2e/tests/rgp/kit-upload-blood-and-rna.spec.ts @@ -0,0 +1,58 @@ +import { test, expect, Page } from '@playwright/test'; +import { login } from 'authentication/auth-dsm'; +import HomePage from 'pages/dsm/home-page'; +import { Navigation } from 'lib/component/dsm/navigation/navigation'; +import { WelcomePage } from 'pages/dsm/welcome-page'; +import { Study } from 'lib/component/dsm/navigation/enums/selectStudyNav.enum'; +import KitUploadPage from 'pages/dsm/kitUpload-page'; +import { SamplesMenu } from 'lib/component/dsm/navigation/enums/samplesNav.enum'; +import { KitType } from 'lib/component/dsm/samples/enums/kitType.enum' +import { Titles } from 'pages/dsm/home-page'; + + +test.describe.serial('RGP Kit Upload', () => { + let welcomePage: WelcomePage; + let homePage: HomePage; + let navigation: Navigation; + + test.beforeEach(async ({ page }) => { + await login(page); + welcomePage = new WelcomePage(page); + homePage = new HomePage(page); + navigation = new Navigation(page); + }); + + //Test the Kit Upload page protion of the the process + test('Can upload a Blood & RNA kit via Kit Upload page @functional @rgp', async ({ page }) => { + //Go to DSM in order to do a kit upload (type: "BLOOD & RNA") + await welcomePage.selectStudy(Study.RGP); + + const welcomeTitle = homePage.welcomeTitle; + const studySelection = homePage.studySelectionTitle; + const rgpStudySelectionText = homePage.getStudySelectionText('RGP'); + + //DSM homepage assertions + await expect(welcomeTitle).toHaveText(Titles.WELCOME); + await expect(studySelection).toHaveText(rgpStudySelectionText); + + //Kit Upload page navigation: (Samples -> Kit Upload) + const kitUploadPage = await navigation.selectFromSamples(SamplesMenu.KIT_UPLOAD); + const kitUploadPageTitle = kitUploadPage.getPageTitle(); + await expect(kitUploadPageTitle).toBeVisible(); + + //Select the "Blood and RNA" kit type + const bloodRNAKit = kitUploadPage.getKitTypeOption(KitType.BLOOD_AND_RNA); + await bloodRNAKit.check(); + await expect(bloodRNAKit).toBeChecked(); + + const skipAddressValidationOption = kitUploadPage.getSkipAddressValidationOption(); + + //This test is with a study admin who has permissions to see the skip address validation option + //This test is also using a valid address - so the option will not be selected here + //todo add test to ensure invalid address is accepted if address validation is skipped + await expect(skipAddressValidationOption).toBeVisible(); + + const browseButton = kitUploadPage.getBrowseButton(); + await expect(browseButton).toBeVisible(); + }) +}) diff --git a/playwright-e2e/utils/templates/kitUploadTemplates/kit-upload-template-one-participant.txt b/playwright-e2e/utils/templates/kitUploadTemplates/kit-upload-template-one-participant.txt new file mode 100644 index 0000000000..67d678034c --- /dev/null +++ b/playwright-e2e/utils/templates/kitUploadTemplates/kit-upload-template-one-participant.txt @@ -0,0 +1,2 @@ +participantId shortId firstName lastName street1 street2 city postalCode state country +family_account_guid family_member_subject_id family_member_first_name family_member_last_name address_line_one optional_address_line_two city_of_residence zipcode_of_residence state_or_province_of_residence country_of_residence \ No newline at end of file diff --git a/playwright-e2e/utils/test-utils.ts b/playwright-e2e/utils/test-utils.ts index 86a7566556..eb88e1d7b1 100644 --- a/playwright-e2e/utils/test-utils.ts +++ b/playwright-e2e/utils/test-utils.ts @@ -148,3 +148,5 @@ export const getEnv = (value: string | undefined, defaultValue: string): string } return value == null ? defaultValue : value; }; + +//todo add a function that handles the creation of a text spreadsheet for DSM Kit Upload