From e217f20655f06992c85499b02ef4211f32e1616b Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Mon, 8 Jul 2024 08:55:41 -0700 Subject: [PATCH 01/16] feat: first draft of anvil-cmg filter tests (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 103 +++++++++++++++++++++++ explorer/e2e/anvil/anvil-tabs.ts | 2 +- explorer/e2e/testFunctions.ts | 42 ++++++++- 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 explorer/e2e/anvil/anvil-filters.spec.ts diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts new file mode 100644 index 000000000..6ec405d08 --- /dev/null +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -0,0 +1,103 @@ +import { expect, test } from "@playwright/test"; +import { testFilterPresence } from "../testFunctions"; +import { anvilFilters, anvilTabs, anvilTabTestOrder } from "./anvil-tabs"; + +test.describe.configure({ mode: "parallel" }); + +const filter_regex = (filter: string): RegExp => + new RegExp(filter + "\\s+\\([0-9]+\\)\\s*"); + +test("Check that all filters exist on the Datasets tab and are clickable", async ({ + page, +}) => { + await testFilterPresence(page, anvilTabs.datasets); +}); + +test("Check that all filters exist on the Donors tab and are clickable", async ({ + page, +}) => { + await testFilterPresence(page, anvilTabs.donors); +}); + +test("Check that all filters exist on the BioSamples tab and are clickable", async ({ + page, +}) => { + await testFilterPresence(page, anvilTabs.biosamples); +}); + +test("Check that all filters exist on the Activities tab and are clickable", async ({ + page, +}) => { + await testFilterPresence(page, anvilTabs.activities); +}); + +test("Check that all filters exist on the Files tab and are clickable", async ({ + page, +}) => { + await testFilterPresence(page, anvilTabs.files); +}); + +test("Check that the first filter on the Datasets tab creates at least one checkbox, and that checking up to the first five does not cause an error and does not cause there to be no entries in the table", async ({ + page, +}) => { + // Goto the datasets tab + await page.goto(anvilTabs.datasets.url); + await expect( + page.getByRole("tab").getByText(anvilTabs.datasets.tabName) + ).toBeVisible(); + + // Select a filter + await page + .getByRole("button") + .getByText(filter_regex(anvilFilters[4])) + .click(); + // Expect all checkboxes to be unchecked initially and to work properly + await expect(page.getByRole("checkbox").first()).toBeVisible(); + const all_checkboxes = await page.getByRole("checkbox").all(); + for (let i = 0; i < all_checkboxes.length && i < 5; i++) { + const checkbox = all_checkboxes[i]; + await checkbox.scrollIntoViewIfNeeded(); + await expect(checkbox).not.toBeChecked(); + await checkbox.click(); + await expect(checkbox).toBeChecked(); + } + await page.locator("body").click(); + const firstElementTextLocator = page + .getByRole("rowgroup") + .nth(1) + .getByRole("row") + .nth(0) + .getByRole("cell") + .first(); + await expect(firstElementTextLocator).toBeVisible(); +}); + +test("Check that filter checkboxes are persistent across pages", async ({ + page, +}) => { + await page.goto(anvilTabs.datasets.url); + await expect( + page.getByRole("tab").getByText(anvilTabs.datasets.tabName) + ).toBeVisible(); + await page.getByText(filter_regex(anvilFilters[3])).click(); // maybe should select a random one instead; + await expect(page.getByRole("checkbox").first()).not.toBeChecked(); + await page.getByRole("checkbox").first().click(); + await expect(page.getByRole("checkbox").first()).toBeChecked(); + await page.locator("body").click(); + for (const blah of anvilTabTestOrder) { + console.log(blah); + await page.getByRole("tab").getByText(anvilTabs[blah].tabName).click(); + const firstElementTextLocator = page + .getByRole("rowgroup") + .nth(1) + .getByRole("row") + .nth(0) + .getByRole("cell") + .first(); + await expect(firstElementTextLocator).toBeVisible(); + await expect(page.getByText(filter_regex(anvilFilters[3]))).toBeVisible(); + await page.getByText(filter_regex(anvilFilters[3])).click(); + await expect(page.getByRole("checkbox").first()).toBeChecked(); + await page.locator("body").click(); + } +}); diff --git a/explorer/e2e/anvil/anvil-tabs.ts b/explorer/e2e/anvil/anvil-tabs.ts index 59502299f..4c6de8781 100644 --- a/explorer/e2e/anvil/anvil-tabs.ts +++ b/explorer/e2e/anvil/anvil-tabs.ts @@ -5,7 +5,7 @@ import { TabDescription, } from "../testInterfaces"; -export const filters: string[] = [ +export const anvilFilters: string[] = [ "Anatomical Site", "BioSample Type", "Consent Group", diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index aa95cb5a6..7d1060d12 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -1,8 +1,23 @@ -import { expect, Page } from "@playwright/test"; +import { expect, Locator, Page } from "@playwright/test"; +import { anvilFilters, anvilTabs, anvilTabTestOrder } from "./anvil/anvil-tabs"; import { TabDescription } from "./testInterfaces"; /* eslint-disable sonarjs/no-duplicate-string -- ignoring duplicate strings here */ // Run the "Expect each tab to appear as selected when the corresponding url is accessed" test + +const getFirstElementTextLocator = ( + page: Page, + workColumnPosition: number +): Locator => { + return page + .getByRole("rowgroup") + .nth(1) + .getByRole("row") + .nth(0) + .getByRole("cell") + .nth(workColumnPosition); +}; + export async function testUrl( page: Page, tab: TabDescription, @@ -207,4 +222,29 @@ export async function testPreSelectedColumns( } } +const filter_regex = (filter: string): RegExp => + new RegExp(filter + "\\s+\\([0-9]+\\)\\s*"); + +export async function testFilterPresence( + page: Page, + tab: TabDescription +): Promise { + await page.goto(tab.url); + await expect(page.getByRole("tab").getByText(tab.tabName)).toBeVisible(); + await page.getByText(filter_regex(anvilFilters[3])).click(); // maybe should select a random one instead; + await expect(page.getByRole("checkbox").first()).not.toBeChecked(); + await page.getByRole("checkbox").first().click(); + await expect(page.getByRole("checkbox").first()).toBeChecked(); + await page.locator("body").click(); + for (const blah of anvilTabTestOrder) { + console.log(blah); + await page.getByRole("tab").getByText(anvilTabs[blah].tabName).click(); + await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); + await expect(page.getByText(filter_regex(anvilFilters[3]))).toBeVisible(); + await page.getByText(filter_regex(anvilFilters[3])).click(); + await expect(page.getByRole("checkbox").first()).toBeChecked(); + await page.locator("body").click(); + } +} + /* eslint-enable sonarjs/no-duplicate-string -- Checking duplicate strings again*/ From ae2eddbe8ae52540522f740559a883e1c0a1f4e8 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Mon, 8 Jul 2024 10:30:57 -0700 Subject: [PATCH 02/16] fix: refactored and debugged filter tests (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 61 +++++++++++++----------- explorer/e2e/testFunctions.ts | 43 ++++++----------- 2 files changed, 47 insertions(+), 57 deletions(-) diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts index 6ec405d08..f89c0e3ce 100644 --- a/explorer/e2e/anvil/anvil-filters.spec.ts +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -1,12 +1,13 @@ import { expect, test } from "@playwright/test"; -import { testFilterPresence } from "../testFunctions"; +import { + filter_regex, + getFirstElementTextLocator, + testFilterPresence, +} from "../testFunctions"; import { anvilFilters, anvilTabs, anvilTabTestOrder } from "./anvil-tabs"; test.describe.configure({ mode: "parallel" }); -const filter_regex = (filter: string): RegExp => - new RegExp(filter + "\\s+\\([0-9]+\\)\\s*"); - test("Check that all filters exist on the Datasets tab and are clickable", async ({ page, }) => { @@ -49,7 +50,11 @@ test("Check that the first filter on the Datasets tab creates at least one check // Select a filter await page .getByRole("button") - .getByText(filter_regex(anvilFilters[4])) + .getByText( + filter_regex( + anvilFilters[Math.floor(Math.random() * anvilFilters.length)] + ) + ) .click(); // Expect all checkboxes to be unchecked initially and to work properly await expect(page.getByRole("checkbox").first()).toBeVisible(); @@ -62,42 +67,42 @@ test("Check that the first filter on the Datasets tab creates at least one check await expect(checkbox).toBeChecked(); } await page.locator("body").click(); - const firstElementTextLocator = page - .getByRole("rowgroup") - .nth(1) - .getByRole("row") - .nth(0) - .getByRole("cell") - .first(); - await expect(firstElementTextLocator).toBeVisible(); + await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); }); test("Check that filter checkboxes are persistent across pages", async ({ page, }) => { - await page.goto(anvilTabs.datasets.url); + // Randomly select a filter + const test_filter = + anvilFilters[Math.floor(Math.random() * anvilFilters.length)]; + // Start on the first tab in the test order (should be files) + await page.goto(anvilTabs[anvilTabTestOrder[0]].url); await expect( page.getByRole("tab").getByText(anvilTabs.datasets.tabName) ).toBeVisible(); - await page.getByText(filter_regex(anvilFilters[3])).click(); // maybe should select a random one instead; + // Select the first checkbox on the test filter + await page.getByText(filter_regex(test_filter)).click(); await expect(page.getByRole("checkbox").first()).not.toBeChecked(); await page.getByRole("checkbox").first().click(); await expect(page.getByRole("checkbox").first()).toBeChecked(); await page.locator("body").click(); - for (const blah of anvilTabTestOrder) { - console.log(blah); - await page.getByRole("tab").getByText(anvilTabs[blah].tabName).click(); - const firstElementTextLocator = page - .getByRole("rowgroup") - .nth(1) - .getByRole("row") - .nth(0) - .getByRole("cell") - .first(); - await expect(firstElementTextLocator).toBeVisible(); - await expect(page.getByText(filter_regex(anvilFilters[3]))).toBeVisible(); - await page.getByText(filter_regex(anvilFilters[3])).click(); + // Expect at least some text to still be visible + await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); + // For each tab, check that the selected filter is still checked + for (const tab of anvilTabTestOrder.slice(1)) { + await page.getByRole("tab").getByText(anvilTabs[tab].tabName).click(); + await expect(page.getByText(filter_regex(test_filter))).toBeVisible(); + await page.getByText(filter_regex(test_filter)).click(); await expect(page.getByRole("checkbox").first()).toBeChecked(); await page.locator("body").click(); } + // Return to the start tab and confirm that the filter stays checked and that some content is visible + await page + .getByRole("tab") + .getByText(anvilTabs[anvilTabTestOrder[0]].tabName) + .click(); + await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); + await page.getByText(filter_regex(test_filter)).click(); + await expect(page.getByRole("checkbox").first()).toBeChecked(); }); diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index 7d1060d12..95296e09e 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -1,11 +1,11 @@ import { expect, Locator, Page } from "@playwright/test"; -import { anvilFilters, anvilTabs, anvilTabTestOrder } from "./anvil/anvil-tabs"; +import { anvilFilters } from "./anvil/anvil-tabs"; import { TabDescription } from "./testInterfaces"; /* eslint-disable sonarjs/no-duplicate-string -- ignoring duplicate strings here */ // Run the "Expect each tab to appear as selected when the corresponding url is accessed" test -const getFirstElementTextLocator = ( +export const getFirstElementTextLocator = ( page: Page, workColumnPosition: number ): Locator => { @@ -82,13 +82,10 @@ export async function testSortAzul( ? columnPosition + 1 : columnPosition; // Locators for the first and last cells in a particular column position on the page - const firstElementTextLocator = page - .getByRole("rowgroup") - .nth(1) - .getByRole("row") - .nth(0) - .getByRole("cell") - .nth(workColumnPosition); + const firstElementTextLocator = getFirstElementTextLocator( + page, + workColumnPosition + ); const lastElementTextLocator = page .getByRole("rowgroup") .nth(1) @@ -141,13 +138,10 @@ export async function testSortCatalog( ? columnPosition + 1 : columnPosition; // Locators for the first and last cells in a particular column position on the page - const firstElementTextLocator = page - .getByRole("rowgroup") - .nth(1) - .getByRole("row") - .nth(0) - .getByRole("cell") - .nth(workColumnPosition); + const firstElementTextLocator = getFirstElementTextLocator( + page, + workColumnPosition + ); // Locator for the sort button const columnSortLocator = page .getByRole("columnheader", { @@ -222,7 +216,7 @@ export async function testPreSelectedColumns( } } -const filter_regex = (filter: string): RegExp => +export const filter_regex = (filter: string): RegExp => new RegExp(filter + "\\s+\\([0-9]+\\)\\s*"); export async function testFilterPresence( @@ -231,18 +225,9 @@ export async function testFilterPresence( ): Promise { await page.goto(tab.url); await expect(page.getByRole("tab").getByText(tab.tabName)).toBeVisible(); - await page.getByText(filter_regex(anvilFilters[3])).click(); // maybe should select a random one instead; - await expect(page.getByRole("checkbox").first()).not.toBeChecked(); - await page.getByRole("checkbox").first().click(); - await expect(page.getByRole("checkbox").first()).toBeChecked(); - await page.locator("body").click(); - for (const blah of anvilTabTestOrder) { - console.log(blah); - await page.getByRole("tab").getByText(anvilTabs[blah].tabName).click(); - await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); - await expect(page.getByText(filter_regex(anvilFilters[3]))).toBeVisible(); - await page.getByText(filter_regex(anvilFilters[3])).click(); - await expect(page.getByRole("checkbox").first()).toBeChecked(); + for (const filter of anvilFilters) { + await page.getByText(filter_regex(filter)).click(); // maybe should select a random one instead; + await expect(page.getByRole("checkbox").first()).not.toBeChecked(); await page.locator("body").click(); } } From 185e3313d1509c2871ec7b72b54cb9e844cdd0f3 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Mon, 8 Jul 2024 10:35:48 -0700 Subject: [PATCH 03/16] fix: added extra check to testFilterPresence (#4068) --- explorer/e2e/testFunctions.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index 95296e09e..5f61ad2ae 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -223,12 +223,17 @@ export async function testFilterPresence( page: Page, tab: TabDescription ): Promise { + // Goto the selected tab await page.goto(tab.url); await expect(page.getByRole("tab").getByText(tab.tabName)).toBeVisible(); for (const filter of anvilFilters) { - await page.getByText(filter_regex(filter)).click(); // maybe should select a random one instead; + // Check that each filter is visible and clickable + await expect(page.getByText(filter_regex(filter))).toBeVisible(); + await page.getByText(filter_regex(filter)).click(); await expect(page.getByRole("checkbox").first()).not.toBeChecked(); + // Check that clicking out of the filter menu causes it to disappear await page.locator("body").click(); + await expect(page.getByRole("checkbox")).toHaveCount(0); } } From 90085d18370d7b186db175f0a281d2deac14d69d Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Tue, 9 Jul 2024 22:30:21 -0700 Subject: [PATCH 04/16] fix: refactored tests (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 101 +++++++++++--------- explorer/e2e/testFunctions.ts | 114 +++++++++++++++++++++-- 2 files changed, 167 insertions(+), 48 deletions(-) diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts index f89c0e3ce..e9ea1480f 100644 --- a/explorer/e2e/anvil/anvil-filters.spec.ts +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -1,41 +1,45 @@ import { expect, test } from "@playwright/test"; import { - filter_regex, + filterRegex, getFirstElementTextLocator, + testFilterBubbles, + testFilterCounts, + testFilterPersistence, testFilterPresence, } from "../testFunctions"; import { anvilFilters, anvilTabs, anvilTabTestOrder } from "./anvil-tabs"; test.describe.configure({ mode: "parallel" }); +const filter_index_list = [3, 4, 5, 7, 6, 2]; test("Check that all filters exist on the Datasets tab and are clickable", async ({ page, }) => { - await testFilterPresence(page, anvilTabs.datasets); + await testFilterPresence(page, anvilTabs.datasets, anvilFilters); }); test("Check that all filters exist on the Donors tab and are clickable", async ({ page, }) => { - await testFilterPresence(page, anvilTabs.donors); + await testFilterPresence(page, anvilTabs.donors, anvilFilters); }); test("Check that all filters exist on the BioSamples tab and are clickable", async ({ page, }) => { - await testFilterPresence(page, anvilTabs.biosamples); + await testFilterPresence(page, anvilTabs.biosamples, anvilFilters); }); test("Check that all filters exist on the Activities tab and are clickable", async ({ page, }) => { - await testFilterPresence(page, anvilTabs.activities); + await testFilterPresence(page, anvilTabs.activities, anvilFilters); }); test("Check that all filters exist on the Files tab and are clickable", async ({ page, }) => { - await testFilterPresence(page, anvilTabs.files); + await testFilterPresence(page, anvilTabs.files, anvilFilters); }); test("Check that the first filter on the Datasets tab creates at least one checkbox, and that checking up to the first five does not cause an error and does not cause there to be no entries in the table", async ({ @@ -51,9 +55,7 @@ test("Check that the first filter on the Datasets tab creates at least one check await page .getByRole("button") .getByText( - filter_regex( - anvilFilters[Math.floor(Math.random() * anvilFilters.length)] - ) + filterRegex(anvilFilters[Math.floor(Math.random() * anvilFilters.length)]) ) .click(); // Expect all checkboxes to be unchecked initially and to work properly @@ -70,39 +72,54 @@ test("Check that the first filter on the Datasets tab creates at least one check await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); }); -test("Check that filter checkboxes are persistent across pages", async ({ +test("Check that filter checkboxes are persistent across pages on an arbitrary filter", async ({ page, }) => { - // Randomly select a filter - const test_filter = - anvilFilters[Math.floor(Math.random() * anvilFilters.length)]; - // Start on the first tab in the test order (should be files) - await page.goto(anvilTabs[anvilTabTestOrder[0]].url); - await expect( - page.getByRole("tab").getByText(anvilTabs.datasets.tabName) - ).toBeVisible(); - // Select the first checkbox on the test filter - await page.getByText(filter_regex(test_filter)).click(); - await expect(page.getByRole("checkbox").first()).not.toBeChecked(); - await page.getByRole("checkbox").first().click(); - await expect(page.getByRole("checkbox").first()).toBeChecked(); - await page.locator("body").click(); - // Expect at least some text to still be visible - await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); - // For each tab, check that the selected filter is still checked - for (const tab of anvilTabTestOrder.slice(1)) { - await page.getByRole("tab").getByText(anvilTabs[tab].tabName).click(); - await expect(page.getByText(filter_regex(test_filter))).toBeVisible(); - await page.getByText(filter_regex(test_filter)).click(); - await expect(page.getByRole("checkbox").first()).toBeChecked(); - await page.locator("body").click(); - } - // Return to the start tab and confirm that the filter stays checked and that some content is visible - await page - .getByRole("tab") - .getByText(anvilTabs[anvilTabTestOrder[0]].tabName) - .click(); - await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); - await page.getByText(filter_regex(test_filter)).click(); - await expect(page.getByRole("checkbox").first()).toBeChecked(); + await testFilterPersistence( + page, + anvilFilters[3], + anvilTabTestOrder.map((x) => anvilTabs[x]) + ); +}); + +test("Check that filter menu counts match actual counts on the Datasets tab", async ({ + page, +}) => { + await testFilterCounts( + page, + anvilTabs.datasets, + filter_index_list.map((x) => anvilFilters[x]), + 25 + ); +}); + +test("Check that filter menu counts match actual counts on the Activities tab", async ({ + page, +}) => { + await testFilterCounts( + page, + anvilTabs.activities, + filter_index_list.map((x) => anvilFilters[x]), + 25 + ); +}); + +test("Check that the blue filter bubbles match the selected filter for an arbitrary filter on the Files tab", async ({ + page, +}) => { + await testFilterBubbles( + page, + anvilTabs.files, + filter_index_list.map((x) => anvilFilters[x]) + ); +}); + +test("Check that the blue filter bubbles match the selected filter for an arbitrary filter on the BioSamples tab", async ({ + page, +}) => { + await testFilterBubbles( + page, + anvilTabs.biosamples, + filter_index_list.map((x) => anvilFilters[x]) + ); }); diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index 5f61ad2ae..78f03d92d 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -1,5 +1,4 @@ import { expect, Locator, Page } from "@playwright/test"; -import { anvilFilters } from "./anvil/anvil-tabs"; import { TabDescription } from "./testInterfaces"; /* eslint-disable sonarjs/no-duplicate-string -- ignoring duplicate strings here */ @@ -216,20 +215,22 @@ export async function testPreSelectedColumns( } } -export const filter_regex = (filter: string): RegExp => +export const filterRegex = (filter: string): RegExp => new RegExp(filter + "\\s+\\([0-9]+\\)\\s*"); export async function testFilterPresence( page: Page, - tab: TabDescription + tab: TabDescription, + filters: string[] ): Promise { // Goto the selected tab await page.goto(tab.url); await expect(page.getByRole("tab").getByText(tab.tabName)).toBeVisible(); - for (const filter of anvilFilters) { + for (const filter of filters) { // Check that each filter is visible and clickable - await expect(page.getByText(filter_regex(filter))).toBeVisible(); - await page.getByText(filter_regex(filter)).click(); + await expect(page.getByText(filterRegex(filter))).toBeVisible(); + await page.getByText(filterRegex(filter)).click(); + await expect(page.getByRole("checkbox").first()).toBeVisible(); await expect(page.getByRole("checkbox").first()).not.toBeChecked(); // Check that clicking out of the filter menu causes it to disappear await page.locator("body").click(); @@ -237,4 +238,105 @@ export async function testFilterPresence( } } +export const getNamedFilterButton = ( + page: Page, + filterName: string +): Locator => { + return page + .getByRole("button") + .filter({ has: page.getByRole("checkbox"), hasText: filterName }); +}; +export const getFirstFilterButton = (page: Page): Locator => { + return page + .getByRole("button") + .filter({ has: page.getByRole("checkbox") }) + .first(); +}; + +export async function testFilterPersistence( + page: Page, + testFilter: string, + tabOrder: TabDescription[] +): Promise { + // Start on the first tab in the test order (should be files) + await page.goto(tabOrder[0].url); + // Select the first checkbox on the test filter + await page.getByText(filterRegex(testFilter)).click(); + const to_select = await getFirstFilterButton(page); + await expect(to_select.getByRole("checkbox")).not.toBeChecked(); + await to_select.getByRole("checkbox").setChecked(true); + const filterName = (await to_select.innerText()).split("\n")[0]; //MAY NEED TO ADD SOME CHECKING MECHANISM HERE + await expect(to_select.getByRole("checkbox")).toBeChecked(); + await page.locator("body").click(); + // Expect at least some text to still be visible + await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); + // For each tab, check that the selected filter is still checked + for (const tab of tabOrder.slice(1)) { + await page.getByRole("tab").getByText(tab.tabName).click(); + await expect(page.getByText(filterRegex(testFilter))).toBeVisible(); + await page.getByText(filterRegex(testFilter)).click(); + const previously_selected = getNamedFilterButton(page, filterName); + await expect(previously_selected.getByRole("checkbox")).toBeChecked(); + await page.locator("body").click(); + } + // Return to the start tab and confirm that the filter stays checked and that some content is visible + await page.getByRole("tab").getByText(tabOrder[0].tabName).click(); + await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); + await page.getByText(filterRegex(testFilter)).click(); + const previously_selected = getFirstFilterButton(page); + await expect(previously_selected).toContainText(filterName, { + useInnerText: true, + }); + await expect(previously_selected.getByRole("checkbox").first()).toBeChecked(); +} + +export async function testFilterCounts( + page: Page, + tab: TabDescription, + filters: string[], + elements_per_page: number +): Promise { + await page.goto(tab.url); + // For each arbitrarily selected filter + for (const filter of filters) { + // Select the filter + await page.getByText(filterRegex(filter)).click(); + // Get the number associated with the first filter button, and select it + const filter_button = getFirstFilterButton(page); + const filterNumber = Number( + (await filter_button.innerText()).split("\n")[1] + ); + await filter_button.getByRole("checkbox").setChecked(true); + // + await page.locator("body").click(); + const firstNumber = + filterNumber <= elements_per_page ? filterNumber : elements_per_page; + await expect( + page.getByText("Results 1 - " + firstNumber + " of " + filterNumber) + ).toBeVisible(); + } +} + +export async function testFilterBubbles( + page: Page, + tab: TabDescription, + filters: string[] +): Promise { + page.goto(tab.url); + for (const filter of filters) { + await page.getByText(filterRegex(filter)).click(); + const firstFilterButton = getFirstFilterButton(page); + const firstFilterName = (await firstFilterButton.innerText()).split( + "\n" + )[0]; + await firstFilterButton.getByRole("checkbox").setChecked(true); + await page.locator("body").click(); + const filterBlueButton = page + .getByRole("button") + .getByText(firstFilterName); + await expect(filterBlueButton).toBeVisible(); + await filterBlueButton.click(); + await expect(filterBlueButton).toHaveCount(0); + } +} /* eslint-enable sonarjs/no-duplicate-string -- Checking duplicate strings again*/ From d23eeecee8c635970dcf67c684313a1223100a51 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 12 Jul 2024 16:53:22 -0700 Subject: [PATCH 05/16] fix: refactored tests (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 7 ++-- explorer/e2e/testFunctions.ts | 42 +++++++++++++++--------- explorer/playwright_anvil.config.ts | 2 +- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts index e9ea1480f..b7f12bc11 100644 --- a/explorer/e2e/anvil/anvil-filters.spec.ts +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -9,7 +9,7 @@ import { } from "../testFunctions"; import { anvilFilters, anvilTabs, anvilTabTestOrder } from "./anvil-tabs"; -test.describe.configure({ mode: "parallel" }); +test.describe.configure({ mode: "parallel", timeout: 60 * 1000 }); const filter_index_list = [3, 4, 5, 7, 6, 2]; test("Check that all filters exist on the Datasets tab and are clickable", async ({ @@ -85,12 +85,15 @@ test("Check that filter checkboxes are persistent across pages on an arbitrary f test("Check that filter menu counts match actual counts on the Datasets tab", async ({ page, }) => { - await testFilterCounts( + const result = await testFilterCounts( page, anvilTabs.datasets, filter_index_list.map((x) => anvilFilters[x]), 25 ); + if (!result) { + test.fail(); + } }); test("Check that filter menu counts match actual counts on the Activities tab", async ({ diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index 78f03d92d..d681ce8e2 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -272,7 +272,7 @@ export async function testFilterPersistence( await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); // For each tab, check that the selected filter is still checked for (const tab of tabOrder.slice(1)) { - await page.getByRole("tab").getByText(tab.tabName).click(); + await page.getByRole("tab").getByText(tab.tabName, { exact: true }).click(); await expect(page.getByText(filterRegex(testFilter))).toBeVisible(); await page.getByText(filterRegex(testFilter)).click(); const previously_selected = getNamedFilterButton(page, filterName); @@ -280,7 +280,10 @@ export async function testFilterPersistence( await page.locator("body").click(); } // Return to the start tab and confirm that the filter stays checked and that some content is visible - await page.getByRole("tab").getByText(tabOrder[0].tabName).click(); + await page + .getByRole("tab") + .getByText(tabOrder[0].tabName, { exact: true }) + .click(); await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); await page.getByText(filterRegex(testFilter)).click(); const previously_selected = getFirstFilterButton(page); @@ -295,7 +298,7 @@ export async function testFilterCounts( tab: TabDescription, filters: string[], elements_per_page: number -): Promise { +): Promise { await page.goto(tab.url); // For each arbitrarily selected filter for (const filter of filters) { @@ -303,18 +306,24 @@ export async function testFilterCounts( await page.getByText(filterRegex(filter)).click(); // Get the number associated with the first filter button, and select it const filter_button = getFirstFilterButton(page); - const filterNumber = Number( - (await filter_button.innerText()).split("\n")[1] - ); + const filter_numbers = (await filter_button.innerText()).split("\n"); + const filter_number = + filter_numbers.map((x) => Number(x)).find((x) => !isNaN(x)) ?? -1; + if (!filter_number) { + return false; + } await filter_button.getByRole("checkbox").setChecked(true); - // await page.locator("body").click(); + await expect(page.getByRole("checkbox")).toHaveCount(0); const firstNumber = - filterNumber <= elements_per_page ? filterNumber : elements_per_page; + filter_number <= elements_per_page ? filter_number : elements_per_page; + console.log("Results 1 - " + firstNumber + " of " + filter_number); + await expect( - page.getByText("Results 1 - " + firstNumber + " of " + filterNumber) + page.getByText("Results 1 - " + firstNumber + " of " + filter_number) ).toBeVisible(); } + return true; } export async function testFilterBubbles( @@ -326,15 +335,18 @@ export async function testFilterBubbles( for (const filter of filters) { await page.getByText(filterRegex(filter)).click(); const firstFilterButton = getFirstFilterButton(page); - const firstFilterName = (await firstFilterButton.innerText()).split( - "\n" - )[0]; - await firstFilterButton.getByRole("checkbox").setChecked(true); - await page.locator("body").click(); + const firstFilterName = + (await firstFilterButton.innerText()) + .split("\n") + .find((x) => x.length > 0) ?? ""; + await firstFilterButton.getByRole("checkbox").click(); + await page.keyboard.press("Escape"); + await expect(page.getByRole("checkbox")).toHaveCount(0); const filterBlueButton = page - .getByRole("button") + .locator("#sidebar-positioner") .getByText(firstFilterName); await expect(filterBlueButton).toBeVisible(); + await filterBlueButton.scrollIntoViewIfNeeded(); await filterBlueButton.click(); await expect(filterBlueButton).toHaveCount(0); } 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 88c0c89e86b3964d4777ebb4cc67c98ea5c8ba0f Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 12 Jul 2024 16:56:11 -0700 Subject: [PATCH 06/16] 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 d753c7658f7e89b6d3ec9ba49c5aa9f3cdae80ae Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 12 Jul 2024 20:16:26 -0700 Subject: [PATCH 07/16] fix: attempted fix for filter test issues (#4068) --- explorer/e2e/testFunctions.ts | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index d681ce8e2..04a6566b2 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -272,11 +272,16 @@ export async function testFilterPersistence( await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); // For each tab, check that the selected filter is still checked for (const tab of tabOrder.slice(1)) { - await page.getByRole("tab").getByText(tab.tabName, { exact: true }).click(); + await page + .getByRole("tab") + .getByText(tab.tabName, { exact: true }) + .dispatchEvent("click"); await expect(page.getByText(filterRegex(testFilter))).toBeVisible(); - await page.getByText(filterRegex(testFilter)).click(); + await page.getByText(filterRegex(testFilter)).dispatchEvent("click"); + await page.waitForLoadState("load"); const previously_selected = getNamedFilterButton(page, filterName); await expect(previously_selected.getByRole("checkbox")).toBeChecked(); + await page.waitForLoadState("load"); await page.locator("body").click(); } // Return to the start tab and confirm that the filter stays checked and that some content is visible @@ -303,21 +308,24 @@ export async function testFilterCounts( // For each arbitrarily selected filter for (const filter of filters) { // Select the filter - await page.getByText(filterRegex(filter)).click(); + await page.getByText(filterRegex(filter)).dispatchEvent("click"); // Get the number associated with the first filter button, and select it + await page.waitForLoadState("load"); const filter_button = getFirstFilterButton(page); const filter_numbers = (await filter_button.innerText()).split("\n"); const filter_number = - filter_numbers.map((x) => Number(x)).find((x) => !isNaN(x)) ?? -1; - if (!filter_number) { + filter_numbers.map((x) => Number(x)).find((x) => !isNaN(x) && x !== 0) ?? + -1; + if (filter_number < 0) { + console.log(filter_numbers.map((x) => Number(x))); return false; } - await filter_button.getByRole("checkbox").setChecked(true); + await filter_button.getByRole("checkbox").dispatchEvent("click"); + await page.waitForLoadState("load"); await page.locator("body").click(); await expect(page.getByRole("checkbox")).toHaveCount(0); const firstNumber = filter_number <= elements_per_page ? filter_number : elements_per_page; - console.log("Results 1 - " + firstNumber + " of " + filter_number); await expect( page.getByText("Results 1 - " + firstNumber + " of " + filter_number) @@ -333,13 +341,16 @@ export async function testFilterBubbles( ): Promise { page.goto(tab.url); for (const filter of filters) { - await page.getByText(filterRegex(filter)).click(); + await page.getByText(filterRegex(filter)).dispatchEvent("click"); + //await page.waitForTimeout(500); + await page.waitForLoadState("load"); const firstFilterButton = getFirstFilterButton(page); const firstFilterName = (await firstFilterButton.innerText()) .split("\n") .find((x) => x.length > 0) ?? ""; await firstFilterButton.getByRole("checkbox").click(); + await page.waitForLoadState("load"); await page.keyboard.press("Escape"); await expect(page.getByRole("checkbox")).toHaveCount(0); const filterBlueButton = page @@ -347,7 +358,7 @@ export async function testFilterBubbles( .getByText(firstFilterName); await expect(filterBlueButton).toBeVisible(); await filterBlueButton.scrollIntoViewIfNeeded(); - await filterBlueButton.click(); + await filterBlueButton.dispatchEvent("click"); await expect(filterBlueButton).toHaveCount(0); } } From 3541949911d45947f4549300511811baee314cf2 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Sat, 13 Jul 2024 22:00:36 -0700 Subject: [PATCH 08/16] fix: switched setchecked to click action (#4068) --- explorer/e2e/testFunctions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index 04a6566b2..e4971748d 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -264,7 +264,7 @@ export async function testFilterPersistence( await page.getByText(filterRegex(testFilter)).click(); const to_select = await getFirstFilterButton(page); await expect(to_select.getByRole("checkbox")).not.toBeChecked(); - await to_select.getByRole("checkbox").setChecked(true); + await to_select.getByRole("checkbox").click; const filterName = (await to_select.innerText()).split("\n")[0]; //MAY NEED TO ADD SOME CHECKING MECHANISM HERE await expect(to_select.getByRole("checkbox")).toBeChecked(); await page.locator("body").click(); From 89c30f5429dedbbbd9e6a86d157be303e3d3860a Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Sun, 14 Jul 2024 15:51:33 -0700 Subject: [PATCH 09/16] fix: fixed typo that caused tests to fail, added clear all test (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 11 +++++ explorer/e2e/testFunctions.ts | 53 ++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts index b7f12bc11..8e1c8c199 100644 --- a/explorer/e2e/anvil/anvil-filters.spec.ts +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -2,6 +2,7 @@ import { expect, test } from "@playwright/test"; import { filterRegex, getFirstElementTextLocator, + testClearAll, testFilterBubbles, testFilterCounts, testFilterPersistence, @@ -126,3 +127,13 @@ test("Check that the blue filter bubbles match the selected filter for an arbitr filter_index_list.map((x) => anvilFilters[x]) ); }); + +test("Check that the clear all button functions on the files tab", async ({ + page, +}) => { + await testClearAll( + page, + anvilTabs.files, + filter_index_list.map((x) => anvilFilters[x]) + ); +}); diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index e4971748d..ddebd97da 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -264,7 +264,7 @@ export async function testFilterPersistence( await page.getByText(filterRegex(testFilter)).click(); const to_select = await getFirstFilterButton(page); await expect(to_select.getByRole("checkbox")).not.toBeChecked(); - await to_select.getByRole("checkbox").click; + await to_select.getByRole("checkbox").click(); const filterName = (await to_select.innerText()).split("\n")[0]; //MAY NEED TO ADD SOME CHECKING MECHANISM HERE await expect(to_select.getByRole("checkbox")).toBeChecked(); await page.locator("body").click(); @@ -320,13 +320,15 @@ export async function testFilterCounts( console.log(filter_numbers.map((x) => Number(x))); return false; } + // Check the filter await filter_button.getByRole("checkbox").dispatchEvent("click"); await page.waitForLoadState("load"); + // Exit the filter menu await page.locator("body").click(); await expect(page.getByRole("checkbox")).toHaveCount(0); + // Expect the displayed count of elements to be 0 const firstNumber = filter_number <= elements_per_page ? filter_number : elements_per_page; - await expect( page.getByText("Results 1 - " + firstNumber + " of " + filter_number) ).toBeVisible(); @@ -341,25 +343,68 @@ export async function testFilterBubbles( ): Promise { page.goto(tab.url); for (const filter of filters) { + // Select a filter await page.getByText(filterRegex(filter)).dispatchEvent("click"); - //await page.waitForTimeout(500); await page.waitForLoadState("load"); const firstFilterButton = getFirstFilterButton(page); + // Get the name of the selected filter const firstFilterName = (await firstFilterButton.innerText()) .split("\n") .find((x) => x.length > 0) ?? ""; + // Click the selected filter and exit the filter menu await firstFilterButton.getByRole("checkbox").click(); await page.waitForLoadState("load"); - await page.keyboard.press("Escape"); + await page.locator("body").click(); await expect(page.getByRole("checkbox")).toHaveCount(0); + // Click the blue button const filterBlueButton = page .locator("#sidebar-positioner") .getByText(firstFilterName); await expect(filterBlueButton).toBeVisible(); await filterBlueButton.scrollIntoViewIfNeeded(); await filterBlueButton.dispatchEvent("click"); + // Expect the blue button to disappear when clicked await expect(filterBlueButton).toHaveCount(0); + // Expect the filter to be deselected in the filter menu + await page.getByText(filterRegex(filter)).dispatchEvent("click"); + await expect(firstFilterButton.getByRole("checkbox")).not.toBeChecked(); + await page.locator("body").click(); + } +} + +export async function testClearAll( + page: Page, + tab: TabDescription, + filters: string[] +): Promise { + await page.goto(tab.url); + const selected_filter_list = []; + for (const filter of filters) { + await page.getByText(filterRegex(filter)).dispatchEvent("click"); + await getFirstFilterButton(page).getByRole("checkbox").click(); + await expect( + getFirstFilterButton(page).getByRole("checkbox") + ).toBeChecked(); + selected_filter_list.push( + (await getFirstFilterButton(page).innerText()) + .split("\n") + .find((x) => x.length > 0) ?? "" + ); + await page.locator("body").click(); + } + await page.getByText("Clear All").dispatchEvent("click"); + for (const filter of selected_filter_list) { + await expect( + page.locator("#sidebar-positioner").getByText(filter) + ).toHaveCount(0); + } + for (let i = 0; i < filters.length; i++) { + await page.getByText(filterRegex(filters[i])).dispatchEvent("click"); + await expect( + getNamedFilterButton(page, selected_filter_list[i]).getByRole("checkbox") + ).not.toBeChecked(); + await page.locator("body").click(); } } /* eslint-enable sonarjs/no-duplicate-string -- Checking duplicate strings again*/ From db8313f42f05e48d867f0117cc3b11c3ef90aa1e Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Sun, 14 Jul 2024 18:18:34 -0700 Subject: [PATCH 10/16] fix: shortened certain tests that would timeout (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts index 8e1c8c199..d91291dab 100644 --- a/explorer/e2e/anvil/anvil-filters.spec.ts +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -11,8 +11,8 @@ import { import { anvilFilters, anvilTabs, anvilTabTestOrder } from "./anvil-tabs"; test.describe.configure({ mode: "parallel", timeout: 60 * 1000 }); -const filter_index_list = [3, 4, 5, 7, 6, 2]; - +const filter_index_list = [3, 4, 5, 10, 6, 2]; +const filter_index_list_short = [1, 10, 3]; test("Check that all filters exist on the Datasets tab and are clickable", async ({ page, }) => { @@ -108,32 +108,35 @@ test("Check that filter menu counts match actual counts on the Activities tab", ); }); +test.setTimeout(120000); test("Check that the blue filter bubbles match the selected filter for an arbitrary filter on the Files tab", async ({ page, }) => { await testFilterBubbles( page, anvilTabs.files, - filter_index_list.map((x) => anvilFilters[x]) + filter_index_list_short.map((x) => anvilFilters[x]) ); }); +test.setTimeout(120000); test("Check that the blue filter bubbles match the selected filter for an arbitrary filter on the BioSamples tab", async ({ page, }) => { await testFilterBubbles( page, anvilTabs.biosamples, - filter_index_list.map((x) => anvilFilters[x]) + filter_index_list_short.map((x) => anvilFilters[x]) ); }); +test.setTimeout(120000); test("Check that the clear all button functions on the files tab", async ({ page, }) => { await testClearAll( page, anvilTabs.files, - filter_index_list.map((x) => anvilFilters[x]) + filter_index_list_short.map((x) => anvilFilters[x]) ); }); From 67719789f31983530aa47899e236c03b897d6199 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 26 Jul 2024 11:27:42 -0700 Subject: [PATCH 11/16] fix: fixed redundant code and bad timeouts (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts index d91291dab..6af5c0a2a 100644 --- a/explorer/e2e/anvil/anvil-filters.spec.ts +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -10,7 +10,6 @@ import { } from "../testFunctions"; import { anvilFilters, anvilTabs, anvilTabTestOrder } from "./anvil-tabs"; -test.describe.configure({ mode: "parallel", timeout: 60 * 1000 }); const filter_index_list = [3, 4, 5, 10, 6, 2]; const filter_index_list_short = [1, 10, 3]; test("Check that all filters exist on the Datasets tab and are clickable", async ({ @@ -108,10 +107,10 @@ test("Check that filter menu counts match actual counts on the Activities tab", ); }); -test.setTimeout(120000); test("Check that the blue filter bubbles match the selected filter for an arbitrary filter on the Files tab", async ({ page, }) => { + test.setTimeout(120000); await testFilterBubbles( page, anvilTabs.files, @@ -119,10 +118,10 @@ test("Check that the blue filter bubbles match the selected filter for an arbitr ); }); -test.setTimeout(120000); test("Check that the blue filter bubbles match the selected filter for an arbitrary filter on the BioSamples tab", async ({ page, }) => { + test.setTimeout(120000); await testFilterBubbles( page, anvilTabs.biosamples, @@ -130,10 +129,10 @@ test("Check that the blue filter bubbles match the selected filter for an arbitr ); }); -test.setTimeout(120000); test("Check that the clear all button functions on the files tab", async ({ page, }) => { + test.setTimeout(120000); await testClearAll( page, anvilTabs.files, From a18b8663987333e9db67a184a95ca1b0baed4604 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 26 Jul 2024 11:38:05 -0700 Subject: [PATCH 12/16] fix: made all variables camelcase in filter tests (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 20 +++++----- explorer/e2e/testFunctions.ts | 48 ++++++++++++------------ 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts index 6af5c0a2a..13599cba4 100644 --- a/explorer/e2e/anvil/anvil-filters.spec.ts +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -10,8 +10,8 @@ import { } from "../testFunctions"; import { anvilFilters, anvilTabs, anvilTabTestOrder } from "./anvil-tabs"; -const filter_index_list = [3, 4, 5, 10, 6, 2]; -const filter_index_list_short = [1, 10, 3]; +const filterIndexList = [3, 4, 5, 10, 6, 2]; +const filterIndexListShort = [1, 10, 3]; test("Check that all filters exist on the Datasets tab and are clickable", async ({ page, }) => { @@ -60,9 +60,9 @@ test("Check that the first filter on the Datasets tab creates at least one check .click(); // Expect all checkboxes to be unchecked initially and to work properly await expect(page.getByRole("checkbox").first()).toBeVisible(); - const all_checkboxes = await page.getByRole("checkbox").all(); - for (let i = 0; i < all_checkboxes.length && i < 5; i++) { - const checkbox = all_checkboxes[i]; + const allCheckboxes = await page.getByRole("checkbox").all(); + for (let i = 0; i < allCheckboxes.length && i < 5; i++) { + const checkbox = allCheckboxes[i]; await checkbox.scrollIntoViewIfNeeded(); await expect(checkbox).not.toBeChecked(); await checkbox.click(); @@ -88,7 +88,7 @@ test("Check that filter menu counts match actual counts on the Datasets tab", as const result = await testFilterCounts( page, anvilTabs.datasets, - filter_index_list.map((x) => anvilFilters[x]), + filterIndexList.map((x) => anvilFilters[x]), 25 ); if (!result) { @@ -102,7 +102,7 @@ test("Check that filter menu counts match actual counts on the Activities tab", await testFilterCounts( page, anvilTabs.activities, - filter_index_list.map((x) => anvilFilters[x]), + filterIndexList.map((x) => anvilFilters[x]), 25 ); }); @@ -114,7 +114,7 @@ test("Check that the blue filter bubbles match the selected filter for an arbitr await testFilterBubbles( page, anvilTabs.files, - filter_index_list_short.map((x) => anvilFilters[x]) + filterIndexListShort.map((x) => anvilFilters[x]) ); }); @@ -125,7 +125,7 @@ test("Check that the blue filter bubbles match the selected filter for an arbitr await testFilterBubbles( page, anvilTabs.biosamples, - filter_index_list_short.map((x) => anvilFilters[x]) + filterIndexListShort.map((x) => anvilFilters[x]) ); }); @@ -136,6 +136,6 @@ test("Check that the clear all button functions on the files tab", async ({ await testClearAll( page, anvilTabs.files, - filter_index_list_short.map((x) => anvilFilters[x]) + filterIndexListShort.map((x) => anvilFilters[x]) ); }); diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index ddebd97da..c52c629c5 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -262,11 +262,11 @@ export async function testFilterPersistence( await page.goto(tabOrder[0].url); // Select the first checkbox on the test filter await page.getByText(filterRegex(testFilter)).click(); - const to_select = await getFirstFilterButton(page); - await expect(to_select.getByRole("checkbox")).not.toBeChecked(); - await to_select.getByRole("checkbox").click(); - const filterName = (await to_select.innerText()).split("\n")[0]; //MAY NEED TO ADD SOME CHECKING MECHANISM HERE - await expect(to_select.getByRole("checkbox")).toBeChecked(); + const toSelect = await getFirstFilterButton(page); + await expect(toSelect.getByRole("checkbox")).not.toBeChecked(); + await toSelect.getByRole("checkbox").click(); + const filterName = (await toSelect.innerText()).split("\n")[0]; //MAY NEED TO ADD SOME CHECKING MECHANISM HERE + await expect(toSelect.getByRole("checkbox")).toBeChecked(); await page.locator("body").click(); // Expect at least some text to still be visible await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); @@ -279,8 +279,8 @@ export async function testFilterPersistence( await expect(page.getByText(filterRegex(testFilter))).toBeVisible(); await page.getByText(filterRegex(testFilter)).dispatchEvent("click"); await page.waitForLoadState("load"); - const previously_selected = getNamedFilterButton(page, filterName); - await expect(previously_selected.getByRole("checkbox")).toBeChecked(); + const previouslySelected = getNamedFilterButton(page, filterName); + await expect(previouslySelected.getByRole("checkbox")).toBeChecked(); await page.waitForLoadState("load"); await page.locator("body").click(); } @@ -291,18 +291,18 @@ export async function testFilterPersistence( .click(); await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); await page.getByText(filterRegex(testFilter)).click(); - const previously_selected = getFirstFilterButton(page); - await expect(previously_selected).toContainText(filterName, { + const previouslySelected = getFirstFilterButton(page); + await expect(previouslySelected).toContainText(filterName, { useInnerText: true, }); - await expect(previously_selected.getByRole("checkbox").first()).toBeChecked(); + await expect(previouslySelected.getByRole("checkbox").first()).toBeChecked(); } export async function testFilterCounts( page: Page, tab: TabDescription, filters: string[], - elements_per_page: number + elementsPerPage: number ): Promise { await page.goto(tab.url); // For each arbitrarily selected filter @@ -311,26 +311,26 @@ export async function testFilterCounts( await page.getByText(filterRegex(filter)).dispatchEvent("click"); // Get the number associated with the first filter button, and select it await page.waitForLoadState("load"); - const filter_button = getFirstFilterButton(page); - const filter_numbers = (await filter_button.innerText()).split("\n"); - const filter_number = - filter_numbers.map((x) => Number(x)).find((x) => !isNaN(x) && x !== 0) ?? + const filterButton = getFirstFilterButton(page); + const filterNumbers = (await filterButton.innerText()).split("\n"); + const filterNumber = + filterNumbers.map((x) => Number(x)).find((x) => !isNaN(x) && x !== 0) ?? -1; - if (filter_number < 0) { - console.log(filter_numbers.map((x) => Number(x))); + if (filterNumber < 0) { + console.log(filterNumbers.map((x) => Number(x))); return false; } // Check the filter - await filter_button.getByRole("checkbox").dispatchEvent("click"); + await filterButton.getByRole("checkbox").dispatchEvent("click"); await page.waitForLoadState("load"); // Exit the filter menu await page.locator("body").click(); await expect(page.getByRole("checkbox")).toHaveCount(0); // Expect the displayed count of elements to be 0 const firstNumber = - filter_number <= elements_per_page ? filter_number : elements_per_page; + filterNumber <= elementsPerPage ? filterNumber : elementsPerPage; await expect( - page.getByText("Results 1 - " + firstNumber + " of " + filter_number) + page.getByText("Results 1 - " + firstNumber + " of " + filterNumber) ).toBeVisible(); } return true; @@ -379,14 +379,14 @@ export async function testClearAll( filters: string[] ): Promise { await page.goto(tab.url); - const selected_filter_list = []; + const selectedFilterList = []; for (const filter of filters) { await page.getByText(filterRegex(filter)).dispatchEvent("click"); await getFirstFilterButton(page).getByRole("checkbox").click(); await expect( getFirstFilterButton(page).getByRole("checkbox") ).toBeChecked(); - selected_filter_list.push( + selectedFilterList.push( (await getFirstFilterButton(page).innerText()) .split("\n") .find((x) => x.length > 0) ?? "" @@ -394,7 +394,7 @@ export async function testClearAll( await page.locator("body").click(); } await page.getByText("Clear All").dispatchEvent("click"); - for (const filter of selected_filter_list) { + for (const filter of selectedFilterList) { await expect( page.locator("#sidebar-positioner").getByText(filter) ).toHaveCount(0); @@ -402,7 +402,7 @@ export async function testClearAll( for (let i = 0; i < filters.length; i++) { await page.getByText(filterRegex(filters[i])).dispatchEvent("click"); await expect( - getNamedFilterButton(page, selected_filter_list[i]).getByRole("checkbox") + getNamedFilterButton(page, selectedFilterList[i]).getByRole("checkbox") ).not.toBeChecked(); await page.locator("body").click(); } From a19a6dfc5d7a7ddc6e99cb5b044b02689c1000b7 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Mon, 29 Jul 2024 16:48:28 -0700 Subject: [PATCH 13/16] fix: updated variable names and created constants (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 45 ++++++++++++++++++------ explorer/e2e/anvil/anvil-tabs.ts | 16 +++++++++ explorer/e2e/testInterfaces.ts | 1 + 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts index 13599cba4..e8abf8d67 100644 --- a/explorer/e2e/anvil/anvil-filters.spec.ts +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -8,10 +8,33 @@ import { testFilterPersistence, testFilterPresence, } from "../testFunctions"; -import { anvilFilters, anvilTabs, anvilTabTestOrder } from "./anvil-tabs"; +import { + anvilFilters, + anvilTabs, + anvilTabTestOrder, + BIOSAMPLE_TYPE_INDEX, + CONSENT_GROUP_INDEX, + DATASET_INDEX, + DATA_MODALITY_INDEX, + DIAGNOSIS_INDEX, + FILE_FORMAT_INDEX, + REPORTED_ETHNICITY_INDEX, +} from "./anvil-tabs"; + +const FILTER_INDEX_LIST = [ + DATA_MODALITY_INDEX, + DATASET_INDEX, + DIAGNOSIS_INDEX, + REPORTED_ETHNICITY_INDEX, + FILE_FORMAT_INDEX, + CONSENT_GROUP_INDEX, +]; +const FILTER_INDEX_LIST_SHORT = [ + BIOSAMPLE_TYPE_INDEX, + FILE_FORMAT_INDEX, + DIAGNOSIS_INDEX, +]; -const filterIndexList = [3, 4, 5, 10, 6, 2]; -const filterIndexListShort = [1, 10, 3]; test("Check that all filters exist on the Datasets tab and are clickable", async ({ page, }) => { @@ -77,7 +100,7 @@ test("Check that filter checkboxes are persistent across pages on an arbitrary f }) => { await testFilterPersistence( page, - anvilFilters[3], + anvilFilters[FILE_FORMAT_INDEX], anvilTabTestOrder.map((x) => anvilTabs[x]) ); }); @@ -88,8 +111,8 @@ test("Check that filter menu counts match actual counts on the Datasets tab", as const result = await testFilterCounts( page, anvilTabs.datasets, - filterIndexList.map((x) => anvilFilters[x]), - 25 + FILTER_INDEX_LIST.map((x) => anvilFilters[x]), + anvilTabs.datasets.maxPages ?? 0 ); if (!result) { test.fail(); @@ -102,8 +125,8 @@ test("Check that filter menu counts match actual counts on the Activities tab", await testFilterCounts( page, anvilTabs.activities, - filterIndexList.map((x) => anvilFilters[x]), - 25 + FILTER_INDEX_LIST.map((x) => anvilFilters[x]), + anvilTabs.activities.maxPages ?? 0 ); }); @@ -114,7 +137,7 @@ test("Check that the blue filter bubbles match the selected filter for an arbitr await testFilterBubbles( page, anvilTabs.files, - filterIndexListShort.map((x) => anvilFilters[x]) + FILTER_INDEX_LIST_SHORT.map((x) => anvilFilters[x]) ); }); @@ -125,7 +148,7 @@ test("Check that the blue filter bubbles match the selected filter for an arbitr await testFilterBubbles( page, anvilTabs.biosamples, - filterIndexListShort.map((x) => anvilFilters[x]) + FILTER_INDEX_LIST_SHORT.map((x) => anvilFilters[x]) ); }); @@ -136,6 +159,6 @@ test("Check that the clear all button functions on the files tab", async ({ await testClearAll( page, anvilTabs.files, - filterIndexListShort.map((x) => anvilFilters[x]) + FILTER_INDEX_LIST_SHORT.map((x) => anvilFilters[x]) ); }); diff --git a/explorer/e2e/anvil/anvil-tabs.ts b/explorer/e2e/anvil/anvil-tabs.ts index 4c6de8781..17eb6f0f9 100644 --- a/explorer/e2e/anvil/anvil-tabs.ts +++ b/explorer/e2e/anvil/anvil-tabs.ts @@ -18,10 +18,22 @@ export const anvilFilters: string[] = [ "Phenotypic Sex", "Reported Ethnicity", ]; +export const ANATOMICAL_SITE_INDEX = 0; +export const BIOSAMPLE_TYPE_INDEX = 1; +export const CONSENT_GROUP_INDEX = 2; +export const DATA_MODALITY_INDEX = 3; +export const DATASET_INDEX = 4; +export const DIAGNOSIS_INDEX = 5; +export const FILE_FORMAT_INDEX = 6; +export const IDENTIFIER_INDEX = 7; +export const ORGANISM_TYPE_INDEX = 8; +export const PHENOTYPIC_SEX_INDEX = 9; +export const REPORTED_ETHNICITY_INDEX = 10; export const anvilTabs: AnvilCMGTabCollection = { activities: { emptyFirstColumn: false, + maxPages: 25, preselectedColumns: [ { name: "Document Id", sortable: true }, { name: "Activity Type", sortable: true }, @@ -40,6 +52,7 @@ export const anvilTabs: AnvilCMGTabCollection = { }, biosamples: { emptyFirstColumn: false, + maxPages: 25, preselectedColumns: [ { name: "BioSample Id", sortable: true }, { name: "Anatomical Site", sortable: true }, @@ -57,6 +70,7 @@ export const anvilTabs: AnvilCMGTabCollection = { }, datasets: { emptyFirstColumn: false, + maxPages: 25, preselectedColumns: [ { name: "Dataset", sortable: true }, { name: "Access", sortable: false }, @@ -75,6 +89,7 @@ export const anvilTabs: AnvilCMGTabCollection = { }, donors: { emptyFirstColumn: false, + maxPages: 25, preselectedColumns: [ { name: "Donor Id", sortable: true }, { name: "Organism Type", sortable: true }, @@ -89,6 +104,7 @@ export const anvilTabs: AnvilCMGTabCollection = { }, files: { emptyFirstColumn: true, + maxPages: 25, preselectedColumns: [ { name: "Name", sortable: true }, { name: "File Format", sortable: true }, diff --git a/explorer/e2e/testInterfaces.ts b/explorer/e2e/testInterfaces.ts index 74cfcbddf..ee5d786c3 100644 --- a/explorer/e2e/testInterfaces.ts +++ b/explorer/e2e/testInterfaces.ts @@ -1,5 +1,6 @@ export interface TabDescription { emptyFirstColumn: boolean; + maxPages?: number; preselectedColumns: columnDescription[]; selectableColumns: columnDescription[]; tabName: string; From 1f8966f3eff64d55c3571bdfec8a28166ddc5438 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Mon, 29 Jul 2024 17:40:24 -0700 Subject: [PATCH 14/16] fix: updated variable names, created constants, fixed bug in persistence test on webkit (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 4 ++ explorer/e2e/testFunctions.ts | 78 ++++++++++++++---------- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts index e8abf8d67..de454748a 100644 --- a/explorer/e2e/anvil/anvil-filters.spec.ts +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -68,6 +68,7 @@ test("Check that all filters exist on the Files tab and are clickable", async ({ test("Check that the first filter on the Datasets tab creates at least one checkbox, and that checking up to the first five does not cause an error and does not cause there to be no entries in the table", async ({ page, }) => { + test.setTimeout(120000); // Goto the datasets tab await page.goto(anvilTabs.datasets.url); await expect( @@ -98,6 +99,7 @@ test("Check that the first filter on the Datasets tab creates at least one check test("Check that filter checkboxes are persistent across pages on an arbitrary filter", async ({ page, }) => { + test.setTimeout(120000); await testFilterPersistence( page, anvilFilters[FILE_FORMAT_INDEX], @@ -108,6 +110,7 @@ test("Check that filter checkboxes are persistent across pages on an arbitrary f test("Check that filter menu counts match actual counts on the Datasets tab", async ({ page, }) => { + test.setTimeout(120000); const result = await testFilterCounts( page, anvilTabs.datasets, @@ -122,6 +125,7 @@ test("Check that filter menu counts match actual counts on the Datasets tab", as test("Check that filter menu counts match actual counts on the Activities tab", async ({ page, }) => { + test.setTimeout(120000); await testFilterCounts( page, anvilTabs.activities, diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index c52c629c5..dbe29fd97 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -1,4 +1,4 @@ -import { expect, Locator, Page } from "@playwright/test"; +import { expect, Locator, Page, test } from "@playwright/test"; import { TabDescription } from "./testInterfaces"; /* eslint-disable sonarjs/no-duplicate-string -- ignoring duplicate strings here */ @@ -238,7 +238,7 @@ export async function testFilterPresence( } } -export const getNamedFilterButton = ( +export const getNamedFilterButtonLocator = ( page: Page, filterName: string ): Locator => { @@ -246,7 +246,7 @@ export const getNamedFilterButton = ( .getByRole("button") .filter({ has: page.getByRole("checkbox"), hasText: filterName }); }; -export const getFirstFilterButton = (page: Page): Locator => { +export const getFirstFilterButtonLocator = (page: Page): Locator => { return page .getByRole("button") .filter({ has: page.getByRole("checkbox") }) @@ -262,11 +262,17 @@ export async function testFilterPersistence( await page.goto(tabOrder[0].url); // Select the first checkbox on the test filter await page.getByText(filterRegex(testFilter)).click(); - const toSelect = await getFirstFilterButton(page); - await expect(toSelect.getByRole("checkbox")).not.toBeChecked(); - await toSelect.getByRole("checkbox").click(); - const filterName = (await toSelect.innerText()).split("\n")[0]; //MAY NEED TO ADD SOME CHECKING MECHANISM HERE - await expect(toSelect.getByRole("checkbox")).toBeChecked(); + const filterToSelectLocator = await getFirstFilterButtonLocator(page); + await expect(filterToSelectLocator.getByRole("checkbox")).not.toBeChecked(); + await filterToSelectLocator.getByRole("checkbox").click(); + const filterNameMatch = (await filterToSelectLocator.innerText()).match( + /\.\S*/ + ); + if (filterNameMatch == null) { + test.fail(); + } + const filterName = (filterNameMatch ?? [""])[0]; + await expect(filterToSelectLocator.getByRole("checkbox")).toBeChecked(); await page.locator("body").click(); // Expect at least some text to still be visible await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); @@ -279,7 +285,7 @@ export async function testFilterPersistence( await expect(page.getByText(filterRegex(testFilter))).toBeVisible(); await page.getByText(filterRegex(testFilter)).dispatchEvent("click"); await page.waitForLoadState("load"); - const previouslySelected = getNamedFilterButton(page, filterName); + const previouslySelected = getNamedFilterButtonLocator(page, filterName); await expect(previouslySelected.getByRole("checkbox")).toBeChecked(); await page.waitForLoadState("load"); await page.locator("body").click(); @@ -291,7 +297,7 @@ export async function testFilterPersistence( .click(); await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); await page.getByText(filterRegex(testFilter)).click(); - const previouslySelected = getFirstFilterButton(page); + const previouslySelected = getFirstFilterButtonLocator(page); await expect(previouslySelected).toContainText(filterName, { useInnerText: true, }); @@ -311,7 +317,7 @@ export async function testFilterCounts( await page.getByText(filterRegex(filter)).dispatchEvent("click"); // Get the number associated with the first filter button, and select it await page.waitForLoadState("load"); - const filterButton = getFirstFilterButton(page); + const filterButton = getFirstFilterButtonLocator(page); const filterNumbers = (await filterButton.innerText()).split("\n"); const filterNumber = filterNumbers.map((x) => Number(x)).find((x) => !isNaN(x) && x !== 0) ?? @@ -346,29 +352,31 @@ export async function testFilterBubbles( // Select a filter await page.getByText(filterRegex(filter)).dispatchEvent("click"); await page.waitForLoadState("load"); - const firstFilterButton = getFirstFilterButton(page); + const firstFilterButtonLocator = getFirstFilterButtonLocator(page); // Get the name of the selected filter const firstFilterName = - (await firstFilterButton.innerText()) + (await firstFilterButtonLocator.innerText()) .split("\n") .find((x) => x.length > 0) ?? ""; // Click the selected filter and exit the filter menu - await firstFilterButton.getByRole("checkbox").click(); + await firstFilterButtonLocator.getByRole("checkbox").click(); await page.waitForLoadState("load"); await page.locator("body").click(); await expect(page.getByRole("checkbox")).toHaveCount(0); // Click the blue button - const filterBlueButton = page + const filterBlueButtonLocator = page .locator("#sidebar-positioner") .getByText(firstFilterName); - await expect(filterBlueButton).toBeVisible(); - await filterBlueButton.scrollIntoViewIfNeeded(); - await filterBlueButton.dispatchEvent("click"); + await expect(filterBlueButtonLocator).toBeVisible(); + await filterBlueButtonLocator.scrollIntoViewIfNeeded(); + await filterBlueButtonLocator.dispatchEvent("click"); // Expect the blue button to disappear when clicked - await expect(filterBlueButton).toHaveCount(0); + await expect(filterBlueButtonLocator).toHaveCount(0); // Expect the filter to be deselected in the filter menu await page.getByText(filterRegex(filter)).dispatchEvent("click"); - await expect(firstFilterButton.getByRole("checkbox")).not.toBeChecked(); + await expect( + firstFilterButtonLocator.getByRole("checkbox") + ).not.toBeChecked(); await page.locator("body").click(); } } @@ -376,33 +384,37 @@ export async function testFilterBubbles( export async function testClearAll( page: Page, tab: TabDescription, - filters: string[] + filterNames: string[] ): Promise { await page.goto(tab.url); - const selectedFilterList = []; - for (const filter of filters) { - await page.getByText(filterRegex(filter)).dispatchEvent("click"); - await getFirstFilterButton(page).getByRole("checkbox").click(); + const selectedFilterNamesList = []; + for (const filterName of filterNames) { + // Select the passed filter names + await page.getByText(filterRegex(filterName)).dispatchEvent("click"); + await getFirstFilterButtonLocator(page).getByRole("checkbox").click(); await expect( - getFirstFilterButton(page).getByRole("checkbox") + getFirstFilterButtonLocator(page).getByRole("checkbox") ).toBeChecked(); - selectedFilterList.push( - (await getFirstFilterButton(page).innerText()) + selectedFilterNamesList.push( + (await getFirstFilterButtonLocator(page).innerText()) .split("\n") .find((x) => x.length > 0) ?? "" ); await page.locator("body").click(); } + // Click the "Clear All" button await page.getByText("Clear All").dispatchEvent("click"); - for (const filter of selectedFilterList) { + for (const filterName of selectedFilterNamesList) { await expect( - page.locator("#sidebar-positioner").getByText(filter) + page.locator("#sidebar-positioner").getByText(filterName) ).toHaveCount(0); } - for (let i = 0; i < filters.length; i++) { - await page.getByText(filterRegex(filters[i])).dispatchEvent("click"); + for (let i = 0; i < filterNames.length; i++) { + await page.getByText(filterRegex(filterNames[i])).dispatchEvent("click"); await expect( - getNamedFilterButton(page, selectedFilterList[i]).getByRole("checkbox") + getNamedFilterButtonLocator(page, selectedFilterNamesList[i]).getByRole( + "checkbox" + ) ).not.toBeChecked(); await page.locator("body").click(); } From 31f93047564b1e220971a88ce06c260af8c5de79 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 16 Aug 2024 12:17:52 -0700 Subject: [PATCH 15/16] fix: updated variable names and tsdocs for e2e tests (#4068) --- explorer/e2e/anvil/anvil-filters.spec.ts | 47 ++--- explorer/e2e/anvil/anvil-tabs.ts | 2 +- explorer/e2e/testFunctions.ts | 207 +++++++++++++++++------ 3 files changed, 182 insertions(+), 74 deletions(-) diff --git a/explorer/e2e/anvil/anvil-filters.spec.ts b/explorer/e2e/anvil/anvil-filters.spec.ts index de454748a..12f8d5b6f 100644 --- a/explorer/e2e/anvil/anvil-filters.spec.ts +++ b/explorer/e2e/anvil/anvil-filters.spec.ts @@ -1,15 +1,15 @@ import { expect, test } from "@playwright/test"; import { filterRegex, - getFirstElementTextLocator, + getFirstRowNthColumnCellLocator, testClearAll, - testFilterBubbles, testFilterCounts, testFilterPersistence, testFilterPresence, + testFilterTags, } from "../testFunctions"; import { - anvilFilters, + anvilFilterNames, anvilTabs, anvilTabTestOrder, BIOSAMPLE_TYPE_INDEX, @@ -38,31 +38,31 @@ const FILTER_INDEX_LIST_SHORT = [ test("Check that all filters exist on the Datasets tab and are clickable", async ({ page, }) => { - await testFilterPresence(page, anvilTabs.datasets, anvilFilters); + await testFilterPresence(page, anvilTabs.datasets, anvilFilterNames); }); test("Check that all filters exist on the Donors tab and are clickable", async ({ page, }) => { - await testFilterPresence(page, anvilTabs.donors, anvilFilters); + await testFilterPresence(page, anvilTabs.donors, anvilFilterNames); }); test("Check that all filters exist on the BioSamples tab and are clickable", async ({ page, }) => { - await testFilterPresence(page, anvilTabs.biosamples, anvilFilters); + await testFilterPresence(page, anvilTabs.biosamples, anvilFilterNames); }); test("Check that all filters exist on the Activities tab and are clickable", async ({ page, }) => { - await testFilterPresence(page, anvilTabs.activities, anvilFilters); + await testFilterPresence(page, anvilTabs.activities, anvilFilterNames); }); test("Check that all filters exist on the Files tab and are clickable", async ({ page, }) => { - await testFilterPresence(page, anvilTabs.files, anvilFilters); + await testFilterPresence(page, anvilTabs.files, anvilFilterNames); }); test("Check that the first filter on the Datasets tab creates at least one checkbox, and that checking up to the first five does not cause an error and does not cause there to be no entries in the table", async ({ @@ -79,7 +79,9 @@ test("Check that the first filter on the Datasets tab creates at least one check await page .getByRole("button") .getByText( - filterRegex(anvilFilters[Math.floor(Math.random() * anvilFilters.length)]) + filterRegex( + anvilFilterNames[Math.floor(Math.random() * anvilFilterNames.length)] + ) ) .click(); // Expect all checkboxes to be unchecked initially and to work properly @@ -93,18 +95,21 @@ test("Check that the first filter on the Datasets tab creates at least one check await expect(checkbox).toBeChecked(); } await page.locator("body").click(); - await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); + await expect(getFirstRowNthColumnCellLocator(page, 0)).toBeVisible(); }); test("Check that filter checkboxes are persistent across pages on an arbitrary filter", async ({ page, }) => { test.setTimeout(120000); - await testFilterPersistence( + const result = await testFilterPersistence( page, - anvilFilters[FILE_FORMAT_INDEX], + anvilFilterNames[FILE_FORMAT_INDEX], anvilTabTestOrder.map((x) => anvilTabs[x]) ); + if (!result) { + test.fail(); + } }); test("Check that filter menu counts match actual counts on the Datasets tab", async ({ @@ -114,7 +119,7 @@ test("Check that filter menu counts match actual counts on the Datasets tab", as const result = await testFilterCounts( page, anvilTabs.datasets, - FILTER_INDEX_LIST.map((x) => anvilFilters[x]), + FILTER_INDEX_LIST.map((x) => anvilFilterNames[x]), anvilTabs.datasets.maxPages ?? 0 ); if (!result) { @@ -129,30 +134,30 @@ test("Check that filter menu counts match actual counts on the Activities tab", await testFilterCounts( page, anvilTabs.activities, - FILTER_INDEX_LIST.map((x) => anvilFilters[x]), + FILTER_INDEX_LIST.map((x) => anvilFilterNames[x]), anvilTabs.activities.maxPages ?? 0 ); }); -test("Check that the blue filter bubbles match the selected filter for an arbitrary filter on the Files tab", async ({ +test("Check that the filter tags match the selected filter for an arbitrary filter on the Files tab", async ({ page, }) => { test.setTimeout(120000); - await testFilterBubbles( + await testFilterTags( page, anvilTabs.files, - FILTER_INDEX_LIST_SHORT.map((x) => anvilFilters[x]) + FILTER_INDEX_LIST_SHORT.map((x) => anvilFilterNames[x]) ); }); -test("Check that the blue filter bubbles match the selected filter for an arbitrary filter on the BioSamples tab", async ({ +test("Check that the filter tags match the selected filter for an arbitrary filter on the BioSamples tab", async ({ page, }) => { test.setTimeout(120000); - await testFilterBubbles( + await testFilterTags( page, anvilTabs.biosamples, - FILTER_INDEX_LIST_SHORT.map((x) => anvilFilters[x]) + FILTER_INDEX_LIST_SHORT.map((x) => anvilFilterNames[x]) ); }); @@ -163,6 +168,6 @@ test("Check that the clear all button functions on the files tab", async ({ await testClearAll( page, anvilTabs.files, - FILTER_INDEX_LIST_SHORT.map((x) => anvilFilters[x]) + FILTER_INDEX_LIST_SHORT.map((x) => anvilFilterNames[x]) ); }); diff --git a/explorer/e2e/anvil/anvil-tabs.ts b/explorer/e2e/anvil/anvil-tabs.ts index 17eb6f0f9..800190506 100644 --- a/explorer/e2e/anvil/anvil-tabs.ts +++ b/explorer/e2e/anvil/anvil-tabs.ts @@ -5,7 +5,7 @@ import { TabDescription, } from "../testInterfaces"; -export const anvilFilters: string[] = [ +export const anvilFilterNames: string[] = [ "Anatomical Site", "BioSample Type", "Consent Group", diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index dbe29fd97..d2619749d 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -1,12 +1,17 @@ -import { expect, Locator, Page, test } from "@playwright/test"; +import { expect, Locator, Page } from "@playwright/test"; import { TabDescription } from "./testInterfaces"; /* eslint-disable sonarjs/no-duplicate-string -- ignoring duplicate strings here */ -// Run the "Expect each tab to appear as selected when the corresponding url is accessed" test -export const getFirstElementTextLocator = ( +/** + * 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, - workColumnPosition: number + columnIndex: number ): Locator => { return page .getByRole("rowgroup") @@ -14,9 +19,16 @@ export const getFirstElementTextLocator = ( .getByRole("row") .nth(0) .getByRole("cell") - .nth(workColumnPosition); + .nth(columnIndex); }; +/** + * Tests that the tab url goes to a valid page and that the correct tab (and only + * the correct tab) appears selected + * @param page - a Playwright page object + * @param tab - the Tab object to check + * @param otherTabs - an array of the other Tab objects for this configuration + */ export async function testUrl( page: Page, tab: TabDescription, @@ -36,6 +48,11 @@ export async function testUrl( } // Run the "Expect each tab to become selected, to go to the correct url, and to show all of its columns when selected" test +/** + * Checks that all preselected columns listed in the tab object are visible in the correct order + * @param page - a Playwright page object + * @param tab - the tab object to test + */ export async function testTab(page: Page, tab: TabDescription): Promise { await expect( page @@ -63,6 +80,14 @@ export async function testTab(page: Page, tab: TabDescription): Promise { } } +/** + * Checks that sorting the tab does not cause the first and last row to break. + * This test does not check whether the sort order is correct. + * This test assumes that this is an Azul explorer with pagination rather than + * a catalog, so that the last element is visible without excessive scrolling. + * @param page - a Playwright page object + * @param tab - the tab to check + */ export async function testSortAzul( page: Page, tab: TabDescription @@ -77,13 +102,13 @@ export async function testSortAzul( ) { // Get the column position, taking into account that some tabs start with a non-text first column if (tab.preselectedColumns[columnPosition].sortable) { - const workColumnPosition: number = tab.emptyFirstColumn + const columnIndex: number = tab.emptyFirstColumn ? columnPosition + 1 : columnPosition; // Locators for the first and last cells in a particular column position on the page - const firstElementTextLocator = getFirstElementTextLocator( + const firstElementTextLocator = getFirstRowNthColumnCellLocator( page, - workColumnPosition + columnIndex ); const lastElementTextLocator = page .getByRole("rowgroup") @@ -91,8 +116,8 @@ export async function testSortAzul( .getByRole("row") .last() .getByRole("cell") - .nth(workColumnPosition); - // Locator for the sort buttonf + .nth(columnIndex); + // Locator for the sort button for the tab const columnSortLocator = page .getByRole("columnheader", { exact: true, @@ -119,6 +144,14 @@ export async function testSortAzul( } } +/** + * Checks that sorting the tab does not cause the first row of the table to break. + * This test does not check whether the sort order is correct. + * This test assumes that this is a catalog explorer without pagination, + * so it only checks the first element of the table. + * @param page - a Playwright page object + * @param tab - the tab to check + */ export async function testSortCatalog( page: Page, tab: TabDescription @@ -133,13 +166,13 @@ export async function testSortCatalog( ) { // Get the column position, taking into account that some tabs start with a non-text first column if (tab.preselectedColumns[columnPosition].sortable) { - const workColumnPosition: number = tab.emptyFirstColumn + const columnIndex: number = tab.emptyFirstColumn ? columnPosition + 1 : columnPosition; // Locators for the first and last cells in a particular column position on the page - const firstElementTextLocator = getFirstElementTextLocator( + const firstElementTextLocator = getFirstRowNthColumnCellLocator( page, - workColumnPosition + columnIndex ); // Locator for the sort button const columnSortLocator = page @@ -148,12 +181,10 @@ export async function testSortCatalog( name: tab.preselectedColumns[columnPosition].name, }) .getByRole("button"); - await expect(firstElementTextLocator).toBeVisible(); - // Click to sort await columnSortLocator.click(); - // Expect the first and cells to still be visible + // Expect the first cell to still be visible await expect(firstElementTextLocator).toBeVisible(); // Click again await columnSortLocator.click(); @@ -164,6 +195,13 @@ export async function testSortCatalog( } } +/** + * Check that all of the selectable columns specified in the tab object + * are initially not selected in the "Edit Columns" menu, and that selecting + * them causes them to appear in the correct order + * @param page - a Playwright page object + * @param tab - the tab object to check + */ export async function testSelectableColumns( page: Page, tab: TabDescription @@ -193,6 +231,12 @@ export async function testSelectableColumns( ); } +/** + * Checks that the preselected columns specified in the tab object appear + * in the "Edit Columns" menu and that their checkbox is checked and disabled + * @param page - the Playwright page object + * @param tab - the tab object to test + */ export async function testPreSelectedColumns( page: Page, tab: TabDescription @@ -215,21 +259,34 @@ export async function testPreSelectedColumns( } } -export const filterRegex = (filter: string): RegExp => - new RegExp(filter + "\\s+\\([0-9]+\\)\\s*"); +/** + * Returns a regex that matches the sidebar filter buttons + * This is useful for selecting a filter from the sidebar + * @param filterName - the name of the filter to match + * @returns a regular expression matching "[filterName] ([n])" + */ +export const filterRegex = (filterName: string): RegExp => + new RegExp(filterName + "\\s+\\([0-9]+\\)\\s*"); +/** + * Checks that each filter specified in filterNames is visible and can be + * selected on the specified tab + * @param page - a Playwright page object + * @param tab - the tab to check + * @param filterNames - the names of the filters who whose existence should be tested for + */ export async function testFilterPresence( page: Page, tab: TabDescription, - filters: string[] + filterNames: string[] ): Promise { // Goto the selected tab await page.goto(tab.url); await expect(page.getByRole("tab").getByText(tab.tabName)).toBeVisible(); - for (const filter of filters) { + for (const filterName of filterNames) { // Check that each filter is visible and clickable - await expect(page.getByText(filterRegex(filter))).toBeVisible(); - await page.getByText(filterRegex(filter)).click(); + await expect(page.getByText(filterRegex(filterName))).toBeVisible(); + await page.getByText(filterRegex(filterName)).click(); await expect(page.getByRole("checkbox").first()).toBeVisible(); await expect(page.getByRole("checkbox").first()).not.toBeChecked(); // Check that clicking out of the filter menu causes it to disappear @@ -238,14 +295,26 @@ export async function testFilterPresence( } } +/** + * Get a locator for the specified filter option. Requires a filter menu to be open + * @param page - a Playwright page object + * @param filterOptionName - the name of the filter option + * @returns a Playwright locator to the filter button + */ export const getNamedFilterButtonLocator = ( page: Page, - filterName: string + filterOptionName: string ): Locator => { return page .getByRole("button") - .filter({ has: page.getByRole("checkbox"), hasText: filterName }); + .filter({ has: page.getByRole("checkbox"), hasText: filterOptionName }); }; + +/** + * Get a locator for the first filter option. Requires a filter menu to be open + * @param page - a Playwright page object + * @returns a Playwright locator to the filter button + */ export const getFirstFilterButtonLocator = (page: Page): Locator => { return page .getByRole("button") @@ -253,37 +322,47 @@ export const getFirstFilterButtonLocator = (page: Page): Locator => { .first(); }; +/** + * Cheks that selecting a specified filter is persistent across the tabs in tabOrder + * @param page - a Playwright page object + * @param testFilterName - the name of the filter to check + * @param tabOrder - the tabs to check, in order. The filter will be selected on the first tab. + * @returns false if the test should fail, and true if the test passes + */ export async function testFilterPersistence( page: Page, - testFilter: string, + testFilterName: string, tabOrder: TabDescription[] -): Promise { +): Promise { // Start on the first tab in the test order (should be files) await page.goto(tabOrder[0].url); // Select the first checkbox on the test filter - await page.getByText(filterRegex(testFilter)).click(); + await page.getByText(filterRegex(testFilterName)).click(); const filterToSelectLocator = await getFirstFilterButtonLocator(page); await expect(filterToSelectLocator.getByRole("checkbox")).not.toBeChecked(); await filterToSelectLocator.getByRole("checkbox").click(); - const filterNameMatch = (await filterToSelectLocator.innerText()).match( - /\.\S*/ - ); + const filterNameMatch = (await filterToSelectLocator.innerText()) + .trim() + .match(/^\S*/); if (filterNameMatch == null) { - test.fail(); + // 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"); + return false; } const filterName = (filterNameMatch ?? [""])[0]; await expect(filterToSelectLocator.getByRole("checkbox")).toBeChecked(); await page.locator("body").click(); // Expect at least some text to still be visible - await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); + await expect(getFirstRowNthColumnCellLocator(page, 0)).toBeVisible(); // For each tab, check that the selected filter is still checked for (const tab of tabOrder.slice(1)) { await page .getByRole("tab") .getByText(tab.tabName, { exact: true }) .dispatchEvent("click"); - await expect(page.getByText(filterRegex(testFilter))).toBeVisible(); - await page.getByText(filterRegex(testFilter)).dispatchEvent("click"); + await expect(page.getByText(filterRegex(testFilterName))).toBeVisible(); + await page.getByText(filterRegex(testFilterName)).dispatchEvent("click"); await page.waitForLoadState("load"); const previouslySelected = getNamedFilterButtonLocator(page, filterName); await expect(previouslySelected.getByRole("checkbox")).toBeChecked(); @@ -295,26 +374,36 @@ export async function testFilterPersistence( .getByRole("tab") .getByText(tabOrder[0].tabName, { exact: true }) .click(); - await expect(getFirstElementTextLocator(page, 0)).toBeVisible(); - await page.getByText(filterRegex(testFilter)).click(); + await expect(getFirstRowNthColumnCellLocator(page, 0)).toBeVisible(); + await page.getByText(filterRegex(testFilterName)).click(); const previouslySelected = getFirstFilterButtonLocator(page); await expect(previouslySelected).toContainText(filterName, { useInnerText: true, }); await expect(previouslySelected.getByRole("checkbox").first()).toBeChecked(); + return true; } +/** + * Test that the counts associated with an array of filter names are reflected + * in the table + * @param page - a Playwright page object + * @param tab - the tab object to test + * @param filterNames - the names of the filters to select, in order + * @param elementsPerPage - the maximum number of elements per page + * @returns false if the test should fail and true if the test should pass + */ export async function testFilterCounts( page: Page, tab: TabDescription, - filters: string[], + filterNames: string[], elementsPerPage: number ): Promise { await page.goto(tab.url); // For each arbitrarily selected filter - for (const filter of filters) { + for (const filterName of filterNames) { // Select the filter - await page.getByText(filterRegex(filter)).dispatchEvent("click"); + await page.getByText(filterRegex(filterName)).dispatchEvent("click"); // Get the number associated with the first filter button, and select it await page.waitForLoadState("load"); const filterButton = getFirstFilterButtonLocator(page); @@ -323,7 +412,7 @@ export async function testFilterCounts( filterNumbers.map((x) => Number(x)).find((x) => !isNaN(x) && x !== 0) ?? -1; if (filterNumber < 0) { - console.log(filterNumbers.map((x) => Number(x))); + console.log("ERROR: The number associated with the filter is negative"); return false; } // Check the filter @@ -342,15 +431,22 @@ export async function testFilterCounts( return true; } -export async function testFilterBubbles( +/** + * Check that the filter tabs appear when a filter is selected and that clicking + * them causes the filter to be deselected + * @param page - a Playwright page objet + * @param tab - the tab to check + * @param filterNames - the names of the filters to check + */ +export async function testFilterTags( page: Page, tab: TabDescription, - filters: string[] + filterNames: string[] ): Promise { page.goto(tab.url); - for (const filter of filters) { + for (const filterName of filterNames) { // Select a filter - await page.getByText(filterRegex(filter)).dispatchEvent("click"); + await page.getByText(filterRegex(filterName)).dispatchEvent("click"); await page.waitForLoadState("load"); const firstFilterButtonLocator = getFirstFilterButtonLocator(page); // Get the name of the selected filter @@ -363,17 +459,17 @@ export async function testFilterBubbles( await page.waitForLoadState("load"); await page.locator("body").click(); await expect(page.getByRole("checkbox")).toHaveCount(0); - // Click the blue button - const filterBlueButtonLocator = page + // Click the filter tag + const filterTagLocator = page .locator("#sidebar-positioner") .getByText(firstFilterName); - await expect(filterBlueButtonLocator).toBeVisible(); - await filterBlueButtonLocator.scrollIntoViewIfNeeded(); - await filterBlueButtonLocator.dispatchEvent("click"); - // Expect the blue button to disappear when clicked - await expect(filterBlueButtonLocator).toHaveCount(0); + await expect(filterTagLocator).toBeVisible(); + await filterTagLocator.scrollIntoViewIfNeeded(); + await filterTagLocator.dispatchEvent("click"); + // Expect the tag to disappear when clicked + await expect(filterTagLocator).toHaveCount(0); // Expect the filter to be deselected in the filter menu - await page.getByText(filterRegex(filter)).dispatchEvent("click"); + await page.getByText(filterRegex(filterName)).dispatchEvent("click"); await expect( firstFilterButtonLocator.getByRole("checkbox") ).not.toBeChecked(); @@ -381,6 +477,13 @@ export async function testFilterBubbles( } } +/** + * Check that selecting some filters then selecting the clear all button causes + * those filters to become deselected + * @param page - a Playwright page object + * @param tab - the tab object to test on + * @param filterNames - the names of the fitlers to check + */ export async function testClearAll( page: Page, tab: TabDescription, From ffa69f5f9270093604bcfb31db7f5928530ea850 Mon Sep 17 00:00:00 2001 From: Jonah Paten Date: Fri, 16 Aug 2024 14:20:16 -0700 Subject: [PATCH 16/16] fix: fixed filter persistence test errors (#4068) --- explorer/e2e/testFunctions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/explorer/e2e/testFunctions.ts b/explorer/e2e/testFunctions.ts index d2619749d..ef4f9021c 100644 --- a/explorer/e2e/testFunctions.ts +++ b/explorer/e2e/testFunctions.ts @@ -373,9 +373,9 @@ export async function testFilterPersistence( await page .getByRole("tab") .getByText(tabOrder[0].tabName, { exact: true }) - .click(); + .dispatchEvent("click"); await expect(getFirstRowNthColumnCellLocator(page, 0)).toBeVisible(); - await page.getByText(filterRegex(testFilterName)).click(); + await page.getByText(filterRegex(testFilterName)).dispatchEvent("click"); const previouslySelected = getFirstFilterButtonLocator(page); await expect(previouslySelected).toContainText(filterName, { useInnerText: true,