diff --git a/e2e/displacement/delete-all-series.spec.ts b/e2e/displacement/delete-all-series.spec.ts new file mode 100644 index 000000000..6b85fec6b --- /dev/null +++ b/e2e/displacement/delete-all-series.spec.ts @@ -0,0 +1,33 @@ +import { test, expect } from 'e2e/fixtures'; + +test('Displacement: deleting all series clears the AOI list', async ({ + page, +}) => { + await page.goto('/'); + + await page.getByRole('button', { name: 'Geographic Search' }).click(); + await page + .getByRole('menuitem', { name: 'Displacement Displacement' }) + .click(); + + const displacementResults = page.locator('app-timeseries-results-menu'); + const seriesItems = displacementResults.locator('li.point-list-item'); + const initialSeriesCount = await seriesItems.count(); + + await expect(page.getByRole('switch', { name: 'Draw' })).toBeChecked(); + + await page.mouse.click(800, 600); + + await expect(seriesItems).toHaveCount(initialSeriesCount + 1); + + const deleteAllSection = displacementResults.locator( + 'span.parent-checkbox-section', + ); + + await deleteAllSection.hover(); + await displacementResults.locator('button.delete-all-btn2').click(); + + await page.getByRole('button', { name: 'Delete', exact: true }).click(); + + await expect(seriesItems).toHaveCount(0); +}); diff --git a/e2e/displacement/delete-series.spec.ts b/e2e/displacement/delete-series.spec.ts new file mode 100644 index 000000000..0a4b3cd9d --- /dev/null +++ b/e2e/displacement/delete-series.spec.ts @@ -0,0 +1,31 @@ +import { test, expect } from 'e2e/fixtures'; + +test('Displacement: deleting a selected series removes it from the AOI list', async ({ + page, +}) => { + await page.goto('/'); + + await page.getByRole('button', { name: 'Geographic Search' }).click(); + await page + .getByRole('menuitem', { name: 'Displacement Displacement' }) + .click(); + + const displacementResults = page.locator('app-timeseries-results-menu'); + const seriesItems = displacementResults.locator('li.point-list-item'); + const initialSeriesCount = await seriesItems.count(); + + await expect(page.getByRole('switch', { name: 'Draw' })).toBeChecked(); + + await page.mouse.click(800, 600); + + await expect(seriesItems).toHaveCount(initialSeriesCount + 1); + + const newSeriesItem = seriesItems.last(); + + await newSeriesItem.click(); + await expect(newSeriesItem).toHaveClass(/ts-highlighted/); + + await newSeriesItem.getByRole('button').click(); + + await expect(seriesItems).toHaveCount(initialSeriesCount); +}); diff --git a/e2e/displacement/draw-point.spec.ts b/e2e/displacement/draw-point.spec.ts new file mode 100644 index 000000000..a7e96b5ab --- /dev/null +++ b/e2e/displacement/draw-point.spec.ts @@ -0,0 +1,27 @@ +import { test, expect } from 'e2e/fixtures'; + +test('Displacement: drawing a point adds a series to the AOI list', async ({ + page, +}) => { + await page.goto('/'); + + await page.getByRole('button', { name: 'Geographic Search' }).click(); + await page + .getByRole('menuitem', { name: 'Displacement Displacement' }) + .click(); + + const displacementResults = page.locator('app-timeseries-results-menu'); + const initialSeriesCount = await displacementResults + .locator('li.point-list-item') + .count(); + + const drawToggle = page.getByRole('switch', { name: 'Draw' }); + await expect(drawToggle).toBeChecked(); + + await page.mouse.click(800, 600); + + await expect(displacementResults.locator('li.point-list-item')).toHaveCount( + initialSeriesCount + 1, + ); + await expect(displacementResults).toContainText(/Series\s+\d+/); +}); diff --git a/e2e/displacement/flight-direction-toggle.spec.ts b/e2e/displacement/flight-direction-toggle.spec.ts new file mode 100644 index 000000000..b8e9e9232 --- /dev/null +++ b/e2e/displacement/flight-direction-toggle.spec.ts @@ -0,0 +1,41 @@ +import { test, expect } from 'e2e/fixtures'; + +test('Displacement: flight direction toggles between ascending and descending', async ({ + page, +}) => { + await page.goto('/'); + + await page.getByRole('button', { name: 'Geographic Search' }).click(); + await page + .getByRole('menuitem', { name: 'Displacement Displacement' }) + .click(); + + const flightDirectionToggle = page.locator( + 'app-timeseries-chart-flight-direction-toggle', + ); + const flightDirectionButton = + flightDirectionToggle.getByRole('button', { name: /Ascending/i }); + + await flightDirectionButton.focus(); + await page.keyboard.press('Enter'); + + const descendingMenuItem = page.getByRole('menuitem', { + name: 'Descending', + exact: true, + }); + await descendingMenuItem.click(); + + const descendingButton = flightDirectionToggle.getByRole('button', { + name: /Descending/i, + }); + + await descendingButton.focus(); + await page.keyboard.press('Enter'); + + const ascendingMenuItem = page.getByRole('menuitem', { + name: 'Ascending', + exact: true, + }); + await ascendingMenuItem.click(); + await expect(flightDirectionToggle.getByRole('button', { name: /Ascending/i })).toBeVisible(); +}); diff --git a/e2e/displacement/highlight-series.spec.ts b/e2e/displacement/highlight-series.spec.ts new file mode 100644 index 000000000..00bb7dce8 --- /dev/null +++ b/e2e/displacement/highlight-series.spec.ts @@ -0,0 +1,31 @@ +import { test, expect } from 'e2e/fixtures'; + +test('Displacement: selecting a series highlights it in the AOI list', async ({ + page, +}) => { + await page.goto('/'); + + await page.getByRole('button', { name: 'Geographic Search' }).click(); + await page + .getByRole('menuitem', { name: 'Displacement Displacement' }) + .click(); + + const displacementResults = page.locator('app-timeseries-results-menu'); + const initialSeriesCount = await displacementResults + .locator('li.point-list-item') + .count(); + + await expect(page.getByRole('switch', { name: 'Draw' })).toBeChecked(); + + await page.mouse.click(800, 600); + + await expect(displacementResults.locator('li.point-list-item')).toHaveCount( + initialSeriesCount + 1, + ); + + const newSeriesItem = displacementResults.locator('li.point-list-item').last(); + + await newSeriesItem.click(); + + await expect(newSeriesItem).toHaveClass(/ts-highlighted/); +}); diff --git a/e2e/displacement/no-points-selected.spec.ts b/e2e/displacement/no-points-selected.spec.ts new file mode 100644 index 000000000..c8fe43369 --- /dev/null +++ b/e2e/displacement/no-points-selected.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from 'e2e/fixtures'; + +test('Displacement: shows the empty state when no points are selected', async ({ + page, +}) => { + await page.goto('/'); + + await page.getByRole('button', { name: 'Geographic Search' }).click(); + await page + .getByRole('menuitem', { name: 'Displacement Displacement' }) + .click(); + + await expect(page.getByRole('tooltip', { name: 'Click point' })).toBeVisible(); + await expect( + page.locator('app-timeseries-results-menu').locator('li.point-list-item'), + ).toHaveCount(0); +}); diff --git a/e2e/displacement/select-multiple-points.spec.ts b/e2e/displacement/select-multiple-points.spec.ts new file mode 100644 index 000000000..24df8c137 --- /dev/null +++ b/e2e/displacement/select-multiple-points.spec.ts @@ -0,0 +1,27 @@ +import { test, expect } from 'e2e/fixtures'; + +test('Displacement: adding multiple points creates multiple series', async ({ + page, +}) => { + await page.goto('/'); + + await page.getByRole('button', { name: 'Geographic Search' }).click(); + await page + .getByRole('menuitem', { name: 'Displacement Displacement' }) + .click(); + + const displacementResults = page.locator('app-timeseries-results-menu'); + const seriesItems = displacementResults.locator('li.point-list-item'); + const initialSeriesCount = await seriesItems.count(); + + await expect(page.getByRole('switch', { name: 'Draw' })).toBeChecked(); + + await page.mouse.click(800, 600); + await expect(seriesItems).toHaveCount(initialSeriesCount + 1); + + await page.mouse.click(650, 400); + + await expect(seriesItems).toHaveCount(initialSeriesCount + 2); + await expect(displacementResults).toContainText(/Series 1\b/); + await expect(displacementResults).toContainText(/Series 2\b/); +}); diff --git a/e2e/displacement/velocity-layer.spec.ts b/e2e/displacement/velocity-layer.spec.ts new file mode 100644 index 000000000..5cb6f9d1f --- /dev/null +++ b/e2e/displacement/velocity-layer.spec.ts @@ -0,0 +1,23 @@ +import { test, expect } from 'e2e/fixtures'; + +test('Displacement: velocity layer is enabled by default and shows legend and opacity slider', async ({ + page, +}) => { + await page.goto('/'); + + await page.getByRole('button', { name: 'Geographic Search' }).click(); + await page + .getByRole('menuitem', { name: 'Displacement Displacement' }) + .click(); + + const displacementLayers = page.locator('app-displacement-layers'); + const velocityCheckbox = displacementLayers.getByRole('checkbox', { + name: /Velocity 2016-2024/i, + }); + + await expect(velocityCheckbox).toBeChecked(); + await expect(displacementLayers.locator('app-map-legend')).toBeVisible(); + await expect( + displacementLayers.locator('mat-slider input[matSliderThumb]'), + ).toBeVisible(); +}); diff --git a/e2e/profile/dark-mode.spec.ts b/e2e/profile/dark-mode.spec.ts new file mode 100644 index 000000000..84f5dbfc5 --- /dev/null +++ b/e2e/profile/dark-mode.spec.ts @@ -0,0 +1,27 @@ +import { test, expect } from 'e2e/pages/auth.page'; + +test( + 'Profile: Set dark mode', + { tag: '@auth' }, + async ({ loggedInPage }) => { + await loggedInPage.goto('/'); + + await loggedInPage + .getByRole('button', { name: 'automatedtesting_fullaccess' }) + .click(); + await loggedInPage.getByRole('menuitem', { name: 'Preferences' }).click(); + + const preferencesDialog = loggedInPage.getByRole('dialog', { + name: /Preferences for automatedtesting_fullaccess/i, + }); + + await preferencesDialog + .getByRole('combobox', { name: 'Theme', exact: true }) + .click(); + await loggedInPage.getByRole('option', { name: 'Dark' }).click(); + + await expect(loggedInPage.locator('body')).toHaveClass(/theme-dark/); + + await preferencesDialog.getByRole('button', { name: 'Done' }).click(); + }, +); diff --git a/e2e/profile/login-state-sync.spec.ts b/e2e/profile/login-state-sync.spec.ts new file mode 100644 index 000000000..924028677 --- /dev/null +++ b/e2e/profile/login-state-sync.spec.ts @@ -0,0 +1,61 @@ +import { test, expect } from 'e2e/fixtures'; + +test('Profile: login state is synced across instances', async ({ page }) => { + let loggedIn = false; + + await page.route('**appdata**/**/Profile', async (route) => { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + defaultDataset: 'SENTINEL-1', + defaultFilterPresets: { + 'Baseline Search': '', + 'Geographic Search': '', + 'SBAS Search': '', + Displacement: '', + }, + defaultMaxConcurrentDownloads: 3, + hyp3BackendUrl: 'https://hyp3-api.asf.alaska.edu', + hyp3SavedUrls: ['https://hyp3-api.asf.alaska.edu'], + language: 'en', + mapLayer: 'Satellite', + maxResults: 250, + theme: 'light', + }), + }); + }); + + await page.route('**appdata**/info/cookie', async (route) => { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify( + loggedIn + ? { + exp: 2978525424, + 'urs-access-token': + 'eyJhbGciOiJub25lIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNzc4MDExOTc4LCJleHAiOjk5OTk5OTk5OTl9.', + 'urs-groups': [], + 'urs-user-id': 'automatedtesting_fullaccess', + } + : {}, + ), + }); + }); + + await page.goto('/'); + + await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible(); + + loggedIn = true; + + await page.evaluate(() => { + const bc = new BroadcastChannel('asf-vertex'); + bc.postMessage({ event: 'login' }); + bc.close(); + }); + + await expect(page.getByRole('button', { name: 'automatedtesting_fullaccess' })) + .toBeVisible(); +});