summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw
diff options
context:
space:
mode:
Diffstat (limited to 'src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw')
-rw-r--r--src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/buckets.e2e-spec.ts66
-rw-r--r--src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/buckets.po.ts213
-rw-r--r--src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/daemons.e2e-spec.ts34
-rw-r--r--src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/daemons.po.ts34
-rw-r--r--src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/roles.e2e-spec.ts19
-rw-r--r--src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/roles.po.ts37
-rw-r--r--src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/users.e2e-spec.ts45
-rw-r--r--src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/users.po.ts139
8 files changed, 587 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/buckets.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/buckets.e2e-spec.ts
new file mode 100644
index 000000000..99c0732fc
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/buckets.e2e-spec.ts
@@ -0,0 +1,66 @@
+import { BucketsPageHelper } from './buckets.po';
+
+describe('RGW buckets page', () => {
+ const buckets = new BucketsPageHelper();
+ const bucket_name = 'e2ebucket';
+
+ beforeEach(() => {
+ cy.login();
+ buckets.navigateTo();
+ });
+
+ describe('breadcrumb tests', () => {
+ it('should open and show breadcrumb', () => {
+ buckets.expectBreadcrumbText('Buckets');
+ });
+ });
+
+ describe('create, edit & delete bucket tests', () => {
+ it('should create bucket', () => {
+ buckets.navigateTo('create');
+ buckets.create(bucket_name, BucketsPageHelper.USERS[0], 'default-placement');
+ buckets.getFirstTableCell(bucket_name).should('exist');
+ });
+
+ it('should edit bucket', () => {
+ buckets.edit(bucket_name, BucketsPageHelper.USERS[1]);
+ buckets.getDataTables().should('contain.text', BucketsPageHelper.USERS[1]);
+ });
+
+ it('should delete bucket', () => {
+ buckets.delete(bucket_name);
+ });
+
+ it('should check default encryption is SSE-S3', () => {
+ buckets.navigateTo('create');
+ buckets.checkForDefaultEncryption();
+ });
+
+ it('should create bucket with object locking enabled', () => {
+ buckets.navigateTo('create');
+ buckets.create(bucket_name, BucketsPageHelper.USERS[0], 'default-placement', true);
+ buckets.getFirstTableCell(bucket_name).should('exist');
+ });
+
+ it('should not allow to edit versioning if object locking is enabled', () => {
+ buckets.edit(bucket_name, BucketsPageHelper.USERS[1], true);
+ buckets.getDataTables().should('contain.text', BucketsPageHelper.USERS[1]);
+
+ buckets.delete(bucket_name);
+ });
+ });
+
+ describe('Invalid Input in Create and Edit tests', () => {
+ it('should test invalid inputs in create fields', () => {
+ buckets.testInvalidCreate();
+ });
+
+ it('should test invalid input in edit owner field', () => {
+ buckets.navigateTo('create');
+ buckets.create(bucket_name, BucketsPageHelper.USERS[0], 'default-placement');
+ buckets.testInvalidEdit(bucket_name);
+ buckets.navigateTo();
+ buckets.delete(bucket_name);
+ });
+ });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/buckets.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/buckets.po.ts
new file mode 100644
index 000000000..47b0639bc
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/buckets.po.ts
@@ -0,0 +1,213 @@
+import { PageHelper } from '../page-helper.po';
+
+const pages = {
+ index: { url: '#/rgw/bucket', id: 'cd-rgw-bucket-list' },
+ create: { url: '#/rgw/bucket/create', id: 'cd-rgw-bucket-form' }
+};
+
+export class BucketsPageHelper extends PageHelper {
+ static readonly USERS = ['dashboard', 'testid'];
+
+ pages = pages;
+
+ columnIndex = {
+ name: 3,
+ owner: 4
+ };
+
+ versioningStateEnabled = 'Enabled';
+ versioningStateSuspended = 'Suspended';
+
+ private selectOwner(owner: string) {
+ return this.selectOption('owner', owner);
+ }
+
+ private selectPlacementTarget(placementTarget: string) {
+ return this.selectOption('placement-target', placementTarget);
+ }
+
+ private selectLockMode(lockMode: string) {
+ return this.selectOption('lock_mode', lockMode);
+ }
+
+ @PageHelper.restrictTo(pages.create.url)
+ create(name: string, owner: string, placementTarget: string, isLocking = false) {
+ // Enter in bucket name
+ cy.get('#bid').type(name);
+
+ // Select bucket owner
+ this.selectOwner(owner);
+ cy.get('#owner').should('have.class', 'ng-valid');
+
+ // Select bucket placement target:
+ this.selectPlacementTarget(placementTarget);
+ cy.get('#placement-target').should('have.class', 'ng-valid');
+
+ if (isLocking) {
+ cy.get('#lock_enabled').click({ force: true });
+ // Select lock mode:
+ this.selectLockMode('Compliance');
+ cy.get('#lock_mode').should('have.class', 'ng-valid');
+ cy.get('#lock_retention_period_days').type('3');
+ }
+
+ // Click the create button and wait for bucket to be made
+ cy.contains('button', 'Create Bucket').click();
+
+ this.getFirstTableCell(name).should('exist');
+ }
+
+ @PageHelper.restrictTo(pages.create.url)
+ checkForDefaultEncryption() {
+ cy.get("cd-helper[aria-label='toggle encryption helper']").click();
+ cy.get("a[aria-label='click here']").click();
+ cy.get('cd-modal').within(() => {
+ cy.get('input[id=s3Enabled]').should('be.checked');
+ });
+ }
+
+ @PageHelper.restrictTo(pages.index.url)
+ edit(name: string, new_owner: string, isLocking = false) {
+ this.navigateEdit(name);
+
+ cy.get('input[name=placement-target]').should('have.value', 'default-placement');
+ this.selectOwner(new_owner);
+
+ // If object locking is enabled versioning shouldn't be visible
+ if (isLocking) {
+ cy.get('input[id=versioning]').should('be.disabled');
+ cy.contains('button', 'Edit Bucket').click();
+
+ this.getTableCell(this.columnIndex.name, name)
+ .parent()
+ .find(`datatable-body-cell:nth-child(${this.columnIndex.owner})`)
+ .should(($elements) => {
+ const bucketName = $elements.text();
+ expect(bucketName).to.eq(new_owner);
+ });
+
+ // wait to be back on buckets page with table visible and click
+ this.getExpandCollapseElement(name).click();
+
+ // check its details table for edited owner field
+ cy.get('.table.table-striped.table-bordered').first().as('bucketDataTable');
+
+ // Check versioning enabled:
+ cy.get('@bucketDataTable').find('tr').its(0).find('td').last().as('versioningValueCell');
+
+ return cy.get('@versioningValueCell').should('have.text', this.versioningStateEnabled);
+ }
+ // Enable versioning
+ cy.get('input[id=versioning]').should('not.be.checked');
+ cy.get('label[for=versioning]').click();
+ cy.get('input[id=versioning]').should('be.checked');
+ cy.contains('button', 'Edit Bucket').click();
+
+ // Check if the owner is updated
+ this.getTableCell(this.columnIndex.name, name)
+ .parent()
+ .find(`datatable-body-cell:nth-child(${this.columnIndex.owner})`)
+ .should(($elements) => {
+ const bucketName = $elements.text();
+ expect(bucketName).to.eq(new_owner);
+ });
+
+ // wait to be back on buckets page with table visible and click
+ this.getExpandCollapseElement(name).click();
+
+ // Check versioning enabled:
+ cy.get('.table.table-striped.table-bordered').first().as('bucketDataTable');
+ cy.get('@bucketDataTable').find('tr').its(0).find('td').last().as('versioningValueCell');
+
+ cy.get('@versioningValueCell').should('have.text', this.versioningStateEnabled);
+
+ // Disable versioning:
+ this.navigateEdit(name);
+
+ cy.get('label[for=versioning]').click();
+ cy.get('input[id=versioning]').should('not.be.checked');
+ cy.contains('button', 'Edit Bucket').click();
+
+ // Check versioning suspended:
+ this.getExpandCollapseElement(name).click();
+
+ return cy.get('@versioningValueCell').should('have.text', this.versioningStateSuspended);
+ }
+
+ testInvalidCreate() {
+ this.navigateTo('create');
+ cy.get('#bid').as('nameInputField'); // Grabs name box field
+
+ // Gives an invalid name (too short), then waits for dashboard to determine validity
+ cy.get('@nameInputField').type('rq');
+
+ cy.contains('button', 'Create Bucket').click(); // To trigger a validation
+
+ // Waiting for website to decide if name is valid or not
+ // Check that name input field was marked invalid in the css
+ cy.get('@nameInputField')
+ .should('not.have.class', 'ng-pending')
+ .and('have.class', 'ng-invalid');
+
+ // Check that error message was printed under name input field
+ cy.get('#bid + .invalid-feedback').should(
+ 'have.text',
+ 'Bucket names must be 3 to 63 characters long.'
+ );
+
+ // Test invalid owner input
+ // select some valid option. The owner drop down error message will not appear unless a valid user was selected at
+ // one point before the invalid placeholder user is selected.
+ this.selectOwner(BucketsPageHelper.USERS[1]);
+
+ // select the first option, which is invalid because it is a placeholder
+ this.selectOwner('-- Select a user --');
+
+ cy.get('@nameInputField').click();
+
+ // Check that owner drop down field was marked invalid in the css
+ cy.get('#owner').should('have.class', 'ng-invalid');
+
+ // Check that error message was printed under owner drop down field
+ cy.get('#owner + .invalid-feedback').should('have.text', 'This field is required.');
+
+ // Check invalid placement target input
+ this.selectOwner(BucketsPageHelper.USERS[1]);
+ // The drop down error message will not appear unless a valid option is previsously selected.
+ this.selectPlacementTarget('default-placement');
+ this.selectPlacementTarget('-- Select a placement target --');
+ cy.get('@nameInputField').click(); // Trigger validation
+ cy.get('#placement-target').should('have.class', 'ng-invalid');
+ cy.get('#placement-target + .invalid-feedback').should('have.text', 'This field is required.');
+
+ // Clicks the Create Bucket button but the page doesn't move.
+ // Done by testing for the breadcrumb
+ cy.contains('button', 'Create Bucket').click(); // Clicks Create Bucket button
+ this.expectBreadcrumbText('Create');
+ // content in fields seems to subsist through tests if not cleared, so it is cleared
+ cy.get('@nameInputField').clear();
+ return cy.contains('button', 'Cancel').click();
+ }
+
+ testInvalidEdit(name: string) {
+ this.navigateEdit(name);
+
+ cy.get('input[id=versioning]').should('exist').and('not.be.checked');
+
+ // Chooses 'Select a user' rather than a valid owner on Edit Bucket page
+ // and checks if it's an invalid input
+
+ // select the first option, which is invalid because it is a placeholder
+ this.selectOwner('-- Select a user --');
+
+ cy.contains('button', 'Edit Bucket').click();
+
+ // Check that owner drop down field was marked invalid in the css
+ cy.get('#owner').should('have.class', 'ng-invalid');
+
+ // Check that error message was printed under owner drop down field
+ cy.get('#owner + .invalid-feedback').should('have.text', 'This field is required.');
+
+ this.expectBreadcrumbText('Edit');
+ }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/daemons.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/daemons.e2e-spec.ts
new file mode 100644
index 000000000..b71d715f8
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/daemons.e2e-spec.ts
@@ -0,0 +1,34 @@
+import { DaemonsPageHelper } from './daemons.po';
+
+describe('RGW daemons page', () => {
+ const daemons = new DaemonsPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ daemons.navigateTo();
+ });
+
+ describe('breadcrumb and tab tests', () => {
+ it('should open and show breadcrumb', () => {
+ daemons.expectBreadcrumbText('Gateways');
+ });
+
+ it('should show two tabs', () => {
+ daemons.getTabsCount().should('eq', 2);
+ });
+
+ it('should show daemons list tab at first', () => {
+ daemons.getTabText(0).should('eq', 'Gateways List');
+ });
+
+ it('should show overall performance as a second tab', () => {
+ daemons.getTabText(1).should('eq', 'Overall Performance');
+ });
+ });
+
+ describe('details and performance counters table tests', () => {
+ it('should check that details/performance tables are visible when daemon is selected', () => {
+ daemons.checkTables();
+ });
+ });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/daemons.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/daemons.po.ts
new file mode 100644
index 000000000..82a179463
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/daemons.po.ts
@@ -0,0 +1,34 @@
+import { PageHelper } from '../page-helper.po';
+
+export class DaemonsPageHelper extends PageHelper {
+ pages = {
+ index: { url: '#/rgw/daemon', id: 'cd-rgw-daemon-list' }
+ };
+
+ getTableCell() {
+ return cy
+ .get('.tab-content')
+ .its(1)
+ .find('cd-table')
+ .should('have.length', 1) // Only 1 table should be renderer
+ .find('datatable-body-cell');
+ }
+
+ checkTables() {
+ // click on a daemon so details table appears
+ cy.get('.datatable-body-cell-label').first().click();
+
+ // check details table is visible
+ // check at least one field is present
+ this.getTableCell().should('be.visible').should('contain.text', 'ceph_version');
+
+ // click on performance counters tab and check table is loaded
+ cy.contains('.nav-link', 'Performance Counters').click();
+
+ // check at least one field is present
+ this.getTableCell().should('be.visible').should('contain.text', 'objecter.op_r');
+
+ // click on performance details tab
+ cy.contains('.nav-link', 'Performance Details').click();
+ }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/roles.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/roles.e2e-spec.ts
new file mode 100644
index 000000000..597f7d1be
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/roles.e2e-spec.ts
@@ -0,0 +1,19 @@
+import { RolesPageHelper } from './roles.po';
+
+describe('RGW roles page', () => {
+ const roles = new RolesPageHelper();
+
+ beforeEach(() => {
+ cy.login();
+ roles.navigateTo();
+ });
+
+ describe('Create, Edit & Delete rgw roles', () => {
+ it('should create rgw roles', () => {
+ roles.navigateTo('create');
+ roles.create('testRole', '/', '{}');
+ roles.navigateTo();
+ roles.checkExist('testRole', true);
+ });
+ });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/roles.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/roles.po.ts
new file mode 100644
index 000000000..b72ca5df9
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/roles.po.ts
@@ -0,0 +1,37 @@
+import { PageHelper } from '../page-helper.po';
+
+const pages = {
+ index: { url: '#/rgw/roles', id: 'cd-crud-table' },
+ create: { url: '#/rgw/roles/create', id: 'cd-crud-form' }
+};
+
+export class RolesPageHelper extends PageHelper {
+ pages = pages;
+
+ columnIndex = {
+ roleName: 2,
+ path: 3,
+ arn: 4
+ };
+
+ @PageHelper.restrictTo(pages.create.url)
+ create(name: string, path: string, policyDocument: string) {
+ cy.get('#formly_3_string_role_name_0').type(name);
+ cy.get('#formly_3_textarea_role_assume_policy_doc_2').type(policyDocument);
+ cy.get('#formly_3_string_role_path_1').type(path);
+ cy.get("[aria-label='Create Role']").should('exist').click();
+ cy.get('cd-crud-table').should('exist');
+ }
+
+ @PageHelper.restrictTo(pages.index.url)
+ checkExist(name: string, exist: boolean) {
+ this.getTableCell(this.columnIndex.roleName, name).should(($elements) => {
+ const roleName = $elements.map((_, el) => el.textContent).get();
+ if (exist) {
+ expect(roleName).to.include(name);
+ } else {
+ expect(roleName).to.not.include(name);
+ }
+ });
+ }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/users.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/users.e2e-spec.ts
new file mode 100644
index 000000000..c107a08dd
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/users.e2e-spec.ts
@@ -0,0 +1,45 @@
+import { UsersPageHelper } from './users.po';
+
+describe('RGW users page', () => {
+ const users = new UsersPageHelper();
+ const tenant = 'e2e_000tenant';
+ const user_id = 'e2e_000user_create_edit_delete';
+ const user_name = tenant + '$' + user_id;
+
+ beforeEach(() => {
+ cy.login();
+ users.navigateTo();
+ });
+
+ describe('breadcrumb tests', () => {
+ it('should open and show breadcrumb', () => {
+ users.expectBreadcrumbText('Users');
+ });
+ });
+
+ describe('create, edit & delete user tests', () => {
+ it('should create user', () => {
+ users.navigateTo('create');
+ users.create(tenant, user_id, 'Some Name', 'original@website.com', '1200');
+ users.getFirstTableCell(user_id).should('exist');
+ });
+
+ it('should edit users full name, email and max buckets', () => {
+ users.edit(user_name, 'Another Identity', 'changed@othersite.com', '1969');
+ });
+
+ it('should delete user', () => {
+ users.delete(user_name);
+ });
+ });
+
+ describe('Invalid input tests', () => {
+ it('should put invalid input into user creation form and check fields are marked invalid', () => {
+ users.invalidCreate();
+ });
+
+ it('should put invalid input into user edit form and check fields are marked invalid', () => {
+ users.invalidEdit();
+ });
+ });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/users.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/users.po.ts
new file mode 100644
index 000000000..980cced88
--- /dev/null
+++ b/src/pybind/mgr/dashboard/frontend/cypress/e2e/rgw/users.po.ts
@@ -0,0 +1,139 @@
+import { PageHelper } from '../page-helper.po';
+
+const pages = {
+ index: { url: '#/rgw/user', id: 'cd-rgw-user-list' },
+ create: { url: '#/rgw/user/create', id: 'cd-rgw-user-form' }
+};
+
+export class UsersPageHelper extends PageHelper {
+ pages = pages;
+
+ @PageHelper.restrictTo(pages.create.url)
+ create(tenant: string, user_id: string, fullname: string, email: string, maxbuckets: string) {
+ // Enter in user_id
+ cy.get('#user_id').type(user_id);
+ // Show Tenanat
+ cy.get('#show_tenant').click({ force: true });
+ // Enter in tenant
+ cy.get('#tenant').type(tenant);
+ // Enter in full name
+ cy.get('#display_name').click().type(fullname);
+
+ // Enter in email
+ cy.get('#email').click().type(email);
+
+ // Enter max buckets
+ this.selectOption('max_buckets_mode', 'Custom');
+ cy.get('#max_buckets').should('exist').should('have.value', '1000');
+ cy.get('#max_buckets').click().clear().type(maxbuckets);
+
+ // Click the create button and wait for user to be made
+ cy.contains('button', 'Create User').click();
+ this.getFirstTableCell(tenant + '$' + user_id).should('exist');
+ }
+
+ @PageHelper.restrictTo(pages.index.url)
+ edit(name: string, new_fullname: string, new_email: string, new_maxbuckets: string) {
+ this.navigateEdit(name);
+
+ // Change the full name field
+ cy.get('#display_name').click().clear().type(new_fullname);
+
+ // Change the email field
+ cy.get('#email').click().clear().type(new_email);
+
+ // Change the max buckets field
+ this.selectOption('max_buckets_mode', 'Custom');
+ cy.get('#max_buckets').click().clear().type(new_maxbuckets);
+
+ cy.contains('button', 'Edit User').click();
+
+ // Click the user and check its details table for updated content
+ this.getExpandCollapseElement(name).click();
+ cy.get('.datatable-row-detail')
+ .should('contain.text', new_fullname)
+ .and('contain.text', new_email)
+ .and('contain.text', new_maxbuckets);
+ }
+
+ invalidCreate() {
+ const tenant = '000invalid_tenant';
+ const uname = '000invalid_create_user';
+ // creating this user in order to check that you can't give two users the same name
+ this.navigateTo('create');
+ this.create(tenant, uname, 'xxx', 'xxx@xxx', '1');
+
+ this.navigateTo('create');
+
+ // Username
+ cy.get('#user_id')
+ // No username had been entered. Field should be invalid
+ .should('have.class', 'ng-invalid')
+ // Try to give user already taken name. Should make field invalid.
+ .type(uname);
+ cy.get('#show_tenant').click({ force: true });
+ cy.get('#tenant').type(tenant).should('have.class', 'ng-invalid');
+ cy.contains('#tenant + .invalid-feedback', 'The chosen user ID exists in this tenant.');
+
+ // check that username field is marked invalid if username has been cleared off
+ cy.get('#user_id').clear().blur().should('have.class', 'ng-invalid');
+ cy.contains('#user_id + .invalid-feedback', 'This field is required.');
+
+ // Full name
+ cy.get('#display_name')
+ // No display name has been given so field should be invalid
+ .should('have.class', 'ng-invalid')
+ // display name field should also be marked invalid if given input then emptied
+ .type('a')
+ .clear()
+ .blur()
+ .should('have.class', 'ng-invalid');
+ cy.contains('#display_name + .invalid-feedback', 'This field is required.');
+
+ // put invalid email to make field invalid
+ cy.get('#email').type('a').blur().should('have.class', 'ng-invalid');
+ cy.contains('#email + .invalid-feedback', 'This is not a valid email address.');
+
+ // put negative max buckets to make field invalid
+ this.expectSelectOption('max_buckets_mode', 'Custom');
+ cy.get('#max_buckets').clear().type('-5').blur().should('have.class', 'ng-invalid');
+ cy.contains('#max_buckets + .invalid-feedback', 'The entered value must be >= 1.');
+
+ this.navigateTo();
+ this.delete(tenant + '$' + uname);
+ }
+
+ invalidEdit() {
+ const tenant = '000invalid_tenant';
+ const uname = '000invalid_edit_user';
+ // creating this user to edit for the test
+ this.navigateTo('create');
+ this.create(tenant, uname, 'xxx', 'xxx@xxx', '50');
+ const name = tenant + '$' + uname;
+ this.navigateEdit(name);
+
+ // put invalid email to make field invalid
+ cy.get('#email')
+ .clear()
+ .type('a')
+ .blur()
+ .should('not.have.class', 'ng-pending')
+ .should('have.class', 'ng-invalid');
+ cy.contains('#email + .invalid-feedback', 'This is not a valid email address.');
+
+ // empty the display name field making it invalid
+ cy.get('#display_name').clear().blur().should('have.class', 'ng-invalid');
+ cy.contains('#display_name + .invalid-feedback', 'This field is required.');
+
+ // put negative max buckets to make field invalid
+ this.selectOption('max_buckets_mode', 'Disabled');
+ cy.get('#max_buckets').should('not.exist');
+ this.selectOption('max_buckets_mode', 'Custom');
+ cy.get('#max_buckets').should('exist').should('have.value', '50');
+ cy.get('#max_buckets').clear().type('-5').blur().should('have.class', 'ng-invalid');
+ cy.contains('#max_buckets + .invalid-feedback', 'The entered value must be >= 1.');
+
+ this.navigateTo();
+ this.delete(tenant + '$' + uname);
+ }
+}