Skip to content

[Playwright] Fix atcp-recieve-kit.spec test #2227

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

Merged
merged 1 commit into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 50 additions & 0 deletions playwright-e2e/dsm/pages/participant-list-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,54 @@ export default class ParticipantListPage {
}
throw new Error(`Failed to find a suitable participant for Kit Upload within max waiting time 90 seconds.`);
}

async findParticipantFor(columnGroup: string, columnName: string, opts: {value?: string, nth?: number} = {}): Promise<number> {
const { value, nth = 1 } = opts;

const compareForMatch = async (index: number): Promise<number> => {
const columnText = await participantListTable.getTextAt(index, columnName);
if (value) {
return columnText.some(text => text.indexOf(value) !== -1) ? index : -1;
}
return columnText.length === 1 && columnText[0].trim().length === 0 ? index : -1;
};

const searchPanel = this.filters.searchPanel;
await searchPanel.open();
await searchPanel.checkboxes('Status', { checkboxValues: ['Enrolled'] });
await searchPanel.search();

const participantListTable = this.participantListTable;
await expect(participantListTable.rowLocator().first()).toBeVisible();

const customizeViewPanel = this.filters.customizeViewPanel;
await customizeViewPanel.open();
await customizeViewPanel.selectColumns(columnGroup, [columnName], {nth});
await customizeViewPanel.close();

let participantsCount = await participantListTable.rowsCount;
expect(participantsCount).toBeGreaterThanOrEqual(1);

const endTime = Date.now() + 90 * 1000;
while (participantsCount > 0 && Date.now() < endTime) {
let rowIndex = -1;
// Iterate rows in random order
const array = shuffle([...Array(participantsCount).keys()]);
for (const index of array) {
rowIndex = await compareForMatch(index);
if (rowIndex !== -1) {
return rowIndex;
}
}
// Next page in table
const hasNextPage = await participantListTable.paginator.hasNext();
if (hasNextPage) {
await participantListTable.nextPage();
participantsCount = await participantListTable.rowsCount;
} else {
participantsCount = 0;
}
}
throw new Error(`Failed to find a suitable participant for ${columnGroup}: ${columnName} = "${value}" within max waiting time 90 seconds.`);
}
}
3 changes: 2 additions & 1 deletion playwright-e2e/dsm/pages/samples/search-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { expect, Page } from '@playwright/test';
import DatePicker from 'dsm/component/date-picker';
import Select from 'dss/component/select';
import Table from 'dss/component/table';
import { waitForNoSpinner } from 'utils/test-utils';
import { waitForNoSpinner, waitForResponse } from 'utils/test-utils';

