From 33c1881315021b93869483bb1f51b6eeb82d4ec9 Mon Sep 17 00:00:00 2001 From: Vicente Date: Thu, 30 Jan 2025 17:50:34 +0100 Subject: [PATCH 01/13] first commit --- test/e2e/cypress/e2e/clusters_overview.cy.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e/cypress/e2e/clusters_overview.cy.js b/test/e2e/cypress/e2e/clusters_overview.cy.js index f7beb84a6c..8dcc4a9dd4 100644 --- a/test/e2e/cypress/e2e/clusters_overview.cy.js +++ b/test/e2e/cypress/e2e/clusters_overview.cy.js @@ -6,6 +6,7 @@ import { unhealthyClusterScenario, } from '../fixtures/clusters-overview/available_clusters'; + const clusterIdByName = (clusterName) => availableClusters.find(({ name }) => name === clusterName).id; From 26576e98e06966eff81c0474bcdb4b865b189ef9 Mon Sep 17 00:00:00 2001 From: Vicente Date: Fri, 31 Jan 2025 15:35:19 +0100 Subject: [PATCH 02/13] wip clusters overview --- test/e2e/cypress/e2e/clusters_overview.cy.js | 50 ++++--------------- .../pageObject/clusters-overview-po.js | 49 ++++++++++++++++++ 2 files changed, 59 insertions(+), 40 deletions(-) create mode 100644 test/e2e/cypress/pageObject/clusters-overview-po.js diff --git a/test/e2e/cypress/e2e/clusters_overview.cy.js b/test/e2e/cypress/e2e/clusters_overview.cy.js index 8dcc4a9dd4..d08b98592c 100644 --- a/test/e2e/cypress/e2e/clusters_overview.cy.js +++ b/test/e2e/cypress/e2e/clusters_overview.cy.js @@ -1,3 +1,4 @@ +import * as clustersOverviewPage from '../pageObject/clusters-overview-po.js'; import { createUserRequestFactory } from '@lib/test-utils/factories'; import { @@ -6,7 +7,6 @@ import { unhealthyClusterScenario, } from '../fixtures/clusters-overview/available_clusters'; - const clusterIdByName = (clusterName) => availableClusters.find(({ name }) => name === clusterName).id; @@ -17,54 +17,24 @@ const clusterTags = { }; context('Clusters Overview', () => { - before(() => { - cy.preloadTestData(); - cy.visit('/clusters'); - cy.url().should('include', '/clusters'); + // before(() => clustersOverviewPage.preloadTestData()); + + beforeEach(() => { + clustersOverviewPage.visit(); + clustersOverviewPage.validateUrl(); }); describe('Registered Clusters should be available in the overview', () => { it('should show all of the registered clusters', () => { - cy.get('.tn-clustername') - .its('length') - .should('eq', availableClusters.length); + clustersOverviewPage.allRegisteredClustersAreDisplayed(); }); it('should have 1 pages', () => { - cy.get(`[data-testid="pagination"]`).should('include.text', '1'); - cy.get(`[data-testid="pagination"]`).should('not.include.text', '2'); + clustersOverviewPage.paginationButtonsAreDisabled(); }); - it('should show the expected clusters data', () => { - cy.get('.container').eq(0).as('clustersTable'); - availableClusters.forEach((cluster, index) => { - cy.get('@clustersTable') - .find('tr') - .eq(index + 1) - .find('td') - .as('clusterRow'); - - cy.get('@clustersTable') - .contains('th', 'Name') - .invoke('index') - .then((i) => { - cy.get('@clusterRow').eq(i).should('contain', cluster.name); - }); - - cy.get('@clustersTable') - .contains('th', 'SID') - .invoke('index') - .then((i) => { - cy.get('@clusterRow').eq(i).should('contain', cluster.sid); - }); - - cy.get('@clustersTable') - .contains('th', 'Type') - .invoke('index') - .then((i) => { - cy.get('@clusterRow').eq(i).should('contain', cluster.type); - }); - }); + it.only('should show the expected clusters data', () => { + clustersOverviewPage.clustersDataIsDisplayedAsExpected(); }); describe('Unnamed cluster', () => { diff --git a/test/e2e/cypress/pageObject/clusters-overview-po.js b/test/e2e/cypress/pageObject/clusters-overview-po.js new file mode 100644 index 0000000000..b61755c9e6 --- /dev/null +++ b/test/e2e/cypress/pageObject/clusters-overview-po.js @@ -0,0 +1,49 @@ +import { createUserRequestFactory } from '@lib/test-utils/factories'; +import { + availableClusters, + healthyClusterScenario, + unhealthyClusterScenario, +} from '../fixtures/clusters-overview/available_clusters'; + +export * from './base-po.js'; +import * as basePage from './base-po.js'; + +const url = '/clusters'; +const clustersEndpoint = '/api/v2/clusters'; +const clustersEndpointAlias = 'clustersEndpoint'; + +//Selectors +const clusterNames = '.tn-clustername'; +const paginationNavigationButtons = 'div[class*="bg-gray-50"] ul button'; +const tableRows = 'tbody tr'; + +export const visit = () => basePage.visit(url); + +export const validateUrl = () => basePage.validateUrl(url); + +export const interceptClustersEndpoint = () => { + cy.intercept(clustersEndpoint).as(clustersEndpointAlias); +}; + +export const waitForClustersEndpoint = () => { + return basePage.waitForRequest(clustersEndpointAlias); +}; + +export const allRegisteredClustersAreDisplayed = () => { + cy.get(clusterNames).its('length').should('eq', availableClusters.length); +}; + +export const paginationButtonsAreDisabled = () => { + cy.get(paginationNavigationButtons).should('be.disabled'); +}; + +export const clustersDataIsDisplayedAsExpected = () => { + interceptClustersEndpoint(); + waitForClustersEndpoint(); + cy.get(tableRows).each(($row, index) => { + const cluster = availableClusters[index]; + cy.wrap($row).find('td').eq(1).should('have.text', cluster.name); + cy.wrap($row).find('td').eq(2).should('have.text', cluster.sid); + cy.wrap($row).find('td').eq(5).should('have.text', cluster.type); + }); +}; From 46899de61837170b5e01c45cd396083e93e0562d Mon Sep 17 00:00:00 2001 From: Vicente Date: Fri, 31 Jan 2025 15:38:14 +0100 Subject: [PATCH 03/13] wip clusters overview --- test/e2e/cypress/e2e/clusters_overview.cy.js | 5 +++-- .../e2e/cypress/pageObject/clusters-overview-po.js | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/test/e2e/cypress/e2e/clusters_overview.cy.js b/test/e2e/cypress/e2e/clusters_overview.cy.js index d08b98592c..49d157bd21 100644 --- a/test/e2e/cypress/e2e/clusters_overview.cy.js +++ b/test/e2e/cypress/e2e/clusters_overview.cy.js @@ -17,9 +17,10 @@ const clusterTags = { }; context('Clusters Overview', () => { - // before(() => clustersOverviewPage.preloadTestData()); + before(() => clustersOverviewPage.preloadTestData()); beforeEach(() => { + clustersOverviewPage.interceptClustersEndpoint(); clustersOverviewPage.visit(); clustersOverviewPage.validateUrl(); }); @@ -33,7 +34,7 @@ context('Clusters Overview', () => { clustersOverviewPage.paginationButtonsAreDisabled(); }); - it.only('should show the expected clusters data', () => { + it('should show the expected clusters data', () => { clustersOverviewPage.clustersDataIsDisplayedAsExpected(); }); diff --git a/test/e2e/cypress/pageObject/clusters-overview-po.js b/test/e2e/cypress/pageObject/clusters-overview-po.js index b61755c9e6..52c3f7c793 100644 --- a/test/e2e/cypress/pageObject/clusters-overview-po.js +++ b/test/e2e/cypress/pageObject/clusters-overview-po.js @@ -38,12 +38,12 @@ export const paginationButtonsAreDisabled = () => { }; export const clustersDataIsDisplayedAsExpected = () => { - interceptClustersEndpoint(); - waitForClustersEndpoint(); - cy.get(tableRows).each(($row, index) => { - const cluster = availableClusters[index]; - cy.wrap($row).find('td').eq(1).should('have.text', cluster.name); - cy.wrap($row).find('td').eq(2).should('have.text', cluster.sid); - cy.wrap($row).find('td').eq(5).should('have.text', cluster.type); + return waitForClustersEndpoint().then(() => { + return cy.get(tableRows).each(($row, index) => { + const cluster = availableClusters[index]; + cy.wrap($row).find('td').eq(1).should('have.text', cluster.name); + cy.wrap($row).find('td').eq(2).should('have.text', cluster.sid); + return cy.wrap($row).find('td').eq(5).should('have.text', cluster.type); + }); }); }; From cea11627b1a9205d9155775e4aea05f67493b5c0 Mon Sep 17 00:00:00 2001 From: Vicente Ruiz Date: Mon, 3 Feb 2025 16:23:01 +0100 Subject: [PATCH 04/13] finish deregistration tests --- test/e2e/cypress/e2e/clusters_overview.cy.js | 75 +++----- test/e2e/cypress/pageObject/base-po.js | 4 +- .../pageObject/clusters-overview-po.js | 177 +++++++++++++++++- 3 files changed, 202 insertions(+), 54 deletions(-) diff --git a/test/e2e/cypress/e2e/clusters_overview.cy.js b/test/e2e/cypress/e2e/clusters_overview.cy.js index 49d157bd21..4cdd282e74 100644 --- a/test/e2e/cypress/e2e/clusters_overview.cy.js +++ b/test/e2e/cypress/e2e/clusters_overview.cy.js @@ -39,19 +39,13 @@ context('Clusters Overview', () => { }); describe('Unnamed cluster', () => { - before(() => { - cy.loadScenario('cluster-unnamed'); - }); - - // Restore cluster name - after(() => { - cy.loadScenario('cluster-4-SOK'); - }); + before(() => clustersOverviewPage.loadScenario('cluster-unnamed')); it('Unnamed clusters should use the ID as details page link', () => { - const clusterID = clusterIdByName('hana_cluster_1'); - cy.get(`a:contains(${clusterID})`).should('be.visible'); + clustersOverviewPage.clusterNameLinkIsDisplayedAsId('hana_cluster_1'); }); + + after(() => clustersOverviewPage.restoreClusterName()); }); // eslint-disable-next-line mocha/no-skipped-tests @@ -111,53 +105,36 @@ context('Clusters Overview', () => { }); describe('Clusters Tagging', () => { - before(() => { - cy.removeTagsFromView(); + beforeEach(() => { + clustersOverviewPage.restoreClusterName(); + clustersOverviewPage.apiRemoveAllTags(); }); - const clustersByMatchingPattern = (pattern) => (clusterName) => - clusterName.includes(pattern); - const taggingRules = [ - ['hana_cluster_1', clusterTags.hana_cluster_1], - ['hana_cluster_2', clusterTags.hana_cluster_2], - ['hana_cluster_3', clusterTags.hana_cluster_3], - ]; - - taggingRules.forEach(([pattern, tag]) => { - describe(`Add tag '${tag}' to all clusters with '${pattern}' in the cluster name`, () => { - availableClusters - .map(({ name }) => name) - .filter(clustersByMatchingPattern(pattern)) - .forEach((clusterName) => { - it(`should tag cluster '${clusterName}'`, () => { - cy.addTagByColumnValue(clusterName, tag); - }); - }); - }); + + it('should tag each cluster with the corresponding tag', () => { + clustersOverviewPage.setClusterTags(); + clustersOverviewPage.eachClusterTagsIsCorrectlyDisplayed(); }); + + after(() => clustersOverviewPage.apiRemoveAllTags()); }); describe('Deregistration', () => { - const hanaCluster1 = { - name: 'hana_cluster_1', - hosts: [ - '13e8c25c-3180-5a9a-95c8-51ec38e50cfc', - '0a055c90-4cb6-54ce-ac9c-ae3fedaf40d4', - ], - }; - - it(`should not display '${hanaCluster1.name}' after deregistering all its nodes`, () => { - cy.deregisterHost(hanaCluster1.hosts[0]); - cy.deregisterHost(hanaCluster1.hosts[1]); - cy.contains(hanaCluster1.name).should('not.exist'); + before(() => { + clustersOverviewPage.apiSetTagsHanaCluster1(); + clustersOverviewPage.deregisterAllClusterHosts(); }); - it(`should show cluster ${hanaCluster1.name} after registering it again with the previous tags`, () => { - cy.loadScenario(`cluster-${hanaCluster1.name}-restore`); - cy.contains(hanaCluster1.name).should('exist'); - cy.contains('tr', hanaCluster1.name).within(() => { - cy.contains(clusterTags[hanaCluster1.name]).should('exist'); - }); + it(`should not display '${clustersOverviewPage.hanaCluster1.name}' after deregistering all its nodes`, () => { + clustersOverviewPage.clusterIsNotDisplayedWhenNodesAreDeregistered(); + }); + + it(`should show cluster '${clustersOverviewPage.hanaCluster1.name}' after registering it again with the previous tags`, () => { + clustersOverviewPage.restoreClusterHosts(); + clustersOverviewPage.clusterNameIsDisplayed(); + clustersOverviewPage.hanaCluster1TagsAreDisplayed(); }); + + after(() => clustersOverviewPage.apiRemoveAllTags()); }); describe('Forbidden action', () => { diff --git a/test/e2e/cypress/pageObject/base-po.js b/test/e2e/cypress/pageObject/base-po.js index 424848e1ee..d669a7010a 100644 --- a/test/e2e/cypress/pageObject/base-po.js +++ b/test/e2e/cypress/pageObject/base-po.js @@ -170,12 +170,12 @@ export const preloadTestData = () => { * scenario is sent in the second time. */ isTestDataLoaded().then((isLoaded) => { - if (!isLoaded) cy.loadScenario('healthy-27-node-SAP-cluster'); + if (!isLoaded) loadScenario('healthy-27-node-SAP-cluster'); }); loadScenario('healthy-27-node-SAP-cluster'); }; -const loadScenario = (scenario) => { +export const loadScenario = (scenario) => { const [projectRoot, photofinishBinary, webAPIHost, webAPIPort] = [ Cypress.env('project_root'), Cypress.env('photofinish_binary'), diff --git a/test/e2e/cypress/pageObject/clusters-overview-po.js b/test/e2e/cypress/pageObject/clusters-overview-po.js index 52c3f7c793..3e4bc574f6 100644 --- a/test/e2e/cypress/pageObject/clusters-overview-po.js +++ b/test/e2e/cypress/pageObject/clusters-overview-po.js @@ -1,3 +1,6 @@ +export * from './base-po.js'; +import * as basePage from './base-po.js'; + import { createUserRequestFactory } from '@lib/test-utils/factories'; import { availableClusters, @@ -5,9 +8,6 @@ import { unhealthyClusterScenario, } from '../fixtures/clusters-overview/available_clusters'; -export * from './base-po.js'; -import * as basePage from './base-po.js'; - const url = '/clusters'; const clustersEndpoint = '/api/v2/clusters'; const clustersEndpointAlias = 'clustersEndpoint'; @@ -16,6 +16,29 @@ const clustersEndpointAlias = 'clustersEndpoint'; const clusterNames = '.tn-clustername'; const paginationNavigationButtons = 'div[class*="bg-gray-50"] ul button'; const tableRows = 'tbody tr'; +const rowCells = 'td'; +const addTagButtons = 'span span:contains("Add Tag")'; + +//Test data +export const hanaCluster1 = { + name: 'hana_cluster_1', + hosts: [ + '13e8c25c-3180-5a9a-95c8-51ec38e50cfc', + '0a055c90-4cb6-54ce-ac9c-ae3fedaf40d4', + ], +}; + +const clusterTags = { + hana_cluster_1: 'env1', + hana_cluster_2: 'env2', + hana_cluster_3: 'env3', +}; + +const taggingRules = [ + ['hana_cluster_1', clusterTags.hana_cluster_1], + ['hana_cluster_2', clusterTags.hana_cluster_2], + ['hana_cluster_3', clusterTags.hana_cluster_3], +]; export const visit = () => basePage.visit(url); @@ -47,3 +70,151 @@ export const clustersDataIsDisplayedAsExpected = () => { }); }); }; + +export const restoreClusterName = () => basePage.loadScenario('cluster-4-SOK'); + +const clusterIdByName = (clusterName) => + availableClusters.find(({ name }) => name === clusterName).id; + +export const clusterNameLinkIsDisplayedAsId = (clusterName) => { + const clusterID = clusterIdByName(clusterName); + return waitForClustersEndpoint().then(() => + cy.get(tableRows).eq(8).find(rowCells).eq(1).should('have.text', clusterID) + ); +}; + +export const apiRemoveTagByClusterId = (clusterId, tagId) => { + return basePage.apiLogin().then(({ accessToken }) => + cy.request({ + url: `api/v1/clusters/${clusterId}/tags/${tagId}`, + method: 'DELETE', + auth: { bearer: accessToken }, + }) + ); +}; + +const apiGetClusters = () => { + return basePage.apiLogin().then(({ accessToken }) => { + const url = '/api/v2/clusters/'; + return cy + .request({ + method: 'GET', + url: url, + auth: { + bearer: accessToken, + }, + }) + .then((response) => response); + }); +}; + +export const apiRemoveAllTags = () => { + apiGetClusters().then((response) => { + const clusterTags = getClusterTags(response.body); + Object.entries(clusterTags).forEach(([clusterId, tags]) => { + tags.forEach((tag) => apiRemoveTagByClusterId(clusterId, tag)); + }); + }); + return basePage.refresh(); +}; + +const getClusterTags = (jsonData) => { + const clusterTags = {}; + jsonData.forEach((cluster) => { + if (cluster.tags && cluster.tags.length > 0) { + clusterTags[cluster.id] = cluster.tags.map((tag) => tag.value); + } + }); + + return clusterTags; +}; + +const addTagByColumnValue = (columnValue, tagValue) => { + cy.get(`td:contains(${columnValue})`) + .parents('tr') + .within(() => { + cy.get(addTagButtons).type(`${tagValue}{enter}`); + }); +}; + +export const setClusterTags = () => { + taggingRules.forEach(([clusterName, tag]) => { + addTagByColumnValue(clusterName, tag); + }); +}; + +export const eachClusterTagsIsCorrectlyDisplayed = () => { + taggingRules.forEach(([tag]) => + cy.get(`span span:contains(${tag})`).should('be.visible') + ); +}; + +export const deregisterHost = (hostId) => { + const [webAPIHost, webAPIPort] = [ + Cypress.env('web_api_host'), + Cypress.env('web_api_port'), + ]; + + const headers = { + 'Content-Type': 'application/json;charset=UTF-8', + }; + + return basePage.apiLogin().then(({ accessToken }) => { + const url = `http://${webAPIHost}:${webAPIPort}/api/v1/hosts/${hostId}`; + cy.request({ + method: 'DELETE', + url: url, + headers: headers, + auth: { + bearer: accessToken, + }, + }); + }); +}; + +export const deregisterAllClusterHosts = () => { + hanaCluster1.hosts.forEach((hostId) => deregisterHost(hostId)); +}; + +export const restoreClusterHosts = () => + basePage.loadScenario(`cluster-${hanaCluster1.name}-restore`); + +export const clusterIsNotDisplayedWhenNodesAreDeregistered = () => + cy.get(`span span:contains("${hanaCluster1.name}")`).should('not.exist'); + +export const clusterNameIsDisplayed = () => { + cy.get(`span span:contains("${hanaCluster1.name}")`).should('be.visible'); +}; + +const apiSetTag = (clusterName, tag) => { + const [webAPIHost, webAPIPort] = [ + Cypress.env('web_api_host'), + Cypress.env('web_api_port'), + ]; + const clusterID = clusterIdByName(clusterName); + return basePage.apiLogin().then(({ accessToken }) => + cy.request({ + url: `http://${webAPIHost}:${webAPIPort}/api/v1/clusters/${clusterID}/tags/`, + method: 'POST', + auth: { bearer: accessToken }, + body: { value: tag }, + }) + ); +}; + +export const apiSetTagsHanaCluster1 = () => { + const tagsForCluster1 = taggingRules + .filter(([cluster]) => cluster === 'hana_cluster_1') // Filter for hana_cluster_1 + .map(([, tag]) => tag); // Extract the tag + tagsForCluster1.forEach((tag) => apiSetTag('hana_cluster_1', tag)); +}; + +export const hanaCluster1TagsAreDisplayed = () => { + return cy + .get(`tr:contains("${hanaCluster1.name}")`) + .within(() => + cy + .get(`span span:contains("${clusterTags[hanaCluster1.name]}")`) + .should('be.visible') + ); +}; From 0232a322917ef08b8ddd8e2b9c3700988f65a6ca Mon Sep 17 00:00:00 2001 From: Vicente Ruiz Date: Mon, 3 Feb 2025 18:04:01 +0100 Subject: [PATCH 05/13] finish tests forbidden actions --- test/e2e/cypress/e2e/clusters_overview.cy.js | 59 ++++++------------- test/e2e/cypress/pageObject/base-po.js | 26 ++++++++ .../pageObject/clusters-overview-po.js | 44 +++++++++++++- 3 files changed, 86 insertions(+), 43 deletions(-) diff --git a/test/e2e/cypress/e2e/clusters_overview.cy.js b/test/e2e/cypress/e2e/clusters_overview.cy.js index 4cdd282e74..a5b0ef3d87 100644 --- a/test/e2e/cypress/e2e/clusters_overview.cy.js +++ b/test/e2e/cypress/e2e/clusters_overview.cy.js @@ -1,5 +1,4 @@ import * as clustersOverviewPage from '../pageObject/clusters-overview-po.js'; -import { createUserRequestFactory } from '@lib/test-utils/factories'; import { availableClusters, @@ -10,12 +9,6 @@ import { const clusterIdByName = (clusterName) => availableClusters.find(({ name }) => name === clusterName).id; -const clusterTags = { - hana_cluster_1: 'env1', - hana_cluster_2: 'env2', - hana_cluster_3: 'env3', -}; - context('Clusters Overview', () => { before(() => clustersOverviewPage.preloadTestData()); @@ -138,47 +131,31 @@ context('Clusters Overview', () => { }); describe('Forbidden action', () => { - beforeEach(() => { - cy.deleteAllUsers(); - cy.logout(); - const user = createUserRequestFactory.build({ - password, - password_confirmation: password, + describe('Tag operations', () => { + beforeEach(() => { + clustersOverviewPage.apiSetTagsHanaCluster1(); + clustersOverviewPage.apiDeleteAllUsers(); }); - cy.wrap(user).as('user'); - }); - - const password = 'password'; - describe('Tag operations', () => { it('should prevent a tag update when the user abilities are not compliant', () => { - cy.get('@user').then((user) => { - cy.createUserWithAbilities(user, []); - cy.login(user.username, password); - }); - - cy.visit('/clusters'); - - cy.contains('span', 'Add Tag').should('have.class', 'opacity-50'); - cy.get('[data-test-id="tag-env1"]').should('have.class', 'opacity-50'); + clustersOverviewPage.logout(); + clustersOverviewPage.createUserWithoutAbilities(); + clustersOverviewPage.loginWithoutTagAbilities(); + clustersOverviewPage.visit(); + clustersOverviewPage.addTagButtonsAreDisabled(); + clustersOverviewPage.removeTagButtonIsDisabled(); }); it('should allow a tag update when the user abilities are compliant', () => { - cy.get('@user').then((user) => { - cy.createUserWithAbilities(user, [ - { name: 'all', resource: 'cluster_tags' }, - ]); - cy.login(user.username, password); - }); - - cy.visit('/clusters'); - - cy.contains('span', 'Add Tag').should('not.have.class', 'opacity-50'); - cy.get('[data-test-id="tag-env1"]').should( - 'not.have.class', - 'opacity-50' - ); + clustersOverviewPage.logout(); + clustersOverviewPage.createUserWithClusterTagsAbilities(); + clustersOverviewPage.loginWithTagAbilities(); + clustersOverviewPage.visit(); + clustersOverviewPage.addTagButtonsAreNotsDisabled(); + clustersOverviewPage.removeTagButtonIsEnabled(); }); + + after(() => clustersOverviewPage.apiRemoveAllTags()); }); }); }); diff --git a/test/e2e/cypress/pageObject/base-po.js b/test/e2e/cypress/pageObject/base-po.js index d669a7010a..af754be74c 100644 --- a/test/e2e/cypress/pageObject/base-po.js +++ b/test/e2e/cypress/pageObject/base-po.js @@ -204,3 +204,29 @@ const isTestDataLoaded = () => }) .then(({ body }) => body.length !== 0) ); + +export const createUserWithAbilities = (payload, abilities) => + apiLogin().then(({ accessToken }) => + cy + .request({ + url: '/api/v1/abilities', + method: 'GET', + auth: { bearer: accessToken }, + body: {}, + }) + .then(({ body }) => { + const abilitiesWithID = abilities.map((ability) => ({ + ...body.find( + ({ name, resource }) => + ability.name === name && ability.resource === resource + ), + })); + + cy.request({ + url: '/api/v1/users', + method: 'POST', + auth: { bearer: accessToken }, + body: { ...payload, abilities: abilitiesWithID }, + }); + }) + ); diff --git a/test/e2e/cypress/pageObject/clusters-overview-po.js b/test/e2e/cypress/pageObject/clusters-overview-po.js index 3e4bc574f6..058ab157fd 100644 --- a/test/e2e/cypress/pageObject/clusters-overview-po.js +++ b/test/e2e/cypress/pageObject/clusters-overview-po.js @@ -18,8 +18,16 @@ const paginationNavigationButtons = 'div[class*="bg-gray-50"] ul button'; const tableRows = 'tbody tr'; const rowCells = 'td'; const addTagButtons = 'span span:contains("Add Tag")'; +const removeEnv1TagButton = 'span span:contains("env1") span'; //Test data +const password = 'password'; + +const user = createUserRequestFactory.build({ + password, + password_confirmation: password, +}); + export const hanaCluster1 = { name: 'hana_cluster_1', hosts: [ @@ -204,8 +212,8 @@ const apiSetTag = (clusterName, tag) => { export const apiSetTagsHanaCluster1 = () => { const tagsForCluster1 = taggingRules - .filter(([cluster]) => cluster === 'hana_cluster_1') // Filter for hana_cluster_1 - .map(([, tag]) => tag); // Extract the tag + .filter(([cluster]) => cluster === 'hana_cluster_1') + .map(([, tag]) => tag); tagsForCluster1.forEach((tag) => apiSetTag('hana_cluster_1', tag)); }; @@ -218,3 +226,35 @@ export const hanaCluster1TagsAreDisplayed = () => { .should('be.visible') ); }; + +export const createUserWithoutAbilities = () => { + return basePage.createUserWithAbilities(user, []); +}; + +export const createUserWithClusterTagsAbilities = () => { + return basePage.createUserWithAbilities(user, [ + { name: 'all', resource: 'cluster_tags' }, + ]); +}; + +export const loginWithoutTagAbilities = () => + basePage.apiLoginAndCreateSession(user.username, password); + +export const loginWithTagAbilities = () => + basePage.apiLoginAndCreateSession(user.username, password); + +export const addTagButtonsAreDisabled = () => { + cy.get(addTagButtons).should('have.class', 'opacity-50'); +}; + +export const addTagButtonsAreNotsDisabled = () => { + cy.get(addTagButtons).should('not.have.class', 'opacity-50'); +}; + +export const removeTagButtonIsDisabled = () => { + cy.get(removeEnv1TagButton).should('have.class', 'opacity-50'); +}; + +export const removeTagButtonIsEnabled = () => { + cy.get(removeEnv1TagButton).should('not.have.class', 'opacity-50'); +}; From caaef65ba550ddf4f3aaeb228316d80011a0140c Mon Sep 17 00:00:00 2001 From: Vicente Ruiz Date: Tue, 4 Feb 2025 13:26:46 +0100 Subject: [PATCH 06/13] finish, pending final touches --- test/e2e/cypress/e2e/clusters_overview.cy.js | 60 +++--------- .../pageObject/clusters-overview-po.js | 98 +++++++++++++++++++ 2 files changed, 109 insertions(+), 49 deletions(-) diff --git a/test/e2e/cypress/e2e/clusters_overview.cy.js b/test/e2e/cypress/e2e/clusters_overview.cy.js index a5b0ef3d87..753fd7d41a 100644 --- a/test/e2e/cypress/e2e/clusters_overview.cy.js +++ b/test/e2e/cypress/e2e/clusters_overview.cy.js @@ -1,14 +1,5 @@ import * as clustersOverviewPage from '../pageObject/clusters-overview-po.js'; -import { - availableClusters, - healthyClusterScenario, - unhealthyClusterScenario, -} from '../fixtures/clusters-overview/available_clusters'; - -const clusterIdByName = (clusterName) => - availableClusters.find(({ name }) => name === clusterName).id; - context('Clusters Overview', () => { before(() => clustersOverviewPage.preloadTestData()); @@ -44,55 +35,26 @@ context('Clusters Overview', () => { // eslint-disable-next-line mocha/no-skipped-tests describe.skip('Health status for each cluster is correct', () => { before(() => { - cy.selectChecks( - clusterIdByName(healthyClusterScenario.clusterName), - healthyClusterScenario.checks - ); + clustersOverviewPage.selectChecksForHealthyCluster(); // wip: set expected results - cy.requestChecksExecution( - clusterIdByName(healthyClusterScenario.clusterName) - ); - - cy.selectChecks( - clusterIdByName(unhealthyClusterScenario.clusterName), - healthyClusterScenario.checks - ); + clustersOverviewPage.requestChecksForHealthyCluster(); + + clustersOverviewPage.selectChecksForUnhealthyCluster(); // wip: set expected results - cy.requestChecksExecution( - clusterIdByName(unhealthyClusterScenario.clusterName) - ); + clustersOverviewPage.requestChecksForUnhealthyCluster(); }); after(() => { - cy.selectChecks( - clusterIdByName(healthyClusterScenario.clusterName), - [] - ); - - cy.selectChecks( - clusterIdByName(unhealthyClusterScenario.clusterName), - [] - ); + clustersOverviewPage.removeHealthyClusterChecks(); + clustersOverviewPage.removeUnhealthyClusterChecks(); }); - it(`should have ${healthyClusterScenario.clusterName} displaying healthy state`, () => { - cy.get('td') - .contains(healthyClusterScenario.clusterName) - .parent() - .parent() - .prev() - .get('div > svg') - .should('have.class', 'fill-jungle-green-500'); + it(`should have ${clustersOverviewPage.healthyClusterName} displaying healthy state`, () => { + clustersOverviewPage.healthyClusterNameDisplaysHealthyState(); }); - it(`should have ${unhealthyClusterScenario.clusterName} displaying unhealthy state`, () => { - cy.get('td') - .contains(unhealthyClusterScenario.clusterName) - .parent() - .parent() - .prev() - .get('div > svg') - .should('have.class', 'fill-red-500'); + it(`should have ${clustersOverviewPage.unhealthyClusterName} displaying unhealthy state`, () => { + clustersOverviewPage.unhealthyClusterNameDisplaysUnhealthyState(); }); }); }); diff --git a/test/e2e/cypress/pageObject/clusters-overview-po.js b/test/e2e/cypress/pageObject/clusters-overview-po.js index 058ab157fd..1e34ef717b 100644 --- a/test/e2e/cypress/pageObject/clusters-overview-po.js +++ b/test/e2e/cypress/pageObject/clusters-overview-po.js @@ -21,6 +21,9 @@ const addTagButtons = 'span span:contains("Add Tag")'; const removeEnv1TagButton = 'span span:contains("env1") span'; //Test data +export const healthyClusterName = healthyClusterScenario.clusterName; +export const unhealthyClusterName = unhealthyClusterScenario.clusterName; + const password = 'password'; const user = createUserRequestFactory.build({ @@ -258,3 +261,98 @@ export const removeTagButtonIsDisabled = () => { export const removeTagButtonIsEnabled = () => { cy.get(removeEnv1TagButton).should('not.have.class', 'opacity-50'); }; + +const selectChecks = (clusterId, checks) => { + const [webAPIHost, webAPIPort] = [ + Cypress.env('web_api_host'), + Cypress.env('web_api_port'), + ]; + const checksBody = JSON.stringify({ + checks: checks, + }); + + const headers = { + 'Content-Type': 'application/json;charset=UTF-8', + }; + + basePage.apiLogin().then(({ accessToken }) => { + const url = `http://${webAPIHost}:${webAPIPort}/api/clusters/${clusterId}/checks`; + cy.request({ + method: 'POST', + url: url, + body: checksBody, + headers: headers, + auth: { + bearer: accessToken, + }, + }); + }); +}; + +const requestChecksExecution = (clusterId) => { + const [webAPIHost, webAPIPort] = [ + Cypress.env('web_api_host'), + Cypress.env('web_api_port'), + ]; + + const headers = { + 'Content-Type': 'application/json;charset=UTF-8', + }; + + basePage.apiLogin().then(({ accessToken }) => { + const url = `http://${webAPIHost}:${webAPIPort}/api/clusters/${clusterId}/checks/request_execution`; + cy.request({ + method: 'POST', + url: url, + headers: headers, + auth: { + bearer: accessToken, + }, + }); + }); +}; + +export const selectChecksForHealthyCluster = () => + selectChecks( + clusterIdByName(healthyClusterScenario.clusterName), + healthyClusterScenario.checks + ); + +export const requestChecksForHealthyCluster = () => + requestChecksExecution(clusterIdByName(healthyClusterScenario.clusterName)); + +export const selectChecksForUnhealthyCluster = () => + selectChecks( + clusterIdByName(unhealthyClusterScenario.clusterName), + healthyClusterScenario.checks + ); + +export const requestChecksForUnhealthyCluster = () => + requestChecksExecution(clusterIdByName(unhealthyClusterScenario.clusterName)); + +export const removeHealthyClusterChecks = () => + selectChecks(clusterIdByName(healthyClusterScenario.clusterName), []); + +export const removeUnhealthyClusterChecks = () => + selectChecks(clusterIdByName(unhealthyClusterScenario.clusterName), []); + +export const healthyClusterNameDisplaysHealthyState = () => + clusterHealthIconHasExpectedClass( + healthyClusterScenario.clusterName, + 'fill-jungle-green-500' + ); + +export const unhealthyClusterNameDisplaysUnhealthyState = () => + clusterHealthIconHasExpectedClass( + unhealthyClusterScenario.clusterName, + 'fill-red-500' + ); + +export const clusterHealthIconHasExpectedClass = (clusterName, className) => { + return cy + .get(`td:contains("${clusterName}")`) + .parents('tr') + .within(() => + cy.get('td').eq(0).find('svg').should('have.class', className) + ); +}; From 0fd143c09c239893b4ffe30aa13b7b741423920d Mon Sep 17 00:00:00 2001 From: Vicente Ruiz Date: Tue, 4 Feb 2025 14:43:51 +0100 Subject: [PATCH 07/13] remove unused functions in commands file --- test/e2e/cypress/support/commands.js | 50 ---------------------------- 1 file changed, 50 deletions(-) diff --git a/test/e2e/cypress/support/commands.js b/test/e2e/cypress/support/commands.js index 6beee3441a..a2bdc130e4 100644 --- a/test/e2e/cypress/support/commands.js +++ b/test/e2e/cypress/support/commands.js @@ -125,33 +125,6 @@ Cypress.Commands.add('clickOutside', () => { return cy.get('body').click(0, 0); //0,0 here are the x and y coordinates }); -Cypress.Commands.add('selectChecks', (clusterId, checks) => { - const [webAPIHost, webAPIPort] = [ - Cypress.env('web_api_host'), - Cypress.env('web_api_port'), - ]; - const checksBody = JSON.stringify({ - checks: checks, - }); - - const headers = { - 'Content-Type': 'application/json;charset=UTF-8', - }; - - cy.apiLogin().then(({ accessToken }) => { - const url = `http://${webAPIHost}:${webAPIPort}/api/clusters/${clusterId}/checks`; - cy.request({ - method: 'POST', - url: url, - body: checksBody, - headers: headers, - auth: { - bearer: accessToken, - }, - }); - }); -}); - const isTestDataLoaded = () => cy.apiLogin().then(({ accessToken }) => cy @@ -198,29 +171,6 @@ Cypress.Commands.add('resetFilterSelection', (filterName) => { }); }); -Cypress.Commands.add('requestChecksExecution', (clusterId) => { - const [webAPIHost, webAPIPort] = [ - Cypress.env('web_api_host'), - Cypress.env('web_api_port'), - ]; - - const headers = { - 'Content-Type': 'application/json;charset=UTF-8', - }; - - cy.apiLogin().then(({ accessToken }) => { - const url = `http://${webAPIHost}:${webAPIPort}/api/clusters/${clusterId}/checks/request_execution`; - cy.request({ - method: 'POST', - url: url, - headers: headers, - auth: { - bearer: accessToken, - }, - }); - }); -}); - Cypress.Commands.add('deregisterHost', (hostId) => { const [webAPIHost, webAPIPort] = [ Cypress.env('web_api_host'), From d60342e170c57431311818606ba48a12b222e0f8 Mon Sep 17 00:00:00 2001 From: Vicente Ruiz Date: Tue, 4 Feb 2025 16:00:28 +0100 Subject: [PATCH 08/13] remove url parameter in visit function parameter where not needed, remove data-testid selectors for consistency --- test/e2e/cypress/pageObject/about-po.js | 4 +--- test/e2e/cypress/pageObject/checks-catalog-po.js | 16 +++++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/e2e/cypress/pageObject/about-po.js b/test/e2e/cypress/pageObject/about-po.js index 40c7b35536..217fa9643c 100644 --- a/test/e2e/cypress/pageObject/about-po.js +++ b/test/e2e/cypress/pageObject/about-po.js @@ -9,9 +9,7 @@ const githubRepositoryLabel = 'div:contains("GitHub repository") + div a'; const amountOfSlesForSapSubscriptionsLabel = 'div:contains("SLES for SAP subscriptions") + div span'; -export const visit = (_url = url) => { - return basePage.visit(_url); -}; +export const visit = () => basePage.visit(url); export const pageTitleIsDisplayed = () => { return cy.get(pageTitle).should('have.text', 'About Trento Console'); diff --git a/test/e2e/cypress/pageObject/checks-catalog-po.js b/test/e2e/cypress/pageObject/checks-catalog-po.js index 9d7836c4d3..078a9b3ffd 100644 --- a/test/e2e/cypress/pageObject/checks-catalog-po.js +++ b/test/e2e/cypress/pageObject/checks-catalog-po.js @@ -13,8 +13,7 @@ const groupNames = '.check-group > div > div > h3'; const checkRows = '.check-row'; const checkPanels = '.check-panel'; -const clusterTargetTypeIcon = '[data-testid="target-icon-cluster"]'; -const hostTargetTypeIcon = '[data-testid="target-icon-host"]'; +const targetIcon = 'div[aria-label="accordion-panel"] span span:nth-child(1)'; const providersSelectionDropdown = 'button.providers-selection-dropdown'; const targetsSelectionDropdown = 'button.targets-selection-dropdown'; @@ -65,9 +64,7 @@ const selectFromCatalogDropdown = (dropdownElementSelector, choice) => { .click(); }; -export const visit = (_url = url) => { - return basePage.visit(_url); -}; +export const visit = () => basePage.visit(url); export const interceptChecksCatalogEndpoint = (forceError = false) => { let interceptArgument; @@ -172,11 +169,16 @@ export const eachGroupHasExpectedCheckIds = () => { }; export const expectedTargetTypeClusterIconsAreDisplayed = () => { - return cy.get(clusterTargetTypeIcon).should('have.length', group1Checks); + return cy + .get(`h3:contains("${clusterChecksGroup}")`) + .parents(checkGroups) + .within(() => cy.get(targetIcon).should('have.length', group1Checks)); }; export const expectedTargetTypeHostIconsAreDisplayed = () => { - return cy.get(hostTargetTypeIcon).should('have.length', group2Checks); + cy.get(`h3:contains("${hostChecksGroup}")`) + .parents(checkGroups) + .within(() => cy.get(targetIcon).should('have.length', group2Checks)); }; export const checkPanelIsNotVisible = () => { From c8531849124295516de5cb7a038b9cf66d0b1337 Mon Sep 17 00:00:00 2001 From: Vicente Ruiz Date: Wed, 5 Feb 2025 11:34:30 +0100 Subject: [PATCH 09/13] remove unnecessary cypress env for api calls & organize validation and interaction functions in clusters overview po --- test/e2e/cypress/e2e/clusters_overview.cy.js | 4 +- .../pageObject/clusters-overview-po.js | 216 ++++++++---------- 2 files changed, 96 insertions(+), 124 deletions(-) diff --git a/test/e2e/cypress/e2e/clusters_overview.cy.js b/test/e2e/cypress/e2e/clusters_overview.cy.js index 753fd7d41a..da71132dda 100644 --- a/test/e2e/cypress/e2e/clusters_overview.cy.js +++ b/test/e2e/cypress/e2e/clusters_overview.cy.js @@ -1,7 +1,7 @@ import * as clustersOverviewPage from '../pageObject/clusters-overview-po.js'; context('Clusters Overview', () => { - before(() => clustersOverviewPage.preloadTestData()); + // before(() => clustersOverviewPage.preloadTestData()); beforeEach(() => { clustersOverviewPage.interceptClustersEndpoint(); @@ -33,7 +33,7 @@ context('Clusters Overview', () => { }); // eslint-disable-next-line mocha/no-skipped-tests - describe.skip('Health status for each cluster is correct', () => { + describe('Health status for each cluster is correct', () => { before(() => { clustersOverviewPage.selectChecksForHealthyCluster(); // wip: set expected results diff --git a/test/e2e/cypress/pageObject/clusters-overview-po.js b/test/e2e/cypress/pageObject/clusters-overview-po.js index 1e34ef717b..52a15b9b89 100644 --- a/test/e2e/cypress/pageObject/clusters-overview-po.js +++ b/test/e2e/cypress/pageObject/clusters-overview-po.js @@ -55,22 +55,65 @@ export const visit = () => basePage.visit(url); export const validateUrl = () => basePage.validateUrl(url); -export const interceptClustersEndpoint = () => { +export const interceptClustersEndpoint = () => cy.intercept(clustersEndpoint).as(clustersEndpointAlias); + +export const waitForClustersEndpoint = () => + basePage.waitForRequest(clustersEndpointAlias); + +// UI Interactions + +export const setClusterTags = () => { + taggingRules.forEach(([clusterName, tag]) => { + addTagByColumnValue(clusterName, tag); + }); }; -export const waitForClustersEndpoint = () => { - return basePage.waitForRequest(clustersEndpointAlias); +const addTagByColumnValue = (columnValue, tagValue) => { + cy.get(`td:contains(${columnValue})`) + .parents('tr') + .within(() => { + cy.get(addTagButtons).type(`${tagValue}{enter}`); + }); }; -export const allRegisteredClustersAreDisplayed = () => { - cy.get(clusterNames).its('length').should('eq', availableClusters.length); +// Validations + +export const hanaCluster1TagsAreDisplayed = () => { + return cy + .get(`tr:contains("${hanaCluster1.name}")`) + .within(() => + cy + .get(`span span:contains("${clusterTags[hanaCluster1.name]}")`) + .should('be.visible') + ); }; -export const paginationButtonsAreDisabled = () => { - cy.get(paginationNavigationButtons).should('be.disabled'); +export const addTagButtonsAreDisabled = () => + cy.get(addTagButtons).should('have.class', 'opacity-50'); + +export const addTagButtonsAreNotsDisabled = () => + cy.get(addTagButtons).should('not.have.class', 'opacity-50'); + +export const removeTagButtonIsDisabled = () => + cy.get(removeEnv1TagButton).should('have.class', 'opacity-50'); + +export const removeTagButtonIsEnabled = () => + cy.get(removeEnv1TagButton).should('not.have.class', 'opacity-50'); + +export const clusterNameLinkIsDisplayedAsId = (clusterName) => { + const clusterID = clusterIdByName(clusterName); + return waitForClustersEndpoint().then(() => + cy.get(tableRows).eq(8).find(rowCells).eq(1).should('have.text', clusterID) + ); }; +export const allRegisteredClustersAreDisplayed = () => + cy.get(clusterNames).its('length').should('eq', availableClusters.length); + +export const paginationButtonsAreDisabled = () => + cy.get(paginationNavigationButtons).should('be.disabled'); + export const clustersDataIsDisplayedAsExpected = () => { return waitForClustersEndpoint().then(() => { return cy.get(tableRows).each(($row, index) => { @@ -82,22 +125,51 @@ export const clustersDataIsDisplayedAsExpected = () => { }); }; -export const restoreClusterName = () => basePage.loadScenario('cluster-4-SOK'); +export const healthyClusterNameDisplaysHealthyState = () => + clusterHealthIconHasExpectedClass( + healthyClusterScenario.clusterName, + 'fill-jungle-green-500' + ); -const clusterIdByName = (clusterName) => - availableClusters.find(({ name }) => name === clusterName).id; +export const unhealthyClusterNameDisplaysUnhealthyState = () => + clusterHealthIconHasExpectedClass( + unhealthyClusterScenario.clusterName, + 'fill-red-500' + ); -export const clusterNameLinkIsDisplayedAsId = (clusterName) => { - const clusterID = clusterIdByName(clusterName); - return waitForClustersEndpoint().then(() => - cy.get(tableRows).eq(8).find(rowCells).eq(1).should('have.text', clusterID) +export const clusterHealthIconHasExpectedClass = (clusterName, className) => { + return cy + .get(`td:contains("${clusterName}")`) + .parents('tr') + .within(() => + cy.get('td').eq(0).find('svg').should('have.class', className) + ); +}; + +export const eachClusterTagsIsCorrectlyDisplayed = () => { + return taggingRules.forEach(([tag]) => + cy.get(`span span:contains(${tag})`).should('be.visible') ); }; +export const clusterIsNotDisplayedWhenNodesAreDeregistered = () => + cy.get(`span span:contains("${hanaCluster1.name}")`).should('not.exist'); + +export const clusterNameIsDisplayed = () => { + cy.get(`span span:contains("${hanaCluster1.name}")`).should('be.visible'); +}; + +// Helpers + +const clusterIdByName = (clusterName) => + availableClusters.find(({ name }) => name === clusterName).id; + +// API Interactions + export const apiRemoveTagByClusterId = (clusterId, tagId) => { return basePage.apiLogin().then(({ accessToken }) => cy.request({ - url: `api/v1/clusters/${clusterId}/tags/${tagId}`, + url: `/api/v1/clusters/${clusterId}/tags/${tagId}`, method: 'DELETE', auth: { bearer: accessToken }, }) @@ -140,42 +212,12 @@ const getClusterTags = (jsonData) => { return clusterTags; }; -const addTagByColumnValue = (columnValue, tagValue) => { - cy.get(`td:contains(${columnValue})`) - .parents('tr') - .within(() => { - cy.get(addTagButtons).type(`${tagValue}{enter}`); - }); -}; - -export const setClusterTags = () => { - taggingRules.forEach(([clusterName, tag]) => { - addTagByColumnValue(clusterName, tag); - }); -}; - -export const eachClusterTagsIsCorrectlyDisplayed = () => { - taggingRules.forEach(([tag]) => - cy.get(`span span:contains(${tag})`).should('be.visible') - ); -}; - export const deregisterHost = (hostId) => { - const [webAPIHost, webAPIPort] = [ - Cypress.env('web_api_host'), - Cypress.env('web_api_port'), - ]; - - const headers = { - 'Content-Type': 'application/json;charset=UTF-8', - }; - return basePage.apiLogin().then(({ accessToken }) => { - const url = `http://${webAPIHost}:${webAPIPort}/api/v1/hosts/${hostId}`; + const url = `/api/v1/hosts/${hostId}`; cy.request({ method: 'DELETE', url: url, - headers: headers, auth: { bearer: accessToken, }, @@ -190,22 +232,11 @@ export const deregisterAllClusterHosts = () => { export const restoreClusterHosts = () => basePage.loadScenario(`cluster-${hanaCluster1.name}-restore`); -export const clusterIsNotDisplayedWhenNodesAreDeregistered = () => - cy.get(`span span:contains("${hanaCluster1.name}")`).should('not.exist'); - -export const clusterNameIsDisplayed = () => { - cy.get(`span span:contains("${hanaCluster1.name}")`).should('be.visible'); -}; - const apiSetTag = (clusterName, tag) => { - const [webAPIHost, webAPIPort] = [ - Cypress.env('web_api_host'), - Cypress.env('web_api_port'), - ]; const clusterID = clusterIdByName(clusterName); return basePage.apiLogin().then(({ accessToken }) => cy.request({ - url: `http://${webAPIHost}:${webAPIPort}/api/v1/clusters/${clusterID}/tags/`, + url: `/api/v1/clusters/${clusterID}/tags/`, method: 'POST', auth: { bearer: accessToken }, body: { value: tag }, @@ -220,16 +251,6 @@ export const apiSetTagsHanaCluster1 = () => { tagsForCluster1.forEach((tag) => apiSetTag('hana_cluster_1', tag)); }; -export const hanaCluster1TagsAreDisplayed = () => { - return cy - .get(`tr:contains("${hanaCluster1.name}")`) - .within(() => - cy - .get(`span span:contains("${clusterTags[hanaCluster1.name]}")`) - .should('be.visible') - ); -}; - export const createUserWithoutAbilities = () => { return basePage.createUserWithAbilities(user, []); }; @@ -246,27 +267,7 @@ export const loginWithoutTagAbilities = () => export const loginWithTagAbilities = () => basePage.apiLoginAndCreateSession(user.username, password); -export const addTagButtonsAreDisabled = () => { - cy.get(addTagButtons).should('have.class', 'opacity-50'); -}; - -export const addTagButtonsAreNotsDisabled = () => { - cy.get(addTagButtons).should('not.have.class', 'opacity-50'); -}; - -export const removeTagButtonIsDisabled = () => { - cy.get(removeEnv1TagButton).should('have.class', 'opacity-50'); -}; - -export const removeTagButtonIsEnabled = () => { - cy.get(removeEnv1TagButton).should('not.have.class', 'opacity-50'); -}; - const selectChecks = (clusterId, checks) => { - const [webAPIHost, webAPIPort] = [ - Cypress.env('web_api_host'), - Cypress.env('web_api_port'), - ]; const checksBody = JSON.stringify({ checks: checks, }); @@ -275,8 +276,8 @@ const selectChecks = (clusterId, checks) => { 'Content-Type': 'application/json;charset=UTF-8', }; - basePage.apiLogin().then(({ accessToken }) => { - const url = `http://${webAPIHost}:${webAPIPort}/api/clusters/${clusterId}/checks`; + return basePage.apiLogin().then(({ accessToken }) => { + const url = `/api/clusters/${clusterId}/checks`; cy.request({ method: 'POST', url: url, @@ -290,21 +291,11 @@ const selectChecks = (clusterId, checks) => { }; const requestChecksExecution = (clusterId) => { - const [webAPIHost, webAPIPort] = [ - Cypress.env('web_api_host'), - Cypress.env('web_api_port'), - ]; - - const headers = { - 'Content-Type': 'application/json;charset=UTF-8', - }; - - basePage.apiLogin().then(({ accessToken }) => { - const url = `http://${webAPIHost}:${webAPIPort}/api/clusters/${clusterId}/checks/request_execution`; + return basePage.apiLogin().then(({ accessToken }) => { + const url = `/api/clusters/${clusterId}/checks/request_execution`; cy.request({ method: 'POST', url: url, - headers: headers, auth: { bearer: accessToken, }, @@ -336,23 +327,4 @@ export const removeHealthyClusterChecks = () => export const removeUnhealthyClusterChecks = () => selectChecks(clusterIdByName(unhealthyClusterScenario.clusterName), []); -export const healthyClusterNameDisplaysHealthyState = () => - clusterHealthIconHasExpectedClass( - healthyClusterScenario.clusterName, - 'fill-jungle-green-500' - ); - -export const unhealthyClusterNameDisplaysUnhealthyState = () => - clusterHealthIconHasExpectedClass( - unhealthyClusterScenario.clusterName, - 'fill-red-500' - ); - -export const clusterHealthIconHasExpectedClass = (clusterName, className) => { - return cy - .get(`td:contains("${clusterName}")`) - .parents('tr') - .within(() => - cy.get('td').eq(0).find('svg').should('have.class', className) - ); -}; +export const restoreClusterName = () => basePage.loadScenario('cluster-4-SOK'); From 54f1c562ccd2014960f37372bdd4d3c530fe7a83 Mon Sep 17 00:00:00 2001 From: Vicente Ruiz Date: Wed, 5 Feb 2025 11:34:53 +0100 Subject: [PATCH 10/13] remove commented preload scenario --- test/e2e/cypress/e2e/clusters_overview.cy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/cypress/e2e/clusters_overview.cy.js b/test/e2e/cypress/e2e/clusters_overview.cy.js index da71132dda..ef53abebed 100644 --- a/test/e2e/cypress/e2e/clusters_overview.cy.js +++ b/test/e2e/cypress/e2e/clusters_overview.cy.js @@ -1,7 +1,7 @@ import * as clustersOverviewPage from '../pageObject/clusters-overview-po.js'; context('Clusters Overview', () => { - // before(() => clustersOverviewPage.preloadTestData()); + before(() => clustersOverviewPage.preloadTestData()); beforeEach(() => { clustersOverviewPage.interceptClustersEndpoint(); From 9a062a0be2ef88c3b5239aaa5dd3182ba41381c8 Mon Sep 17 00:00:00 2001 From: Vicente Ruiz Date: Wed, 5 Feb 2025 12:13:32 +0100 Subject: [PATCH 11/13] move logout to before each in forbidden actions tests --- test/e2e/cypress/e2e/clusters_overview.cy.js | 15 +++++---------- test/e2e/cypress/pageObject/base-po.js | 6 ++++-- .../cypress/pageObject/clusters-overview-po.js | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/test/e2e/cypress/e2e/clusters_overview.cy.js b/test/e2e/cypress/e2e/clusters_overview.cy.js index ef53abebed..93a90065ec 100644 --- a/test/e2e/cypress/e2e/clusters_overview.cy.js +++ b/test/e2e/cypress/e2e/clusters_overview.cy.js @@ -33,7 +33,7 @@ context('Clusters Overview', () => { }); // eslint-disable-next-line mocha/no-skipped-tests - describe('Health status for each cluster is correct', () => { + describe.skip('Health status for each cluster is correct', () => { before(() => { clustersOverviewPage.selectChecksForHealthyCluster(); // wip: set expected results @@ -69,12 +69,11 @@ context('Clusters Overview', () => { clustersOverviewPage.setClusterTags(); clustersOverviewPage.eachClusterTagsIsCorrectlyDisplayed(); }); - - after(() => clustersOverviewPage.apiRemoveAllTags()); }); describe('Deregistration', () => { before(() => { + clustersOverviewPage.apiRemoveAllTags(); clustersOverviewPage.apiSetTagsHanaCluster1(); clustersOverviewPage.deregisterAllClusterHosts(); }); @@ -88,19 +87,18 @@ context('Clusters Overview', () => { clustersOverviewPage.clusterNameIsDisplayed(); clustersOverviewPage.hanaCluster1TagsAreDisplayed(); }); - - after(() => clustersOverviewPage.apiRemoveAllTags()); }); describe('Forbidden action', () => { describe('Tag operations', () => { beforeEach(() => { + clustersOverviewPage.apiRemoveAllTags(); clustersOverviewPage.apiSetTagsHanaCluster1(); clustersOverviewPage.apiDeleteAllUsers(); + clustersOverviewPage.logout(); }); it('should prevent a tag update when the user abilities are not compliant', () => { - clustersOverviewPage.logout(); clustersOverviewPage.createUserWithoutAbilities(); clustersOverviewPage.loginWithoutTagAbilities(); clustersOverviewPage.visit(); @@ -109,15 +107,12 @@ context('Clusters Overview', () => { }); it('should allow a tag update when the user abilities are compliant', () => { - clustersOverviewPage.logout(); clustersOverviewPage.createUserWithClusterTagsAbilities(); clustersOverviewPage.loginWithTagAbilities(); clustersOverviewPage.visit(); - clustersOverviewPage.addTagButtonsAreNotsDisabled(); + clustersOverviewPage.addTagButtonsAreNotDisabled(); clustersOverviewPage.removeTagButtonIsEnabled(); }); - - after(() => clustersOverviewPage.apiRemoveAllTags()); }); }); }); diff --git a/test/e2e/cypress/pageObject/base-po.js b/test/e2e/cypress/pageObject/base-po.js index af754be74c..21cd391917 100644 --- a/test/e2e/cypress/pageObject/base-po.js +++ b/test/e2e/cypress/pageObject/base-po.js @@ -102,8 +102,10 @@ export const apiLoginAndCreateSession = ( }; export const logout = () => { - window.localStorage.removeItem('access_token'); - window.localStorage.removeItem('refresh_token'); + cy.window().then((win) => { + win.localStorage.removeItem('access_token'); + win.localStorage.removeItem('refresh_token'); + }); Cypress.session.clearAllSavedSessions(); }; diff --git a/test/e2e/cypress/pageObject/clusters-overview-po.js b/test/e2e/cypress/pageObject/clusters-overview-po.js index 52a15b9b89..e89521a5ce 100644 --- a/test/e2e/cypress/pageObject/clusters-overview-po.js +++ b/test/e2e/cypress/pageObject/clusters-overview-po.js @@ -92,7 +92,7 @@ export const hanaCluster1TagsAreDisplayed = () => { export const addTagButtonsAreDisabled = () => cy.get(addTagButtons).should('have.class', 'opacity-50'); -export const addTagButtonsAreNotsDisabled = () => +export const addTagButtonsAreNotDisabled = () => cy.get(addTagButtons).should('not.have.class', 'opacity-50'); export const removeTagButtonIsDisabled = () => From df177b508bb264e5730048d11781253e98beea12 Mon Sep 17 00:00:00 2001 From: Vicente Ruiz Date: Wed, 5 Feb 2025 12:46:32 +0100 Subject: [PATCH 12/13] rename api functions adding api prefix --- test/e2e/cypress/e2e/clusters_overview.cy.js | 20 +++---- .../pageObject/clusters-overview-po.js | 55 ++++++++++--------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/test/e2e/cypress/e2e/clusters_overview.cy.js b/test/e2e/cypress/e2e/clusters_overview.cy.js index 93a90065ec..879d3f5a24 100644 --- a/test/e2e/cypress/e2e/clusters_overview.cy.js +++ b/test/e2e/cypress/e2e/clusters_overview.cy.js @@ -35,18 +35,18 @@ context('Clusters Overview', () => { // eslint-disable-next-line mocha/no-skipped-tests describe.skip('Health status for each cluster is correct', () => { before(() => { - clustersOverviewPage.selectChecksForHealthyCluster(); + clustersOverviewPage.apiSelectChecksForHealthyCluster(); // wip: set expected results - clustersOverviewPage.requestChecksForHealthyCluster(); + clustersOverviewPage.apiRequestChecksForHealthyCluster(); - clustersOverviewPage.selectChecksForUnhealthyCluster(); + clustersOverviewPage.apiSelectChecksForUnhealthyCluster(); // wip: set expected results - clustersOverviewPage.requestChecksForUnhealthyCluster(); + clustersOverviewPage.apiRequestChecksForUnhealthyCluster(); }); after(() => { - clustersOverviewPage.removeHealthyClusterChecks(); - clustersOverviewPage.removeUnhealthyClusterChecks(); + clustersOverviewPage.apiRemoveHealthyClusterChecks(); + clustersOverviewPage.apiRemoveUnhealthyClusterChecks(); }); it(`should have ${clustersOverviewPage.healthyClusterName} displaying healthy state`, () => { @@ -75,7 +75,7 @@ context('Clusters Overview', () => { before(() => { clustersOverviewPage.apiRemoveAllTags(); clustersOverviewPage.apiSetTagsHanaCluster1(); - clustersOverviewPage.deregisterAllClusterHosts(); + clustersOverviewPage.apiDeregisterAllClusterHosts(); }); it(`should not display '${clustersOverviewPage.hanaCluster1.name}' after deregistering all its nodes`, () => { @@ -83,7 +83,7 @@ context('Clusters Overview', () => { }); it(`should show cluster '${clustersOverviewPage.hanaCluster1.name}' after registering it again with the previous tags`, () => { - clustersOverviewPage.restoreClusterHosts(); + clustersOverviewPage.apiRestoreClusterHosts(); clustersOverviewPage.clusterNameIsDisplayed(); clustersOverviewPage.hanaCluster1TagsAreDisplayed(); }); @@ -99,7 +99,7 @@ context('Clusters Overview', () => { }); it('should prevent a tag update when the user abilities are not compliant', () => { - clustersOverviewPage.createUserWithoutAbilities(); + clustersOverviewPage.apiCreateUserWithoutAbilities(); clustersOverviewPage.loginWithoutTagAbilities(); clustersOverviewPage.visit(); clustersOverviewPage.addTagButtonsAreDisabled(); @@ -107,7 +107,7 @@ context('Clusters Overview', () => { }); it('should allow a tag update when the user abilities are compliant', () => { - clustersOverviewPage.createUserWithClusterTagsAbilities(); + clustersOverviewPage.apiCreateUserWithClusterTagsAbilities(); clustersOverviewPage.loginWithTagAbilities(); clustersOverviewPage.visit(); clustersOverviewPage.addTagButtonsAreNotDisabled(); diff --git a/test/e2e/cypress/pageObject/clusters-overview-po.js b/test/e2e/cypress/pageObject/clusters-overview-po.js index e89521a5ce..dd6d6d9780 100644 --- a/test/e2e/cypress/pageObject/clusters-overview-po.js +++ b/test/e2e/cypress/pageObject/clusters-overview-po.js @@ -109,7 +109,7 @@ export const clusterNameLinkIsDisplayedAsId = (clusterName) => { }; export const allRegisteredClustersAreDisplayed = () => - cy.get(clusterNames).its('length').should('eq', availableClusters.length); + cy.get(clusterNames).should('have.length', availableClusters.length); export const paginationButtonsAreDisabled = () => cy.get(paginationNavigationButtons).should('be.disabled'); @@ -212,7 +212,7 @@ const getClusterTags = (jsonData) => { return clusterTags; }; -export const deregisterHost = (hostId) => { +const apiDeregisterHost = (hostId) => { return basePage.apiLogin().then(({ accessToken }) => { const url = `/api/v1/hosts/${hostId}`; cy.request({ @@ -225,11 +225,10 @@ export const deregisterHost = (hostId) => { }); }; -export const deregisterAllClusterHosts = () => { - hanaCluster1.hosts.forEach((hostId) => deregisterHost(hostId)); -}; +export const apiDeregisterAllClusterHosts = () => + hanaCluster1.hosts.forEach((hostId) => apiDeregisterHost(hostId)); -export const restoreClusterHosts = () => +export const apiRestoreClusterHosts = () => basePage.loadScenario(`cluster-${hanaCluster1.name}-restore`); const apiSetTag = (clusterName, tag) => { @@ -248,18 +247,16 @@ export const apiSetTagsHanaCluster1 = () => { const tagsForCluster1 = taggingRules .filter(([cluster]) => cluster === 'hana_cluster_1') .map(([, tag]) => tag); - tagsForCluster1.forEach((tag) => apiSetTag('hana_cluster_1', tag)); + return tagsForCluster1.forEach((tag) => apiSetTag('hana_cluster_1', tag)); }; -export const createUserWithoutAbilities = () => { - return basePage.createUserWithAbilities(user, []); -}; +export const apiCreateUserWithoutAbilities = () => + basePage.createUserWithAbilities(user, []); -export const createUserWithClusterTagsAbilities = () => { - return basePage.createUserWithAbilities(user, [ +export const apiCreateUserWithClusterTagsAbilities = () => + basePage.createUserWithAbilities(user, [ { name: 'all', resource: 'cluster_tags' }, ]); -}; export const loginWithoutTagAbilities = () => basePage.apiLoginAndCreateSession(user.username, password); @@ -267,7 +264,7 @@ export const loginWithoutTagAbilities = () => export const loginWithTagAbilities = () => basePage.apiLoginAndCreateSession(user.username, password); -const selectChecks = (clusterId, checks) => { +const apiSelectChecks = (clusterId, checks) => { const checksBody = JSON.stringify({ checks: checks, }); @@ -290,7 +287,7 @@ const selectChecks = (clusterId, checks) => { }); }; -const requestChecksExecution = (clusterId) => { +const apiRequestChecksExecution = (clusterId) => { return basePage.apiLogin().then(({ accessToken }) => { const url = `/api/clusters/${clusterId}/checks/request_execution`; cy.request({ @@ -303,28 +300,32 @@ const requestChecksExecution = (clusterId) => { }); }; -export const selectChecksForHealthyCluster = () => - selectChecks( +export const apiSelectChecksForHealthyCluster = () => + apiSelectChecks( clusterIdByName(healthyClusterScenario.clusterName), healthyClusterScenario.checks ); -export const requestChecksForHealthyCluster = () => - requestChecksExecution(clusterIdByName(healthyClusterScenario.clusterName)); +export const apiRequestChecksForHealthyCluster = () => + apiRequestChecksExecution( + clusterIdByName(healthyClusterScenario.clusterName) + ); -export const selectChecksForUnhealthyCluster = () => - selectChecks( +export const apiSelectChecksForUnhealthyCluster = () => + apiSelectChecks( clusterIdByName(unhealthyClusterScenario.clusterName), healthyClusterScenario.checks ); -export const requestChecksForUnhealthyCluster = () => - requestChecksExecution(clusterIdByName(unhealthyClusterScenario.clusterName)); +export const apiRequestChecksForUnhealthyCluster = () => + apiRequestChecksExecution( + clusterIdByName(unhealthyClusterScenario.clusterName) + ); -export const removeHealthyClusterChecks = () => - selectChecks(clusterIdByName(healthyClusterScenario.clusterName), []); +export const apiRemoveHealthyClusterChecks = () => + apiSelectChecks(clusterIdByName(healthyClusterScenario.clusterName), []); -export const removeUnhealthyClusterChecks = () => - selectChecks(clusterIdByName(unhealthyClusterScenario.clusterName), []); +export const apiRemoveUnhealthyClusterChecks = () => + apiSelectChecks(clusterIdByName(unhealthyClusterScenario.clusterName), []); export const restoreClusterName = () => basePage.loadScenario('cluster-4-SOK'); From 62c198d84d1eb075b75817f3d448ae44b1afb671 Mon Sep 17 00:00:00 2001 From: Vicente Ruiz Date: Thu, 6 Feb 2025 15:17:32 +0100 Subject: [PATCH 13/13] remove unnecessary slashes --- test/e2e/cypress/pageObject/clusters-overview-po.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/cypress/pageObject/clusters-overview-po.js b/test/e2e/cypress/pageObject/clusters-overview-po.js index dd6d6d9780..3dafa47220 100644 --- a/test/e2e/cypress/pageObject/clusters-overview-po.js +++ b/test/e2e/cypress/pageObject/clusters-overview-po.js @@ -178,7 +178,7 @@ export const apiRemoveTagByClusterId = (clusterId, tagId) => { const apiGetClusters = () => { return basePage.apiLogin().then(({ accessToken }) => { - const url = '/api/v2/clusters/'; + const url = '/api/v2/clusters'; return cy .request({ method: 'GET', @@ -235,7 +235,7 @@ const apiSetTag = (clusterName, tag) => { const clusterID = clusterIdByName(clusterName); return basePage.apiLogin().then(({ accessToken }) => cy.request({ - url: `/api/v1/clusters/${clusterID}/tags/`, + url: `/api/v1/clusters/${clusterID}/tags`, method: 'POST', auth: { bearer: accessToken }, body: { value: tag },