Skip to content

Commit

Permalink
refactor: isolate tests (#5433)
Browse files Browse the repository at this point in the history
This PR fixes a race condition between e2e tests where bulk archiving
all toggles in the default project would delete toggles used for the
features e2e tests.

It does by isolating the features.spec and overview.spec to their
respective projects, so that they always operate on isolated data.

### Future enhancements: 

I'm not particularly fond of passing the projectName through to all the
helper methods. It complicates the tests more than it should. I would
like to be able to set the project once per test and have all the helper
methods be aware of the context. Something like this should work:

```
before(() => {
   cy.wrap('projectId').as('project');
})
```

And in the helpers: 

```
export const createFeature_API = (
    featureName: string,
    options?: Partial<Cypress.RequestOptions>,
): Chainable<any> => {
    return cy.get('@project').then((project) => {
        projectName = project || 'default';
        return cy.request({
            url: `${baseUrl}/api/admin/projects/${projectName}/features`,
            method: 'POST',
            body: {
                name: `${featureName}`,
                description: 'hello-world',
                type: 'release',
                impressionData: false,
            },
            ...options,
        });
    });
};
```
  • Loading branch information
FredrikOseberg authored Nov 27, 2023
1 parent 5c726c3 commit c0369b7
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 25 deletions.
7 changes: 5 additions & 2 deletions frontend/cypress/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ declare namespace Cypress {
addFlexibleRolloutStrategyToFeature_UI(
options: AddFlexibleRolloutStrategyOptions,
): Chainable;
updateFlexibleRolloutStrategy_UI(featureToggleName: string);
updateFlexibleRolloutStrategy_UI(
featureToggleName: string,
projectName?: string,
);
deleteFeatureStrategy_UI(
featureName: string,
shouldWait?: boolean,
Expand All @@ -84,7 +87,7 @@ declare namespace Cypress {
projectName?: string,
options?: Partial<Cypress.RequestOptions>,
): Chainable;
deleteFeature_API(name: string): Chainable;
deleteFeature_API(name: string, projectName?: string): Chainable;
createEnvironment_API(
environment: IEnvironment,
options?: Partial<Cypress.RequestOptions>,
Expand Down
38 changes: 28 additions & 10 deletions frontend/cypress/integration/feature/feature.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@
describe('feature', () => {
const randomId = String(Math.random()).split('.')[1];
const featureToggleName = `unleash-e2e-${randomId}`;
const projectName = `unleash-e2e-project-${randomId}`;

const variant1 = 'variant1';
const variant2 = 'variant2';

before(() => {
cy.runBefore();
cy.login_UI();
cy.createProject_API(projectName);
});

after(() => {
cy.deleteFeature_API(featureToggleName);
cy.deleteFeature_API(featureToggleName, projectName);
cy.deleteProject_API(projectName);
});

beforeEach(() => {
Expand All @@ -21,19 +25,19 @@ describe('feature', () => {
});

it('can create a feature toggle', () => {
cy.createFeature_UI(featureToggleName, true);
cy.createFeature_UI(featureToggleName, true, projectName);
cy.url().should('include', featureToggleName);
});

it('gives an error if a toggle exists with the same name', () => {
cy.createFeature_UI(featureToggleName, false);
cy.createFeature_UI(featureToggleName, false, projectName);
cy.get("[data-testid='INPUT_ERROR_TEXT']").contains(
'A toggle with that name already exists',
);
});

it('gives an error if a toggle name is url unsafe', () => {
cy.createFeature_UI('featureToggleUnsafe####$#//', false);
cy.createFeature_UI('featureToggleUnsafe####$#//', false, projectName);
cy.get("[data-testid='INPUT_ERROR_TEXT']").contains(
`"name" must be URL friendly`,
);
Expand All @@ -42,19 +46,33 @@ describe('feature', () => {
it('can add, update and delete a gradual rollout strategy to the development environment', () => {
cy.addFlexibleRolloutStrategyToFeature_UI({
featureToggleName,
project: projectName,
}).then(() => {
cy.updateFlexibleRolloutStrategy_UI(featureToggleName).then(() =>
cy.deleteFeatureStrategy_UI(featureToggleName),
cy.updateFlexibleRolloutStrategy_UI(
featureToggleName,
projectName,
).then(() =>
cy.deleteFeatureStrategy_UI(
featureToggleName,
false,
projectName,
),
);
});
});

it('can add variants to the development environment', () => {
cy.addVariantsToFeature_UI(featureToggleName, [variant1, variant2]);
cy.addVariantsToFeature_UI(
featureToggleName,
[variant1, variant2],
projectName,
);
});

it('can update variants', () => {
cy.visit(`/projects/default/features/${featureToggleName}/variants`);
cy.visit(
`/projects/${projectName}/features/${featureToggleName}/variants`,
);

cy.get('[data-testid=EDIT_VARIANTS_BUTTON]').click();
cy.get('[data-testid=VARIANT_NAME_INPUT]')
Expand All @@ -70,7 +88,7 @@ describe('feature', () => {

cy.intercept(
'PATCH',
`/api/admin/projects/default/features/${featureToggleName}/environments/development/variants`,
`/api/admin/projects/${projectName}/features/${featureToggleName}/environments/development/variants`,
(req) => {
expect(req.body[0].op).to.equal('replace');
expect(req.body[0].path).to.equal('/1/weightType');
Expand All @@ -92,6 +110,6 @@ describe('feature', () => {
});

it('can delete variants', () => {
cy.deleteVariant_UI(featureToggleName, variant2);
cy.deleteVariant_UI(featureToggleName, variant2, projectName);
});
});
22 changes: 13 additions & 9 deletions frontend/cypress/integration/projects/overview.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,26 @@ describe('project overview', () => {
const randomId = String(Math.random()).split('.')[1];
const featureTogglePrefix = 'unleash-e2e-project-overview';
const featureToggleName = `${featureTogglePrefix}-${randomId}`;
const projectName = `unleash-e2e-project-overview-${randomId}`;
const baseUrl = Cypress.config().baseUrl;
const selectAll =
'[title="Toggle All Rows Selected"] input[type="checkbox"]';

before(() => {
cy.runBefore();
cy.login_UI();
cy.createProject_API(projectName);
});

after(() => {
cy.request({
method: 'DELETE',
url: `${baseUrl}/api/admin/projects/default/features/${featureToggleName}-A`,
url: `${baseUrl}/api/admin/projects/${projectName}/features/${featureToggleName}-A`,
failOnStatusCode: false,
});
cy.request({
method: 'DELETE',
url: `${baseUrl}/api/admin/projects/default/features/${featureToggleName}-B`,
url: `${baseUrl}/api/admin/projects/${projectName}/features/${featureToggleName}-B`,
failOnStatusCode: false,
});
cy.request({
Expand All @@ -39,13 +42,14 @@ describe('project overview', () => {
method: 'DELETE',
url: `${baseUrl}/api/admin/archive/${featureToggleName}-B`,
});
cy.deleteProject_API(projectName);
});

it('loads the table', () => {
cy.login_UI();
cy.createFeature_API(`${featureToggleName}-A`);
cy.createFeature_API(`${featureToggleName}-B`);
cy.visit('/projects/default');
cy.createFeature_API(`${featureToggleName}-A`, projectName);
cy.createFeature_API(`${featureToggleName}-B`, projectName);
cy.visit(`/projects/${projectName}`);

// Use search to filter feature toggles and check that the feature toggle is listed in the table.
cy.get(`[data-testid="${SEARCH_INPUT}"]`).as('search').click();
Expand All @@ -58,7 +62,7 @@ describe('project overview', () => {

it('can select and deselect feature toggles', () => {
cy.login_UI();
cy.visit('/projects/default');
cy.visit(`/projects/${projectName}`);
cy.viewport(1920, 1080);
cy.get(`[data-testid="${SEARCH_INPUT}"]`).as('search').click();
cy.get('@search').type(featureToggleName);
Expand Down Expand Up @@ -120,7 +124,7 @@ describe('project overview', () => {

it('can mark selected togggles as stale', () => {
cy.login_UI();
cy.visit('/projects/default');
cy.visit(`/projects/${projectName}`);
cy.viewport(1920, 1080);
cy.get(`[data-testid="${SEARCH_INPUT}"]`).as('search').click();
cy.get('@search').type(featureToggleName);
Expand All @@ -134,13 +138,13 @@ describe('project overview', () => {

cy.get('[role="menuitem"]').contains('Mark as stale').click();

cy.visit(`/projects/default/features/${featureToggleName}-A`);
cy.visit(`/projects/${projectName}/features/${featureToggleName}-A`);
cy.get('[title="Feature toggle is deprecated."]').should('exist');
});

it('can archive selected togggles', () => {
cy.login_UI();
cy.visit('/projects/default');
cy.visit(`/projects/${projectName}`);
cy.viewport(1920, 1080);
cy.get(`[data-testid="${SEARCH_INPUT}"]`).as('search').click();
cy.get('@search').type(featureToggleName);
Expand Down
9 changes: 7 additions & 2 deletions frontend/cypress/support/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Chainable = Cypress.Chainable;
const baseUrl = Cypress.config().baseUrl;
const password = `${Cypress.env(`AUTH_PASSWORD`)}_A`;
const PROJECT_MEMBER = 5;

export const createFeature_API = (
featureName: string,
projectName?: string,
Expand All @@ -23,10 +24,14 @@ export const createFeature_API = (
});
};

export const deleteFeature_API = (name: string): Chainable<any> => {
export const deleteFeature_API = (
name: string,
projectName?: string,
): Chainable<any> => {
const project = projectName || 'default';
cy.request({
method: 'DELETE',
url: `${baseUrl}/api/admin/projects/default/features/${name}`,
url: `${baseUrl}/api/admin/projects/${projectName}/features/${name}`,
});
return cy.request({
method: 'DELETE',
Expand Down
4 changes: 2 additions & 2 deletions frontend/cypress/support/UI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const createFeature_UI = (
project?: string,
): Chainable<any> => {
const projectName = project || 'default';

cy.visit(`/projects/${project}`);
cy.get('[data-testid=NAVIGATE_TO_CREATE_FEATURE').click();

cy.intercept('POST', `/api/admin/projects/${projectName}/features`).as(
Expand Down Expand Up @@ -118,7 +118,7 @@ export const addFlexibleRolloutStrategyToFeature_UI = (
const env = environment || 'development';
const defaultStickiness = stickiness || 'default';

cy.visit(`/projects/default/features/${featureToggleName}`);
cy.visit(`/projects/${projectName}/features/${featureToggleName}`);

cy.intercept(
'POST',
Expand Down

0 comments on commit c0369b7

Please sign in to comment.