diff options
Diffstat (limited to 'src/pybind/mgr/dashboard/frontend/cypress/e2e/block')
6 files changed, 411 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/images.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/images.e2e-spec.ts new file mode 100644 index 000000000..962c135d5 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/images.e2e-spec.ts @@ -0,0 +1,92 @@ +import { PoolPageHelper } from '../pools/pools.po'; +import { ImagesPageHelper } from './images.po'; + +describe('Images page', () => { + const pools = new PoolPageHelper(); + const images = new ImagesPageHelper(); + + const poolName = 'e2e_images_pool'; + + before(() => { + cy.login(); + // Need pool for image testing + pools.navigateTo('create'); + pools.create(poolName, 8, 'rbd'); + pools.existTableCell(poolName); + }); + + after(() => { + // Deletes images test pool + pools.navigateTo(); + pools.delete(poolName); + pools.navigateTo(); + pools.existTableCell(poolName, false); + }); + + beforeEach(() => { + cy.login(); + images.navigateTo(); + }); + + it('should open and show breadcrumb', () => { + images.expectBreadcrumbText('Images'); + }); + + it('should show four tabs', () => { + images.getTabsCount().should('eq', 4); + }); + + it('should show text for all tabs', () => { + images.getTabText(0).should('eq', 'Images'); + images.getTabText(1).should('eq', 'Namespaces'); + images.getTabText(2).should('eq', 'Trash'); + images.getTabText(3).should('eq', 'Overall Performance'); + }); + + describe('create, edit & delete image test', () => { + const imageName = 'e2e_images#image'; + const newImageName = 'e2e_images#image_new'; + + it('should create image', () => { + images.createImage(imageName, poolName, '1'); + images.getFirstTableCell(imageName).should('exist'); + }); + + it('should edit image', () => { + images.editImage(imageName, poolName, newImageName, '2'); + images.getFirstTableCell(newImageName).should('exist'); + }); + + it('should delete image', () => { + images.delete(newImageName); + }); + }); + + describe('move to trash, restore and purge image tests', () => { + const imageName = 'e2e_trash#image'; + const newImageName = 'e2e_newtrash#image'; + + before(() => { + cy.login(); + // Need image for trash testing + images.createImage(imageName, poolName, '1'); + images.getFirstTableCell(imageName).should('exist'); + }); + + it('should move the image to the trash', () => { + images.moveToTrash(imageName); + images.getFirstTableCell(imageName).should('exist'); + }); + + it('should restore image to images table', () => { + images.restoreImage(imageName, newImageName); + images.getFirstTableCell(newImageName).should('exist'); + }); + + it('should purge trash in images trash tab', () => { + images.getFirstTableCell(newImageName).should('exist'); + images.moveToTrash(newImageName); + images.purgeTrash(newImageName, poolName); + }); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/images.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/images.po.ts new file mode 100644 index 000000000..bf6cbc052 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/images.po.ts @@ -0,0 +1,110 @@ +import { PageHelper } from '../page-helper.po'; + +export class ImagesPageHelper extends PageHelper { + pages = { + index: { url: '#/block/rbd', id: 'cd-rbd-list' }, + create: { url: '#/block/rbd/create', id: 'cd-rbd-form' } + }; + + // Creates a block image and fills in the name, pool, and size fields. + // Then checks if the image is present in the Images table. + createImage(name: string, pool: string, size: string) { + this.navigateTo('create'); + + cy.get('#name').type(name); // Enter in image name + + // Select image pool + cy.contains('Loading...').should('not.exist'); + this.selectOption('pool', pool); + cy.get('#pool').should('have.class', 'ng-valid'); // check if selected + + // Enter in the size of the image + cy.get('#size').type(size); + + // Click the create button and wait for image to be made + cy.get('[data-cy=submitBtn]').click(); + this.getFirstTableCell(name).should('exist'); + } + + editImage(name: string, pool: string, newName: string, newSize: string) { + this.navigateEdit(name); + + // Wait until data is loaded + cy.get('#pool').should('contain.value', pool); + + cy.get('#name').clear().type(newName); + cy.get('#size').clear().type(newSize); // click the size box and send new size + + cy.get('[data-cy=submitBtn]').click(); + + this.getExpandCollapseElement(newName).click(); + cy.get('.table.table-striped.table-bordered').contains('td', newSize); + } + + // Selects RBD image and moves it to the trash, + // checks that it is present in the trash table + moveToTrash(name: string) { + // wait for image to be created + cy.get('.datatable-body').first().should('not.contain.text', '(Creating...)'); + + this.getFirstTableCell(name).click(); + + // click on the drop down and selects the move to trash option + cy.get('.table-actions button.dropdown-toggle').first().click(); + cy.get('button.move-to-trash').click(); + + cy.get('[data-cy=submitBtn]').should('be.visible').click(); + + // Clicks trash tab + cy.contains('.nav-link', 'Trash').click(); + this.getFirstTableCell(name).should('exist'); + } + + // Checks trash tab table for image and then restores it to the RBD Images table + // (could change name if new name is given) + restoreImage(name: string, newName?: string) { + // clicks on trash tab + cy.contains('.nav-link', 'Trash').click(); + + // wait for table to load + this.getFirstTableCell(name).click(); + cy.contains('button', 'Restore').click(); + + // wait for pop-up to be visible (checks for title of pop-up) + cy.get('cd-modal #name').should('be.visible'); + + // If a new name for the image is passed, it changes the name of the image + if (newName !== undefined) { + // click name box and send new name + cy.get('cd-modal #name').clear().type(newName); + } + + cy.get('[data-cy=submitBtn]').click(); + + // clicks images tab + cy.contains('.nav-link', 'Images').click(); + + this.getFirstTableCell(newName).should('exist'); + } + + // Enters trash tab and purges trash, thus emptying the trash table. + // Checks if Image is still in the table. + purgeTrash(name: string, pool?: string) { + // clicks trash tab + cy.contains('.nav-link', 'Trash').click(); + cy.contains('button', 'Purge Trash').click(); + + // Check for visibility of modal container + cy.get('.modal-header').should('be.visible'); + + // If purgeing a specific pool, selects that pool if given + if (pool !== undefined) { + this.selectOption('poolName', pool); + cy.get('#poolName').should('have.class', 'ng-valid'); // check if pool is selected + } + cy.get('[data-cy=submitBtn]').click(); + // Wait for image to delete and check it is not present + + this.getFirstTableCell(name).should('not.exist'); + } +} diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/iscsi.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/iscsi.e2e-spec.ts new file mode 100644 index 000000000..2788c4f9b --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/iscsi.e2e-spec.ts @@ -0,0 +1,24 @@ +import { IscsiPageHelper } from './iscsi.po'; + +describe('Iscsi Page', () => { + const iscsi = new IscsiPageHelper(); + + beforeEach(() => { + cy.login(); + iscsi.navigateTo(); + }); + + it('should open and show breadcrumb', () => { + iscsi.expectBreadcrumbText('Overview'); + }); + + it('should check that tables are displayed and legends are correct', () => { + // Check tables are displayed + iscsi.getDataTables().its(0).should('be.visible'); + iscsi.getDataTables().its(1).should('be.visible'); + + // Check that legends are correct + iscsi.getLegends().its(0).should('contain.text', 'Gateways'); + iscsi.getLegends().its(1).should('contain.text', 'Images'); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/iscsi.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/iscsi.po.ts new file mode 100644 index 000000000..08efa6408 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/iscsi.po.ts @@ -0,0 +1,7 @@ +import { PageHelper } from '../page-helper.po'; + +export class IscsiPageHelper extends PageHelper { + pages = { + index: { url: '#/block/iscsi/overview', id: 'cd-iscsi' } + }; +} diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/mirroring.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/mirroring.e2e-spec.ts new file mode 100644 index 000000000..fb7db2712 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/mirroring.e2e-spec.ts @@ -0,0 +1,117 @@ +import { PoolPageHelper } from '../pools/pools.po'; +import { MirroringPageHelper } from './mirroring.po'; + +describe('Mirroring page', () => { + const pools = new PoolPageHelper(); + const mirroring = new MirroringPageHelper(); + + beforeEach(() => { + cy.login(); + mirroring.navigateTo(); + }); + + it('should open and show breadcrumb', () => { + mirroring.expectBreadcrumbText('Mirroring'); + }); + + it('should show three tabs', () => { + mirroring.getTabsCount().should('eq', 3); + }); + + it('should show text for all tabs', () => { + mirroring.getTabText(0).should('eq', 'Issues (0)'); + mirroring.getTabText(1).should('eq', 'Syncing (0)'); + mirroring.getTabText(2).should('eq', 'Ready (0)'); + }); + + describe('rbd mirroring bootstrap', () => { + const poolName = 'rbd-mirror'; + + beforeEach(() => { + // login to the second ceph cluster + cy.ceph2Login(); + cy.login(); + pools.navigateTo('create'); + pools.create(poolName, 8, 'rbd'); + pools.navigateTo(); + pools.existTableCell(poolName, true); + mirroring.navigateTo(); + }); + + it('should generate and import the bootstrap token between clusters', () => { + const url: string = Cypress.env('CEPH2_URL'); + mirroring.navigateTo(); + mirroring.generateToken(poolName); + cy.get('@token').then((bootstrapToken) => { + // pass the token to the origin as an arg + const args = { name: poolName, bootstrapToken: String(bootstrapToken) }; + // can't use any imports or functions inside the origin + // so writing the code to copy the token inside the origin manually + // rather than using a function call + // @ts-ignore + cy.origin(url, { args }, ({ name, bootstrapToken }) => { + // Create an rbd pool in the second cluster + + // Login to the second cluster + // Somehow its not working with the cypress login function + cy.visit('#/pool/create').wait(100); + + cy.get('[name=username]').type('admin'); + cy.get('#password').type('admin'); + cy.get('[type=submit]').click(); + cy.get('input[name=name]').clear().type(name); + cy.get(`select[name=poolType]`).select('replicated'); + cy.get(`select[name=poolType] option:checked`).contains('replicated'); + cy.get('.float-start.me-2.select-menu-edit').click(); + cy.get('.popover-body').should('be.visible'); + // Choose rbd as the application label + cy.get('.select-menu-item-content').contains('rbd').click(); + cy.get('cd-submit-button').click(); + cy.get('cd-pool-list').should('exist'); + + cy.visit('#/block/mirroring').wait(1000); + cy.get('.table-actions button.dropdown-toggle').first().click(); + cy.get('[aria-label="Import Bootstrap Token"]').click(); + cy.get('cd-bootstrap-import-modal').within(() => { + cy.get(`label[for=${name}]`).click(); + cy.get('textarea[id=token]').wait(100).type(bootstrapToken); + cy.get('button[type=submit]').click(); + }); + }); + }); + + // login again since origin removes all the cookies + // sessions, localStorage items etc.. + cy.login(); + mirroring.navigateTo(); + mirroring.checkPoolHealthStatus(poolName, 'OK'); + }); + }); + + describe('checks that edit mode functionality shows in the pools table', () => { + const poolName = 'mirroring_test'; + + beforeEach(() => { + pools.navigateTo('create'); // Need pool for mirroring testing + pools.create(poolName, 8, 'rbd'); + pools.navigateTo(); + pools.existTableCell(poolName, true); + }); + + it('tests editing mode for pools', () => { + mirroring.navigateTo(); + + mirroring.editMirror(poolName, 'Pool'); + mirroring.getFirstTableCell('pool').should('be.visible'); + mirroring.editMirror(poolName, 'Image'); + mirroring.getFirstTableCell('image').should('be.visible'); + mirroring.editMirror(poolName, 'Disabled'); + mirroring.getFirstTableCell('disabled').should('be.visible'); + }); + + afterEach(() => { + pools.navigateTo(); + pools.delete(poolName); + }); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/mirroring.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/mirroring.po.ts new file mode 100644 index 000000000..c4adca8b7 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/block/mirroring.po.ts @@ -0,0 +1,61 @@ +import { PageHelper } from '../page-helper.po'; + +const pages = { + index: { url: '#/block/mirroring', id: 'cd-mirroring' } +}; + +export class MirroringPageHelper extends PageHelper { + pages = pages; + + poolsColumnIndex = { + name: 1, + health: 6 + }; + + /** + * Goes to the mirroring page and edits a pool in the Pool table. Clicks on the + * pool and chooses an option (either pool, image, or disabled) + */ + @PageHelper.restrictTo(pages.index.url) + editMirror(name: string, option: string) { + // Clicks the pool in the table + this.getFirstTableCell(name).click(); + + // Clicks the Edit Mode button + cy.contains('button', 'Edit Mode').click(); + + // Clicks the drop down in the edit pop-up, then clicks the Update button + cy.get('.modal-content').should('be.visible'); + this.selectOption('mirrorMode', option); + + // Clicks update button and checks if the mode has been changed + cy.contains('button', 'Update').click(); + cy.contains('.modal-dialog', 'Edit pool mirror mode').should('not.exist'); + const val = option.toLowerCase(); // used since entries in table are lower case + this.getFirstTableCell(val).should('be.visible'); + } + + @PageHelper.restrictTo(pages.index.url) + generateToken(poolName: string) { + cy.get('[aria-label="Create Bootstrap Token"]').first().click(); + cy.get('cd-bootstrap-create-modal').within(() => { + cy.get(`label[for=${poolName}]`).click(); + cy.get('button[type=submit]').click(); + cy.get('textarea[id=token]').wait(200).invoke('val').as('token'); + cy.get('[aria-label="Back"]').click(); + }); + } + + @PageHelper.restrictTo(pages.index.url) + checkPoolHealthStatus(poolName: string, status: string) { + cy.get('cd-mirroring-pools').within(() => { + this.getTableCell(this.poolsColumnIndex.name, poolName) + .parent() + .find(`datatable-body-cell:nth-child(${this.poolsColumnIndex.health}) .badge`) + .should(($ele) => { + const newLabels = $ele.toArray().map((v) => v.innerText); + expect(newLabels).to.include(status); + }); + }); + } +} |