Skip to content

PEPPER-587: RGP DSM playwright tests_assert creation of new participant by checking DSM #2048

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bbd993f
PR for checking the new RGP pt is located in DSM
Rinchan20 Mar 2, 2023
e14f1f2
Merge branch 'develop' into rgp_dsm_playwright_tests
Rinchan20 Mar 2, 2023
8726024
Update adult-enrollment.spec.ts
Rinchan20 Mar 2, 2023
3af6483
ESLint errors fixed
Rinchan20 Mar 2, 2023
a5c04ad
Update faker-utils.ts
Rinchan20 Mar 2, 2023
384cba6
implementing improvement suggestions
Rinchan20 Mar 7, 2023
2caa969
Merge branch 'develop' into rgp_dsm_playwright_tests
Rinchan20 Mar 7, 2023
f9dba01
Merge branch 'develop' into rgp_dsm_playwright_tests
Rinchan20 Mar 15, 2023
426c4d9
WIP
Rinchan20 May 12, 2023
5c6f7e7
contact info automated
Rinchan20 May 15, 2023
39752e2
study status automated
Rinchan20 May 15, 2023
e48a821
medical records automated
Rinchan20 May 15, 2023
a8239b2
primary sample automated
Rinchan20 May 15, 2023
d469d71
tissue section in progress
Rinchan20 May 15, 2023
8867a4d
tissue section automated
Rinchan20 May 15, 2023
694159d
checking family ids WIP
Rinchan20 May 15, 2023
5f72c4c
added initial add family member automation
Rinchan20 May 18, 2023
be80184
added separate test for non copied family member
Rinchan20 May 18, 2023
1bd3cf1
implemented feedback
Rinchan20 May 19, 2023
1c5d304
a few more assertions
Rinchan20 May 19, 2023
5b984b4
slight tweaks and validation for textareas
Rinchan20 May 19, 2023
a59a080
Merge branch 'develop' into rgp_dsm_playwright_tests
Rinchan20 May 19, 2023
34c9344
some tweaks with textarea automation wip
Rinchan20 May 19, 2023
d22331b
tweaks and textcontent automation done
Rinchan20 May 19, 2023
fa4cd15
Merge branch 'develop' into rgp_dsm_playwright_tests
Rinchan20 May 19, 2023
cdb83e4
Update adult-enrollment.spec.ts
Rinchan20 May 24, 2023
32cad26
Merge branch 'develop' into rgp_dsm_playwright_tests
Rinchan20 May 24, 2023
7a44501
moved things to match develop update
Rinchan20 May 24, 2023
2d26c89
fixed typo in family member enums
Rinchan20 May 24, 2023
861cb6c
added extra check for non-copied family member
Rinchan20 May 25, 2023
121cb9f
tweaks
Rinchan20 May 31, 2023
db2305e
Create rgpFinalScan-page.ts
Rinchan20 May 31, 2023
ac2d1e3
rgp kit upload - work in progress
Rinchan20 Jun 5, 2023
f20dc8b
added fs-extra in an attempt to get file location recognized
Rinchan20 Jun 5, 2023
b1962b3
WIP
Rinchan20 Jun 5, 2023
5ee26be
WIP
Rinchan20 Jun 5, 2023
4ad5bd7
Merge branch 'develop' into rgp_dsm_playwright_tests
Rinchan20 Jun 5, 2023
89beaed
WIP
Rinchan20 Jun 7, 2023
ee60525
changes to get most recent playwright participant
Rinchan20 Jun 7, 2023
fe0d7a2
update tests to use new method for getting guids
Rinchan20 Jun 7, 2023
5a22e4f
Merge branch 'develop' into rgp_dsm_playwright_tests
Rinchan20 Jun 7, 2023
0eb71e8
kit upload part 99 percent done want to add more checks
Rinchan20 Jun 8, 2023
7cec3b9
some changes after some tests failed in docker
Rinchan20 Jun 8, 2023
7c84f34
participant guid changes
Rinchan20 Jun 8, 2023
4642393
some cleanup and adding results of screenshot comparison
Rinchan20 Jun 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions playwright-e2e/.eslintignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.eslintrc.js
.eslintrc
node_modules/
build/
playwright-report/
Expand Down
1 change: 1 addition & 0 deletions playwright-e2e/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ junit/
**/*.iml
**/*.zip
**/*.log

1 change: 1 addition & 0 deletions playwright-e2e/data/fake-user.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"middleName": "Tester",
"lastName": "Playwright",
"fullName": "E2E Tester Playwright",
"participantGuid": "",
"birthDate": {
"MM": "10",
"DD": "02",
Expand Down
5 changes: 3 additions & 2 deletions playwright-e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions playwright-e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-prettier": "^4.2.1",
"prettier": "^2.8.4",
"typescript": "^4.9.5",
"uuid": "^9.0.0"
},
"dependencies": {
Expand All @@ -41,8 +42,7 @@
"googleapis": "^105.0.0",
"lodash": "^4.17.21",
"mailparser": "^3.6.3",
"node-vault": "^0.9.22",
"typescript": "^4.9.5"
"node-vault": "^0.9.22"
},
"engines": {
"node": ">=14"
Expand Down
54 changes: 52 additions & 2 deletions playwright-e2e/pages/dsm/home-page.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,63 @@
import { expect, Locator, Page } from '@playwright/test';
import { HomePageInterface } from 'pages/page-interface';
import { DSMPageBase } from './page-base';
import * as auth from 'authentication/auth-dsm';
import Select from 'lib/widget/select';
import { Navigation } from 'lib/component/dsm/navigation/navigation';
import { StudyNav } from 'lib/component/dsm/navigation/enums/studyNav.enum';
import ParticipantListPage from 'pages/dsm/participantList-page';
import ParticipantPage from 'pages/dsm/participant-page';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import:
import ParticipantPage from 'pages/dsm/participant-page';


enum Titles {
WELCOME = 'Welcome to the DDP Study Management System',
SELECTED_STUDY = 'You have selected the ',
STUDY = ' study.'
}

export default class HomePage {
constructor(private readonly page: Page) {}
export default class HomePage extends DSMPageBase implements HomePageInterface {
private readonly navigation: Navigation;
private readonly participantList: ParticipantListPage;

constructor(page: Page) {
super(page);
this.navigation = new Navigation(page);
this.participantList = new ParticipantListPage(page);
}

/**
* Log into DSM application
* @param opts
*/
async logIn(opts: { email?: string; password?: string; waitForNavigation?: boolean } = {}) {
await auth.login(this.page);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think when you are on this page, you are already logged in, therefore I don't think this method is useful here. What do you think about removing it?
Please correct me if I'm wrong.

Usually, we just call the login() method from beforeEach.
It would be good if we considered later adding Login Page Object Model, which would handle all login-related things

}

