From f0ece552004cad1d3847691aab0bfd8b0ab6b8e3 Mon Sep 17 00:00:00 2001 From: Erin Hoops <109251328+ehoops-cz@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:36:17 -0700 Subject: [PATCH] refactor: download dialog tests (#1011) Resolves #962 --- .../_templates/e2e/new/pageActor.cjs.t | 35 + .../_templates/e2e/new/pageObject.cjs.t | 1 + .../_templates/e2e/new/testFile.cjs.t | 11 +- .../packages/data-portal/e2e/dialogs/index.ts | 2 - .../data-portal/e2e/dialogs/singleDataset.ts | 207 ----- .../data-portal/e2e/dialogs/singleRun.ts | 738 ------------------ .../packages/data-portal/e2e/dialogs/utils.ts | 106 --- .../data-portal/e2e/downloadDialog.test.ts | 703 ++++++++++++++++- .../packages/data-portal/e2e/filters/index.ts | 4 - .../testGroundTruthAnnotationFilter.ts | 65 -- .../e2e/filters/testMultiInputFilter.ts | 126 --- .../e2e/filters/testMultiSelectFilter.ts | 120 --- .../e2e/filters/testSingleSelectFilter.ts | 95 --- .../downloadDialog/downloadDialogActor.ts | 316 ++++++++ .../downloadDialog/downloadDialogPage.ts | 110 +++ .../e2e/pageObjects/downloadDialog/types.ts | 14 + .../e2e/pageObjects/downloadDialog/utils.ts | 110 +++ 17 files changed, 1293 insertions(+), 1470 deletions(-) create mode 100644 frontend/packages/data-portal/_templates/e2e/new/pageActor.cjs.t delete mode 100644 frontend/packages/data-portal/e2e/dialogs/index.ts delete mode 100644 frontend/packages/data-portal/e2e/dialogs/singleDataset.ts delete mode 100644 frontend/packages/data-portal/e2e/dialogs/singleRun.ts delete mode 100644 frontend/packages/data-portal/e2e/dialogs/utils.ts delete mode 100644 frontend/packages/data-portal/e2e/filters/index.ts delete mode 100644 frontend/packages/data-portal/e2e/filters/testGroundTruthAnnotationFilter.ts delete mode 100644 frontend/packages/data-portal/e2e/filters/testMultiInputFilter.ts delete mode 100644 frontend/packages/data-portal/e2e/filters/testMultiSelectFilter.ts delete mode 100644 frontend/packages/data-portal/e2e/filters/testSingleSelectFilter.ts create mode 100644 frontend/packages/data-portal/e2e/pageObjects/downloadDialog/downloadDialogActor.ts create mode 100644 frontend/packages/data-portal/e2e/pageObjects/downloadDialog/downloadDialogPage.ts create mode 100644 frontend/packages/data-portal/e2e/pageObjects/downloadDialog/types.ts create mode 100644 frontend/packages/data-portal/e2e/pageObjects/downloadDialog/utils.ts diff --git a/frontend/packages/data-portal/_templates/e2e/new/pageActor.cjs.t b/frontend/packages/data-portal/_templates/e2e/new/pageActor.cjs.t new file mode 100644 index 000000000..e554ce54f --- /dev/null +++ b/frontend/packages/data-portal/_templates/e2e/new/pageActor.cjs.t @@ -0,0 +1,35 @@ +--- +to: e2e/pageObjects/<%= name %>/<%= name %>Actor.ts +--- +/** + * This file contains combinations of page interactions or data fetching. Remove if not needed. + */ +import { <%= h.changeCase.pascal(name) %>Page } from 'e2e/pageObjects/<%= name %>/<%= name %>Page' + +export class <%= h.changeCase.pascal(name) %>Actor { + private <%= name %>Page: <%= h.changeCase.pascal(name) %>Page + + constructor(<%= name %>Page: <%= h.changeCase.pascal(name) %>Page) { + this.<%= name %>Page = <%= name %>Page + } + // #region Navigate + // #endregion Navigate + + // #region Click + // #endregion Click + + // #region Hover + // #endregion Hover + + // #region Get + // #endregion Get + + // #region Macro + // #endregion Macro + + // #region Validation + // #endregion Validation + + // #region Bool + // #endregion Bool +} diff --git a/frontend/packages/data-portal/_templates/e2e/new/pageObject.cjs.t b/frontend/packages/data-portal/_templates/e2e/new/pageObject.cjs.t index ac716ebef..2f234977c 100644 --- a/frontend/packages/data-portal/_templates/e2e/new/pageObject.cjs.t +++ b/frontend/packages/data-portal/_templates/e2e/new/pageObject.cjs.t @@ -1,6 +1,7 @@ --- to: e2e/pageObjects/<%= name %>/<%= name %>Page.ts --- +import { expect } from '@playwright/test' import { BasePage } from 'e2e/pageObjects/basePage' export class <%= h.changeCase.pascal(name) %>Page extends BasePage { diff --git a/frontend/packages/data-portal/_templates/e2e/new/testFile.cjs.t b/frontend/packages/data-portal/_templates/e2e/new/testFile.cjs.t index a8d7399a0..02be478e3 100644 --- a/frontend/packages/data-portal/_templates/e2e/new/testFile.cjs.t +++ b/frontend/packages/data-portal/_templates/e2e/new/testFile.cjs.t @@ -1,12 +1,19 @@ --- to: e2e/<%= name %>.test.ts --- +import { ApolloClient, NormalizedCacheObject } from '@apollo/client' import { test } from '@playwright/test' import { <%= h.changeCase.pascal(name) %>Page } from 'e2e/pageObjects/<%= name %>/<%= name %>Page' test.describe('<%= name %>', () => { - test('should work', async ({ page }) => { - const <%= name %>Page = new <%= h.changeCase.pascal(name) %>Page(page) + let client: ApolloClient + let <%= name %>Page: <%= h.changeCase.pascal(name) %>Page + + test.beforeEach(({page}) => { + <%= name %>Page = new <%= h.changeCase.pascal(name) %>Page(page) + }) + + test('should work', async () => { await <%= name %>Page.goTo('https://playwright.dev/') }) }) diff --git a/frontend/packages/data-portal/e2e/dialogs/index.ts b/frontend/packages/data-portal/e2e/dialogs/index.ts deleted file mode 100644 index c40c44e78..000000000 --- a/frontend/packages/data-portal/e2e/dialogs/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './singleDataset' -export * from './singleRun' diff --git a/frontend/packages/data-portal/e2e/dialogs/singleDataset.ts b/frontend/packages/data-portal/e2e/dialogs/singleDataset.ts deleted file mode 100644 index 25bb03667..000000000 --- a/frontend/packages/data-portal/e2e/dialogs/singleDataset.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { expect, test } from '@playwright/test' -import { getApolloClient } from 'e2e/apollo' -import { goTo, skipClipboardTestsForWebkit } from 'e2e/filters/utils' - -import { getDatasetCodeSnippet } from 'app/components/Download/APIDownloadTab' -import { getDatasetById } from 'app/graphql/getDatasetById.server' -import { DownloadTab } from 'app/types/download' - -import { E2E_CONFIG, SINGLE_DATASET_URL, translations } from '../constants' -import { - constructDialogUrl, - expectClickToSwitchTab, - expectTabSelected, - getIconButton, - validateDialogOpen, -} from './utils' - -async function fetchData() { - const client = getApolloClient() - return getDatasetById({ client, id: +E2E_CONFIG.datasetId }) -} - -export function testSingleDatasetDownloadDialog() { - test.describe('Single Dataset', () => { - test('should open when clicking download button', async ({ page }) => { - await goTo(page, SINGLE_DATASET_URL) - await page - .getByRole('button', { name: translations.downloadDataset }) - .click() - await validateDialogOpen(page, translations.downloadOptions) - const expectedUrl = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.AWS, - }) - await page.waitForURL(expectedUrl.href) - }) - - test('should display dataset name', async ({ page }) => { - await goTo(page, SINGLE_DATASET_URL) - await page - .getByRole('button', { name: translations.downloadDataset }) - .click() - const { data } = await fetchData() - await validateDialogOpen(page, translations.downloadOptions, [ - `${translations.dataset}: ${data.datasets[0].title}`, - ]) - const expectedUrl = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.AWS, - }) - await page.waitForURL(expectedUrl.href) - }) - - test('should open aws tab via url', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.AWS, - }) - - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await expectTabSelected(dialog, translations.viaAwsS3) - await expectTabSelected(dialog, translations.viaApi, false) - }) - - test('should open api tab via url', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.API, - }) - - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await expectTabSelected(dialog, translations.viaAwsS3, false) - await expectTabSelected(dialog, translations.viaApi) - }) - - test('should open api tab by clicking', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.AWS, - }) - const expectedUrl = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.API, - }) - - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await expectClickToSwitchTab( - dialog, - translations.viaAwsS3, - translations.viaApi, - ) - - await page.waitForURL(expectedUrl.href) - }) - - test('should open aws tab by clicking', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.API, - }) - const expectedUrl = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.AWS, - }) - - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await expectClickToSwitchTab( - dialog, - translations.viaApi, - translations.viaAwsS3, - ) - - await page.waitForURL(expectedUrl.href) - }) - - test('should copy from aws tab', async ({ page, browserName }) => { - skipClipboardTestsForWebkit(browserName) - - const url = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.AWS, - }) - - await goTo(page, url.href) - - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - await dialog.getByRole('button', { name: translations.copy }).click() - - const handle = await page.evaluateHandle(() => - navigator.clipboard.readText(), - ) - const clipboardValue = await handle.jsonValue() - expect(clipboardValue).toContain('aws s3') - expect(clipboardValue).toContain(E2E_CONFIG.datasetId) - }) - - test('should copy from api tab', async ({ page, browserName }) => { - skipClipboardTestsForWebkit(browserName) - - const url = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.API, - }) - - await goTo(page, url.href) - - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - await dialog.getByRole('button', { name: translations.copy }).click() - - const handle = await page.evaluateHandle(() => - navigator.clipboard.readText(), - ) - const clipboardValue = await handle.jsonValue() - expect(clipboardValue).toBe(getDatasetCodeSnippet(+E2E_CONFIG.datasetId)) - }) - - test('should close when x button clicked', async ({ page }) => { - const url = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.AWS, - }) - - await goTo(page, url.href) - - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await getIconButton(dialog).click() - - await expect(dialog).toBeHidden() - }) - - test('should close when close button clicked', async ({ page }) => { - const url = constructDialogUrl(SINGLE_DATASET_URL, { - tab: DownloadTab.AWS, - }) - - await goTo(page, url.href) - - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await dialog.getByRole('button', { name: translations.close }).click() - - await expect(dialog).toBeHidden() - }) - }) -} diff --git a/frontend/packages/data-portal/e2e/dialogs/singleRun.ts b/frontend/packages/data-portal/e2e/dialogs/singleRun.ts deleted file mode 100644 index 0caf7d814..000000000 --- a/frontend/packages/data-portal/e2e/dialogs/singleRun.ts +++ /dev/null @@ -1,738 +0,0 @@ -import { expect, test } from '@playwright/test' -import { getApolloClient } from 'e2e/apollo' -import { goTo, skipClipboardTestsForWebkit } from 'e2e/filters/utils' - -import { - getAllTomogramsCodeSnippet, - getTomogramCodeSnippet, -} from 'app/components/Download/APIDownloadTab' -import { getAwsCommand } from 'app/components/Download/AWSDownloadTab' -import { getCurlCommand } from 'app/components/Download/CurlDownloadTab' -import { getRunById } from 'app/graphql/getRunById.server' -import { DownloadConfig, DownloadStep, DownloadTab } from 'app/types/download' - -import { E2E_CONFIG, SINGLE_RUN_URL, translations } from '../constants' -import { - constructDialogUrl, - expectClickToSwitchTab, - expectTabSelected, - expectUrlsToMatch, - getIconButton, - validateDialogOpen, -} from './utils' - -const TOMOTABS = [ - DownloadTab.API, - DownloadTab.AWS, - DownloadTab.Curl, - DownloadTab.Download, -] - -async function fetchData() { - const client = getApolloClient() - return getRunById({ client, id: +E2E_CONFIG.runId, annotationsPage: 1 }) -} - -export function testSingleRunDownloadDialog() { - test.describe('Single Run', () => { - test('should open when clicking download button', async ({ page }) => { - await goTo(page, SINGLE_RUN_URL) - await page - .getByRole('button', { name: translations.download }) - .first() - .click() - - await validateDialogOpen(page, translations.configureDownload) - const expectedUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - }) - await page.waitForURL(expectedUrl.href) - }) - - test('should open configure step from url', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - }) - await goTo(page, initialUrl.href) - await validateDialogOpen(page, translations.configureDownload) - }) - - test('should contain subfield data in step 1', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.AllAnnotations, - }) - const { data } = await fetchData() - const runName = data.runs[0].name - const datasetName = data.runs[0].dataset.title - - await goTo(page, initialUrl.href) - await validateDialogOpen(page, translations.configureDownload, [ - `${translations.dataset}: ${datasetName}`, - `${translations.run}: ${runName}`, - ]) - }) - - test('should close step 1 when x button clicked', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.AllAnnotations, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.configureDownload, - ) - - await getIconButton(dialog).first().click() - await expect(dialog).toBeHidden() - }) - - // TODO: test info box once expanding/collapsing is fixed - - test.describe('Download All Annotations', () => { - test('should select on click', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.configureDownload, - ) - - await dialog - .getByRole('button', { name: translations.downloadAllAnnotations }) - .click() - - await expect(dialog.getByRole('radio', { checked: true })).toHaveValue( - DownloadConfig.AllAnnotations, - ) - - const expectedUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.AllAnnotations, - }) - await page.waitForURL(expectedUrl.href) - }) - - test('should select from url', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.AllAnnotations, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.configureDownload, - ) - - await expect(dialog.getByRole('radio', { checked: true })).toHaveValue( - DownloadConfig.AllAnnotations, - ) - }) - - test('should change selection on click', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.Tomogram, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.configureDownload, - ) - - await expect(dialog.getByRole('radio', { checked: true })).toHaveValue( - DownloadConfig.Tomogram, - ) - - await dialog - .getByRole('button', { name: translations.downloadAllAnnotations }) - .click() - - await expect(dialog.getByRole('radio', { checked: true })).toHaveValue( - DownloadConfig.AllAnnotations, - ) - - const expectedUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.AllAnnotations, - }) - - await page.waitForURL(expectedUrl.href) - }) - - test('should go to next step on click', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.AllAnnotations, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.configureDownload, - ) - - await dialog.getByRole('button', { name: translations.next }).click() - - await validateDialogOpen(page, translations.downloadOptions) - - const expectedUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.AWS, - }) - await page.waitForURL(expectedUrl.href) - }) - - test('should go back on click', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.AWS, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await dialog.getByRole('button', { name: translations.back }).click() - - await validateDialogOpen(page, translations.configureDownload) - - const expectedUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.AllAnnotations, - }) - await page.waitForURL(expectedUrl.href) - }) - - test('should open aws tab from url', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.AWS, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await expectTabSelected(dialog, DownloadTab.AWS) - await expectTabSelected(dialog, DownloadTab.API, false) - }) - - test('should open api tab from url', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.API, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await expectTabSelected(dialog, DownloadTab.API) - await expectTabSelected(dialog, DownloadTab.AWS, false) - }) - - test('should contain subfield data in step 2', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.AWS, - }) - const { data } = await fetchData() - const runName = data.runs[0].name - const datasetName = data.runs[0].dataset.title - - await goTo(page, initialUrl.href) - await validateDialogOpen(page, translations.downloadOptions, [ - `${translations.dataset}: ${datasetName}`, - `${translations.run}: ${runName}`, - `${translations.annotations}: ${translations.all}`, - ]) - }) - - test('should change tabs from aws to api on click', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.AWS, - fileFormat: 'mrc', - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await expectClickToSwitchTab(dialog, DownloadTab.AWS, DownloadTab.API) - - const expectedUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.API, - fileFormat: 'mrc', - }) - - expectUrlsToMatch(page.url(), expectedUrl.href) - }) - - test('should change tabs from api to aws on click', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.API, - fileFormat: 'mrc', - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await expectClickToSwitchTab(dialog, DownloadTab.API, DownloadTab.AWS) - - const expectedUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.AWS, - fileFormat: 'mrc', - }) - - expectUrlsToMatch(page.url(), expectedUrl.href) - }) - - test('should copy from aws tab', async ({ page, browserName }) => { - skipClipboardTestsForWebkit(browserName) - - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.AWS, - }) - await goTo(page, initialUrl.href) - - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await dialog.getByRole('button', { name: translations.copy }).click() - - const handle = await page.evaluateHandle(() => - navigator.clipboard.readText(), - ) - const clipboardValue = await handle.jsonValue() - - const { data } = await fetchData() - const s3Prefix = `${data.runs[0].tomogram_voxel_spacings[0].s3_prefix}Annotations` - - expect(clipboardValue).toBe( - getAwsCommand({ s3Path: s3Prefix, s3Command: 'sync' }), - ) - }) - - test('should copy from api tab', async ({ page, browserName }) => { - skipClipboardTestsForWebkit(browserName) - - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.API, - }) - await goTo(page, initialUrl.href) - - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await dialog.getByRole('button', { name: translations.copy }).click() - - const handle = await page.evaluateHandle(() => - navigator.clipboard.readText(), - ) - const clipboardValue = await handle.jsonValue() - - const { data } = await fetchData() - const voxelSpacingId = data.runs[0].tomogram_voxel_spacings[0].id - - expect(clipboardValue).toBe(getAllTomogramsCodeSnippet(voxelSpacingId)) - }) - - test('should close step 2 when x button clicked', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.AWS, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await getIconButton(dialog).first().click() - await expect(dialog).toBeHidden() - }) - - test('should close step 2 when close button clicked', async ({ - page, - }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.AllAnnotations, - tab: DownloadTab.AWS, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await dialog.getByRole('button', { name: translations.close }).click() - await expect(dialog).toBeHidden() - }) - }) - - test.describe('Download Tomogram', () => { - test('should select on click', async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.configureDownload, - ) - - const { data } = await fetchData() - const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] - - await dialog - .getByRole('button', { name: translations.downloadTomogram }) - .click() - - await expect(dialog.getByRole('radio', { checked: true })).toHaveValue( - DownloadConfig.Tomogram, - ) - - const expectedUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.Tomogram, - tomogram: { - sampling: tomogram.voxel_spacing, - processing: tomogram.processing, - }, - fileFormat: 'mrc', - }) - await page.waitForURL(expectedUrl.href) - }) - - // TODO: when we support multiple tomograms per run add tests for the different dropdowns - - test('should select from url', async ({ page }) => { - const { data } = await fetchData() - const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] - - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.Tomogram, - tomogram: { - sampling: tomogram.voxel_spacing, - processing: tomogram.processing, - }, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.configureDownload, - ) - - await expect(dialog.getByRole('radio', { checked: true })).toHaveValue( - DownloadConfig.Tomogram, - ) - }) - - test('should change selection on click', async ({ page }) => { - const { data } = await fetchData() - const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] - - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.AllAnnotations, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.configureDownload, - ) - - await expect(dialog.getByRole('radio', { checked: true })).toHaveValue( - DownloadConfig.AllAnnotations, - ) - - await dialog - .getByRole('button', { name: translations.downloadTomogram }) - .click() - - await expect(dialog.getByRole('radio', { checked: true })).toHaveValue( - DownloadConfig.Tomogram, - ) - - const expectedUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Configure, - config: DownloadConfig.Tomogram, - tomogram: { - sampling: tomogram.voxel_spacing, - processing: tomogram.processing, - }, - fileFormat: 'mrc', - }) - await page.waitForURL(expectedUrl.href) - }) - - test.describe('should open tabs from url', () => { - TOMOTABS.forEach((tab, i) => { - test(`should open ${tab} tab from url`, async ({ page }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.Tomogram, - fileFormat: 'mrc', - tab, - }) - - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await expectTabSelected(dialog, tab) - await Promise.all( - TOMOTABS.toSpliced(i, 1).map(async (t) => { - await expectTabSelected(dialog, t, false) - }), - ) - }) - }) - }) - - test.describe('should change tabs on click', () => { - const testCases = TOMOTABS.flatMap((v1, i) => - TOMOTABS.toSpliced(i, 1).map((v2) => ({ fromTab: v1, toTab: v2 })), - ) - - testCases.forEach(({ fromTab, toTab }) => { - test(`should change tabs from ${fromTab} to ${toTab}`, async ({ - page, - }) => { - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.Tomogram, - tab: fromTab, - fileFormat: 'mrc', - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await expectClickToSwitchTab(dialog, fromTab, toTab) - - const expectedUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.Tomogram, - tab: toTab, - fileFormat: 'mrc', - }) - - expectUrlsToMatch(page.url(), expectedUrl.href) - }) - }) - }) - - test('should copy from aws tab', async ({ page, browserName }) => { - skipClipboardTestsForWebkit(browserName) - - const { data } = await fetchData() - const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] - const activeTomogram = - data.runs[0].tomogram_stats[0].tomogram_resolutions.find((tomo) => { - return ( - tomo.voxel_spacing === tomogram.voxel_spacing && - tomo.processing === tomogram.processing - ) - }) - - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.Tomogram, - tomogram: { - sampling: tomogram.voxel_spacing, - processing: tomogram.processing, - }, - tab: DownloadTab.AWS, - fileFormat: 'mrc', - }) - await goTo(page, initialUrl.href) - - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await dialog.getByRole('button', { name: translations.copy }).click() - - const handle = await page.evaluateHandle(() => - navigator.clipboard.readText(), - ) - const clipboardValue = await handle.jsonValue() - - const s3Prefix = activeTomogram?.s3_mrc_scale0 - - expect(clipboardValue).toBe( - getAwsCommand({ s3Path: s3Prefix, s3Command: 'cp' }), - ) - }) - - test('should copy from api tab', async ({ page, browserName }) => { - skipClipboardTestsForWebkit(browserName) - - const { data } = await fetchData() - const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] - const activeTomogram = - data.runs[0].tomogram_stats[0].tomogram_resolutions.find((tomo) => { - return ( - tomo.voxel_spacing === tomogram.voxel_spacing && - tomo.processing === tomogram.processing - ) - }) - - const fileFormat = 'mrc' - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - fileFormat, - step: DownloadStep.Download, - config: DownloadConfig.Tomogram, - tomogram: { - sampling: tomogram.voxel_spacing, - processing: tomogram.processing, - }, - tab: DownloadTab.API, - }) - await goTo(page, initialUrl.href) - - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await dialog.getByRole('button', { name: translations.copy }).click() - - const handle = await page.evaluateHandle(() => - navigator.clipboard.readText(), - ) - const clipboardValue = await handle.jsonValue() - - expect(clipboardValue).toBe( - getTomogramCodeSnippet(activeTomogram?.id, fileFormat), - ) - }) - - test('should copy from curl tab', async ({ page, browserName }) => { - skipClipboardTestsForWebkit(browserName) - - const { data } = await fetchData() - const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] - const activeTomogram = - data.runs[0].tomogram_stats[0].tomogram_resolutions.find((tomo) => { - return ( - tomo.voxel_spacing === tomogram.voxel_spacing && - tomo.processing === tomogram.processing - ) - }) - - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.Tomogram, - tomogram: { - sampling: tomogram.voxel_spacing, - processing: tomogram.processing, - }, - tab: DownloadTab.Curl, - }) - await goTo(page, initialUrl.href) - - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await dialog.getByRole('button', { name: translations.copy }).click() - - const handle = await page.evaluateHandle(() => - navigator.clipboard.readText(), - ) - const clipboardValue = await handle.jsonValue() - - expect(clipboardValue).toBe( - getCurlCommand(activeTomogram?.https_mrc_scale0), - ) - }) - - test('should close when x clicked', async ({ page }) => { - const { data } = await fetchData() - const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] - - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.Tomogram, - tomogram: { - sampling: tomogram.voxel_spacing, - processing: tomogram.processing, - }, - tab: DownloadTab.AWS, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await getIconButton(dialog).click() - await expect(dialog).toBeHidden() - }) - - test('should close when close button clicked', async ({ page }) => { - const { data } = await fetchData() - const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] - - const initialUrl = constructDialogUrl(SINGLE_RUN_URL, { - step: DownloadStep.Download, - config: DownloadConfig.Tomogram, - tomogram: { - sampling: tomogram.voxel_spacing, - processing: tomogram.processing, - }, - tab: DownloadTab.AWS, - }) - await goTo(page, initialUrl.href) - const dialog = await validateDialogOpen( - page, - translations.downloadOptions, - ) - - await dialog.getByRole('button', { name: translations.close }).click() - await expect(dialog).toBeHidden() - }) - }) - }) -} diff --git a/frontend/packages/data-portal/e2e/dialogs/utils.ts b/frontend/packages/data-portal/e2e/dialogs/utils.ts deleted file mode 100644 index f680cbfcb..000000000 --- a/frontend/packages/data-portal/e2e/dialogs/utils.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { expect, Locator, Page } from '@playwright/test' - -import { QueryParams } from 'app/constants/query' - -export async function validateDialogOpen( - page: Page, - title: string, - substrings?: string[], -): Promise { - const dialog = page.getByRole('dialog') - await expect(dialog).toBeVisible() - await expect(dialog.getByRole('heading').first()).toHaveText(title) - - if (substrings) { - await Promise.all( - substrings.map(async (str) => { - await expect(dialog.getByText(str)).toBeVisible() - }), - ) - } - - return dialog -} - -export function getIconButton(loc: Locator) { - return loc.locator('button:has(svg)') -} - -export function constructDialogUrl( - url: URL | string, - { - tab, - step, - config, - tomogram, - fileFormat, - }: { - tab?: string - step?: string - config?: string - tomogram?: { sampling: number; processing: string } - fileFormat?: string - }, -): URL { - const expectedUrl = new URL(url) - const params = expectedUrl.searchParams - - if (step) { - params.append(QueryParams.DownloadStep, step) - } - - if (config) { - params.append(QueryParams.DownloadConfig, config) - } - - if (tomogram) { - params.append(QueryParams.TomogramSampling, String(tomogram.sampling)) - params.append(QueryParams.TomogramProcessing, tomogram.processing) - } - - if (tab) { - params.append(QueryParams.DownloadTab, tab) - } - - if (fileFormat) { - params.append(QueryParams.FileFormat, fileFormat) - } - - return expectedUrl -} - -export function expectUrlsToMatch(urlStr1: string, urlStr2: string) { - const url1 = new URL(urlStr1) - const url2 = new URL(urlStr2) - - expect(url1.pathname).toBe(url2.pathname) - - url1.searchParams.forEach((value, key) => - expect(url2.searchParams.get(key)).toBe(value), - ) -} - -export async function expectTabSelected( - loc: Locator, - tab: string, - selected: boolean = true, -) { - await expect(loc.getByRole('tab', { name: tab })).toHaveAttribute( - 'aria-selected', - selected ? 'true' : 'false', - ) -} - -export async function expectClickToSwitchTab( - loc: Locator, - fromTab: string, - toTab: string, -) { - await expectTabSelected(loc, fromTab) - await expectTabSelected(loc, toTab, false) - - await loc.getByRole('tab', { name: toTab }).click() - - await expectTabSelected(loc, toTab) - await expectTabSelected(loc, fromTab, false) -} diff --git a/frontend/packages/data-portal/e2e/downloadDialog.test.ts b/frontend/packages/data-portal/e2e/downloadDialog.test.ts index 9fc76feee..35289062e 100644 --- a/frontend/packages/data-portal/e2e/downloadDialog.test.ts +++ b/frontend/packages/data-portal/e2e/downloadDialog.test.ts @@ -1,7 +1,700 @@ +import { ApolloClient, NormalizedCacheObject } from '@apollo/client' +import { test } from '@playwright/test' +import { DownloadDialogPage } from 'e2e/pageObjects/downloadDialog/downloadDialogPage' + +import { DownloadConfig, DownloadStep, DownloadTab } from 'app/types/download' + +import { getApolloClient } from './apollo' +import { SINGLE_DATASET_URL, SINGLE_RUN_URL, translations } from './constants' +import { DownloadDialogActor } from './pageObjects/downloadDialog/downloadDialogActor' import { - testSingleDatasetDownloadDialog, - testSingleRunDownloadDialog, -} from './dialogs' + SINGLE_DATASET_DOWNLOAD_TABS, + TOMOGRAM_DOWNLOAD_TABS, +} from './pageObjects/downloadDialog/types' +import { skipClipboardTestsForWebkit } from './pageObjects/downloadDialog/utils' + +test.describe('downloadDialog', () => { + let client: ApolloClient + let downloadDialogPage: DownloadDialogPage + let downloadDialogActor: DownloadDialogActor + + test.beforeEach(({ page }) => { + client = getApolloClient() + downloadDialogPage = new DownloadDialogPage(page) + downloadDialogActor = new DownloadDialogActor(downloadDialogPage) + }) + + test.describe('Single Dataset', () => { + test('should open when clicking download button', async () => { + await downloadDialogPage.goTo(SINGLE_DATASET_URL) + await downloadDialogPage.openDialog(translations.downloadDataset) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogActor.expectDialogUrlToMatch({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.AWS, + }) + }) + + test('should display correct content', async () => { + await downloadDialogPage.goTo(SINGLE_DATASET_URL) + await downloadDialogPage.openDialog(translations.downloadDataset) + + await downloadDialogActor.expectDownloadDatasetDialogToShowCorrectContent( + { client }, + ) + await downloadDialogActor.expectDialogUrlToMatch({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.AWS, + }) + }) + + test.describe('AWS Tab', () => { + test('should open tab via url', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.AWS, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogActor.expectDialogToBeOnCorrectTab({ + tab: DownloadTab.AWS, + tabGroup: SINGLE_DATASET_DOWNLOAD_TABS, + }) + }) + + test('should open tab when clicking', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.API, + }) + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogPage.clickTab(DownloadTab.AWS) + + await downloadDialogActor.expectDialogToBeOnCorrectTab({ + tab: DownloadTab.AWS, + tabGroup: SINGLE_DATASET_DOWNLOAD_TABS, + }) + await downloadDialogActor.expectDialogUrlToMatch({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.AWS, + }) + }) + + test('should copy from aws tab', async ({ browserName }) => { + skipClipboardTestsForWebkit(browserName) + + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.AWS, + }) + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogPage.clickCopyButton() + + await downloadDialogActor.expectClipboardToHaveAwsValue() + }) + + test('should close dialog', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.AWS, + }) + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogPage.clickCloseButton() + + await downloadDialogPage.expectDialogToBeHidden() + }) + }) + + test.describe('API Tab', () => { + test('should open api tab via url', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.API, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogActor.expectDialogToBeOnCorrectTab({ + tab: DownloadTab.API, + tabGroup: SINGLE_DATASET_DOWNLOAD_TABS, + }) + }) + + test('should open api tab by clicking', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.AWS, + }) + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogPage.clickTab(DownloadTab.API) + + await downloadDialogActor.expectDialogToBeOnCorrectTab({ + tab: DownloadTab.API, + tabGroup: SINGLE_DATASET_DOWNLOAD_TABS, + }) + await downloadDialogActor.expectDialogUrlToMatch({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.API, + }) + }) + + test('should copy from api tab', async ({ browserName }) => { + skipClipboardTestsForWebkit(browserName) + + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.API, + }) + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogPage.clickCopyButton() + + await downloadDialogActor.expectClipboardToHaveApiValue() + }) + test('should close dialog', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_DATASET_URL, + tab: DownloadTab.API, + }) + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogPage.clickCloseButton() + + await downloadDialogPage.expectDialogToBeHidden() + }) + }) + }) + + test.describe('Single Run', () => { + test('should open when clicking download button', async () => { + await downloadDialogPage.goTo(SINGLE_RUN_URL) + await downloadDialogPage.openDialog(translations.download) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + await downloadDialogActor.expectDialogUrlToMatch({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Configure, + }) + }) + + test('should open configure step from url', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Configure, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + }) + + test('should contain subfield data in step 1', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + config: DownloadConfig.AllAnnotations, + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Configure, + }) + + await downloadDialogActor.expectDownloadRunStepOneToShowCorrectContent({ + client, + }) + }) + + test('should close step 1 when x button clicked', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Configure, + }) + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + await downloadDialogPage.clickXButton() + + await downloadDialogPage.expectDialogToBeHidden() + }) + + // TODO: test info box once expanding/collapsing is fixed + + test.describe('All Annotations', () => { + test('should select on click', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Configure, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + + await downloadDialogPage.clickDialogRadio( + translations.downloadAllAnnotations, + ) + + await downloadDialogPage.expectRadioToBeSelected( + DownloadConfig.AllAnnotations, + ) + + await downloadDialogActor.expectDialogUrlToMatch({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Configure, + config: DownloadConfig.AllAnnotations, + }) + }) + + test('should select from url', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + config: DownloadConfig.AllAnnotations, + step: DownloadStep.Configure, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + + await downloadDialogPage.expectRadioToBeSelected( + DownloadConfig.AllAnnotations, + ) + }) + + test('should change selection on click', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + config: DownloadConfig.Tomogram, + step: DownloadStep.Configure, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + + await downloadDialogPage.clickDialogRadio( + translations.downloadAllAnnotations, + ) + + await downloadDialogPage.expectRadioToBeSelected( + DownloadConfig.AllAnnotations, + ) + }) + + test('should go to next step on click', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + config: DownloadConfig.AllAnnotations, + step: DownloadStep.Configure, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + + await downloadDialogPage.clickNextButton() + + await downloadDialogActor.expectDialogUrlToMatch({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.AWS, + }) + }) + + test('should go back on click', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.AWS, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogPage.clickBackButton() + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + + await downloadDialogActor.expectDialogUrlToMatch({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Configure, + config: DownloadConfig.AllAnnotations, + }) + }) + + test('should open aws tab from url', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.AWS, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogActor.expectDialogToBeOnCorrectTab({ + tab: DownloadTab.AWS, + tabGroup: SINGLE_DATASET_DOWNLOAD_TABS, + }) + }) + + test('should open api tab from url', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.API, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + await downloadDialogActor.expectDialogToBeOnCorrectTab({ + tab: DownloadTab.API, + tabGroup: SINGLE_DATASET_DOWNLOAD_TABS, + }) + }) + + test('should contain subfield data in step 2', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.AWS, + }) + + await downloadDialogActor.expectDownloadRunStepTwoToShowCorrectContent({ + client, + }) + }) + + test('should change tabs from aws to api on click', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.AWS, + fileFormat: 'mrc', + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogPage.clickTab(DownloadTab.API) + + await downloadDialogActor.expectDialogToBeOnCorrectTab({ + tab: DownloadTab.API, + tabGroup: SINGLE_DATASET_DOWNLOAD_TABS, + }) + await downloadDialogActor.expectDialogUrlToMatch({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.API, + fileFormat: 'mrc', + }) + }) + + test('should copy from aws tab', async ({ browserName }) => { + skipClipboardTestsForWebkit(browserName) + + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.AWS, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogPage.clickCopyButton() + + await downloadDialogActor.expectClipboardToHaveCorrectDownloadRunAnnotationsAwsCommand( + { client }, + ) + }) + + test('should copy from api tab', async ({ browserName }) => { + skipClipboardTestsForWebkit(browserName) + + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.API, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogPage.clickCopyButton() + + await downloadDialogActor.expectClipboardToHaveCorrectDownloadRunAnnotationsAPICommand( + { client }, + ) + }) + + test('should close step 2 when x button clicked', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.AWS, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogPage.clickXButton() + + await downloadDialogPage.expectDialogToBeHidden() + }) + + test('should close step 2 when close button clicked', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Download, + config: DownloadConfig.AllAnnotations, + tab: DownloadTab.AWS, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogPage.clickCloseButton() + + await downloadDialogPage.expectDialogToBeHidden() + }) + }) + + // TODO: when we support multiple tomograms per run add tests for the different dropdowns + test.describe('Download Tomogram', () => { + test('should select on click', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + step: DownloadStep.Configure, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + + await downloadDialogPage.clickDialogRadio(translations.downloadTomogram) + + await downloadDialogPage.expectRadioToBeSelected( + DownloadConfig.Tomogram, + ) + + await downloadDialogActor.expectTomogramDialogUrlToMatch({ + baseUrl: SINGLE_RUN_URL, + client, + step: DownloadStep.Configure, + config: DownloadConfig.Tomogram, + fileFormat: 'mrc', + }) + }) + + test('should select from url', async () => { + await downloadDialogActor.goToTomogramDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + client, + config: DownloadConfig.Tomogram, + step: DownloadStep.Configure, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + + await downloadDialogPage.expectRadioToBeSelected( + DownloadConfig.Tomogram, + ) + }) + + test('should change selection on click', async () => { + await downloadDialogActor.goToDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + config: DownloadConfig.AllAnnotations, + step: DownloadStep.Configure, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.configureDownload, + }) + + await downloadDialogPage.clickDialogRadio(translations.downloadTomogram) + + await downloadDialogPage.expectRadioToBeSelected( + DownloadConfig.Tomogram, + ) + + await downloadDialogActor.expectTomogramDialogUrlToMatch({ + baseUrl: SINGLE_RUN_URL, + client, + step: DownloadStep.Configure, + config: DownloadConfig.Tomogram, + fileFormat: 'mrc', + }) + }) + + test.describe('should open tabs from url', () => { + TOMOGRAM_DOWNLOAD_TABS.forEach((tab) => { + test(`should open ${tab} tab from url`, async () => { + await downloadDialogActor.goToTomogramDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + client, + config: DownloadConfig.Tomogram, + step: DownloadStep.Download, + fileFormat: 'mrc', + tab, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogActor.expectDialogToBeOnCorrectTab({ + tab, + tabGroup: TOMOGRAM_DOWNLOAD_TABS, + }) + }) + }) + }) + + test.describe('should change tabs on click', () => { + const testCases = TOMOGRAM_DOWNLOAD_TABS.flatMap((v1, i) => + TOMOGRAM_DOWNLOAD_TABS.toSpliced(i, 1).map((v2) => ({ + fromTab: v1, + toTab: v2, + })), + ) + testCases.forEach(({ fromTab, toTab }) => { + test(`should change from ${fromTab} to ${toTab} on click`, async () => { + await downloadDialogActor.goToTomogramDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + client, + config: DownloadConfig.Tomogram, + fileFormat: 'mrc', + step: DownloadStep.Download, + tab: fromTab, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogPage.clickTab(toTab) + + await downloadDialogActor.expectDialogToBeOnCorrectTab({ + tab: toTab, + tabGroup: TOMOGRAM_DOWNLOAD_TABS, + }) + await downloadDialogActor.expectTomogramDialogUrlToMatch({ + baseUrl: SINGLE_RUN_URL, + client, + config: DownloadConfig.Tomogram, + fileFormat: 'mrc', + step: DownloadStep.Download, + tab: toTab, + }) + }) + }) + }) + + test.describe('should copy from tabs', () => { + const testCases = TOMOGRAM_DOWNLOAD_TABS.filter( + (tab) => tab !== DownloadTab.Download, + ) + + testCases.forEach((tab) => { + test(`should copy from ${tab} tab`, async ({ browserName }) => { + skipClipboardTestsForWebkit(browserName) + + await downloadDialogActor.goToTomogramDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + client, + config: DownloadConfig.Tomogram, + fileFormat: 'mrc', + step: DownloadStep.Download, + tab, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogPage.clickCopyButton() + + await downloadDialogActor.expectClipboardToHaveCorrectDownloadTomogramCommand( + { client, fileFormat: 'mrc', tab }, + ) + }) + }) + }) + + test('should close when x button clicked', async () => { + await downloadDialogActor.goToTomogramDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + client, + config: DownloadConfig.Tomogram, + fileFormat: 'mrc', + step: DownloadStep.Download, + tab: DownloadTab.AWS, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogPage.clickXButton() + + await downloadDialogPage.expectDialogToBeHidden() + }) + + test('should close when close button clicked', async () => { + await downloadDialogActor.goToTomogramDownloadDialogUrl({ + baseUrl: SINGLE_RUN_URL, + client, + config: DownloadConfig.Tomogram, + fileFormat: 'mrc', + step: DownloadStep.Download, + tab: DownloadTab.AWS, + }) + + await downloadDialogActor.expectDialogToBeOpen({ + title: translations.downloadOptions, + }) + + await downloadDialogPage.clickCloseButton() -testSingleDatasetDownloadDialog() -testSingleRunDownloadDialog() + await downloadDialogPage.expectDialogToBeHidden() + }) + }) + }) +}) diff --git a/frontend/packages/data-portal/e2e/filters/index.ts b/frontend/packages/data-portal/e2e/filters/index.ts deleted file mode 100644 index f2676c9ae..000000000 --- a/frontend/packages/data-portal/e2e/filters/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './testGroundTruthAnnotationFilter' -export * from './testMultiInputFilter' -export * from './testMultiSelectFilter' -export * from './testSingleSelectFilter' diff --git a/frontend/packages/data-portal/e2e/filters/testGroundTruthAnnotationFilter.ts b/frontend/packages/data-portal/e2e/filters/testGroundTruthAnnotationFilter.ts deleted file mode 100644 index 8f99ebbd0..000000000 --- a/frontend/packages/data-portal/e2e/filters/testGroundTruthAnnotationFilter.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { test } from '@playwright/test' -import { getApolloClient } from 'e2e/apollo' -import { TableValidator } from 'e2e/pageObjects/filters/types' - -import { QueryParams } from 'app/constants/query' - -import { goTo } from './utils' - -export function testGroundTruthAnnotationFilter({ - url, - validateTable, -}: { - url: string - validateTable: TableValidator -}) { - test.describe('Ground Truth Annotation', () => { - let client = getApolloClient() - - test.beforeEach(() => { - client = getApolloClient() - }) - - test('should filter on click', async ({ page }) => { - const expectedUrl = new URL(url) - const params = expectedUrl.searchParams - params.set(QueryParams.GroundTruthAnnotation, 'true') - - await goTo(page, url) - await page.getByText('Ground Truth Annotation').click() - await page.waitForURL(expectedUrl.href) - await validateTable({ - client, - page, - params, - }) - }) - - test('should filter when opening URL', async ({ page }) => { - const expectedUrl = new URL(url) - expectedUrl.searchParams.set(QueryParams.GroundTruthAnnotation, 'true') - - await goTo(page, expectedUrl.href) - await validateTable({ - client, - page, - params: expectedUrl.searchParams, - }) - }) - - test('should disable filter on click', async ({ page }) => { - const expectedUrl = new URL(url) - const params = expectedUrl.searchParams - params.set(QueryParams.GroundTruthAnnotation, 'true') - - await goTo(page, expectedUrl.href) - await page.getByText('Ground Truth Annotation').click() - await page.waitForURL(url) - - await validateTable({ - client, - page, - }) - }) - }) -} diff --git a/frontend/packages/data-portal/e2e/filters/testMultiInputFilter.ts b/frontend/packages/data-portal/e2e/filters/testMultiInputFilter.ts deleted file mode 100644 index 1d3f0a222..000000000 --- a/frontend/packages/data-portal/e2e/filters/testMultiInputFilter.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { ApolloClient, NormalizedCacheObject } from '@apollo/client' -import { test } from '@playwright/test' -import { getApolloClient } from 'e2e/apollo' -import { E2E_CONFIG } from 'e2e/constants' -import { TableValidator } from 'e2e/pageObjects/filters/types' - -import { QueryParams } from 'app/constants/query' - -import { goTo } from './utils' - -export interface MultiInputFilter { - label: string - queryParam: QueryParams - valueKey: keyof typeof E2E_CONFIG -} - -interface SharedOptions { - url: string - validateTable: TableValidator -} - -function testFilter({ - buttonLabel, - client, - filter, - hasMultipleFilters, - url, - validateTable, -}: SharedOptions & { - buttonLabel: string - client: ApolloClient - filter: MultiInputFilter - hasMultipleFilters: boolean -}) { - const value = E2E_CONFIG[filter.valueKey] as string - - test(`should filter by ${filter.label}`, async ({ page }) => { - const expectedUrl = new URL(url) - const params = expectedUrl.searchParams - params.append(filter.queryParam, value) - - await goTo(page, url) - - // Open multi input filter - await page.getByRole('button', { name: buttonLabel }).click() - - // Fill input - const pageLabel = `${filter.label}${hasMultipleFilters ? ':' : ''}` - await page.getByLabel(pageLabel).click() - await page.getByLabel(pageLabel).fill(value) - - // Apply filter - await page.getByRole('button', { name: 'Apply' }).click() - await page.waitForURL(expectedUrl.href) - - await validateTable({ - client, - page, - params, - }) - }) - - test(`should filter by ${filter.label} when opening URL`, async ({ - page, - }) => { - const expectedUrl = new URL(url) - const params = expectedUrl.searchParams - params.append(filter.queryParam, value) - - await goTo(page, expectedUrl.href) - await validateTable({ - client, - page, - params, - }) - }) - - test(`should clear ${filter.label} filter`, async ({ page }) => { - const expectedUrl = new URL(url) - expectedUrl.searchParams.append(filter.queryParam, value) - - await goTo(page, expectedUrl.href) - - // Clear filter - await page - .locator('span', { hasText: value }) - .locator('..') - .getByRole('button') - .click() - - await page.waitForURL(url) - await validateTable({ - client, - page, - }) - }) -} - -export function testMultiInputFilter({ - filters, - label, - url, - validateTable, -}: SharedOptions & { - filters: MultiInputFilter[] - label: string -}) { - test.describe(label, () => { - let client = getApolloClient() - - test.beforeEach(() => { - client = getApolloClient() - }) - - filters.forEach((filter) => - testFilter({ - client, - filter, - url, - validateTable, - buttonLabel: label, - hasMultipleFilters: filters.length > 1, - }), - ) - }) -} diff --git a/frontend/packages/data-portal/e2e/filters/testMultiSelectFilter.ts b/frontend/packages/data-portal/e2e/filters/testMultiSelectFilter.ts deleted file mode 100644 index 85652702c..000000000 --- a/frontend/packages/data-portal/e2e/filters/testMultiSelectFilter.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { ApolloClient, NormalizedCacheObject } from '@apollo/client' -import { Page, test } from '@playwright/test' -import { getApolloClient } from 'e2e/apollo' -import { TableValidator } from 'e2e/pageObjects/filters/types' -import { identity } from 'lodash-es' - -import { QueryParams } from 'app/constants/query' - -import { goTo } from './utils' - -async function openDropdown(page: Page, label: string) { - await page.getByRole('button', { name: label }).click() -} - -async function selectOptions(page: Page, ...values: string[]) { - for (const value of values) { - await page.getByRole('option', { name: value }).locator('div').click() - } - - await page.keyboard.press('Escape') -} - -async function deselectOption(page: Page, value: string) { - await page.click(`[role=button]:has-text("${value}") svg`) -} - -export type SelectOptionsValidator = ( - page: Page, - client: ApolloClient, -) => Promise - -export function testMultiSelectFilter({ - label, - queryParam, - serialize = identity, - testOptions, - testQuery, - url, - validateSelectOptions, - validateTable, -}: { - label: string - queryParam: QueryParams - serialize?(value: string): string - testOptions: string[] - testQuery?: string - url: string - validateSelectOptions?: SelectOptionsValidator - validateTable: TableValidator -}) { - test.describe(label, () => { - let client = getApolloClient() - - test.beforeEach(() => { - client = getApolloClient() - }) - - testOptions.forEach((option) => { - test(`should filter when selecting ${option}`, async ({ page }) => { - const expectedUrl = new URL(url) - const params = expectedUrl.searchParams - params.append(queryParam, serialize(testOptions[0])) - - await goTo(page, url) - await openDropdown(page, label) - await selectOptions(page, testOptions[0]) - await page.waitForURL(expectedUrl.href) - - await validateTable({ - client, - page, - params, - }) - }) - }) - - test('should filter multiple values', async ({ page }) => { - const expectedUrl = new URL(url) - const params = expectedUrl.searchParams - testOptions.forEach((option) => - params.append(queryParam, serialize(option)), - ) - - await goTo(page, expectedUrl.href) - await validateTable({ - client, - page, - params, - }) - }) - - test('should clear filter', async ({ page }) => { - const expectedUrl = new URL(url) - const params = expectedUrl.searchParams - params.append(queryParam, serialize(testOptions[0])) - - await goTo(page, expectedUrl.href) - await deselectOption(page, testOptions[0]) - await page.waitForURL(url) - - await validateTable({ - client, - page, - }) - }) - - if (validateSelectOptions && testQuery) { - test('should filter values within filter dropdown', async ({ page }) => { - await goTo(page, url) - await openDropdown(page, label) - - const searchInput = page.getByRole('combobox', { name: 'Search' }) - await searchInput.click() - await searchInput.fill(testQuery) - - await validateSelectOptions(page, client) - }) - } - }) -} diff --git a/frontend/packages/data-portal/e2e/filters/testSingleSelectFilter.ts b/frontend/packages/data-portal/e2e/filters/testSingleSelectFilter.ts deleted file mode 100644 index d894bab0a..000000000 --- a/frontend/packages/data-portal/e2e/filters/testSingleSelectFilter.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Page, test } from '@playwright/test' -import { getApolloClient } from 'e2e/apollo' -import { TableValidator } from 'e2e/pageObjects/filters/types' -import { identity } from 'lodash-es' - -import { QueryParams } from 'app/constants/query' - -import { goTo } from './utils' - -async function openFilterDropdown(page: Page, label: string) { - await page.getByRole('button', { name: label }).click() -} - -async function selectFilterOption(page: Page, label: string) { - await page - .getByRole('option', { name: label }) - .locator('span') - .first() - .click() - - await page.keyboard.press('Escape') -} - -async function removeFilterOption(page: Page, label: string) { - await page.click(`[role=button]:has-text("${label}") svg`) -} - -export function testSingleSelectFilter({ - url, - validateTable, - label, - queryParam, - serialize = identity, - values, -}: { - label: string - queryParam: QueryParams - serialize?: (value: string) => string - url: string - validateTable: TableValidator - values: string[] -}) { - test.describe(label, () => { - let client = getApolloClient() - - test.beforeEach(() => { - client = getApolloClient() - }) - - values.forEach((value) => - test(`should filter when selecting ${value}`, async ({ page }) => { - const expectedUrl = new URL(url) - const params = expectedUrl.searchParams - params.set(queryParam, serialize(value) ?? value) - - await goTo(page, url) - await openFilterDropdown(page, label) - await selectFilterOption(page, value) - await page.waitForURL(expectedUrl.href) - - await validateTable({ - client, - page, - params, - }) - }), - ) - - test('should filter when opening URL', async ({ page }) => { - const expectedUrl = new URL(url) - const params = expectedUrl.searchParams - params.append(queryParam, serialize(values[0])) - - await goTo(page, expectedUrl.href) - await validateTable({ - client, - page, - params, - }) - }) - - test('should disable filter when deselecting', async ({ page }) => { - const expectedUrl = new URL(url) - expectedUrl.searchParams.append(queryParam, serialize(values[0])) - - await goTo(page, expectedUrl.href) - await removeFilterOption(page, values[0]) - await page.waitForURL(url) - await validateTable({ - client, - page, - }) - }) - }) -} diff --git a/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/downloadDialogActor.ts b/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/downloadDialogActor.ts new file mode 100644 index 000000000..e6caaaf4d --- /dev/null +++ b/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/downloadDialogActor.ts @@ -0,0 +1,316 @@ +/** + * This file contains combinations of page interactions or data fetching. Remove if not needed. + */ +import { ApolloClient, NormalizedCacheObject } from '@apollo/client' +import { expect } from '@playwright/test' +import { E2E_CONFIG, translations } from 'e2e/constants' +import { DownloadDialogPage } from 'e2e/pageObjects/downloadDialog/downloadDialogPage' + +import { + getAllTomogramsCodeSnippet, + getDatasetCodeSnippet, +} from 'app/components/Download/APIDownloadTab' +import { getAwsCommand } from 'app/components/Download/AWSDownloadTab' +import { DownloadConfig, DownloadStep, DownloadTab } from 'app/types/download' + +import { + constructDialogUrl, + fetchTestSingleDataset, + fetchTestSingleRun, + getTomogramDownloadCommand, +} from './utils' + +export class DownloadDialogActor { + private downloadDialogPage: DownloadDialogPage + + constructor(downloadDialogPage: DownloadDialogPage) { + this.downloadDialogPage = downloadDialogPage + } + + // #region Navigate + public async goToDownloadDialogUrl({ + config, + fileFormat, + baseUrl, + step, + tab, + tomogram, + }: { + config?: DownloadConfig + fileFormat?: string + baseUrl: string + step?: DownloadStep + tab?: DownloadTab + tomogram?: { sampling: number; processing: string } + }) { + const expectedUrl = constructDialogUrl(baseUrl, { + config, + fileFormat, + step, + tab, + tomogram, + }) + await this.downloadDialogPage.goTo(expectedUrl.href) + } + + public async goToTomogramDownloadDialogUrl({ + client, + config, + fileFormat, + baseUrl, + step, + tab, + }: { + client: ApolloClient + config?: DownloadConfig + fileFormat?: string + baseUrl: string + step?: DownloadStep + tab?: DownloadTab + }) { + const { data } = await fetchTestSingleRun(client) + const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] + + await this.goToDownloadDialogUrl({ + baseUrl, + config, + fileFormat, + step, + tab, + tomogram: { + sampling: tomogram.voxel_spacing, + processing: tomogram.processing, + }, + }) + } + // #endregion Navigate + + // #region Click + // #endregion Click + + // #region Hover + // #endregion Hover + + // #region Get + // #endregion Get + + // #region Macro + // #endregion Macro + + // #region Validation + public async expectDialogToBeOpen({ + title, + substrings, + }: { + title: string + substrings?: string[] + }) { + const dialog = this.downloadDialogPage.getDialog() + await this.downloadDialogPage.expectDialogToBeVisible(dialog) + await this.downloadDialogPage.expectDialogToHaveTitle(dialog, title) + if (substrings) { + await Promise.all( + substrings.map(async (str) => { + await this.downloadDialogPage.expectSubstringToBeVisible(dialog, str) + }), + ) + } + } + + public async expectDownloadDatasetDialogToShowCorrectContent({ + client, + }: { + client: ApolloClient + }) { + const { data } = await fetchTestSingleDataset(client) + await this.expectDialogToBeOpen({ + title: translations.downloadOptions, + substrings: [`${translations.dataset}: ${data.datasets[0].title}`], + }) + } + + public async expectDownloadRunStepOneToShowCorrectContent({ + client, + }: { + client: ApolloClient + }) { + const { data } = await fetchTestSingleRun(client) + const runName = data.runs[0].name + const datasetName = data.runs[0].dataset.title + await this.expectDialogToBeOpen({ + title: translations.configureDownload, + substrings: [ + `${translations.dataset}: ${datasetName}`, + `${translations.run}: ${runName}`, + ], + }) + } + + public async expectDownloadRunStepTwoToShowCorrectContent({ + client, + }: { + client: ApolloClient + }) { + const { data } = await fetchTestSingleRun(client) + const runName = data.runs[0].name + const datasetName = data.runs[0].dataset.title + await this.expectDialogToBeOpen({ + title: translations.downloadOptions, + substrings: [ + `${translations.dataset}: ${datasetName}`, + `${translations.run}: ${runName}`, + `${translations.annotations}: ${translations.all}`, + ], + }) + } + + public async expectDialogUrlToMatch({ + baseUrl, + config, + fileFormat, + tab, + tomogram, + step, + }: { + baseUrl: string + config?: string + fileFormat?: string + tab?: DownloadTab + tomogram?: { sampling: number; processing: string } + step?: DownloadStep + }) { + const expectedUrl = constructDialogUrl(baseUrl, { + config, + fileFormat, + tab, + tomogram, + step, + }) + await this.downloadDialogPage.page.waitForURL(expectedUrl.href) + } + + public async expectTomogramDialogUrlToMatch({ + baseUrl, + client, + config, + fileFormat, + tab, + step, + }: { + baseUrl: string + client: ApolloClient + config?: string + fileFormat?: string + tab?: DownloadTab + step?: DownloadStep + }) { + const { data } = await fetchTestSingleRun(client) + const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] + + await this.expectDialogUrlToMatch({ + baseUrl, + config, + fileFormat, + tab, + tomogram: { + sampling: tomogram.voxel_spacing, + processing: tomogram.processing, + }, + step, + }) + } + + public async expectDialogToBeOnCorrectTab({ + tab, + tabGroup, + }: { + tab: DownloadTab + tabGroup: DownloadTab[] + }) { + const dialog = this.downloadDialogPage.getDialog() + await this.downloadDialogPage.expectTabSelected({ + dialog, + tab, + isSelected: true, + }) + + await Promise.all( + tabGroup.map(async (t) => { + if (t !== tab) { + await this.downloadDialogPage.expectTabSelected({ + dialog, + tab: t, + isSelected: false, + }) + } + }), + ) + } + + public async expectClipboardToHaveAwsValue() { + const clipboard = await this.downloadDialogPage.getClipboardHandle() + const clipboardValue = await clipboard.jsonValue() + expect(clipboardValue).toContain('aws s3') + expect(clipboardValue).toContain(E2E_CONFIG.datasetId) + } + + public async expectClipboardToHaveCorrectDownloadRunAnnotationsAwsCommand({ + client, + }: { + client: ApolloClient + }) { + const clipboard = await this.downloadDialogPage.getClipboardHandle() + const clipboardValue = await clipboard.jsonValue() + const { data } = await fetchTestSingleRun(client) + const s3Prefix = `${data.runs[0].tomogram_voxel_spacings[0].s3_prefix}Annotations` + const expectedCommand = getAwsCommand({ + s3Path: s3Prefix, + s3Command: 'sync', + }) + expect(clipboardValue).toBe(expectedCommand) + } + + public async expectClipboardToHaveCorrectDownloadTomogramCommand({ + client, + fileFormat, + tab, + }: { + client: ApolloClient + fileFormat?: string + tab: DownloadTab + }) { + const clipboard = await this.downloadDialogPage.getClipboardHandle() + const clipboardValue = await clipboard.jsonValue() + const { data } = await fetchTestSingleRun(client) + + const expectedCommand = getTomogramDownloadCommand({ + data, + fileFormat, + tab, + }) + + expect(clipboardValue).toBe(expectedCommand) + } + + public async expectClipboardToHaveApiValue() { + const clipboard = await this.downloadDialogPage.getClipboardHandle() + const clipboardValue = await clipboard.jsonValue() + expect(clipboardValue).toBe(getDatasetCodeSnippet(+E2E_CONFIG.datasetId)) + } + + public async expectClipboardToHaveCorrectDownloadRunAnnotationsAPICommand({ + client, + }: { + client: ApolloClient + }) { + const clipboard = await this.downloadDialogPage.getClipboardHandle() + const clipboardValue = await clipboard.jsonValue() + + const { data } = await fetchTestSingleRun(client) + const voxelSpacingId = data.runs[0].tomogram_voxel_spacings[0].id + const expectedCommand = getAllTomogramsCodeSnippet(voxelSpacingId) + + expect(clipboardValue).toBe(expectedCommand) + } + // #endregion Validation +} diff --git a/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/downloadDialogPage.ts b/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/downloadDialogPage.ts new file mode 100644 index 000000000..8d962c7ea --- /dev/null +++ b/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/downloadDialogPage.ts @@ -0,0 +1,110 @@ +import { expect, Locator } from '@playwright/test' +import { translations } from 'e2e/constants' +import { BasePage } from 'e2e/pageObjects/basePage' + +import { DownloadConfig, DownloadTab } from 'app/types/download' + +export class DownloadDialogPage extends BasePage { + // #region Navigate + // #endregion Navigate + + // #region Click + public async openDialog(name: string): Promise { + await this.page.getByRole('button', { name }).first().click() + } + + public async clickTab(tab: DownloadTab): Promise { + const dialog = this.getDialog() + await dialog.getByRole('tab', { name: tab }).click() + } + + public async clickCopyButton(): Promise { + const dialog = this.getDialog() + await dialog.getByRole('button', { name: translations.copy }).click() + } + + public async clickCloseButton(): Promise { + const dialog = this.getDialog() + await dialog.getByRole('button', { name: translations.close }).click() + } + + public async clickXButton(): Promise { + const dialog = this.getDialog() + await dialog.locator('button:has(svg)').first().click() + } + + public async clickDialogRadio(name: string): Promise { + const dialog = this.getDialog() + await dialog.getByRole('button', { name }).click() + } + + public async clickNextButton(): Promise { + const dialog = this.getDialog() + await dialog.getByRole('button', { name: translations.next }).click() + } + + public async clickBackButton(): Promise { + const dialog = this.getDialog() + await dialog.getByRole('button', { name: translations.back }).click() + } + // #endregion Click + + // #region Hover + // #endregion Hover + + // #region Get + public getDialog(): Locator { + return this.page.getByRole('dialog') + } + + public async getClipboardHandle() { + return this.page.evaluateHandle(() => navigator.clipboard.readText()) + } + // #endregion Get + + // #region Macro + // #endregion Macro + + // #region Validation + public async expectDialogToBeVisible(dialog: Locator) { + await expect(dialog).toBeVisible() + } + + public async expectDialogToBeHidden() { + const dialog = this.getDialog() + await expect(dialog).toBeHidden() + } + + public async expectDialogToHaveTitle(dialog: Locator, title: string) { + await expect(dialog.getByRole('heading').first()).toHaveText(title) + } + + public async expectSubstringToBeVisible(dialog: Locator, str: string) { + await expect(dialog.getByText(str)).toBeVisible() + } + + public async expectTabSelected({ + dialog, + tab, + isSelected, + }: { + dialog: Locator + tab: DownloadTab + isSelected: boolean + }) { + await expect(dialog.getByRole('tab', { name: tab })).toHaveAttribute( + 'aria-selected', + isSelected ? 'true' : 'false', + ) + } + + public async expectRadioToBeSelected(value: DownloadConfig) { + const dialog = this.getDialog() + await expect(dialog.getByRole('radio', { checked: true })).toHaveValue( + value, + ) + } + // #endregion Validation + // #region Bool + // #endregion Bool +} diff --git a/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/types.ts b/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/types.ts new file mode 100644 index 000000000..cc8a05285 --- /dev/null +++ b/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/types.ts @@ -0,0 +1,14 @@ +/** + * This file contains test-specific types. Remove if not needed. + */ + +import { DownloadTab } from 'app/types/download' + +export const SINGLE_DATASET_DOWNLOAD_TABS = [DownloadTab.AWS, DownloadTab.API] + +export const TOMOGRAM_DOWNLOAD_TABS = [ + DownloadTab.API, + DownloadTab.AWS, + DownloadTab.Curl, + DownloadTab.Download, +] diff --git a/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/utils.ts b/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/utils.ts new file mode 100644 index 000000000..5d0293dd9 --- /dev/null +++ b/frontend/packages/data-portal/e2e/pageObjects/downloadDialog/utils.ts @@ -0,0 +1,110 @@ +import { ApolloClient, NormalizedCacheObject } from '@apollo/client' +import { test } from '@playwright/test' +import { E2E_CONFIG } from 'e2e/constants' + +import { GetRunByIdQuery } from 'app/__generated__/graphql' +import { getTomogramCodeSnippet } from 'app/components/Download/APIDownloadTab' +import { getAwsCommand } from 'app/components/Download/AWSDownloadTab' +import { getCurlCommand } from 'app/components/Download/CurlDownloadTab' +import { QueryParams } from 'app/constants/query' +import { getDatasetById } from 'app/graphql/getDatasetById.server' +import { getRunById } from 'app/graphql/getRunById.server' +import { DownloadTab } from 'app/types/download' + +export function skipClipboardTestsForWebkit(browserName: string) { + // eslint-disable-next-line playwright/no-skipped-test + test.skip( + browserName === 'webkit', + 'Skipping for safari because clipboard permissions are not availabe.', + ) +} + +export function constructDialogUrl( + url: URL | string, + { + tab, + step, + config, + tomogram, + fileFormat, + }: { + tab?: string + step?: string + config?: string + tomogram?: { sampling: number; processing: string } + fileFormat?: string + }, +): URL { + const expectedUrl = new URL(url) + const params = expectedUrl.searchParams + + if (step) { + params.append(QueryParams.DownloadStep, step) + } + + if (config) { + params.append(QueryParams.DownloadConfig, config) + } + + if (tomogram) { + params.append(QueryParams.TomogramSampling, String(tomogram.sampling)) + params.append(QueryParams.TomogramProcessing, tomogram.processing) + } + + if (fileFormat) { + params.append(QueryParams.FileFormat, fileFormat) + } + + if (tab) { + params.append(QueryParams.DownloadTab, tab) + } + + return expectedUrl +} + +export function fetchTestSingleDataset( + client: ApolloClient, +) { + return getDatasetById({ client, id: +E2E_CONFIG.datasetId }) +} + +export function fetchTestSingleRun( + client: ApolloClient, +) { + return getRunById({ client, id: +E2E_CONFIG.runId, annotationsPage: 1 }) +} + +export function getTomogramDownloadCommand({ + data, + fileFormat, + tab, +}: { + data: GetRunByIdQuery + fileFormat?: string + tab: DownloadTab +}): string { + const tomogram = data.runs[0].tomogram_voxel_spacings[0].tomograms[0] + const activeTomogram = + data.runs[0].tomogram_stats[0].tomogram_resolutions.find((tomo) => { + return ( + tomo.voxel_spacing === tomogram.voxel_spacing && + tomo.processing === tomogram.processing + ) + }) + + switch (tab) { + case DownloadTab.API: + return getTomogramCodeSnippet(activeTomogram?.id, fileFormat) + case DownloadTab.AWS: + return getAwsCommand({ + s3Path: activeTomogram?.s3_mrc_scale0, + s3Command: 'cp', + }) + case DownloadTab.Curl: + return getCurlCommand(activeTomogram?.https_mrc_scale0) + case DownloadTab.Download: + return '' + default: + throw new Error('Invalid tab') + } +}