From 05bf68ecd254ba92ac135d7f48ca45ea99856764 Mon Sep 17 00:00:00 2001 From: piotrk39 Date: Mon, 16 Dec 2024 13:16:12 +0100 Subject: [PATCH] Add cherry picks to change the base --- tests/e2e/Pages/ChatComponent.ts | 44 +++++++++- tests/e2e/Pages/MapComponent.ts | 36 +++++++++ tests/e2e/testData/testData.enum.ts | 10 +++ ...ChatActionButtonsActiveInNoTrigger.spec.ts | 2 +- .../AllChatComponentsPresentNotrigger.spec.ts | 4 +- .../ChatPredictionButtonActive.spec.ts | 54 +++++++++++++ ...> AlertThresholdIsVisibleInLegend.spec.ts} | 0 ...efaultLegendLayersInTriggeredFlood.spec.ts | 79 ++++++++++++++++++ ...DefaultMarkersVisibleFloodsTrigger.spec.ts | 81 +++++++++++++++++++ .../ExposedPopulationVisibleInTrigger.spec.ts | 69 ++++++++++++++++ .../Map/FloodExtentVisibleInLegend.spec.ts | 60 ++++++++++++++ 11 files changed, 434 insertions(+), 5 deletions(-) create mode 100644 tests/e2e/tests/ChatSection/ChatPredictionButtonActive.spec.ts rename tests/e2e/tests/Map/{NoAlertThresholdIsVisibleInLegend.spec.ts => AlertThresholdIsVisibleInLegend.spec.ts} (100%) create mode 100644 tests/e2e/tests/Map/DefaultLegendLayersInTriggeredFlood.spec.ts create mode 100644 tests/e2e/tests/Map/DefaultMarkersVisibleFloodsTrigger.spec.ts create mode 100644 tests/e2e/tests/Map/ExposedPopulationVisibleInTrigger.spec.ts create mode 100644 tests/e2e/tests/Map/FloodExtentVisibleInLegend.spec.ts diff --git a/tests/e2e/Pages/ChatComponent.ts b/tests/e2e/Pages/ChatComponent.ts index c3479d664..c9ae5d97b 100644 --- a/tests/e2e/Pages/ChatComponent.ts +++ b/tests/e2e/Pages/ChatComponent.ts @@ -80,13 +80,42 @@ class ChatComponent extends DashboardPage { const noTriggerChatDialogue = this.chatDialogue.filter({ hasText: chatDialogueContentNoAlerts, }); - // Assertions await expect(welcomeChatDialogue).toBeVisible(); await expect(noTriggerChatDialogue).toBeVisible(); } - async allChatButtonsArePresent() { + async chatColumnIsVisibleForTriggerState({ + firstName, + lastName, + }: { + firstName: string; + lastName: string; + }) { + // String cleaning to remove tags and replace placeholders with actual values + const cleanedString = chatDialogueWarnLabel.replace(/<\/?strong>/g, ''); + + const date = new Date(); + const formattedDate = format(date, 'EEEE, dd MMMM'); + const formattedTime = format(date, 'HH:mm'); + + const lastModelRunDate = `${formattedDate} ${formattedTime}`; + + // Formatted Strings + const chatDialogueContent = cleanedString + .replace('{{ name }}', `${firstName} ${lastName}`) + .replace('{{lastModelRunDate}}', lastModelRunDate); + + // Locators based on data-testid and filtered by formatted strings + const welcomeChatDialogue = this.chatDialogue.filter({ + hasText: chatDialogueContent, + }); + + // Assertions + await expect(welcomeChatDialogue).toBeVisible(); + } + + async allDefaultButtonsArePresent() { await expect(this.chatAboutButton).toBeVisible(); await expect(this.chatGuideButton).toBeVisible(); await expect(this.exportViewButton).toBeVisible(); @@ -153,6 +182,17 @@ class ChatComponent extends DashboardPage { await expect(newPage).toHaveURL(url); await newPage.close(); } + + async predictionButtonsAreActive() { + const showPredictionButton = this.page.getByRole('button', { + name: 'Show prediction', + }); + const countPredictionButtons = await showPredictionButton.count(); + for (let i = 0; i < countPredictionButtons; i++) { + const predictionButton = showPredictionButton.nth(i); + await expect(predictionButton).toBeEnabled(); + } + } } export default ChatComponent; diff --git a/tests/e2e/Pages/MapComponent.ts b/tests/e2e/Pages/MapComponent.ts index d41dca7c8..e7263b635 100644 --- a/tests/e2e/Pages/MapComponent.ts +++ b/tests/e2e/Pages/MapComponent.ts @@ -16,6 +16,7 @@ class MapComponent extends DashboardPage { readonly layerMenu: Locator; readonly adminBoundry: Locator; readonly layerCheckbox: Locator; + readonly layerRadioButton: Locator; readonly legendHeader: Locator; readonly layerMenuToggle: Locator; readonly redCrossMarker: Locator; @@ -23,6 +24,7 @@ class MapComponent extends DashboardPage { readonly alerThresholdLines: Locator; readonly closeButtonIcon: Locator; readonly layerInfoContent: Locator; + readonly ibfAggregatePane: Locator; constructor(page: Page) { super(page); @@ -45,6 +47,7 @@ class MapComponent extends DashboardPage { this.layerMenu = this.page.getByTestId('layer-menu'); this.adminBoundry = this.page.locator('.leaflet-interactive'); this.layerCheckbox = this.page.getByTestId('matrix-checkbox'); + this.layerRadioButton = this.page.getByTestId('matrix-radio-button'); this.legendHeader = this.page.getByTestId('map-legend-header'); this.layerMenuToggle = this.page.getByTestId('layer-menu-toggle-button'); this.redCrossMarker = this.page.getByAltText('Red Cross branches'); @@ -54,6 +57,9 @@ class MapComponent extends DashboardPage { ); this.closeButtonIcon = this.page.getByTestId('close-matrix-icon'); this.layerInfoContent = this.page.getByTestId('layer-info-content'); + this.ibfAggregatePane = this.page.locator( + '.leaflet-pane.leaflet-ibf-aggregate-pane', + ); } async mapComponentIsVisible() { @@ -160,6 +166,25 @@ class MapComponent extends DashboardPage { } } + async verifyLayerRadioButtonCheckedByName({ + layerName, + }: { + layerName: string; + }) { + const getLayerRow = this.page + .getByTestId('matrix-layer-name') + .filter({ hasText: layerName }); + const layerCheckbox = getLayerRow.locator(this.layerRadioButton); + + // In case of checbox being checked the name attribute should be "checkbox" + const nameAttribute = await layerCheckbox.getAttribute('name'); + const isChecked = nameAttribute === 'radio-button-on-outline'; + + if (!isChecked) { + throw new Error(`Radio button for layer ${layerName} is not checked`); + } + } + async clickInfoButtonByName({ layerName }: { layerName: string }) { await this.page .locator(`ion-item`) @@ -356,5 +381,16 @@ class MapComponent extends DashboardPage { expect(await glofasMarker.count()).toBe(0); } } + + // This method checks that when radio button is checked then the layer is visible in leaflet-ibf-aggregate-pane + // Only one radio button can be checked at a time + // It valdates the functionality not data displayed + async validateAggregatePaneIsNotEmpty() { + const aggregatePaneContent = this.ibfAggregatePane.locator( + '.leaflet-interactive', + ); + const aggregatePaneContentCount = await aggregatePaneContent.count(); + expect(aggregatePaneContentCount).toBeGreaterThan(0); + } } export default MapComponent; diff --git a/tests/e2e/testData/testData.enum.ts b/tests/e2e/testData/testData.enum.ts index 936a1d09d..9e23e6edd 100644 --- a/tests/e2e/testData/testData.enum.ts +++ b/tests/e2e/testData/testData.enum.ts @@ -7,3 +7,13 @@ export enum NoTriggerDataSet { firstName = 'Uganda', lastName = 'Manager', } + +export enum TriggerDataSet { + TriggerScenario = 'trigger', + CountryCode = 'UGA', + CountryName = 'Uganda', + UserMail = 'uganda@redcross.nl', + UserPassword = 'password', + firstName = 'Uganda', + lastName = 'Manager', +} diff --git a/tests/e2e/tests/ChatSection/AllChatActionButtonsActiveInNoTrigger.spec.ts b/tests/e2e/tests/ChatSection/AllChatActionButtonsActiveInNoTrigger.spec.ts index ee9804bac..9d65d0e10 100644 --- a/tests/e2e/tests/ChatSection/AllChatActionButtonsActiveInNoTrigger.spec.ts +++ b/tests/e2e/tests/ChatSection/AllChatActionButtonsActiveInNoTrigger.spec.ts @@ -50,7 +50,7 @@ test( await userState.headerComponentIsVisible({ countryName: NoTriggerDataSet.CountryName, }); - await chat.allChatButtonsArePresent(); + await chat.allDefaultButtonsArePresent(); await chat.clickAndAssertAboutButton(); await chat.clickAndAssertGuideButton(); await chat.clickAndAssertExportViewButton(); diff --git a/tests/e2e/tests/ChatSection/AllChatComponentsPresentNotrigger.spec.ts b/tests/e2e/tests/ChatSection/AllChatComponentsPresentNotrigger.spec.ts index cf96d44cf..f012356aa 100644 --- a/tests/e2e/tests/ChatSection/AllChatComponentsPresentNotrigger.spec.ts +++ b/tests/e2e/tests/ChatSection/AllChatComponentsPresentNotrigger.spec.ts @@ -35,7 +35,7 @@ test.beforeEach(async ({ page }) => { }); test( - qase(5, ' All Chat section elements are present in no-trigger mode'), + qase(5, 'All Chat section elements are present in no-trigger mode'), async ({ page }) => { const dashboard = new DashboardPage(page); const userState = new UserStateComponent(page); @@ -51,6 +51,6 @@ test( firstName: NoTriggerDataSet.firstName, lastName: NoTriggerDataSet.lastName, }); - await chat.allChatButtonsArePresent(); + await chat.allDefaultButtonsArePresent(); }, ); diff --git a/tests/e2e/tests/ChatSection/ChatPredictionButtonActive.spec.ts b/tests/e2e/tests/ChatSection/ChatPredictionButtonActive.spec.ts new file mode 100644 index 000000000..78bcf7fb7 --- /dev/null +++ b/tests/e2e/tests/ChatSection/ChatPredictionButtonActive.spec.ts @@ -0,0 +1,54 @@ +import { test } from '@playwright/test'; +import ChatComponent from 'Pages/ChatComponent'; +import DashboardPage from 'Pages/DashboardPage'; +import UserStateComponent from 'Pages/UserStateComponent'; +import { qase } from 'playwright-qase-reporter'; +import { TriggerDataSet } from 'testData/testData.enum'; + +import { + getAccessToken, + mockFloods, + resetDB, +} from '../../helpers/utility.helper'; +import LoginPage from '../../Pages/LoginPage'; + +let accessToken: string; + +test.beforeEach(async ({ page }) => { + // Login + const loginPage = new LoginPage(page); + + accessToken = await getAccessToken(); + await resetDB(accessToken); + // We should maybe create one mock for all different disaster types for now we can just use floods + await mockFloods( + TriggerDataSet.TriggerScenario, + TriggerDataSet.CountryCode, + accessToken, + ); + + await page.goto('/'); + await loginPage.login(TriggerDataSet.UserMail, TriggerDataSet.UserPassword); +}); + +test( + qase(44, '[Trigger] Show prediction button is clickable'), + async ({ page }) => { + const dashboard = new DashboardPage(page); + const userState = new UserStateComponent(page); + const chat = new ChatComponent(page); + + // Navigate to disaster type the data was mocked for + await dashboard.navigateToFloodDisasterType(); + // Assertions + await userState.headerComponentIsVisible({ + countryName: TriggerDataSet.CountryName, + }); + await chat.chatColumnIsVisibleForTriggerState({ + firstName: TriggerDataSet.firstName, + lastName: TriggerDataSet.lastName, + }); + await chat.allDefaultButtonsArePresent(); + await chat.predictionButtonsAreActive(); + }, +); diff --git a/tests/e2e/tests/Map/NoAlertThresholdIsVisibleInLegend.spec.ts b/tests/e2e/tests/Map/AlertThresholdIsVisibleInLegend.spec.ts similarity index 100% rename from tests/e2e/tests/Map/NoAlertThresholdIsVisibleInLegend.spec.ts rename to tests/e2e/tests/Map/AlertThresholdIsVisibleInLegend.spec.ts diff --git a/tests/e2e/tests/Map/DefaultLegendLayersInTriggeredFlood.spec.ts b/tests/e2e/tests/Map/DefaultLegendLayersInTriggeredFlood.spec.ts new file mode 100644 index 000000000..10998820f --- /dev/null +++ b/tests/e2e/tests/Map/DefaultLegendLayersInTriggeredFlood.spec.ts @@ -0,0 +1,79 @@ +import { test } from '@playwright/test'; +import DashboardPage from 'Pages/DashboardPage'; +import MapComponent from 'Pages/MapComponent'; +import UserStateComponent from 'Pages/UserStateComponent'; +import { qase } from 'playwright-qase-reporter'; +import { TriggerDataSet } from 'testData/testData.enum'; + +import { FloodsScenario } from '../../../../services/API-service/src/scripts/enum/mock-scenario.enum'; +import { + getAccessToken, + mockFloods, + resetDB, +} from '../../helpers/utility.helper'; +import LoginPage from '../../Pages/LoginPage'; + +let accessToken: string; + +test.beforeEach(async ({ page }) => { + // Login + const loginPage = new LoginPage(page); + accessToken = await getAccessToken(); + await resetDB(accessToken); + + // We should maybe create one mock for all different disaster types for now we can just use floods + await mockFloods( + FloodsScenario.Trigger, + TriggerDataSet.CountryCode, + accessToken, + ); + + await page.goto('/'); + await loginPage.login(TriggerDataSet.UserMail, TriggerDataSet.UserPassword); +}); +// https://app.qase.io/project/IBF?previewMode=side&suite=3&tab=&case=37 +test( + qase( + 37, + '[Trigger] Map layer: "Flood extent" and "Exposed population" should be active by default', + ), + async ({ page }) => { + const dashboard = new DashboardPage(page); + const userState = new UserStateComponent(page); + const map = new MapComponent(page); + + // Navigate to disaster type the data was mocked for + await dashboard.navigateToFloodDisasterType(); + // Assertions + await userState.headerComponentIsVisible({ + countryName: TriggerDataSet.CountryName, + }); + // Wait for the page to load + await dashboard.waitForLoaderToDisappear(); + + await map.mapComponentIsVisible(); + + await map.clickLayerMenu(); + await map.isLayerMenuOpen({ layerMenuOpen: true }); + await map.verifyLayerCheckboxCheckedByName({ + layerName: 'Flood extent', + }); + await map.verifyLayerRadioButtonCheckedByName({ + layerName: 'Exposed population', + }); + // Validate legend + await map.isLegendOpen({ legendOpen: true }); + await map.assertLegendElementIsVisible({ + legendComponentName: 'Flood extent', + }); + await map.assertLegendElementIsVisible({ + legendComponentName: 'Exposed population', + }); + // Validatge that the layer checked with radio button is visible on the map in this case 'Exposed population' only one such layer can be checked at a time + await map.validateAggregatePaneIsNotEmpty(); + // Validate rest of the map + // await map.validateLayersAreVisibleByName({ + // layerNames: ['Flood extent'], + // }); + }, +); diff --git a/tests/e2e/tests/Map/DefaultMarkersVisibleFloodsTrigger.spec.ts b/tests/e2e/tests/Map/DefaultMarkersVisibleFloodsTrigger.spec.ts new file mode 100644 index 000000000..2b4f61f67 --- /dev/null +++ b/tests/e2e/tests/Map/DefaultMarkersVisibleFloodsTrigger.spec.ts @@ -0,0 +1,81 @@ +import { test } from '@playwright/test'; +import DashboardPage from 'Pages/DashboardPage'; +import MapComponent from 'Pages/MapComponent'; +import UserStateComponent from 'Pages/UserStateComponent'; +import { qase } from 'playwright-qase-reporter'; +import { TriggerDataSet } from 'testData/testData.enum'; + +import { FloodsScenario } from '../../../../services/API-service/src/scripts/enum/mock-scenario.enum'; +import { + getAccessToken, + mockFloods, + resetDB, +} from '../../helpers/utility.helper'; +import LoginPage from '../../Pages/LoginPage'; + +let accessToken: string; + +test.beforeEach(async ({ page }) => { + // Login + const loginPage = new LoginPage(page); + accessToken = await getAccessToken(); + await resetDB(accessToken); + + // We should maybe create one mock for all different disaster types for now we can just use floods + await mockFloods( + FloodsScenario.Trigger, + TriggerDataSet.CountryCode, + accessToken, + ); + + await page.goto('/'); + await loginPage.login(TriggerDataSet.UserMail, TriggerDataSet.UserPassword); +}); + +test( + qase( + 38, + '[Trigger] At least one red/orange/yellow GloFAS station should be visible', + ), + async ({ page }) => { + const dashboard = new DashboardPage(page); + const userState = new UserStateComponent(page); + const map = new MapComponent(page); + + // Navigate to disaster type the data was mocked for + await dashboard.navigateToFloodDisasterType(); + // Assertions + await userState.headerComponentIsVisible({ + countryName: TriggerDataSet.CountryName, + }); + // Wait for the page to load + await dashboard.waitForLoaderToDisappear(); + + await map.mapComponentIsVisible(); + await map.isLegendOpen({ legendOpen: true }); + await map.isLayerMenuOpen({ layerMenuOpen: false }); + await map.clickLayerMenu(); + await map.verifyLayerCheckboxCheckedByName({ + layerName: 'Glofas stations', + }); + await map.assertLegendElementIsVisible({ + legendComponentName: 'GloFAS No action', + }); + + // At least one red/orange/yellow GloFAS station should be visible by default in trigger mode + await map.gloFASMarkersAreVisibleByWarning({ + glosfasStationStatus: 'glofas-station-max-trigger', + isVisible: true, + }); + // At least one orange GloFAS station should be visible by default in trigger mode + await map.gloFASMarkersAreVisibleByWarning({ + glosfasStationStatus: 'glofas-station-med-trigger', + isVisible: true, + }); + // At least one yellow GloFAS station should be visible by default in trigger mode + await map.gloFASMarkersAreVisibleByWarning({ + glosfasStationStatus: 'glofas-station-min-trigger', + isVisible: true, + }); + }, +); diff --git a/tests/e2e/tests/Map/ExposedPopulationVisibleInTrigger.spec.ts b/tests/e2e/tests/Map/ExposedPopulationVisibleInTrigger.spec.ts new file mode 100644 index 000000000..d03a60671 --- /dev/null +++ b/tests/e2e/tests/Map/ExposedPopulationVisibleInTrigger.spec.ts @@ -0,0 +1,69 @@ +import { test } from '@playwright/test'; +import DashboardPage from 'Pages/DashboardPage'; +import MapComponent from 'Pages/MapComponent'; +import UserStateComponent from 'Pages/UserStateComponent'; +import { qase } from 'playwright-qase-reporter'; +import { TriggerDataSet } from 'testData/testData.enum'; + +import { FloodsScenario } from '../../../../services/API-service/src/scripts/enum/mock-scenario.enum'; +import { + getAccessToken, + mockFloods, + resetDB, +} from '../../helpers/utility.helper'; +import LoginPage from '../../Pages/LoginPage'; + +let accessToken: string; + +test.beforeEach(async ({ page }) => { + // Login + const loginPage = new LoginPage(page); + accessToken = await getAccessToken(); + await resetDB(accessToken); + + // We should maybe create one mock for all different disaster types for now we can just use floods + await mockFloods( + FloodsScenario.Trigger, + TriggerDataSet.CountryCode, + accessToken, + ); + + await page.goto('/'); + await loginPage.login(TriggerDataSet.UserMail, TriggerDataSet.UserPassword); +}); + +test( + qase( + 36, + '[Trigger] Exposed population legend is visible when exposed population layer is active', + ), + async ({ page }) => { + const dashboard = new DashboardPage(page); + const userState = new UserStateComponent(page); + const map = new MapComponent(page); + + // Navigate to disaster type the data was mocked for + await dashboard.navigateToFloodDisasterType(); + // Assertions + await userState.headerComponentIsVisible({ + countryName: TriggerDataSet.CountryName, + }); + // Wait for the page to load + await dashboard.waitForLoaderToDisappear(); + + await map.mapComponentIsVisible(); + await map.isLegendOpen({ legendOpen: true }); + await map.isLayerMenuOpen({ layerMenuOpen: false }); + await map.clickLayerMenu(); + await map.isLayerMenuOpen({ layerMenuOpen: true }); + await map.verifyLayerRadioButtonCheckedByName({ + layerName: 'Exposed population', + }); + await map.assertLegendElementIsVisible({ + legendComponentName: 'Exposed population', + }); + // Exposed population layer should be visible by default in trigger mode + // Validate that the aggregate pane is not empty after the layer is checked and loaded + await map.validateAggregatePaneIsNotEmpty(); + }, +); diff --git a/tests/e2e/tests/Map/FloodExtentVisibleInLegend.spec.ts b/tests/e2e/tests/Map/FloodExtentVisibleInLegend.spec.ts new file mode 100644 index 000000000..1e7257c62 --- /dev/null +++ b/tests/e2e/tests/Map/FloodExtentVisibleInLegend.spec.ts @@ -0,0 +1,60 @@ +import { test } from '@playwright/test'; +import DashboardPage from 'Pages/DashboardPage'; +import MapComponent from 'Pages/MapComponent'; +import UserStateComponent from 'Pages/UserStateComponent'; +import { qase } from 'playwright-qase-reporter'; +import { TriggerDataSet } from 'testData/testData.enum'; + +import { FloodsScenario } from '../../../../services/API-service/src/scripts/enum/mock-scenario.enum'; +import { + getAccessToken, + mockFloods, + resetDB, +} from '../../helpers/utility.helper'; +import LoginPage from '../../Pages/LoginPage'; + +let accessToken: string; + +test.beforeEach(async ({ page }) => { + // Login + const loginPage = new LoginPage(page); + accessToken = await getAccessToken(); + await resetDB(accessToken); + + // We should maybe create one mock for all different disaster types for now we can just use floods + await mockFloods( + FloodsScenario.Trigger, + TriggerDataSet.CountryCode, + accessToken, + ); + + await page.goto('/'); + await loginPage.login(TriggerDataSet.UserMail, TriggerDataSet.UserPassword); +}); +// https://app.qase.io/project/IBF?previewMode=side&suite=3&tab=&case=35 +test( + qase( + 35, + '[Trigger] Flood extent legend is visible when flood extent layer is active', + ), + async ({ page }) => { + const dashboard = new DashboardPage(page); + const userState = new UserStateComponent(page); + const map = new MapComponent(page); + + // Navigate to disaster type the data was mocked for + await dashboard.navigateToFloodDisasterType(); + // Assertions + await userState.headerComponentIsVisible({ + countryName: TriggerDataSet.CountryName, + }); + // Wait for the page to load + await dashboard.waitForLoaderToDisappear(); + + await map.mapComponentIsVisible(); + await map.isLegendOpen({ legendOpen: true }); + await map.assertLegendElementIsVisible({ + legendComponentName: 'Flood extent', + }); + }, +);