-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(GDB-11138) Cypress Tests for Cluster Multi-Region Support
## WHAT: Refactored and expanded Cypress tests and UI components for cluster configuration and multi-region support.11 ## WHY To test common use cases ## HOW - Added helper methods in ClusterConfigurationSteps for interacting with the modal dialog and multi-region tab elements. - Enhanced test configuration with new stubs - Updated templates for cluster-properties, cluster-nodes, and multi-region tabs with unique identifiers and modular class names - Modified existing Cypress fixtures and added new fixtures to support primary and secondary cluster mode scenarios
- Loading branch information
1 parent
f298e2c
commit 58c0d6b
Showing
12 changed files
with
557 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 3 additions & 3 deletions
6
src/js/angular/clustermanagement/templates/cluster-configuration/cluster-nodes.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
src/js/angular/clustermanagement/templates/cluster-configuration/cluster-properties.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
test-cypress/fixtures/cluster/3-nodes-cluster-group-status-with-tag.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
[ | ||
{ | ||
"address": "pc-desktop:7300", | ||
"nodeState": "FOLLOWER", | ||
"term": 2, | ||
"syncStatus": {}, | ||
"lastLogTerm": 0, | ||
"lastLogIndex": 0, | ||
"endpoint": "http://pc-desktop:7200", | ||
"recoveryStatus": {}, | ||
"topologyStatus": { | ||
"state": "PRIMARY_NODE" | ||
} | ||
}, | ||
{ | ||
"address": "pc-desktop:7301", | ||
"nodeState": "LEADER", | ||
"term": 2, | ||
"syncStatus": { | ||
"pc-desktop:7300": "IN_SYNC", | ||
"pc-desktop:7302": "IN_SYNC" | ||
}, | ||
"lastLogTerm": 0, | ||
"lastLogIndex": 0, | ||
"endpoint": "http://pc-desktop:7201", | ||
"recoveryStatus": {}, | ||
"topologyStatus": { | ||
"state": "PRIMARY_NODE", | ||
"primaryTags": { | ||
"test": 0 | ||
} | ||
} | ||
}, | ||
{ | ||
"address": "pc-desktop:7302", | ||
"nodeState": "FOLLOWER", | ||
"term": 2, | ||
"syncStatus": {}, | ||
"lastLogTerm": 0, | ||
"lastLogIndex": 0, | ||
"endpoint": "http://pc-desktop:7202", | ||
"recoveryStatus": {}, | ||
"topologyStatus": { | ||
"state": "PRIMARY_NODE" | ||
} | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
277 changes: 277 additions & 0 deletions
277
test-cypress/integration/cluster/cluster-configuration.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,277 @@ | ||
import {ClusterPageSteps} from "../../steps/cluster/cluster-page-steps"; | ||
import {GlobalOperationsStatusesStub} from "../../stubs/global-operations-statuses-stub"; | ||
import {ClusterStubs} from "../../stubs/cluster/cluster-stubs"; | ||
import {RemoteLocationStubs} from "../../stubs/cluster/remote-location-stubs"; | ||
import {DeleteClusterDialogSteps} from "../../steps/cluster/delete-cluster-dialog-steps"; | ||
import {ClusterConfigurationSteps} from "../../steps/cluster/cluster-configuration-steps"; | ||
import {ModalDialogSteps} from "../../steps/modal-dialog-steps"; | ||
import {ApplicationSteps} from "../../steps/application-steps"; | ||
|
||
describe('Cluster configuration', () => { | ||
|
||
let repositoryId; | ||
|
||
beforeEach(() => { | ||
repositoryId = 'cluster-repo' + Date.now(); | ||
GlobalOperationsStatusesStub.stubNoOperationsResponse(repositoryId); | ||
}); | ||
|
||
it('Should display cluster configuration', () => { | ||
// Given there is an existing cluster created | ||
ClusterStubs.stubClusterConfig(); | ||
ClusterStubs.stubClusterGroupStatus(); | ||
ClusterStubs.stubClusterNodeStatus(); | ||
RemoteLocationStubs.stubRemoteLocationFilter(); | ||
RemoteLocationStubs.stubRemoteLocationStatusInCluster(); | ||
// Given I have opened the cluster management page | ||
ClusterPageSteps.visit(); | ||
// When I click on edit properties | ||
ClusterPageSteps.previewClusterConfig(); | ||
// Then I should see cluster configuration with 3 tabs | ||
ClusterConfigurationSteps.getClusterConfig().should('be.visible'); | ||
ClusterConfigurationSteps.getTabs().should('have.length', 3); | ||
ClusterConfigurationSteps.getActiveTab().should('have.text', 'Properties'); | ||
ClusterConfigurationSteps.getClusterPropertiesTabContent().should('be.visible'); | ||
ClusterConfigurationSteps.selectTab(1); | ||
ClusterConfigurationSteps.getActiveTab().should('have.text', 'Nodes'); | ||
ClusterConfigurationSteps.getClusterNodesTabContent().should('be.visible'); | ||
ClusterConfigurationSteps.selectTab(2); | ||
ClusterConfigurationSteps.getActiveTab().should('have.text', 'Multi-region'); | ||
ClusterConfigurationSteps.getMultiRegionTabContent().should('be.visible'); | ||
}); | ||
|
||
context('Properties', () => { | ||
beforeEach(() => { | ||
// Given there is an existing cluster created | ||
ClusterStubs.stubClusterConfig(); | ||
ClusterStubs.stubClusterGroupStatus(); | ||
ClusterStubs.stubClusterNodeStatus(); | ||
RemoteLocationStubs.stubRemoteLocationFilter(); | ||
RemoteLocationStubs.stubRemoteLocationStatusInCluster(); | ||
}); | ||
|
||
it('Should be able to delete cluster', () => { | ||
// Given I have opened the cluster management page | ||
ClusterPageSteps.visit(); | ||
|
||
ClusterPageSteps.getClusterPage().should('be.visible'); | ||
ClusterPageSteps.getCreateClusterButton().should('not.have.class', 'no-cluster'); | ||
// When I click on delete cluster | ||
ClusterPageSteps.previewClusterConfig(); | ||
ClusterConfigurationSteps.getClusterConfig().should('be.visible'); | ||
ClusterConfigurationSteps.deleteCluster(); | ||
// Then I expect a confirmation dialog to appear | ||
DeleteClusterDialogSteps.getDialog().should('be.visible'); | ||
// When I confirm | ||
ClusterStubs.stubDeleteCluster(); | ||
ClusterStubs.stubNoClusterGroupStatus(); | ||
ClusterStubs.stubNoClusterNodeStatus(); | ||
DeleteClusterDialogSteps.confirmDeleteCluster(); | ||
// Then Cluster should be deleted | ||
ClusterStubs.stubNoClusterConfig(); | ||
RemoteLocationStubs.stubRemoteLocationStatusNotCluster(); | ||
DeleteClusterDialogSteps.getDialog().should('not.exist'); | ||
ClusterPageSteps.getRemoveNodesButton().should('not.exist'); | ||
ClusterPageSteps.getAddNodesButton().should('not.exist'); | ||
ClusterPageSteps.getReplaceNodesButton().should('not.exist'); | ||
ClusterPageSteps.getPreviewClusterConfigButton().should('not.exist'); | ||
ClusterPageSteps.getCreateClusterButton().should('have.class', 'no-cluster'); | ||
}); | ||
|
||
it('Should be able to edit cluster properties', () => { | ||
// Given I have opened the cluster management page | ||
ClusterPageSteps.visit(); | ||
// When I click on edit properties | ||
ClusterPageSteps.previewClusterConfig(); | ||
ClusterConfigurationSteps.getClusterConfig().should('be.visible'); | ||
// When I press edit properties | ||
ClusterConfigurationSteps.editProperties(); | ||
// Then I expect a modal to be shown | ||
ClusterConfigurationSteps.getModal().should('be.visible'); | ||
// I can set values in form fields | ||
ClusterConfigurationSteps.setFieldValue('electionMinTimeout', '5000'); | ||
ClusterConfigurationSteps.setFieldValue('electionRangeTimeout', '3000'); | ||
// Verify a field validation error appears if left empty | ||
ClusterConfigurationSteps.getFieldByName('heartbeatInterval').clear(); | ||
ClusterConfigurationSteps.verifyFieldError('heartbeatInterval', 'This field is required'); | ||
// And Save button is disabled | ||
ClusterConfigurationSteps.getSaveButton().should('be.disabled'); | ||
// Set value for required field to enable Save button | ||
ClusterConfigurationSteps.setFieldValue('heartbeatInterval', '1500'); | ||
// And Save button is disabled | ||
ClusterConfigurationSteps.getSaveButton().should('be.enabled'); | ||
ClusterConfigurationSteps.setFieldValue('messageSizeKB', '64'); | ||
ClusterConfigurationSteps.setFieldValue('verificationTimeout', '1200'); | ||
ClusterConfigurationSteps.setFieldValue('transactionLogMaximumSizeGB', '50'); | ||
ClusterConfigurationSteps.setFieldValue('batchUpdateInterval', '2000'); | ||
|
||
// Click Save button to submit | ||
ClusterStubs.stubSaveClusterConfiguration(); | ||
ClusterConfigurationSteps.save(); | ||
const expectedRequestBody = { | ||
electionMinTimeout: 5000, | ||
electionRangeTimeout: 3000, | ||
heartbeatInterval: 1500, | ||
messageSizeKB: 64, | ||
verificationTimeout: 1200, | ||
transactionLogMaximumSizeGB: 50, | ||
batchUpdateInterval: 2000 | ||
}; | ||
cy.wait('@save-cluster-properties').then((interception) => { | ||
expect(interception.request.body).to.deep.equal(expectedRequestBody); | ||
}); | ||
// And the modal is closed | ||
ClusterConfigurationSteps.getModal().should('not.exist'); | ||
}); | ||
}); | ||
|
||
context('Nodes', () => { | ||
beforeEach(() => { | ||
// Given there is an existing cluster created | ||
ClusterStubs.stubClusterConfig(); | ||
ClusterStubs.stubClusterGroupStatus(); | ||
ClusterStubs.stubClusterNodeStatus(); | ||
RemoteLocationStubs.stubRemoteLocationFilter(); | ||
RemoteLocationStubs.stubRemoteLocationStatusInCluster(); | ||
}); | ||
it('should display the nodes list with correct node information in the modal', () => { | ||
// Given I have opened the cluster management page | ||
ClusterPageSteps.visit(); | ||
// When I click on edit properties and open Nodes tab | ||
ClusterPageSteps.previewClusterConfig(); | ||
ClusterConfigurationSteps.selectTab(1); | ||
// I expect to see | ||
ClusterConfigurationSteps.getNodesListHeader().should('contain.text', 'Nodes list'); | ||
ClusterConfigurationSteps.assertNodesCount(3); | ||
|
||
const nodeData = [ | ||
{ | ||
url: 'http://pc-desktop:7200', | ||
rpcAddress: 'pc-desktop:7300', | ||
state: 'FOLLOWER', | ||
local: true | ||
}, | ||
{ | ||
url: 'http://pc-desktop:7201', | ||
rpcAddress: 'pc-desktop:7301', | ||
state: 'LEADER', | ||
local: false | ||
}, | ||
{ | ||
url: 'http://pc-desktop:7202', | ||
rpcAddress: 'pc-desktop:7302', | ||
state: 'FOLLOWER', | ||
local: false | ||
} | ||
]; | ||
|
||
nodeData.forEach((data, index) => { | ||
ClusterConfigurationSteps.getNodeLink(index) | ||
.should('have.attr', 'href', data.url) | ||
.and('contain.text', data.url); | ||
|
||
ClusterConfigurationSteps.getNodeRPCAddress(index) | ||
.should('contain.text', data.rpcAddress); | ||
|
||
ClusterConfigurationSteps.getNodeState(index) | ||
.should('contain.text', data.state); | ||
|
||
if (data.local) { | ||
ClusterConfigurationSteps.isNodeLocal(index).should('exist'); | ||
} else { | ||
ClusterConfigurationSteps.isNodeLocal(index).should('not.exist'); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
context('Multi-region', () => { | ||
beforeEach(() => { | ||
// Given there is an existing cluster created | ||
ClusterStubs.stubClusterConfig(); | ||
ClusterStubs.stubClusterGroupStatus(); | ||
ClusterStubs.stubClusterNodeStatus(); | ||
RemoteLocationStubs.stubRemoteLocationFilter(); | ||
RemoteLocationStubs.stubRemoteLocationStatusInCluster(); | ||
}); | ||
|
||
context('Primary cluster', () => { | ||
it('should be able to add and delete tags', () => { | ||
const tagName = 'test'; | ||
// Given I have opened the cluster management page | ||
ClusterPageSteps.visit(); | ||
// When I click on edit properties and open Nodes tab | ||
ClusterPageSteps.previewClusterConfig(); | ||
ClusterConfigurationSteps.selectTab(2); | ||
// I expect to see | ||
ClusterConfigurationSteps.getMultiRegionHeader().should('contain.text', 'Primary cluster'); | ||
ClusterConfigurationSteps.getAddTagButton().should('be.visible').and('not.be.disabled'); | ||
ClusterConfigurationSteps.getEnableSecondaryModeButton().should('be.visible').and('not.be.disabled'); | ||
ClusterConfigurationSteps.getTagsTable().should('not.exist'); | ||
|
||
ClusterStubs.stubAddTag(tagName); | ||
ClusterConfigurationSteps.clickAddTagButton(); | ||
ClusterConfigurationSteps.typeTag(tagName); | ||
ClusterConfigurationSteps.clickSubmitTagButton(); | ||
cy.wait('@add-tag').then((interception) => { | ||
expect(interception.request.body).to.deep.equal({tag: tagName}); | ||
}); | ||
|
||
ClusterStubs.stubClusterGroupStatusWithTag(); | ||
cy.wait('@3-nodes-cluster-group-status-tag').then(() => { | ||
// Assert the tags table contains the expected tag | ||
ClusterConfigurationSteps.getTagsTable().should('be.visible'); | ||
ClusterConfigurationSteps.getTagsTableRows().should('contain.text', tagName); | ||
}); | ||
// And expect success message to be displayed. | ||
ApplicationSteps.getSuccessNotifications().contains(`Successfully added to cluster primary identifier tag: ${tagName}`); | ||
|
||
//When I delete the tag | ||
ClusterConfigurationSteps.clickDeleteTagButton(); | ||
// I expect to see deleting confirmation dialog. | ||
ModalDialogSteps.getDialogHeader().should('contain', `Delete identifier tag ${tagName}`); | ||
ModalDialogSteps.getDialogBody().should('contain', 'Deleting identifier tag would stop any secondary cluster identified with it from pulling updates.'); | ||
|
||
// When I confirm | ||
ClusterStubs.stubDeleteTag(tagName); | ||
ModalDialogSteps.getConfirmButton().click(); | ||
cy.wait('@delete-tag').then((interception) => { | ||
expect(interception.request.body).to.deep.equal({tag: tagName}); | ||
}); | ||
}); | ||
}); | ||
|
||
context('Secondary cluster', () => { | ||
it('should be able to switch modes', () => { | ||
ClusterStubs.stubEnableSecondaryMode(); | ||
const rpcAddress = 'node-name:7300'; | ||
const tag = 'us-central'; | ||
|
||
// Given I have opened the cluster management page | ||
ClusterPageSteps.visit(); | ||
// When I click on edit properties and open Nodes tab | ||
ClusterPageSteps.previewClusterConfig(); | ||
ClusterConfigurationSteps.selectTab(2); | ||
// I click enable secondary mode btn | ||
ClusterConfigurationSteps.clickEnableSecondaryModeButton(); | ||
// I expect to see enable secondary mode confirmation dialog. | ||
ModalDialogSteps.getDialogHeader().should('contain', `Enable secondary mode`); | ||
ModalDialogSteps.getDialogBody().should('contain', 'By enabling secondary mode this cluster would become a read-only replica of the specified primary cluster.'); | ||
// When I confirm I expect to see configuration modal | ||
ModalDialogSteps.getConfirmButton().click(); | ||
ModalDialogSteps.getDialogHeader().should('contain', `Secondary cluster settings`); | ||
ClusterConfigurationSteps.getEnableButton().should('be.disabled'); | ||
ClusterConfigurationSteps.typeRpcAddress(rpcAddress); | ||
ClusterConfigurationSteps.getEnableButton().should('be.disabled'); | ||
ClusterConfigurationSteps.typePrimaryTag(tag); | ||
ClusterConfigurationSteps.getEnableButton().should('not.be.disabled'); | ||
ClusterConfigurationSteps.clickEnableButton(); | ||
cy.wait('@enable-secondary-mode').then((interception) => { | ||
expect(interception.request.body).to.deep.equal({primaryNode: rpcAddress, tag}); | ||
}); | ||
// And expect success message to be displayed. | ||
ApplicationSteps.getSuccessNotifications().contains(`Successfully enabled secondary mode`); | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.