diff --git a/cypress/e2e/po/components/card.po.ts b/cypress/e2e/po/components/card.po.ts index 050e545c9bb..c456dfd6c31 100644 --- a/cypress/e2e/po/components/card.po.ts +++ b/cypress/e2e/po/components/card.po.ts @@ -14,6 +14,10 @@ export default class CardPo extends ComponentPo { return this.self().get('[data-testid="card-body-slot"]'); } + getError(): CypressChainable { + return this.self().get('[data-testid="card-body-slot"] > .text-error'); + } + getActionButton(): CypressChainable { return this.self().get('[data-testid="card-actions-slot"]'); } diff --git a/cypress/e2e/po/pages/global-settings/feature-flags.po.ts b/cypress/e2e/po/pages/global-settings/feature-flags.po.ts index 9ba6893bddf..d04893e4755 100644 --- a/cypress/e2e/po/pages/global-settings/feature-flags.po.ts +++ b/cypress/e2e/po/pages/global-settings/feature-flags.po.ts @@ -54,6 +54,17 @@ export class FeatureFlagsPagePo extends RootClusterPage { return card.getBody().contains(label); } + /** + * Get card body error + * @param error + * @returns + */ + cardActionError(error: string): CypressChainable { + const card = new CardPo(); + + return card.getError().contains(error); + } + /** * Click action button * @param label Activate or Deactivate diff --git a/cypress/e2e/tests/pages/global-settings/feature-flags.spec.ts b/cypress/e2e/tests/pages/global-settings/feature-flags.spec.ts index 4e42709ddac..6ff0f77d3ad 100644 --- a/cypress/e2e/tests/pages/global-settings/feature-flags.spec.ts +++ b/cypress/e2e/tests/pages/global-settings/feature-flags.spec.ts @@ -191,6 +191,43 @@ describe('Feature Flags', { testIsolation: 'off' }, () => { sideNav.groups().contains('Legacy').should('not.exist'); }); + it('error when toggling a feature flag is handled correctly', { tags: ['@globalSettings', '@adminUser'] }, () => { + // Check Current State: should be disabled by default + FeatureFlagsPagePo.navTo(); + featureFlagsPage.list().details('unsupported-storage-drivers', 0).should('include.text', 'Disabled'); + + // Intercept the request to change the feature flag and return an error - 403, permission denied + cy.intercept({ + method: 'PUT', + pathname: '/v1/management.cattle.io.features/unsupported-storage-drivers', + times: 1, + }, { + statusCode: 403, + body: { + type: 'error', + links: {}, + code: 'Forbidden', + message: 'User does not have permission' + } + }).as('updateFeatureFlag'); + + // Activate + featureFlagsPage.list().elementWithName('unsupported-storage-drivers').scrollIntoView().should('be.visible'); + featureFlagsPage.list().clickRowActionMenuItem('unsupported-storage-drivers', 'Activate'); + featureFlagsPage.cardActionButton('Activate').click(); + + cy.wait(`@updateFeatureFlag`).its('response.statusCode').should('eq', 403); + + // Check Updated State: should be active + featureFlagsPage.list().details('unsupported-storage-drivers', 0).should('include.text', 'Disabled'); + + // Check error message is displayed + featureFlagsPage.cardActionError('User does not have permission'); + + // Press cancel + featureFlagsPage.cardActionButton('Cancel').click(); + }); + it('standard user has only read access to Feature Flag page', { tags: ['@globalSettings', '@standardUser'] }, () => { // verify action menus are hidden for standard user diff --git a/shell/list/management.cattle.io.feature.vue b/shell/list/management.cattle.io.feature.vue index a49d1a87580..ab36928d781 100644 --- a/shell/list/management.cattle.io.feature.vue +++ b/shell/list/management.cattle.io.feature.vue @@ -95,6 +95,9 @@ export default { watch: { showPromptUpdate(show) { if (show) { + // Clear last error + this.error = null; + this.showModal = true; } else { this.showModal = false; @@ -140,7 +143,9 @@ export default { btnCB(true); this.close(); } catch (err) { - this.error = err; + // An error occurred, so toggle back the value - the call failed, so the change was not made + this.update.spec.value = !this.update.enabled; + this.error = err.message || err; btnCB(false); } },