From db76dbbe611f5abc94a85e6a51f2bbe5420902be Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 12 Jul 2024 16:53:22 -0700 Subject: [PATCH 01/10] fix: refactored tests (#4068) --- explorer/playwright_anvil.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/explorer/playwright_anvil.config.ts b/explorer/playwright_anvil.config.ts index 696be3a4d..8ce4e4ed9 100644 --- a/explorer/playwright_anvil.config.ts +++ b/explorer/playwright_anvil.config.ts @@ -33,6 +33,6 @@ const config: PlaywrightTestConfig = { timeout: 240 * 1000, url: "http://localhost:3000/", }, - workers: "75%", + workers: 1, }; export default config; From 7e760454a0e800cbd83a3a96aca79289ee73a414 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 12 Jul 2024 16:56:11 -0700 Subject: [PATCH 02/10] fix: refactored tests (#4068) --- explorer/playwright_anvil.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/explorer/playwright_anvil.config.ts b/explorer/playwright_anvil.config.ts index 8ce4e4ed9..696be3a4d 100644 --- a/explorer/playwright_anvil.config.ts +++ b/explorer/playwright_anvil.config.ts @@ -33,6 +33,6 @@ const config: PlaywrightTestConfig = { timeout: 240 * 1000, url: "http://localhost:3000/", }, - workers: 1, + workers: "75%", }; export default config; From 420b5a380a5ab191873e21ba90a4ef1f90c39227 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Mon, 22 Jul 2024 15:40:02 -0700 Subject: [PATCH 03/10] fix: refactored locators and cleaned up typos in tests (#4078) --- .../anvilcatalog-tabs-buttons.spec.ts | 12 +++-------- .../anvil/anvil-pagination-content.spec.ts | 9 ++------- explorer/e2e/anvil/anvil-tabs-buttons.spec.ts | 20 +++++-------------- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/explorer/e2e/anvil-catalog/anvilcatalog-tabs-buttons.spec.ts b/explorer/e2e/anvil-catalog/anvilcatalog-tabs-buttons.spec.ts index b92dd42ff..fb44f915f 100644 --- a/explorer/e2e/anvil-catalog/anvilcatalog-tabs-buttons.spec.ts +++ b/explorer/e2e/anvil-catalog/anvilcatalog-tabs-buttons.spec.ts @@ -5,23 +5,17 @@ import { anvilcatalogTabs } from "./anvilcatalog-tabs"; test("Expect clicking the consortia tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - const tab = anvilcatalogTabs.consortia; - await page.goto(anvilcatalogTabs.studies.url); - await testTab(page, tab); + await testTab(page, anvilcatalogTabs.studies, anvilcatalogTabs.consortia); }); test("Expect clicking the studies tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - const tab = anvilcatalogTabs.studies; - await page.goto(anvilcatalogTabs.consortia.url); - await testTab(page, tab); + await testTab(page, anvilcatalogTabs.workspaces, anvilcatalogTabs.studies); }); test("Expect clicking the workspaces tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - const tab = anvilcatalogTabs.workspaces; - await page.goto(anvilcatalogTabs.workspaces.url); - await testTab(page, tab); + await testTab(page, anvilcatalogTabs.consortia, anvilcatalogTabs.workspaces); }); diff --git a/explorer/e2e/anvil/anvil-pagination-content.spec.ts b/explorer/e2e/anvil/anvil-pagination-content.spec.ts index b5489b07a..7712b5af8 100644 --- a/explorer/e2e/anvil/anvil-pagination-content.spec.ts +++ b/explorer/e2e/anvil/anvil-pagination-content.spec.ts @@ -1,4 +1,5 @@ import { expect, test } from "@playwright/test"; +import { getFirstElementTextLocator } from "../testFunctions"; import { anvilTabs } from "./anvil-tabs"; const pageCountRegex = /Page [0-9]+ of [0-9]+/; @@ -16,13 +17,7 @@ test("Check forward and backwards pagination causes the page content to change o page.getByRole("tab").getByText(tab.tabName, { exact: true }) ).toHaveAttribute("aria-selected", "true", { timeout: 25000 }); - const firstElementTextLocator = page - .getByRole("rowgroup") - .nth(1) - .getByRole("row") - .nth(0) - .getByRole("cell") - .nth(0); + const firstElementTextLocator = getFirstElementTextLocator(page, 0); // Should start on first page await expect(page.getByText(pageCountRegex, { exact: true })).toHaveText( diff --git a/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts b/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts index 8dc1289ae..2aacb185c 100644 --- a/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts +++ b/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts @@ -5,39 +5,29 @@ import { anvilTabs } from "./anvil-tabs"; test("Expect clicking the activities tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - const tab = anvilTabs.activities; - await page.goto(anvilTabs.datasets.url); - await testTab(page, tab); + await testTab(page, anvilTabs.datasets, anvilTabs.activities); }); test("Expect clicking the datasets tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - const tab = anvilTabs.datasets; - await page.goto(anvilTabs.activities.url); - await testTab(page, tab); + await testTab(page, anvilTabs.activities, anvilTabs.datasets); }); test("Expect clicking the files tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - const tab = anvilTabs.files; - await page.goto(anvilTabs.datasets.url); - await testTab(page, tab); + await testTab(page, anvilTabs.datasets, anvilTabs.files); }); test("Expect clicking the donors tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - const tab = anvilTabs.donors; - await page.goto(anvilTabs.datasets.url); - await testTab(page, tab); + await testTab(page, anvilTabs.datasets, anvilTabs.donors); }); test("Expect clicking the biosamples tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - const tab = anvilTabs.biosamples; - await page.goto(anvilTabs.datasets.url); - await testTab(page, tab); + await testTab(page, anvilTabs.datasets, anvilTabs.biosamples); }); From c746531700a36e88c73956b29a308e3d97931add Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Thu, 22 Aug 2024 20:23:12 -0700 Subject: [PATCH 04/10] test: added tests for anvil-cmg datasets backpages (#4080) --- explorer/e2e/anvil/anvil-backpages.spec.ts | 28 +++ explorer/e2e/anvil/anvil-tabs.ts | 98 ++++++-- explorer/e2e/testFunctions.ts | 259 ++++++++++++++++++++- explorer/e2e/testInterfaces.ts | 34 ++- 4 files changed, 399 insertions(+), 20 deletions(-) create mode 100644 explorer/e2e/anvil/anvil-backpages.spec.ts diff --git a/explorer/e2e/anvil/anvil-backpages.spec.ts b/explorer/e2e/anvil/anvil-backpages.spec.ts new file mode 100644 index 000000000..acf24bdf7 --- /dev/null +++ b/explorer/e2e/anvil/anvil-backpages.spec.ts @@ -0,0 +1,28 @@ +import { test } from "@playwright/test"; +import { + testBackpageAccess, + testBackpageDetails, + testExportBackpage, +} from "../testFunctions"; +import { anvilTabs } from "./anvil-tabs"; +test("Smoke test `Export to Terra` button on the first available dataset", async ({ + context, + page, +}) => { + test.setTimeout(120000); + await testExportBackpage(context, page, anvilTabs.datasets); +}); + +test("Check access controls on the datasets backpages work for the first two tabs", async ({ + page, +}) => { + test.setTimeout(120000); + await testBackpageAccess(page, anvilTabs.datasets); +}); + +test("Check that information on the backpages matches information in the data tables", async ({ + page, +}) => { + test.setTimeout(120000); + await testBackpageDetails(page, anvilTabs.datasets); +}); diff --git a/explorer/e2e/anvil/anvil-tabs.ts b/explorer/e2e/anvil/anvil-tabs.ts index 800190506..171b7eb0c 100644 --- a/explorer/e2e/anvil/anvil-tabs.ts +++ b/explorer/e2e/anvil/anvil-tabs.ts @@ -30,6 +30,28 @@ export const ORGANISM_TYPE_INDEX = 8; export const PHENOTYPIC_SEX_INDEX = 9; export const REPORTED_ETHNICITY_INDEX = 10; +const anvilDatasetsPreselectedColumns = [ + { name: "Dataset", sortable: true }, + { name: "Access", sortable: false }, + { name: "Identifier", sortable: true }, + { name: "Consent Group", sortable: true }, + { name: "Organism Type", sortable: true }, + { name: "Diagnosis", sortable: true }, + { name: "Data Modality", sortable: true }, +]; +const anvilDatasetsSelectableColumns = [ + { + name: "Phenotypic Sex", + pluralizedLabel: "phenotypic sexes", + sortable: true, + }, + { + name: "Reported Ethnicity", + pluralizedLabel: "reported ethnicities", + sortable: true, + }, +]; + export const anvilTabs: AnvilCMGTabCollection = { activities: { emptyFirstColumn: false, @@ -62,28 +84,74 @@ export const anvilTabs: AnvilCMGTabCollection = { { name: "Dataset", sortable: true }, ], selectableColumns: [ - { name: "Phenotypic Sex", sortable: true }, - { name: "Reported Ethnicity", sortable: true }, + { + name: "Phenotypic Sex", + pluralizedLabel: "phenotypic sexes", + sortable: true, + }, + { + name: "Reported Ethnicity", + pluralizedLabel: "reported ethnicities", + sortable: true, + }, ], tabName: "BioSamples", url: "/biosamples", }, datasets: { + backpageAccessTags: { + deniedLongName: "Access Required", + deniedShortName: "Required", + grantedLongName: "Access Granted", + grantedShortName: "Granted", + }, + backpageExportButtons: { + accessNotGrantedMessage: + "To export this dataset, please sign in and, if necessary, request access.", + detailsName: "Dataset Details", + exportTabName: "Export", + exportUrlRegExp: /\.*\/export-to-terra/, + firstButtonName: "Request Link", + firstLoadingMessage: "Your link will be ready shortly...", + newTabMessage: + "If you are a new user or returning user, click sign in to continue.", + secondButtonName: "Open Terra", + secondLandingMessage: "Your Terra Workspace Link is Ready", + }, + backpageHeaders: [ + { + name: "Dataset ID", + }, + { + correspondingColumn: anvilDatasetsPreselectedColumns[3], + name: "Consent group", + }, + { + correspondingColumn: anvilDatasetsPreselectedColumns[4], + name: "Organism type", + }, + { + correspondingColumn: anvilDatasetsPreselectedColumns[5], + name: "Diagnosis", + }, + { + correspondingColumn: anvilDatasetsPreselectedColumns[6], + name: "Data modality", + }, + // Skipped the below two columns, since they aren't always readable + { + correspondingColumn: anvilDatasetsSelectableColumns[0], + name: "Phenotypic sex", + }, + { + correspondingColumn: anvilDatasetsSelectableColumns[1], + name: "Reported ethnicity", + }, + ], emptyFirstColumn: false, maxPages: 25, - preselectedColumns: [ - { name: "Dataset", sortable: true }, - { name: "Access", sortable: false }, - { name: "Identifier", sortable: true }, - { name: "Consent Group", sortable: true }, - { name: "Organism Type", sortable: true }, - { name: "Diagnosis", sortable: true }, - { name: "Data Modality", sortable: true }, - ], - selectableColumns: [ - { name: "Phenotypic Sex", sortable: true }, - { name: "Reported Ethnicity", sortable: true }, - ], + preselectedColumns: anvilDatasetsPreselectedColumns, + selectableColumns: anvilDatasetsSelectableColumns, tabName: "Datasets", url: "/datasets", }, diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index ef4f9021c..5019af57c 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -1,5 +1,9 @@ -import { expect, Locator, Page } from "@playwright/test"; -import { TabDescription } from "./testInterfaces"; +import { BrowserContext, expect, Locator, Page } from "@playwright/test"; +import { + BackpageHeader, + ColumnDescription, + TabDescription, +} from "./testInterfaces"; /* eslint-disable sonarjs/no-duplicate-string -- ignoring duplicate strings here */ @@ -522,4 +526,255 @@ export async function testClearAll( await page.locator("body").click(); } } + +const getRowLocatorByAccess = (page: Page, access: string): Locator => + page + .getByRole("row") + .filter({ has: page.getByRole("cell", { name: access }) }) + .first() + .getByRole("cell") + .first() + .getByRole("link"); + +// Backpages tests +export async function testExportBackpage( + context: BrowserContext, + page: Page, + tab: TabDescription +): Promise { + if (tab.backpageExportButtons == null || tab.backpageAccessTags == null) { + // Fail if this test is ran on a tab without defined backpages + await expect(false); + return; + } + // Goto the specified tab + await page.goto(tab.url); + // Expect to find row with a granted status indicator + const grantedRowLocator = getRowLocatorByAccess( + page, + tab.backpageAccessTags.grantedShortName + ); + await expect(grantedRowLocator).toBeVisible(); + // Click into the selected row + await grantedRowLocator.dispatchEvent("click"); + await expect( + page.getByText(tab.backpageExportButtons.detailsName) + ).toBeVisible(); + // Click the "Export" tab + await page + .getByText(tab.backpageExportButtons.exportTabName, { + exact: true, + }) + .click(); + await expect(page).toHaveURL(tab.backpageExportButtons.exportUrlRegExp); + await expect(page.getByRole("checkbox").first()).toBeVisible(); + const firstButtonLocator = page.getByRole("button", { + name: tab.backpageExportButtons.firstButtonName, + }); + await expect(firstButtonLocator).toBeEnabled(); + // Select all checkboxes on the pages + const checkboxLocators = await page.getByRole("checkbox").all(); + for (const checkboxLocator of checkboxLocators) { + if (!(await checkboxLocator.isChecked())) { + await checkboxLocator.click(); + await expect(checkboxLocator).toBeChecked(); + await expect(checkboxLocator).toBeEnabled({ timeout: 10000 }); + } + } + await expect(firstButtonLocator).toBeEnabled({ timeout: 10000 }); + // Uncheck all checkboxes except one in each table, to reduce overhead + for (const tableLocator of await page.getByRole("table").all()) { + const checkboxLocatorsInTable = await tableLocator + .getByRole("checkbox") + .all(); + for (const checkboxLocator of checkboxLocatorsInTable.slice(2)) { + await checkboxLocator.click(); + await expect(checkboxLocator).not.toBeChecked(); + await expect(checkboxLocator).toBeEnabled({ timeout: 10000 }); + } + } + // Click the "Request Link" button + await expect(firstButtonLocator).toBeEnabled({ timeout: 10000 }); + await firstButtonLocator.click(); + await expect( + page.getByText(tab.backpageExportButtons.firstLoadingMessage, { + exact: true, + }) + ).toBeVisible(); + await expect( + page.getByText(tab.backpageExportButtons.secondLandingMessage, { + exact: true, + }) + ).toBeVisible({ timeout: 60000 }); + const secondButtonLocator = page.getByRole("button", { + name: tab.backpageExportButtons?.secondButtonName, + }); + await expect(secondButtonLocator).toBeEnabled(); + // Click the "Open Terra" Button and await a new browser tab + const newPagePromise = context.waitForEvent("page"); + await secondButtonLocator.click(); + const newPage = await newPagePromise; + // Expect the new browser tab to look like the Terra page + await expect( + newPage.getByText(tab.backpageExportButtons?.newTabMessage) + ).toBeVisible(); +} + +export async function testBackpageAccess( + page: Page, + tab: TabDescription +): Promise { + if (tab.backpageExportButtons == null || tab.backpageAccessTags == null) { + // Fail if this test is ran on a tab without defined backpages + await expect(false); + return; + } + // Goto the specified tab + await page.goto(tab.url); + // Check that the first "Granted" tab has access granted + const grantedRowLocator = getRowLocatorByAccess( + page, + tab.backpageAccessTags.grantedShortName + ); + await expect(grantedRowLocator).toBeVisible(); + await grantedRowLocator.dispatchEvent("click"); + await expect( + page.getByText(tab.backpageExportButtons.detailsName) + ).toBeVisible(); + await expect( + page.getByText(tab.backpageAccessTags.grantedLongName) + ).toBeVisible(); + await page + .getByText(tab.backpageExportButtons.exportTabName, { + exact: true, + }) + .click(); + await expect(page).toHaveURL(tab.backpageExportButtons.exportUrlRegExp); + await expect(page.getByRole("checkbox").first()).toBeVisible(); + const requestLinkButtonLocator = page.getByRole("button", { + name: tab.backpageExportButtons.firstButtonName, + }); + await expect(requestLinkButtonLocator).toBeEnabled(); + // Go back to the table page + await page.getByRole("link", { name: tab.tabName }).click(); + // Check that the first "Required" tab does not have access granted + const deniedRowLocator = getRowLocatorByAccess( + page, + tab.backpageAccessTags.deniedShortName + ); + await expect(deniedRowLocator).toBeVisible(); + await deniedRowLocator.dispatchEvent("click"); + await expect( + page.getByText(tab.backpageAccessTags.deniedLongName) + ).toBeVisible(); + await page + .getByText(tab.backpageExportButtons.exportTabName, { + exact: true, + }) + .click(); + await expect(page).toHaveURL(tab.backpageExportButtons.exportUrlRegExp); + await expect( + page.getByText(tab.backpageExportButtons.accessNotGrantedMessage, { + exact: true, + }) + ).toBeVisible(); +} + +const hoverAndGetText = async ( + page: Page, + columnDescription: ColumnDescription | undefined, + rowPosition: number, + columnPosition: number +): Promise => { + const cellLocator = getNthElementTextLocator( + page, + rowPosition, + columnPosition + ); + const cellText = await cellLocator.innerText(); + // Check if the cell appears to be an Ntag cell + if ( + columnDescription != undefined && + columnDescription.pluralizedLabel != undefined && + RegExp("\\s*[0-9]+ " + columnDescription.pluralizedLabel + "\\s*").test( + cellText + ) + ) { + // Hover over the text of the NTag cell + await cellLocator.locator("*").last().hover(); + // Read the tooltip + await page.getByRole("tooltip").waitFor(); + const outputText = (await page.getByRole("tooltip").innerText()).trim(); + // Hover over a different part of the page to ensure that the tooltip disappears + await page.getByRole("columnheader").first().hover(); + await expect(page.getByRole("tooltip")).toHaveCount(0); + // Return the tooltip contents + return outputText; + } + return cellText.trim(); +}; + +export async function testBackpageDetails( + page: Page, + tab: TabDescription +): Promise { + if (tab.backpageHeaders == null || tab.backpageExportButtons == null) { + // If the tab is not set up with backpage info, fail the test + await expect(false); + return; + } + await page.goto(tab.url); + // Enable test columns + await testSelectableColumns(page, tab); + const headers: { header: string; value: string }[] = []; + const combinedColumns = tab.preselectedColumns.concat(tab.selectableColumns); + const filterString = (x: string | undefined): x is string => x !== undefined; + // Get the columns that correspond with a header on the backpage details + const backpageCorrespondingColumns: string[] = tab.backpageHeaders + .map((header) => header?.correspondingColumn?.name) + .filter(filterString) + .map((x) => x.trim()); + for (let i = 0; i < combinedColumns.length; i++) { + // Get the name of the current column + const columnHeaderName = ( + await page.getByRole("columnheader").nth(i).innerText() + ).trim(); + // If the selected column has an entry on the backpage + if (backpageCorrespondingColumns.includes(columnHeaderName)) { + // Get the object representing the current column + const columnObject = combinedColumns.find( + (x) => x.name == columnHeaderName + ); + // Get the entry text + const tableEntryText = await hoverAndGetText(page, columnObject, 0, i); + // Get the name of the corresponding header on the backpage + const correspondingHeaderName = tab.backpageHeaders.find( + (header: BackpageHeader) => + header?.correspondingColumn?.name === columnHeaderName + )?.name; + if (correspondingHeaderName == null) { + // Fail the test, because this means there is an incorrect configuraiton in the tab definition + await expect(false); + return; + } + headers.push({ header: correspondingHeaderName, value: tableEntryText }); + } + } + // Go to the backpage + await getNthElementTextLocator(page, 0, 0).click(); + // Expect the details name to be visible + await expect( + page.getByText(tab.backpageExportButtons.detailsName) + ).toBeVisible(); + 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(); + } +} + /* eslint-enable sonarjs/no-duplicate-string -- Checking duplicate strings again*/ diff --git a/explorer/e2e/testInterfaces.ts b/explorer/e2e/testInterfaces.ts index ee5d786c3..329331c7c 100644 --- a/explorer/e2e/testInterfaces.ts +++ b/explorer/e2e/testInterfaces.ts @@ -1,8 +1,11 @@ export interface TabDescription { + backpageAccessTags?: BackpageAccessTags; + backpageExportButtons?: BackpageExportButtons; + backpageHeaders?: BackpageHeader[]; emptyFirstColumn: boolean; maxPages?: number; - preselectedColumns: columnDescription[]; - selectableColumns: columnDescription[]; + preselectedColumns: ColumnDescription[]; + selectableColumns: ColumnDescription[]; tabName: string; url: string; } @@ -23,7 +26,32 @@ export interface AnvilCatalogTabCollection { export type TabCollectionKeys = keyof AnvilCMGTabCollection; -export interface columnDescription { +export interface ColumnDescription { name: string; + pluralizedLabel?: string; sortable: boolean; } + +export interface BackpageHeader { + correspondingColumn?: ColumnDescription; + name: string; +} + +export interface BackpageAccessTags { + deniedLongName: string; + deniedShortName: string; + grantedLongName: string; + grantedShortName: string; +} + +export interface BackpageExportButtons { + accessNotGrantedMessage: string; + detailsName: string; + exportTabName: string; + exportUrlRegExp: RegExp; + firstButtonName: string; + firstLoadingMessage: string; + newTabMessage: string; + secondButtonName: string; + secondLandingMessage: string; +} From 3d98361e311cd0632c243fdba5b18a254c0df05e Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 23 Aug 2024 14:28:14 -0700 Subject: [PATCH 05/10] test: fixed changes that broke with rebasing and made export tests skip (#4080) --- .../anvilcatalog-tabs-buttons.spec.ts | 12 ++++++--- explorer/e2e/anvil/anvil-backpages.spec.ts | 5 ++-- .../anvil/anvil-pagination-content.spec.ts | 4 +-- explorer/e2e/anvil/anvil-pagination.spec.ts | 2 +- explorer/e2e/anvil/anvil-sort.spec.ts | 5 ++++ explorer/e2e/anvil/anvil-tabs-buttons.spec.ts | 15 +++++++---- explorer/e2e/testFunctions.ts | 25 +++++++++++++++---- 7 files changed, 50 insertions(+), 18 deletions(-) diff --git a/explorer/e2e/anvil-catalog/anvilcatalog-tabs-buttons.spec.ts b/explorer/e2e/anvil-catalog/anvilcatalog-tabs-buttons.spec.ts index fb44f915f..f507d7fe5 100644 --- a/explorer/e2e/anvil-catalog/anvilcatalog-tabs-buttons.spec.ts +++ b/explorer/e2e/anvil-catalog/anvilcatalog-tabs-buttons.spec.ts @@ -5,17 +5,23 @@ import { anvilcatalogTabs } from "./anvilcatalog-tabs"; test("Expect clicking the consortia tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - await testTab(page, anvilcatalogTabs.studies, anvilcatalogTabs.consortia); + const tab = anvilcatalogTabs.consortia; + await page.goto(anvilcatalogTabs.studies.url); + await testTab(page, tab); }); test("Expect clicking the studies tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - await testTab(page, anvilcatalogTabs.workspaces, anvilcatalogTabs.studies); + const tab = anvilcatalogTabs.studies; + await page.goto(anvilcatalogTabs.workspaces.url); + await testTab(page, tab); }); test("Expect clicking the workspaces tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - await testTab(page, anvilcatalogTabs.consortia, anvilcatalogTabs.workspaces); + const tab = anvilcatalogTabs.workspaces; + await page.goto(anvilcatalogTabs.consortia.url); + await testTab(page, tab); }); diff --git a/explorer/e2e/anvil/anvil-backpages.spec.ts b/explorer/e2e/anvil/anvil-backpages.spec.ts index acf24bdf7..593f958fc 100644 --- a/explorer/e2e/anvil/anvil-backpages.spec.ts +++ b/explorer/e2e/anvil/anvil-backpages.spec.ts @@ -5,7 +5,8 @@ import { testExportBackpage, } from "../testFunctions"; import { anvilTabs } from "./anvil-tabs"; -test("Smoke test `Export to Terra` button on the first available dataset", async ({ + +test.skip("Smoke test `Export to Terra` button on the first available dataset", async ({ context, page, }) => { @@ -13,7 +14,7 @@ test("Smoke test `Export to Terra` button on the first available dataset", async await testExportBackpage(context, page, anvilTabs.datasets); }); -test("Check access controls on the datasets backpages work for the first two tabs", async ({ +test.skip("Check access controls on the datasets backpages work for the first two tabs", async ({ page, }) => { test.setTimeout(120000); diff --git a/explorer/e2e/anvil/anvil-pagination-content.spec.ts b/explorer/e2e/anvil/anvil-pagination-content.spec.ts index 7712b5af8..2c4b90445 100644 --- a/explorer/e2e/anvil/anvil-pagination-content.spec.ts +++ b/explorer/e2e/anvil/anvil-pagination-content.spec.ts @@ -1,5 +1,5 @@ import { expect, test } from "@playwright/test"; -import { getFirstElementTextLocator } from "../testFunctions"; +import { getFirstRowNthColumnCellLocator } from "../testFunctions"; import { anvilTabs } from "./anvil-tabs"; const pageCountRegex = /Page [0-9]+ of [0-9]+/; @@ -17,7 +17,7 @@ test("Check forward and backwards pagination causes the page content to change o page.getByRole("tab").getByText(tab.tabName, { exact: true }) ).toHaveAttribute("aria-selected", "true", { timeout: 25000 }); - const firstElementTextLocator = getFirstElementTextLocator(page, 0); + const firstElementTextLocator = getFirstRowNthColumnCellLocator(page, 0); // Should start on first page await expect(page.getByText(pageCountRegex, { exact: true })).toHaveText( diff --git a/explorer/e2e/anvil/anvil-pagination.spec.ts b/explorer/e2e/anvil/anvil-pagination.spec.ts index 63b3d7edf..15e6a7c4b 100644 --- a/explorer/e2e/anvil/anvil-pagination.spec.ts +++ b/explorer/e2e/anvil/anvil-pagination.spec.ts @@ -32,10 +32,10 @@ test("Check first page has disabled back and enabled forward pagination buttons ).toBeDisabled(); }); -test.setTimeout(300000); test("Check that forward pagination increments the current page and that page count stays static for the first five pages on the donors tab", async ({ page, }) => { + test.setTimeout(500000); // Should start on first page, and there should be multiple pages available await expect(page.getByText(pageCountRegex, { exact: true })).toHaveText( /Page 1 of [0-9]+/ diff --git a/explorer/e2e/anvil/anvil-sort.spec.ts b/explorer/e2e/anvil/anvil-sort.spec.ts index 17d0d5507..4f1b1111b 100644 --- a/explorer/e2e/anvil/anvil-sort.spec.ts +++ b/explorer/e2e/anvil/anvil-sort.spec.ts @@ -7,29 +7,34 @@ test.describe.configure({ mode: "parallel" }); test("Expect clicking each column header three times to keep the first text element visible on the Datasets tab", async ({ page, }) => { + test.setTimeout(180000); await testSortAzul(page, anvilTabs.datasets); }); test("Expect clicking each column header three times to keep the first text element visible on the Donors tab", async ({ page, }) => { + test.setTimeout(180000); await testSortAzul(page, anvilTabs.donors); }); test("Expect clicking each column header of each tab three times to keep the first text element visible on the BioSamples tab", async ({ page, }) => { + test.setTimeout(180000); await testSortAzul(page, anvilTabs.biosamples); }); test("Expect clicking each column header three times to keep the first text element visible on the Activities tab", async ({ page, }) => { + test.setTimeout(180000); await testSortAzul(page, anvilTabs.activities); }); test("Expect clicking each column header three times to keep the first text element visible on the Files tab", async ({ page, }) => { + test.setTimeout(180000); await testSortAzul(page, anvilTabs.files); }); diff --git a/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts b/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts index 2aacb185c..494ba7a3e 100644 --- a/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts +++ b/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts @@ -5,29 +5,34 @@ import { anvilTabs } from "./anvil-tabs"; test("Expect clicking the activities tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - await testTab(page, anvilTabs.datasets, anvilTabs.activities); + await page.goto(anvilTabs.activities.url); + await testTab(page, anvilTabs.datasets); }); test("Expect clicking the datasets tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - await testTab(page, anvilTabs.activities, anvilTabs.datasets); + await page.goto(anvilTabs.datasets.url); + await testTab(page, anvilTabs.activities); }); test("Expect clicking the files tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - await testTab(page, anvilTabs.datasets, anvilTabs.files); + await page.goto(anvilTabs.datasets.url); + await testTab(page, anvilTabs.files); }); test("Expect clicking the donors tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - await testTab(page, anvilTabs.datasets, anvilTabs.donors); + await page.goto(anvilTabs.datasets.url); + await testTab(page, anvilTabs.donors); }); test("Expect clicking the biosamples tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { - await testTab(page, anvilTabs.datasets, anvilTabs.biosamples); + await page.goto(anvilTabs.datasets.url); + await testTab(page, anvilTabs.datasets); }); diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index 5019af57c..37fe8c4c1 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -8,24 +8,39 @@ import { /* eslint-disable sonarjs/no-duplicate-string -- ignoring duplicate strings here */ /** - * Get a locator to the cell in the first row's nth column + * Get a locator to the cell in the mth row's nth column * @param page - a Playwright page object + * @param rowIndex - the zero-indexed row to return * @param columnIndex - the zero-indexed column to return * @returns a Playwright locator object to the selected cell **/ -export const getFirstRowNthColumnCellLocator = ( +export const getMthRowNthColumnCellLocator = ( page: Page, + rowIndex: number, columnIndex: number ): Locator => { return page .getByRole("rowgroup") .nth(1) .getByRole("row") - .nth(0) + .nth(rowIndex) .getByRole("cell") .nth(columnIndex); }; +/** + * Get a locator to the cell in the first row's nth column + * @param page - a Playwright page object + * @param columnIndex - the zero-indexed column to return + * @returns a Playwright locator object to the selected cell + **/ +export const getFirstRowNthColumnCellLocator = ( + page: Page, + columnIndex: number +): Locator => { + return getMthRowNthColumnCellLocator(page, 0, columnIndex); +}; + /** * Tests that the tab url goes to a valid page and that the correct tab (and only * the correct tab) appears selected @@ -686,7 +701,7 @@ const hoverAndGetText = async ( rowPosition: number, columnPosition: number ): Promise => { - const cellLocator = getNthElementTextLocator( + const cellLocator = getMthRowNthColumnCellLocator( page, rowPosition, columnPosition @@ -761,7 +776,7 @@ export async function testBackpageDetails( } } // Go to the backpage - await getNthElementTextLocator(page, 0, 0).click(); + await getMthRowNthColumnCellLocator(page, 0, 0).click(); // Expect the details name to be visible await expect( page.getByText(tab.backpageExportButtons.detailsName) From 11191e22aa98504998278493a67f040d903affa3 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 23 Aug 2024 15:01:48 -0700 Subject: [PATCH 06/10] test: documented backpage tests in `testReadme.md`(#4080) --- explorer/e2e/testReadme.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/explorer/e2e/testReadme.md b/explorer/e2e/testReadme.md index b73b47d9b..338eecfce 100644 --- a/explorer/e2e/testReadme.md +++ b/explorer/e2e/testReadme.md @@ -70,6 +70,19 @@ through the actions taken as part of the test and view the impact on the web pag - Runs from "Datasets" to "Activities", "Activities" to "Datasets", "Datasets" to "Files", "Datasets" to "Donors", "Datasets" to "BioSamples" - `anvil-tabs-buttons.spec.ts` - All tests rely on correct lists of tabs, columns, and filters in `anvil-tabs.ts` +- Backpages (`anvil-backpages.spec.ts`) + - Test the export process on the "Datasets" tab + - Select the first dataset that does not have access control enabled and go to the export tab on its backpage. Select a one filter checkbox for each category then select the "Export to Terra" button + - Check that buttons, text, and loading text appears as expected, and that a new tab appears when the "Export to Terra" button is pressed + - Relevant text constants are stored in `anvil-tabs.ts` + - Currently disabled as the export button is disabled + - Test that access control works properly on the "Datasets" tab + - Select a non access controlled tab and check that text associataed with access being granted is present, then repeat for an access controleld tab + - Relevant text constants are stored in `anvil-tabs.ts` + - Currently disabled as the export button is disabled + - Test that data in the sidebar of the "Datasets" tab is the same as the text displayed in the main tab le + - Enable all non-preselected columns and read values from all columns in the first row, including n-tag cells. Then select the backpage for the first row and check that all matching columns are reflected + - Requires a list of the values in the sidebar to be present in `anvil-tabs.ts` and that plural labels are defined there for any columns that include n-tag cells #### AnVIL-Catalog @@ -96,13 +109,5 @@ through the actions taken as part of the test and view the impact on the web pag - Sign in (probably smoke test / existence only for ease) - "Contact Us" form - Edit columns Button (already on AnVIL-Catalog) -- Datasets backpages - - Check that "Dataset details" match relevant fields in the table - - Check access control works properly - - Smoke test for "Request link" button - - Check boxes - - Press "Request link" button - - Press resulting button to go to Terra - - Check that url begins "anvil.terra.bio" - Smoke test buttons at bottom of screen (logos, help, privacy) - Smoke test "Help & Documentation" buttons From 29a80f75f344016d4fa16f45bcd52bc4145da04d Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Sat, 31 Aug 2024 12:26:04 -0700 Subject: [PATCH 07/10] test: fixed comment and readme typos, revised backpage tests (#4080) --- explorer/e2e/anvil/anvil-tabs-buttons.spec.ts | 6 +- explorer/e2e/anvil/anvil-tabs.ts | 4 +- explorer/e2e/testFunctions.ts | 129 ++++++++++++------ explorer/e2e/testInterfaces.ts | 4 +- explorer/e2e/testReadme.md | 6 +- 5 files changed, 98 insertions(+), 51 deletions(-) diff --git a/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts b/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts index 494ba7a3e..a85c96f6d 100644 --- a/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts +++ b/explorer/e2e/anvil/anvil-tabs-buttons.spec.ts @@ -2,14 +2,14 @@ import { test } from "@playwright/test"; import { testTab } from "../testFunctions"; import { anvilTabs } from "./anvil-tabs"; -test("Expect clicking the activities tab to go to the correct url and to show all of the relevant columns when selected", async ({ +test("Expect clicking the datasets tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { await page.goto(anvilTabs.activities.url); await testTab(page, anvilTabs.datasets); }); -test("Expect clicking the datasets tab to go to the correct url and to show all of the relevant columns when selected", async ({ +test("Expect clicking the activities tab to go to the correct url and to show all of the relevant columns when selected", async ({ page, }) => { await page.goto(anvilTabs.datasets.url); @@ -34,5 +34,5 @@ test("Expect clicking the biosamples tab to go to the correct url and to show al page, }) => { await page.goto(anvilTabs.datasets.url); - await testTab(page, anvilTabs.datasets); + await testTab(page, anvilTabs.biosamples); }); diff --git a/explorer/e2e/anvil/anvil-tabs.ts b/explorer/e2e/anvil/anvil-tabs.ts index 171b7eb0c..26f3ff504 100644 --- a/explorer/e2e/anvil/anvil-tabs.ts +++ b/explorer/e2e/anvil/anvil-tabs.ts @@ -109,13 +109,13 @@ export const anvilTabs: AnvilCMGTabCollection = { accessNotGrantedMessage: "To export this dataset, please sign in and, if necessary, request access.", detailsName: "Dataset Details", + exportActionButtonText: "Open Terra", + exportRequestButtonText: "Request Link", exportTabName: "Export", exportUrlRegExp: /\.*\/export-to-terra/, - firstButtonName: "Request Link", firstLoadingMessage: "Your link will be ready shortly...", newTabMessage: "If you are a new user or returning user, click sign in to continue.", - secondButtonName: "Open Terra", secondLandingMessage: "Your Terra Workspace Link is Ready", }, backpageHeaders: [ diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index 37fe8c4c1..3cac7b986 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -363,7 +363,7 @@ export async function testFilterPersistence( const filterNameMatch = (await filterToSelectLocator.innerText()) .trim() .match(/^\S*/); - if (filterNameMatch == null) { + if (!filterNameMatch) { // This means that the selected filter did not have any non-whitespace text // associated with it, making the test impossible to complete. console.log("ERROR: Filter name is blank, so the test cannot continue"); @@ -542,7 +542,13 @@ export async function testClearAll( } } -const getRowLocatorByAccess = (page: Page, access: string): Locator => +/** + * Get the first link to a backpage with specified backpage access + * @param page - a Playright page locator + * @param access - the string denoting the level of access desired + * @returns a Pla + */ +const getBackpageLinkLocatorByAccess = (page: Page, access: string): Locator => page .getByRole("row") .filter({ has: page.getByRole("cell", { name: access }) }) @@ -551,13 +557,21 @@ const getRowLocatorByAccess = (page: Page, access: string): Locator => .first() .getByRole("link"); -// Backpages tests +/** + * Test the export process for the specified tab + * @param context - a Playwright browser context object + * @param page - a Playwright page object + * @param tab - the tab to test on + */ export async function testExportBackpage( context: BrowserContext, page: Page, tab: TabDescription ): Promise { - if (tab.backpageExportButtons == null || tab.backpageAccessTags == null) { + if ( + tab.backpageExportButtons === undefined || + tab.backpageAccessTags === undefined + ) { // Fail if this test is ran on a tab without defined backpages await expect(false); return; @@ -565,7 +579,7 @@ export async function testExportBackpage( // Goto the specified tab await page.goto(tab.url); // Expect to find row with a granted status indicator - const grantedRowLocator = getRowLocatorByAccess( + const grantedRowLocator = getBackpageLinkLocatorByAccess( page, tab.backpageAccessTags.grantedShortName ); @@ -583,34 +597,41 @@ export async function testExportBackpage( .click(); await expect(page).toHaveURL(tab.backpageExportButtons.exportUrlRegExp); await expect(page.getByRole("checkbox").first()).toBeVisible(); - const firstButtonLocator = page.getByRole("button", { - name: tab.backpageExportButtons.firstButtonName, + const exportRequestButtonLocator = page.getByRole("button", { + name: tab.backpageExportButtons.exportRequestButtonText, }); - await expect(firstButtonLocator).toBeEnabled(); - // Select all checkboxes on the pages - const checkboxLocators = await page.getByRole("checkbox").all(); - for (const checkboxLocator of checkboxLocators) { - if (!(await checkboxLocator.isChecked())) { - await checkboxLocator.click(); - await expect(checkboxLocator).toBeChecked(); - await expect(checkboxLocator).toBeEnabled({ timeout: 10000 }); - } + await expect(exportRequestButtonLocator).toBeEnabled(); + // Select all checkboxes that are not in a table + 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 }); } - await expect(firstButtonLocator).toBeEnabled({ timeout: 10000 }); - // Uncheck all checkboxes except one in each table, to reduce overhead + // Check one checkbox in each table for (const tableLocator of await page.getByRole("table").all()) { - const checkboxLocatorsInTable = await tableLocator + const allInTableCheckboxLocators = await tableLocator .getByRole("checkbox") .all(); - for (const checkboxLocator of checkboxLocatorsInTable.slice(2)) { - await checkboxLocator.click(); - await expect(checkboxLocator).not.toBeChecked(); - await expect(checkboxLocator).toBeEnabled({ timeout: 10000 }); + const secondCheckboxInTableLocator = allInTableCheckboxLocators[1]; + await secondCheckboxInTableLocator.click(); + await expect(secondCheckboxInTableLocator).toBeChecked(); + await expect(secondCheckboxInTableLocator).toBeEnabled({ timeout: 10000 }); + const otherInTableCheckboxLocators = [ + allInTableCheckboxLocators[0], + ...allInTableCheckboxLocators.slice(2), + ]; + // Make sure that no other checkboxes are selected + for (const otherCheckboxLocator of otherInTableCheckboxLocators) { + await expect(otherCheckboxLocator).not.toBeChecked(); + await expect(otherCheckboxLocator).toBeEnabled(); } } - // Click the "Request Link" button - await expect(firstButtonLocator).toBeEnabled({ timeout: 10000 }); - await firstButtonLocator.click(); + // Click the Export Request button + await expect(exportRequestButtonLocator).toBeEnabled({ timeout: 10000 }); + await exportRequestButtonLocator.click(); await expect( page.getByText(tab.backpageExportButtons.firstLoadingMessage, { exact: true, @@ -621,25 +642,34 @@ export async function testExportBackpage( exact: true, }) ).toBeVisible({ timeout: 60000 }); - const secondButtonLocator = page.getByRole("button", { - name: tab.backpageExportButtons?.secondButtonName, + const exportActionButtonLocator = page.getByRole("button", { + name: tab.backpageExportButtons?.exportActionButtonText, }); - await expect(secondButtonLocator).toBeEnabled(); - // Click the "Open Terra" Button and await a new browser tab + await expect(exportActionButtonLocator).toBeEnabled(); + // Click the Export Action Button and await a new browser tab const newPagePromise = context.waitForEvent("page"); - await secondButtonLocator.click(); + await exportActionButtonLocator.click(); const newPage = await newPagePromise; - // Expect the new browser tab to look like the Terra page + // Expect the new browser tab to display the new tab content await expect( newPage.getByText(tab.backpageExportButtons?.newTabMessage) ).toBeVisible(); } +/** + * Test that export access is available on entries where access shows as available + * and is not on entries where access shows as unavailable + * @param page - a Playwright page objext + * @param tab - the Tab to test on + */ export async function testBackpageAccess( page: Page, tab: TabDescription ): Promise { - if (tab.backpageExportButtons == null || tab.backpageAccessTags == null) { + if ( + tab.backpageExportButtons === undefined || + tab.backpageAccessTags === undefined + ) { // Fail if this test is ran on a tab without defined backpages await expect(false); return; @@ -647,7 +677,7 @@ export async function testBackpageAccess( // Goto the specified tab await page.goto(tab.url); // Check that the first "Granted" tab has access granted - const grantedRowLocator = getRowLocatorByAccess( + const grantedRowLocator = getBackpageLinkLocatorByAccess( page, tab.backpageAccessTags.grantedShortName ); @@ -667,13 +697,13 @@ export async function testBackpageAccess( await expect(page).toHaveURL(tab.backpageExportButtons.exportUrlRegExp); await expect(page.getByRole("checkbox").first()).toBeVisible(); const requestLinkButtonLocator = page.getByRole("button", { - name: tab.backpageExportButtons.firstButtonName, + name: tab.backpageExportButtons.exportRequestButtonText, }); await expect(requestLinkButtonLocator).toBeEnabled(); // Go back to the table page await page.getByRole("link", { name: tab.tabName }).click(); // Check that the first "Required" tab does not have access granted - const deniedRowLocator = getRowLocatorByAccess( + const deniedRowLocator = getBackpageLinkLocatorByAccess( page, tab.backpageAccessTags.deniedShortName ); @@ -695,6 +725,15 @@ export async function testBackpageAccess( ).toBeVisible(); } +/** + * Get the text from a cell by reading the tooltip if it appears to be an N-tag + * cell or by reading the text if it does not appear to be + * @param page - a Playwright Page object + * @param columnDescription - a columnDescription object for the column + * @param rowPosition - the zero-indexed position of the row + * @param columnPosition - the zero-indexed position of the column + * @returns - a Promise with the cell's text + */ const hoverAndGetText = async ( page: Page, columnDescription: ColumnDescription | undefined, @@ -709,8 +748,8 @@ const hoverAndGetText = async ( const cellText = await cellLocator.innerText(); // Check if the cell appears to be an Ntag cell if ( - columnDescription != undefined && - columnDescription.pluralizedLabel != undefined && + !columnDescription !== undefined && + columnDescription?.pluralizedLabel !== undefined && RegExp("\\s*[0-9]+ " + columnDescription.pluralizedLabel + "\\s*").test( cellText ) @@ -729,11 +768,19 @@ const hoverAndGetText = async ( return cellText.trim(); }; +/** + * Check that the details in the backpage sidebar match information in the data table + * @param page - a Playwright page object + * @param tab - the tab to test on + */ export async function testBackpageDetails( page: Page, tab: TabDescription ): Promise { - if (tab.backpageHeaders == null || tab.backpageExportButtons == null) { + if ( + tab.backpageHeaders === undefined || + tab.backpageExportButtons === undefined + ) { // If the tab is not set up with backpage info, fail the test await expect(false); return; @@ -767,8 +814,8 @@ export async function testBackpageDetails( (header: BackpageHeader) => header?.correspondingColumn?.name === columnHeaderName )?.name; - if (correspondingHeaderName == null) { - // Fail the test, because this means there is an incorrect configuraiton in the tab definition + if (correspondingHeaderName === undefined) { + // Fail the test, because this means there is an incorrect configuration in the tab definition await expect(false); return; } diff --git a/explorer/e2e/testInterfaces.ts b/explorer/e2e/testInterfaces.ts index 329331c7c..e404c1b10 100644 --- a/explorer/e2e/testInterfaces.ts +++ b/explorer/e2e/testInterfaces.ts @@ -47,11 +47,11 @@ export interface BackpageAccessTags { export interface BackpageExportButtons { accessNotGrantedMessage: string; detailsName: string; + exportActionButtonText: string; + exportRequestButtonText: string; exportTabName: string; exportUrlRegExp: RegExp; - firstButtonName: string; firstLoadingMessage: string; newTabMessage: string; - secondButtonName: string; secondLandingMessage: string; } diff --git a/explorer/e2e/testReadme.md b/explorer/e2e/testReadme.md index 338eecfce..100216659 100644 --- a/explorer/e2e/testReadme.md +++ b/explorer/e2e/testReadme.md @@ -72,12 +72,12 @@ through the actions taken as part of the test and view the impact on the web pag - All tests rely on correct lists of tabs, columns, and filters in `anvil-tabs.ts` - Backpages (`anvil-backpages.spec.ts`) - Test the export process on the "Datasets" tab - - Select the first dataset that does not have access control enabled and go to the export tab on its backpage. Select a one filter checkbox for each category then select the "Export to Terra" button - - Check that buttons, text, and loading text appears as expected, and that a new tab appears when the "Export to Terra" button is pressed + - Selects the first dataset that does not have access control enabled and go to the export tab on its backpage. Then, selects one filter checkbox for each category, then finally selects the "Export to Terra" button + - Checks that buttons, text, and loading text appears as expected, and that a new tab appears when the "Export to Terra" button is pressed - Relevant text constants are stored in `anvil-tabs.ts` - Currently disabled as the export button is disabled - Test that access control works properly on the "Datasets" tab - - Select a non access controlled tab and check that text associataed with access being granted is present, then repeat for an access controleld tab + - Selects a non-access-controlled tab and check that text associated with access being granted is present, then repeats for an access-controlled tab - Relevant text constants are stored in `anvil-tabs.ts` - Currently disabled as the export button is disabled - Test that data in the sidebar of the "Datasets" tab is the same as the text displayed in the main tab le From d95ee594dc00d3f9807c031ed6076c386af178dc Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Tue, 3 Sep 2024 16:54:24 -0700 Subject: [PATCH 08/10] test: refactored constants and backpage export test (#4080) --- explorer/e2e/anvil/anvil-backpages.spec.ts | 4 +- explorer/e2e/anvil/anvil-tabs.ts | 112 +++++++++++---------- explorer/e2e/anvil/constants.ts | 38 +++++++ explorer/e2e/testFunctions.ts | 39 +++---- 4 files changed, 119 insertions(+), 74 deletions(-) create mode 100644 explorer/e2e/anvil/constants.ts diff --git a/explorer/e2e/anvil/anvil-backpages.spec.ts b/explorer/e2e/anvil/anvil-backpages.spec.ts index 593f958fc..977f71ced 100644 --- a/explorer/e2e/anvil/anvil-backpages.spec.ts +++ b/explorer/e2e/anvil/anvil-backpages.spec.ts @@ -6,7 +6,7 @@ import { } from "../testFunctions"; import { anvilTabs } from "./anvil-tabs"; -test.skip("Smoke test `Export to Terra` button on the first available dataset", async ({ +test("Smoke test `Export to Terra` button on the first available dataset", async ({ context, page, }) => { @@ -14,7 +14,7 @@ test.skip("Smoke test `Export to Terra` button on the first available dataset", await testExportBackpage(context, page, anvilTabs.datasets); }); -test.skip("Check access controls on the datasets backpages work for the first two tabs", async ({ +test("Check access controls on the datasets backpages work for the first two tabs", async ({ page, }) => { test.setTimeout(120000); diff --git a/explorer/e2e/anvil/anvil-tabs.ts b/explorer/e2e/anvil/anvil-tabs.ts index 26f3ff504..b7601f41b 100644 --- a/explorer/e2e/anvil/anvil-tabs.ts +++ b/explorer/e2e/anvil/anvil-tabs.ts @@ -4,6 +4,11 @@ import { TabCollectionKeys, TabDescription, } from "../testInterfaces"; +import { + ANVIL_COLUMN_NAMES, + ANVIL_DATASETS_BACKPAGE_HEADER_NAMES, + ANVIL_PLURALIZED_METADATA_LABELS, +} from "./constants"; export const anvilFilterNames: string[] = [ "Anatomical Site", @@ -31,23 +36,23 @@ export const PHENOTYPIC_SEX_INDEX = 9; export const REPORTED_ETHNICITY_INDEX = 10; const anvilDatasetsPreselectedColumns = [ - { name: "Dataset", sortable: true }, - { name: "Access", sortable: false }, - { name: "Identifier", sortable: true }, - { name: "Consent Group", sortable: true }, - { name: "Organism Type", sortable: true }, - { name: "Diagnosis", sortable: true }, - { name: "Data Modality", sortable: true }, + { name: ANVIL_COLUMN_NAMES.DATASET, sortable: true }, + { name: ANVIL_COLUMN_NAMES.ACCESS, sortable: false }, + { name: ANVIL_COLUMN_NAMES.IDENTIFIER, sortable: true }, + { name: ANVIL_COLUMN_NAMES.CONSENT_GROUP, sortable: true }, + { name: ANVIL_COLUMN_NAMES.ORGANISM_TYPE, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DIAGNOSIS, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DATA_MODALITY, sortable: true }, ]; const anvilDatasetsSelectableColumns = [ { - name: "Phenotypic Sex", - pluralizedLabel: "phenotypic sexes", + name: ANVIL_COLUMN_NAMES.PHENOTYPIC_SEX, + pluralizedLabel: ANVIL_PLURALIZED_METADATA_LABELS.PHENOTYPIC_SEX, sortable: true, }, { - name: "Reported Ethnicity", - pluralizedLabel: "reported ethnicities", + name: ANVIL_COLUMN_NAMES.REPORTED_ETHNICITY, + pluralizedLabel: ANVIL_PLURALIZED_METADATA_LABELS.PHENOTYPIC_SEX, sortable: true, }, ]; @@ -57,17 +62,17 @@ export const anvilTabs: AnvilCMGTabCollection = { emptyFirstColumn: false, maxPages: 25, preselectedColumns: [ - { name: "Document Id", sortable: true }, - { name: "Activity Type", sortable: true }, - { name: "Data Modality", sortable: true }, - { name: "BioSample Type", sortable: true }, - { name: "Organism Type", sortable: true }, - { name: "Dataset", sortable: true }, + { name: ANVIL_COLUMN_NAMES.DOCUMENT_ID, sortable: true }, + { name: ANVIL_COLUMN_NAMES.ACTIVITY_TYPE, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DATA_MODALITY, sortable: true }, + { name: ANVIL_COLUMN_NAMES.BIOSAMPLE_TYPE, sortable: true }, + { name: ANVIL_COLUMN_NAMES.ORGANISM_TYPE, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DATASET, sortable: true }, ], selectableColumns: [ - { name: "Phenotypic Sex", sortable: true }, - { name: "Reported Ethnicity", sortable: true }, - { name: "Diagnosis", sortable: true }, + { name: ANVIL_COLUMN_NAMES.PHENOTYPIC_SEX, sortable: true }, + { name: ANVIL_COLUMN_NAMES.REPORTED_ETHNICITY, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DIAGNOSIS, sortable: true }, ], tabName: "Activities", url: "/activities", @@ -76,22 +81,22 @@ export const anvilTabs: AnvilCMGTabCollection = { emptyFirstColumn: false, maxPages: 25, preselectedColumns: [ - { name: "BioSample Id", sortable: true }, - { name: "Anatomical Site", sortable: true }, - { name: "BioSample Type", sortable: true }, - { name: "Organism Type", sortable: true }, - { name: "Diagnosis", sortable: true }, - { name: "Dataset", sortable: true }, + { name: ANVIL_COLUMN_NAMES.BIOSAMPLE_ID, sortable: true }, + { name: ANVIL_COLUMN_NAMES.ANATOMICAL_SITE, sortable: true }, + { name: ANVIL_COLUMN_NAMES.BIOSAMPLE_TYPE, sortable: true }, + { name: ANVIL_COLUMN_NAMES.ORGANISM_TYPE, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DIAGNOSIS, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DATASET, sortable: true }, ], selectableColumns: [ { - name: "Phenotypic Sex", - pluralizedLabel: "phenotypic sexes", + name: ANVIL_COLUMN_NAMES.PHENOTYPIC_SEX, + pluralizedLabel: ANVIL_PLURALIZED_METADATA_LABELS.PHENOTYPIC_SEX, sortable: true, }, { - name: "Reported Ethnicity", - pluralizedLabel: "reported ethnicities", + name: ANVIL_COLUMN_NAMES.REPORTED_ETHNICITY, + pluralizedLabel: ANVIL_PLURALIZED_METADATA_LABELS.REPORTED_ETHNICITIES, sortable: true, }, ], @@ -120,32 +125,31 @@ export const anvilTabs: AnvilCMGTabCollection = { }, backpageHeaders: [ { - name: "Dataset ID", + name: ANVIL_DATASETS_BACKPAGE_HEADER_NAMES.DATASET_ID, }, { correspondingColumn: anvilDatasetsPreselectedColumns[3], - name: "Consent group", + name: ANVIL_DATASETS_BACKPAGE_HEADER_NAMES.CONSENT_GROUP, }, { correspondingColumn: anvilDatasetsPreselectedColumns[4], - name: "Organism type", + name: ANVIL_DATASETS_BACKPAGE_HEADER_NAMES.ORGANISM_TYPE, }, { correspondingColumn: anvilDatasetsPreselectedColumns[5], - name: "Diagnosis", + name: ANVIL_DATASETS_BACKPAGE_HEADER_NAMES.DIAGNOSIS, }, { correspondingColumn: anvilDatasetsPreselectedColumns[6], - name: "Data modality", + name: ANVIL_DATASETS_BACKPAGE_HEADER_NAMES.DATA_MODALITY, }, - // Skipped the below two columns, since they aren't always readable { correspondingColumn: anvilDatasetsSelectableColumns[0], - name: "Phenotypic sex", + name: ANVIL_DATASETS_BACKPAGE_HEADER_NAMES.PHENOTYPIC_SEX, }, { correspondingColumn: anvilDatasetsSelectableColumns[1], - name: "Reported ethnicity", + name: ANVIL_DATASETS_BACKPAGE_HEADER_NAMES.REPORTED_ETHNICITY, }, ], emptyFirstColumn: false, @@ -159,12 +163,12 @@ export const anvilTabs: AnvilCMGTabCollection = { emptyFirstColumn: false, maxPages: 25, preselectedColumns: [ - { name: "Donor Id", sortable: true }, - { name: "Organism Type", sortable: true }, - { name: "Phenotypic Sex", sortable: true }, - { name: "Reported Ethnicity", sortable: true }, - { name: "Diagnosis", sortable: true }, - { name: "Dataset", sortable: true }, + { name: ANVIL_COLUMN_NAMES.DONOR_ID, sortable: true }, + { name: ANVIL_COLUMN_NAMES.ORGANISM_TYPE, sortable: true }, + { name: ANVIL_COLUMN_NAMES.PHENOTYPIC_SEX, sortable: true }, + { name: ANVIL_COLUMN_NAMES.REPORTED_ETHNICITY, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DIAGNOSIS, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DATASET, sortable: true }, ], selectableColumns: [], tabName: "Donors", @@ -174,18 +178,18 @@ export const anvilTabs: AnvilCMGTabCollection = { emptyFirstColumn: true, maxPages: 25, preselectedColumns: [ - { name: "Name", sortable: true }, - { name: "File Format", sortable: true }, - { name: "Size", sortable: true }, - { name: "DRS URI", sortable: false }, - { name: "Data Modality", sortable: true }, - { name: "Organism Type", sortable: true }, - { name: "Dataset", sortable: true }, + { name: ANVIL_COLUMN_NAMES.NAME, sortable: true }, + { name: ANVIL_COLUMN_NAMES.FILE_FORMAT, sortable: true }, + { name: ANVIL_COLUMN_NAMES.SIZE, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DRS_URI, sortable: false }, + { name: ANVIL_COLUMN_NAMES.DATA_MODALITY, sortable: true }, + { name: ANVIL_COLUMN_NAMES.ORGANISM_TYPE, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DATASET, sortable: true }, ], selectableColumns: [ - { name: "Phenotypic Sex", sortable: true }, - { name: "Reported Ethnicity", sortable: true }, - { name: "Diagnosis", sortable: true }, + { name: ANVIL_COLUMN_NAMES.PHENOTYPIC_SEX, sortable: true }, + { name: ANVIL_COLUMN_NAMES.REPORTED_ETHNICITY, sortable: true }, + { name: ANVIL_COLUMN_NAMES.DIAGNOSIS, sortable: true }, ], tabName: "Files", url: "/files", diff --git a/explorer/e2e/anvil/constants.ts b/explorer/e2e/anvil/constants.ts new file mode 100644 index 000000000..bd38caa72 --- /dev/null +++ b/explorer/e2e/anvil/constants.ts @@ -0,0 +1,38 @@ +export const ANVIL_DATASETS_BACKPAGE_HEADER_NAMES = { + CONSENT_GROUP: "Consent group", + DATASET_ID: "Dataset Id", + DATA_MODALITY: "Data modality", + DIAGNOSIS: "Diagnosis", + ORGANISM_TYPE: "Organism type", + PHENOTYPIC_SEX: "Phenotypic sex", + REPORTED_ETHNICITY: "Reported ethnicity", +}; + +export const ANVIL_COLUMN_NAMES = { + ACCESS: "Access", + ACTIVITY_TYPE: "Activity Type", + ANATOMICAL_SITE: "Anatomical Site", + BIOSAMPLE_ID: "BioSample Id", + BIOSAMPLE_TYPE: "BioSample Type", + CONSENT_GROUP: "Consent Group", + DATASET: "Dataset", + DATA_MODALITY: "Data Modality", + DIAGNOSIS: "Diagnosis", + DOCUMENT_ID: "Document Id", + DONOR_ID: "Donor Id", + DRS_URI: "DRS URI", + FILE_FORMAT: "File Format", + IDENTIFIER: "Identifier", + NAME: "Name", + ORGANISM_TYPE: "Organism Type", + PHENOTYPIC_SEX: "Phenotypic Sex", + REPORTED_ETHNICITY: "Reported Ethnicity", + SIZE: "Size", +}; + +export const ANVIL_PLURALIZED_METADATA_LABELS = { + DATA_MODALITY: "data modalities", + DIAGNOSIS: "diagnoses", + PHENOTYPIC_SEX: "phenotypic sexes", + REPORTED_ETHNICITIES: "reported ethnicities", +}; diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index 3cac7b986..cdaf494be 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -610,27 +610,29 @@ export async function testExportBackpage( await expect(checkboxLocator).toBeChecked(); await expect(checkboxLocator).toBeEnabled({ timeout: 10000 }); } - // Check one checkbox in each table - for (const tableLocator of await page.getByRole("table").all()) { - const allInTableCheckboxLocators = await tableLocator - .getByRole("checkbox") - .all(); - const secondCheckboxInTableLocator = allInTableCheckboxLocators[1]; - await secondCheckboxInTableLocator.click(); - await expect(secondCheckboxInTableLocator).toBeChecked(); - await expect(secondCheckboxInTableLocator).toBeEnabled({ timeout: 10000 }); - const otherInTableCheckboxLocators = [ - allInTableCheckboxLocators[0], - ...allInTableCheckboxLocators.slice(2), - ]; - // Make sure that no other checkboxes are selected - for (const otherCheckboxLocator of otherInTableCheckboxLocators) { - await expect(otherCheckboxLocator).not.toBeChecked(); - await expect(otherCheckboxLocator).toBeEnabled(); - } + // Expect there to be exactly one table on the backpage + await expect(page.getByRole("table")).toHaveCount(1); + // 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(); await expect( page.getByText(tab.backpageExportButtons.firstLoadingMessage, { @@ -654,6 +656,7 @@ export async function testExportBackpage( await expect( newPage.getByText(tab.backpageExportButtons?.newTabMessage) ).toBeVisible(); + */ } /** From 3564080e3658d78a25859a8dae9e1d1b3247cfa6 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Tue, 3 Sep 2024 18:38:55 -0700 Subject: [PATCH 09/10] test: made backpages tests skip again (#4080) --- explorer/e2e/anvil/anvil-backpages.spec.ts | 4 ++-- explorer/e2e/testFunctions.ts | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/explorer/e2e/anvil/anvil-backpages.spec.ts b/explorer/e2e/anvil/anvil-backpages.spec.ts index 977f71ced..593f958fc 100644 --- a/explorer/e2e/anvil/anvil-backpages.spec.ts +++ b/explorer/e2e/anvil/anvil-backpages.spec.ts @@ -6,7 +6,7 @@ import { } from "../testFunctions"; import { anvilTabs } from "./anvil-tabs"; -test("Smoke test `Export to Terra` button on the first available dataset", async ({ +test.skip("Smoke test `Export to Terra` button on the first available dataset", async ({ context, page, }) => { @@ -14,7 +14,7 @@ test("Smoke test `Export to Terra` button on the first available dataset", async await testExportBackpage(context, page, anvilTabs.datasets); }); -test("Check access controls on the datasets backpages work for the first two tabs", async ({ +test.skip("Check access controls on the datasets backpages work for the first two tabs", async ({ page, }) => { test.setTimeout(120000); diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index cdaf494be..272f4b36f 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -632,7 +632,6 @@ export async function testExportBackpage( } // Click the Export Request button await expect(exportRequestButtonLocator).toBeEnabled({ timeout: 10000 }); - /* await exportRequestButtonLocator.click(); await expect( page.getByText(tab.backpageExportButtons.firstLoadingMessage, { @@ -656,7 +655,6 @@ export async function testExportBackpage( await expect( newPage.getByText(tab.backpageExportButtons?.newTabMessage) ).toBeVisible(); - */ } /** From a7701148340a81a459d63184515dc7030b5c9b3a Mon Sep 17 00:00:00 2001 From: Jonah - CC Date: Fri, 6 Sep 2024 15:01:54 -0700 Subject: [PATCH 10/10] test: fixed another typo in testReadme.md... (#4080) --- explorer/e2e/testReadme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/explorer/e2e/testReadme.md b/explorer/e2e/testReadme.md index 100216659..54a3510ab 100644 --- a/explorer/e2e/testReadme.md +++ b/explorer/e2e/testReadme.md @@ -72,16 +72,16 @@ through the actions taken as part of the test and view the impact on the web pag - All tests rely on correct lists of tabs, columns, and filters in `anvil-tabs.ts` - Backpages (`anvil-backpages.spec.ts`) - Test the export process on the "Datasets" tab - - Selects the first dataset that does not have access control enabled and go to the export tab on its backpage. Then, selects one filter checkbox for each category, then finally selects the "Export to Terra" button - - Checks that buttons, text, and loading text appears as expected, and that a new tab appears when the "Export to Terra" button is pressed + - Selects the first dataset that does not have access control enabled and goes to the export tab on its backpage. Then, selects one filter checkbox for each category, then finally selects the "Export to Terra" button + - Checks that buttons, text, and loading text appear as expected, and that a new tab appears when the "Export to Terra" button is pressed - Relevant text constants are stored in `anvil-tabs.ts` - Currently disabled as the export button is disabled - Test that access control works properly on the "Datasets" tab - - Selects a non-access-controlled tab and check that text associated with access being granted is present, then repeats for an access-controlled tab + - Selects a non-access-controlled tab and checks that text associated with access being granted is present, then repeats for an access-controlled tab - Relevant text constants are stored in `anvil-tabs.ts` - Currently disabled as the export button is disabled - - Test that data in the sidebar of the "Datasets" tab is the same as the text displayed in the main tab le - - Enable all non-preselected columns and read values from all columns in the first row, including n-tag cells. Then select the backpage for the first row and check that all matching columns are reflected + - Test that data in the sidebar of the "Datasets" tab is the same as the text displayed in the main table + - Enables all non-preselected columns and read values from all columns in the first row, including n-tag cells. Then selects the backpage for the first row and checks that all matching columns are reflected - Requires a list of the values in the sidebar to be present in `anvil-tabs.ts` and that plural labels are defined there for any columns that include n-tag cells #### AnVIL-Catalog