async selectStudy(study: string, page: Page) {
const studySelector = await new Select(page, { label: 'Select study' });
Copy link
Contributor

@GiCharkviani GiCharkviani Mar 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead, you could use this way: this.navigation.selectStudy()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, anyway, this method is not used anywhere. Maybe remove it?

await studySelector.click();
await studySelector.selectOption(study);
}

async selectStudyMenuOption(menuOption: string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not used as well

if (menuOption === 'Participant List') {
//Select the Participant List from the available Study menu options
await this.navigation.selectFromStudy<ParticipantListPage>(StudyNav.PARTICIPANT_LIST);
await this.participantList.waitForReady();
await this.participantList.assertPageTitle();
await this.participantList.waitForReady();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line #47 is not needed. It's dupl of line #45. assertPageTitle() does not click.

}
}

async selectCustomizeViewOption(columnGroup: string, columnName: string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not used as well.
If you need to use customize view functionality anywhere, you can find it in CustomizeView class, which is located in the Filters class, that is available (according to DSM structure) in the ParticipantListPage class (POM).

//Select column group e.g. Participant Columns
await this.page.locator('text=Customize View >> button').click();
await this.page.locator(`text='${columnGroup}' >> button`).click();
await this.page.locator(`text='${columnName}' >> button`).click();
}

