Files
zitadel/tests/functional-ui/cypress/e2e/settings/features.cy.ts
T
f69a6ed4f3 chore: rehaul DevX (#10571)
# Which Problems Are Solved

Replaces Turbo by Nx and lays the foundation for the next CI
improvements. It enables using Nx Cloud to speed the up the pipelines
that affect any node package.
It streamlines the dev experience for frontend and backend developers by
providing the following commands:

| Task | Command | Notes |
|------|---------|--------|
| **Production** | `nx run PROJECT:prod` | Production server |
| **Develop** | `nx run PROJECT:dev` | Hot reloading development server
|
| **Test** | `nx run PROJECT:test` | Run all tests |
| **Lint** | `nx run PROJECT:lint` | Check code style |
| **Lint Fix** | `nx run PROJECT:lint-fix` | Auto-fix style issues |

The following values can be used for PROJECT:

- @zitadel/zitadel (root commands)
- @zitadel/api,
- @zitadel/login,
- @zitadel/console,
- @zitadel/docs,
- @zitadel/client
- @zitadel/proto

The project names and folders are streamlined:

| Old Folder | New Folder |
| --- | --- |
| ./e2e | ./tests/functional-ui |
| ./load-test | ./benchmark |
| ./build/zitadel | ./apps/api |
| ./console | ./apps/console (postponed so the PR is reviewable) |  

Also, all references to the TypeScript repo are removed so we can
archive it.

# How the Problems Are Solved

- Ran `npx nx@latest init`
- Replaced all turbo.json by project.json and fixed the target configs
- Removed Turbo dependency
- All JavaScript related code affected by a PRs changes is
quality-checked using the `nx affected` command
- We move PR checks that are runnable using Nx into the `check`
workflow. For workflows where we don't use Nx, yet, we restore
previously built dependency artifacts from Nx.
- We only use a single and easy to understand dev container
- The CONTRIBUTING.md is streamlined
- The setup with a generated client pat is orchestrated with Nx
- Everything related to the TypeScript repo is updated or removed. A
**Deploy with Vercel** button is added to the docs and the
CONTRIBUTING.md.

# Additional Changes

- NPM package names have a consistent pattern.
- Docker bake is removed. The login container is built and released like
the core container.
- The integration tests build the login container before running, so
they don't rely on the login container action anymore. This fixes
consistently failing checks on PRs from forks.
- The docs build in GitHub actions is removed, as we already build on
Vercel.

# Additional Context

- Internal discussion:
https://zitadel.slack.com/archives/C087ADF8LRX/p1756277884928169
- Workflow dispatch test:
https://github.com/zitadel/zitadel/actions/runs/17760122959

---------

Co-authored-by: Florian Forster <[email protected]>
Co-authored-by: Tim Möhlmann <[email protected]>
Co-authored-by: Copilot <[email protected]>
2025-10-08 10:27:02 +02:00

171 lines
6.2 KiB
TypeScript

import { apiAuth } from '../../support/api/apiauth';
import { resetInstanceFeatures } from '../../support/api/features';
import { login, User } from '../../support/login/users';
describe('features settings', () => {
const featuresPath = '/instance?id=features';
beforeEach(() => {
cy.context().as('ctx');
cy.visit(featuresPath);
});
describe('UI and Display Tests', () => {
it('should display features page with correct elements', () => {
// Check page title contains relevant text (flexible for translation issues)
cy.get('h2').should('be.visible').and('contain.text', 'Feature');
cy.get('.events-desc').should('be.visible');
// Check info link
cy.get('a[href*="feature-service"]').should('be.visible').and('have.attr', 'target', '_blank');
// Check reset button is always present
cy.get('[data-e2e="reset-features-button"]').should('be.visible');
});
it('should display feature toggles', () => {
cy.get('cnsl-card').should('be.visible');
cy.get('.features').should('be.visible');
// Check that feature toggles are present
cy.get('cnsl-feature-toggle').should('have.length.greaterThan', 0);
cy.get('cnsl-login-v2-feature-toggle').should('be.visible');
});
it('should maintain feature states after page reload', () => {
// Wait for features to load
cy.get('cnsl-feature-toggle').should('be.visible');
// Get the count of feature toggles before reload
cy.get('cnsl-feature-toggle').then(($toggles) => {
const initialCount = $toggles.length;
// Reload page
cy.reload();
// Wait for features to load again
cy.get('cnsl-feature-toggle').should('be.visible');
// Verify that the same number of toggles are still present
cy.get('cnsl-feature-toggle').should('have.length', initialCount);
// Verify that toggles are still functional
cy.get('cnsl-feature-toggle')
.first()
.within(() => {
cy.get('mat-button-toggle').should('be.visible');
});
});
});
it('should handle API errors gracefully', () => {
// Intercept API calls and force them to fail
cy.intercept('POST', '**/features*', {
statusCode: 500,
body: { message: 'Internal server error' },
}).as('featureError');
// Try to toggle a feature
cy.get('cnsl-feature-toggle')
.first()
.within(() => {
cy.get('mat-button-toggle').first().click();
});
// Wait for the error response (optional since it might not always trigger)
cy.get('body').then(() => {
// Just verify that the page is still functional
cy.get('cnsl-feature-toggle').should('be.visible');
});
});
describe('permissions', () => {
it('should show appropriate elements for admin users', () => {
// Admin should see reset button
cy.get('[data-e2e="reset-features-button"]').should('be.visible');
// Admin should see all feature toggles
cy.get('cnsl-feature-toggle').should('have.length.greaterThan', 0);
});
});
});
describe('Feature Modification Tests', () => {
afterEach(() => {
// Reset features after each feature modification test to ensure clean state
apiAuth().then((api) => {
resetInstanceFeatures(api);
});
});
it('should be able to toggle a feature', () => {
// Wait for features to load
cy.get('cnsl-feature-toggle').should('be.visible');
// Get the first feature toggle and check its initial state
cy.get('cnsl-feature-toggle')
.first()
.within(() => {
// Ensure we always trigger a state change by clicking an unchecked button
cy.get('mat-button-toggle').then(($allButtons) => {
const uncheckedButtons = $allButtons.not('.mat-button-toggle-checked');
if (uncheckedButtons.length > 0) {
// Click an unchecked button to enable it
const targetButton = uncheckedButtons.first();
cy.wrap(targetButton).click();
// Verify the toggle reflected the new state
cy.wrap(targetButton).should('have.class', 'mat-button-toggle-checked');
} else {
// All buttons are checked, click the first one to uncheck it
const targetButton = $allButtons.first();
cy.wrap(targetButton).click();
// Verify the toggle reflected the new state (should be unchecked now)
cy.wrap(targetButton).should('not.have.class', 'mat-button-toggle-checked');
}
});
});
// Check for success toast since we made a real change
cy.shouldConfirmSuccess();
});
it('should reset features when reset button is clicked', () => {
// Change a feature first to have something to reset
cy.get('cnsl-feature-toggle')
.first()
.within(() => {
// Check current state and click the opposite to ensure we trigger a change
cy.get('mat-button-toggle').then(($buttons) => {
const checkedButton = $buttons.filter('.mat-button-toggle-checked');
const uncheckedButtons = $buttons.not('.mat-button-toggle-checked');
if (uncheckedButtons.length > 0) {
// Click an unchecked button to enable it
cy.wrap(uncheckedButtons.first()).click();
// Verify the change was applied
cy.wrap(uncheckedButtons.first()).should('have.class', 'mat-button-toggle-checked');
} else {
// All buttons are checked, click the first one to uncheck it
cy.wrap(checkedButton.first()).click();
// Verify the change was applied
cy.wrap(checkedButton.first()).should('not.have.class', 'mat-button-toggle-checked');
}
});
});
// Check for success toast since we made a real change
cy.shouldConfirmSuccess();
// Click the reset button
cy.get('[data-e2e="reset-features-button"]').click();
// Check for success toast from reset operation
cy.shouldConfirmSuccess();
// Verify features are still loaded and functional after UI reset
cy.get('cnsl-feature-toggle').should('be.visible');
});
});
});