Skip to content

Commit

Permalink
test: first draft index export tests (#4117)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpaten committed Oct 30, 2024
1 parent 555cbe0 commit 770ec45
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 5 deletions.
18 changes: 18 additions & 0 deletions explorer/e2e/anvil/anvil-main-export-button.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import test from "@playwright/test";
import {
testIndexExportDetails,
testIndexExportWorkflow,
} from "../testFunctions";
import { ANVIL_TABS } from "./anvil-tabs";

test("Check that the export button on the index page functions as expected, on the Files tab", async ({
page,
}) => {
await testIndexExportWorkflow(page, ANVIL_TABS.FILES);
});

test("Check that figures in the details tab on the index export page matches figures on the index page, on the BioSamples tab", async ({
page,
}) => {
await testIndexExportDetails(page, ANVIL_TABS.BIOSAMPLES);
});
19 changes: 19 additions & 0 deletions explorer/e2e/anvil/anvil-tabs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable sonarjs/no-duplicate-string -- ignoring duplicate strings here */
import {
AnvilCMGTabCollection,
IndexExportButtons,
TabCollectionKeys,
TabDescription,
} from "../testInterfaces";
Expand Down Expand Up @@ -45,9 +46,23 @@ export const REPORTED_ETHNICITY_INDEX = 10;

const ANVIL_CMG_SEARCH_FILTERS_PLACEHOLDER_TEXT = "Search all filters...";

export const anvilIndexExportButtons: IndexExportButtons = {
detailsName: "Selected Data Summary",
detailsToCheck: ["BioSamples", "Donors", "Files"],
exportActionButtonText: "Download Manifest",
exportOptionButtonText: "Request File Manifest",
exportRequestButtonText: "Prepare Manifest",
firstLandingMessage:
"Download a File Manifest with Metadata for the Selected Data",
indexExportButtonText: "Export",
secondLandingMessage: "Confirm Organism Type and Manifest File Formats",
secondLoadingMessage: "Your manifest will be ready shortly...",
};

export const ANVIL_TABS: AnvilCMGTabCollection = {
ACTIVITIES: {
emptyFirstColumn: false,
indexExportPage: anvilIndexExportButtons,
maxPages: 25,
preselectedColumns: ANVIL_ACTIVITIES_PRESELECTED_COLUMNS_BY_NAME,
searchFiltersPlaceholderText: ANVIL_CMG_SEARCH_FILTERS_PLACEHOLDER_TEXT,
Expand All @@ -57,6 +72,7 @@ export const ANVIL_TABS: AnvilCMGTabCollection = {
},
BIOSAMPLES: {
emptyFirstColumn: false,
indexExportPage: anvilIndexExportButtons,
maxPages: 25,
preselectedColumns: ANVIL_BIOSAMPLES_PRESELECTED_COLUMNS_BY_NAME,
searchFiltersPlaceholderText: ANVIL_CMG_SEARCH_FILTERS_PLACEHOLDER_TEXT,
Expand Down Expand Up @@ -132,6 +148,7 @@ export const ANVIL_TABS: AnvilCMGTabCollection = {
},
],
emptyFirstColumn: false,
indexExportPage: anvilIndexExportButtons,
maxPages: 25,
preselectedColumns: ANVIL_DATASETS_PRESELECTED_COLUMNS_BY_NAME,
searchFiltersPlaceholderText: ANVIL_CMG_SEARCH_FILTERS_PLACEHOLDER_TEXT,
Expand All @@ -141,6 +158,7 @@ export const ANVIL_TABS: AnvilCMGTabCollection = {
},
DONORS: {
emptyFirstColumn: false,
indexExportPage: anvilIndexExportButtons,
maxPages: 25,
preselectedColumns: ANVIL_DONORS_PRESELECTED_COLUMNS_BY_NAME,
searchFiltersPlaceholderText: ANVIL_CMG_SEARCH_FILTERS_PLACEHOLDER_TEXT,
Expand All @@ -150,6 +168,7 @@ export const ANVIL_TABS: AnvilCMGTabCollection = {
},
FILES: {
emptyFirstColumn: true,
indexExportPage: anvilIndexExportButtons,
maxPages: 25,
preselectedColumns: ANVIL_FILES_PRESELECTED_COLUMNS_BY_NAME,
searchFiltersPlaceholderText: ANVIL_CMG_SEARCH_FILTERS_PLACEHOLDER_TEXT,
Expand Down
156 changes: 155 additions & 1 deletion explorer/e2e/testFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,9 @@ export async function testExportBackpage(
): Promise<void> {
if (
tab.backpageExportButtons === undefined ||
tab.backpageAccessTags === undefined
tab.backpageAccessTags === undefined ||
tab.backpageExportButtons?.firstLoadingMessage === undefined ||
tab.backpageExportButtons?.secondLandingMessage === undefined
) {
// Fail if this test is ran on a tab without defined backpages
await expect(false);
Expand Down Expand Up @@ -1052,6 +1054,158 @@ export async function testBackpageDetails(
return true;
}

type DownloadResult = {
filename: string;
url: string;
};

/**
* Attempt to download a file, confirm that it succeeds, and get the filename and file url
* @param page - a Playwright Page object
* @param downloadActionLocator - a locator that initiates the file download when clicked
* @returns - an object containing the url and filename
*/
async function checkDownloadAndReturnLink(
page: Page,
downloadActionLocator: Locator
): Promise<DownloadResult> {
const downloadPromise = page.waitForEvent("download");
await downloadActionLocator.click();
const download = await downloadPromise;
const downloadFilename = download.suggestedFilename();
const downloadUrl = download.url();
return {
filename: downloadFilename,
url: downloadUrl,
};
}

export async function testIndexExportWorkflow(
page: Page,
tab: TabDescription
): Promise<boolean> {
if (tab?.indexExportPage === undefined) {
console.log(
"testIndexExportWorkflow Error: indexExportPage not specified for given tab, so test cannot run"
);
return false;
}
await page.goto(tab.url);
const exportButtonLocator = page.getByRole("link", {
name: tab.indexExportPage.indexExportButtonText,
});
await expect(exportButtonLocator).toBeVisible();
await exportButtonLocator.click();
await expect(
page.getByText(tab.indexExportPage.firstLandingMessage ?? "")
).toBeVisible();
await expect(
page.getByRole("link", {
name: tab.indexExportPage.exportOptionButtonText,
})
).toBeEnabled();
await page
.getByRole("link", { name: tab.indexExportPage.exportOptionButtonText })
.click();
const exportRequestButtonLocator = page.getByRole("button", {
name: tab.indexExportPage.exportRequestButtonText,
});
await expect(exportRequestButtonLocator).toBeEnabled();
// TODO: below is code copied from #4080, refactor this to a separate function to call that instead
// Expect there to be exactly one table on the backpage
await expect(page.getByRole("table")).toHaveCount(1);
const allNonTableCheckboxLocators = await page
.locator("input[type='checkbox']:not(table input[type='checkbox'])")
.all();
for (const checkboxLocator of allNonTableCheckboxLocators) {
await checkboxLocator.click();
await expect(checkboxLocator).toBeChecked();
await expect(checkboxLocator).toBeEnabled({ timeout: 10000 });
}
// Check the second checkbox in the table (this should be the checkbox after the "select all checkbox")
const tableLocator = page.getByRole("table");
const allInTableCheckboxLocators = await tableLocator
.getByRole("checkbox")
.all();
const secondCheckboxInTableLocator = allInTableCheckboxLocators[1];
await secondCheckboxInTableLocator.click();
await expect(secondCheckboxInTableLocator).toBeChecked();
await expect(secondCheckboxInTableLocator).toBeEnabled({ timeout: 10000 });
// Make sure that no other checkboxes are selected
const otherInTableCheckboxLocators = [
allInTableCheckboxLocators[0],
...allInTableCheckboxLocators.slice(2),
];
for (const otherCheckboxLocator of otherInTableCheckboxLocators) {
await expect(otherCheckboxLocator).not.toBeChecked();
await expect(otherCheckboxLocator).toBeEnabled();
}
// Click the Export Request button
await expect(exportRequestButtonLocator).toBeEnabled({ timeout: 10000 });
await exportRequestButtonLocator.click();
if (tab.indexExportPage?.secondLoadingMessage !== undefined) {
await expect(
page.getByText(tab.indexExportPage.secondLoadingMessage, {
exact: true,
})
).toBeVisible();
}
// END copying from #4080
const exportActionButtonLocator = page.getByRole("link", {
name: tab.indexExportPage?.exportActionButtonText,
});
await expect(exportActionButtonLocator).toBeEnabled();
const downloadResult = await checkDownloadAndReturnLink(
page,
exportActionButtonLocator
);
console.log(downloadResult);
return true;
//TODO: validate the results from the downnload
}

export async function testIndexExportDetails(
page: Page,
tab: TabDescription
): Promise<boolean> {
if (tab?.indexExportPage === undefined) {
console.log(
"testIndexExportDetails Error: indexExportPage not specified for given tab, so test cannot run"
);
return false;
}
await page.goto(tab.url);
//await expect(getFirstRowNthColumnCellLocator(page, 0)).toBeVisible();
const headers: { header: string; value: string }[] = [];
const indexExportButtonLocator = page.getByRole("link", {
name: tab.indexExportPage.indexExportButtonText,
});
await expect(indexExportButtonLocator).toBeVisible();
for (const detail of tab.indexExportPage.detailsToCheck) {
// This Regexp gets a decimal number, some whitespace, then the name of the detail, matching how the detail box appears to Playwright.
const detailBoxRegexp = RegExp(`^([0-9]+\\.[0-9]+k)\\s*${detail}$`);
console.log(await page.getByText(detailBoxRegexp).innerText());
// This gets the detail's value. The .trim() is necessary since innertext adds extraneous whitespace on Webkit
headers.push({
header: detail,
value: ((await page.getByText(detailBoxRegexp).innerText())
.trim()
.match(detailBoxRegexp) ?? ["", "ERROR"])[1],
});
}
await indexExportButtonLocator.click();
for (const headerValue of headers) {
// Expect the correct value to be below the correct header in the dataset values table
await expect(
page
.locator(`:below(:text('${headerValue.header}'))`)
.getByText(headerValue.value)
.first()
).toBeVisible();
}
return true;
}

const PAGE_COUNT_REGEX = /Page [0-9]+ of [0-9]+/;
const BACK_BUTTON_TEST_ID = "WestRoundedIcon";
const FORWARD_BUTTON_TEST_ID = "EastRoundedIcon";
Expand Down
22 changes: 18 additions & 4 deletions explorer/e2e/testInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface TabDescription {
backpageExportButtons?: BackpageExportButtons;
backpageHeaders?: BackpageHeader[];
emptyFirstColumn: boolean;
indexExportPage?: IndexExportButtons;
maxPages?: number;
preselectedColumns: StringToColumnDescription;
searchFiltersPlaceholderText: string;
Expand Down Expand Up @@ -49,14 +50,27 @@ export interface BackpageAccessTags {
grantedShortName: string;
}

export interface BackpageExportButtons {
accessNotGrantedMessage: string;
export interface ExportButtonInfo {
detailsName: string;
exportActionButtonText: string;
exportRequestButtonText: string;
firstLandingMessage?: string; //TODO: rename to requestLoadingMessage
firstLoadingMessage?: string;
secondLandingMessage?: string; //TODO: rename to actionLaodingMessage
secondLoadingMessage?: string;
}

export interface BackpageExportButtons extends ExportButtonInfo {
accessNotGrantedMessage: string;
exportTabName: string;
exportUrlRegExp: RegExp;
firstLoadingMessage: string;
newTabMessage: string;
secondLandingMessage: string;
}

//TODO: might need to make it so that there's an interface with indexExportButtonText
// and a list of other objects that go to different export pages for this to work with HCA
export interface IndexExportButtons extends ExportButtonInfo {
detailsToCheck: string[];
exportOptionButtonText: string;
indexExportButtonText: string;
}

0 comments on commit 770ec45

Please sign in to comment.