export enum SearchByField {
SHORT_ID = 'Short ID',
Expand All @@ -23,6 +23,7 @@ export default class SearchPage {
const locator = this.page.locator('//div[button[normalize-space()="Search Kit"]]');
await locator.locator('//input').fill(value);
await locator.locator('//button').click();
await waitForResponse(this.page, {uri: '/ui/searchKit'});
await waitForNoSpinner(this.page);

const table = new Table(this.page);
Expand Down
103 changes: 11 additions & 92 deletions playwright-e2e/tests/dsm/atcp/atcp-receive-kit.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from '@playwright/test';
import { AdditionalFilter } from 'dsm/component/filters/sections/search/search-enums';
import { test } from 'fixtures/dsm-fixture';
import { SamplesNavEnum } from 'dsm/component/navigation/enums/samplesNav-enum';
import { StudyEnum } from 'dsm/component/navigation/enums/selectStudyNav-enum';
import { StudyNavEnum } from 'dsm/component/navigation/enums/studyNav-enum';
Expand All @@ -11,129 +11,47 @@ import ParticipantPage from 'dsm/pages/participant-page/participant-page';
import AtcpSearchPage, { SearchByField } from 'dsm/pages/samples/search-page';
import { WelcomePage } from 'dsm/pages/welcome-page';
import Radiobutton from 'dss/component/radiobutton';
import { test } from 'fixtures/dsm-fixture';
import { getUtcDate } from 'utils/date-utils';
import { generateAlphaNumeric } from 'utils/faker-utils';
import { logError, logGenomeStudySampleKitReceived, logInfo } from 'utils/log-utils';
import { studyShortName, waitForNoSpinner, waitForResponse } from 'utils/test-utils';
import { logInfo } from 'utils/log-utils';

test.describe('Receive Genome Study Kit', () => {
const studies = [StudyEnum.AT];
for (const study of studies) {
let newBarcode = generateAlphaNumeric().toUpperCase();

let shortId: string;
let guid: string;

let participantPage: ParticipantPage;
let participantListPage: ParticipantListPage;
let navigation: Navigation;

// Fallback to use non-e2e participant if all e2e participants have kit received before
const nonE2EParticipants: string[] = [];

test.beforeEach(async ({page, request}) => {
navigation = new Navigation(page, request);
const welcomePage = new WelcomePage(page);
await welcomePage.selectStudy(study);
});

test(`Receive genome sample kit for ${study} @dsm @${study} @functional`, async ({page}) => {
// Instead using UI table filter and search, it is much quicker and more accurate to intercept DSM API request to find the right participant to use.
// Find a Playwright test user that does not have GENOME_STUDY_SPIT_KIT_BARCODE.
await page.route('**/*', async (route, request): Promise<void> => {
Copy link
Contributor Author

@aweng98 aweng98 Sep 20, 2023

Choose a reason for hiding this comment

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

await page.route('**/*', async (route, request) is not working because response only contains participants on 1 page at a time, not all paricipants from database.

const regex = new RegExp(/applyFilter\?realm=.*&userId=.*&parent=participantList/i);
if (!shortId && request && request.url().match(regex)) {
logInfo(`Intercepting API request ${request.url()} to search for a E2E participant`);
const response = await route.fetch();
const json = JSON.parse(await response.text());
for (const i in json.participants) {
const participant = json.participants[i];
const profile = participant.esData.profile;
const participantData = participant.esData.dsm.participantData;
let hruId = profile.hruid;
expect(hruId).toBeTruthy(); // hruid must exists
try {
let existField = false;
for (const dataId in participantData) {
if ((participantData[dataId].fieldTypeId as string) === 'AT_GROUP_GENOME_STUDY') {
existField = true;
if ((participantData[dataId].data as string).indexOf('GENOME_STUDY_SPIT_KIT_BARCODE') !== -1) {
hruId = null; // discard this participant because person has received genome study kit before
}
break;
}
}
expect(existField).toBe(true); // AT_GROUP_GENOME_STUDY must exists in participantData
if (hruId && profile.firstName) {
if (profile.firstName.includes('E2E')) {
shortId = hruId;
break; // finish searching for a participant who is Playwright automation test created and does not have genome study kit barcode
} else {
// save non-e2e participant short id then check next participant
nonE2EParticipants.push(hruId);
}
}
} catch (err) {
logError(`Failed to read response json.\n${err}`);
logError(JSON.stringify(participant));
}
}
}
return route.continue();
});

await test.step('Search for the right participant on Participant List page', async () => {
participantListPage = await navigation.selectFromStudy<ParticipantListPage>(StudyNavEnum.PARTICIPANT_LIST);
await participantListPage.waitForReady();

// Search for a participant that meets the search criteria
const customizeViewPanel = participantListPage.filters.customizeViewPanel;
await customizeViewPanel.open();
await customizeViewPanel.selectColumns('Participant Columns', ['Registration Date']);
await customizeViewPanel.selectColumns('Genome Study Columns', ['Sample kit barcode for genome study'], { nth: 1 });
await customizeViewPanel.close();

const searchPanel = participantListPage.filters.searchPanel;
await searchPanel.open();
await searchPanel.checkboxes('Status', { checkboxValues: ['Registered', 'Enrolled'] });
await searchPanel.text('Sample kit barcode for genome study', { additionalFilters: [AdditionalFilter.EMPTY] });
await searchPanel.search();
});

await test.step('Verify participant detail on Participant page', async () => {
const searchPanel = participantListPage.filters.searchPanel;
await searchPanel.clear();

if (!shortId) {
// Attempt to find a non-e2e test participant
const [firstParticipant] = nonE2EParticipants;
shortId = firstParticipant;
}

await participantListPage.filterListByShortId(shortId);
logGenomeStudySampleKitReceived(shortId);
const rowIndex = await participantListPage.findParticipantFor('Genome Study Columns', 'Sample kit barcode for genome study', {nth: 1});

const row = 0;
const participantsTable = participantListPage.participantListTable;
const status = await participantsTable.getParticipantDataAt(row, 'Status');
expect(status).toMatch(/Enrolled|Registered/);
const rowShortId = await participantsTable.getParticipantDataAt(row, 'Short ID');
expect(rowShortId).toBe(shortId);
const registrationDate = await participantsTable.getParticipantDataAt(row, 'Registration Date', { exactMatch: false });

// Open the Participant page
participantPage = await participantsTable.openParticipantPageAt(row);

expect(await participantPage.getStatus()).toBe(status);
expect(await participantPage.getShortId()).toBe(shortId);
expect(await participantPage.getRegistrationDate()).toBe(registrationDate);
guid = await participantPage.getGuid();
const participantListTable = participantListPage.participantListTable;
shortId = await participantListTable.getParticipantDataAt(rowIndex, 'Short ID');
participantPage = await participantListTable.openParticipantPageAt(rowIndex);
logInfo(`Participant Short ID: ${shortId}`);
});

await test.step('Set new sample kit barcode', async () => {
newBarcode = `${shortId}-${newBarcode}`;
const genomeStudyTab = await participantPage.clickTab<GenomeStudyTab>(TabEnum.GENOME_STUDY);
const value = await genomeStudyTab.getField('Sample kit barcode for genome study').locator('input').inputValue();
expect(value).toBe(''); // Sample Kit Barcode input should be empty

await Promise.all([
genomeStudyTab.setValue('Sample kit barcode for genome study', newBarcode),
page.waitForResponse(resp => {
Expand All @@ -157,6 +75,7 @@ test.describe('Receive Genome Study Kit', () => {
expect(await table.getRowText(row, 'Short ID')).toBe(shortId);

const button = table.findButtonInCell(table.rowLocator(), { label: 'Mark Received' });
await expect(button.toLocator()).toBeVisible();
await Promise.all([
waitForResponse(page, { uri: `ui/receivedKits?realm=${studyShortName(study).realm}&userId=` }),
button.click()
Expand Down