async waitForReady(): Promise<void> {
await this.page.waitForFunction(() => document.title === 'DDP Study Management');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, document.title === 'DDP Study Managment' tells me current page is the expected page. But it doesn't checks page is fully loaded and ready for user interaction. Just wanted you to know this, it's okay to leave it as is.


public get welcomeTitle(): Locator {
return this.page.locator('h1');
Expand Down
15 changes: 15 additions & 0 deletions playwright-e2e/pages/dsm/page-base.ts
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏻👍🏻

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);
}
}
18 changes: 17 additions & 1 deletion playwright-e2e/pages/dsm/participantList-page.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { expect, Locator, Page } from '@playwright/test';

import { waitForNoSpinner } from 'utils/test-utils';
import { Filters } from 'lib/component/dsm/filters/filters';
import { ParticipantListTable } from 'lib/component/dsm/tables/participantListTable';

export enum SearchFieldLabel {
ShortId = 'Short ID',
ParticipantID = 'Participant ID'
}

export default class ParticipantListPage {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ParticipantListPage extends DSMPageBase? because you created new abstract class DSMPageBase.

private readonly PAGE_TITLE: string = 'Participant List';

Expand All @@ -20,6 +24,18 @@ export default class ParticipantListPage {
await waitForNoSpinner(this.page);
}

public async filterListByParticipantGUID(participantGUID: string): Promise<void> {
await this.page.locator('text=Customize View >> button').click();
Copy link
Contributor

@GiCharkviani GiCharkviani Mar 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead, you could use customize view functionality, which is available from the _filters field. Like so:

const customizeViewPanel = this.filters.customizeViewPanel;
await customizeViewPanel.open();
await customizeViewPanel.selectColumns('Participant Columns', ['Participant ID']);

const searchPanel = this.filters.searchPanel;
await searchPanel.open();
await searchPanel.text('Participant ID', {textValue:participantGUID});
await searchPanel.search();

And later to make assertions.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good - will make the changes

await this.page.locator('text=Participant Columns').click();
await this.page.locator(`//mat-checkbox/label[span[normalize-space(text()) = 'Participant ID']]`).check();
await this.page.locator('text=Search >> button').click();
await this.page.locator(`//input[@data-placeholder='Participant ID']`).fill(participantGUID);
await this.page.locator("button:has-text('Search') >> nth=0").click();
await this.page.getByRole('cell', { name: participantGUID }).click();
await expect(this.page.getByRole('heading', { name: 'Participant Page' })).toBeVisible();
await expect(this.page.getByRole('cell', { name: participantGUID })).toBeVisible();
}

public async addBulkCohortTags(): Promise<void> {
await this.page.locator('//button[.//*[@tooltip="Bulk Cohort Tag"]]').click();
}
Expand Down
35 changes: 32 additions & 3 deletions playwright-e2e/tests/rgp/adult-enrollment.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@ import TellUsAboutYourFamilyPage from 'pages/rgp/enrollment/tell-us-about-your-f
import TellUsYourStoryPage, { WHO } from 'pages/rgp/enrollment/tell-us-your-story-page';
import HomePage from 'pages/rgp/home-page';
import { setAuth0UserEmailVerified } from 'utils/api-utils';

const { RGP_USER_EMAIL, RGP_USER_PASSWORD } = process.env;
import { request } from 'http';
import { updateTypeAssertion } from 'typescript';
import { setPatientParticipantGuid } from 'utils/faker-utils';
import dsmHome from 'pages/dsm/home-page';
import * as dsmAuth from 'authentication/auth-dsm';
import Select from 'lib/widget/select';
import { Navigation } from 'lib/component/dsm/navigation/navigation';
import ParticipantListPage from 'pages/dsm/participantList-page';
import { StudyNav } from 'lib/component/dsm/navigation/enums/studyNav.enum';

const { RGP_USER_EMAIL, RGP_USER_PASSWORD, DSM_USER_EMAIL, DSM_USER_PASSWORD } = process.env;

test.describe('Adult Self Enrollment', () => {
const assertProgressActiveItem = async (page: Page, itemName: string): Promise<void> => {
Expand Down Expand Up @@ -59,9 +68,12 @@ test.describe('Adult Self Enrollment', () => {
await auth.login(page, { email: userEmail });

const tellUsAboutYourFamily = new TellUsAboutYourFamilyPage(page);

await setPatientParticipantGuid(page);

await tellUsAboutYourFamily.waitForReady();
await assertProgressActiveItem(page, '1');

await assertProgressActiveItem(page, '1');
await tellUsAboutYourFamily.yourTitle().selectOption(user.patient.title);
await tellUsAboutYourFamily.yourFirstName().fill(user.patient.firstName);
await tellUsAboutYourFamily.phone().fill(user.patient.phone);
Expand Down Expand Up @@ -152,5 +164,22 @@ test.describe('Adult Self Enrollment', () => {
// fields should be disabled. check one field to verify is disabled
expect(await tellUsAboutYourFamily.yourTitle().isDisabled()).toEqual(true);
expect(await tellUsAboutYourFamily.yourFirstName().isDisabled()).toEqual(true);
//Go to DSM to verify the newly created account can be found there
const dsm = new dsmHome(page);
const dsmUserEmail = await dsmAuth.login(page, {
email: DSM_USER_EMAIL,
password: DSM_USER_PASSWORD
});
const navigation = new Navigation(page);

//select RGP study
await new Select(page, { label: 'Select study' }).selectOption('RGP');

const participantListPage = await navigation.selectFromStudy<ParticipantListPage>(StudyNav.PARTICIPANT_LIST);

await participantListPage.assertPageTitle();

await participantListPage.waitForReady();
await participantListPage.filterListByParticipantGUID(user.patient.participantGuid);
});
});
12 changes: 12 additions & 0 deletions playwright-e2e/tests/rgp/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { PlaywrightTestConfig } from '@playwright/test';
import testConfig from 'playwright.config';

const rgpConfig: PlaywrightTestConfig = {
...testConfig,
testDir: './',
use: {
video: 'on'
}
};

export default rgpConfig;
12 changes: 12 additions & 0 deletions playwright-e2e/utils/faker-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { faker } from '@faker-js/faker';
import { Page } from '@playwright/test';
import * as user from 'data/fake-user.json';

export const generateUserName = (namePrefix: string): string => {
return `${namePrefix}-${faker.name.lastName()}${faker.random.word()}`;
Expand All @@ -17,3 +19,13 @@ export const generateEmailAlias = (email: string | undefined): string => {
const domain = splintedEmail[1];
return `${name}+${Math.floor(Math.random() * 1000000000)}@${domain}`;
};

export const setPatientParticipantGuid = async (page: Page) => {
const [response] = await Promise.all([
page.waitForResponse((resp) => resp.url().includes('/participants') && resp.status() === 200)
]);

const participantResponse = response.url();
const urlArray = participantResponse.split('/');
user.patient.participantGuid = urlArray[6];
};
Comment on lines +28 to +36
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Waiting and parsing a request in a separate function is going to make tests flaky because request could have been finished before this function starts to run. Best way to do this is, perform action and wait for response together a in Promise.all.

await Promise.all([
  some_action(),
  waitForResponse()
]);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do - thinking a bit more about this one - in terms of which action to use - will make the changes