2023-04-04 10:46:28 +02:00
|
|
|
///<reference path="../global.d.ts" />
|
|
|
|
|
|
|
|
import Chainable = Cypress.Chainable;
|
|
|
|
import AddStrategyOptions = Cypress.AddFlexibleRolloutStrategyOptions;
|
|
|
|
const AUTH_USER = Cypress.env('AUTH_USER');
|
|
|
|
const AUTH_PASSWORD = Cypress.env('AUTH_PASSWORD');
|
|
|
|
const ENTERPRISE = Boolean(Cypress.env('ENTERPRISE'));
|
2023-05-08 10:16:18 +02:00
|
|
|
|
|
|
|
let strategyId: string | undefined;
|
|
|
|
|
2023-04-04 10:46:28 +02:00
|
|
|
const disableActiveSplashScreens = () => {
|
|
|
|
return cy.visit(`/splash/operators`);
|
|
|
|
};
|
|
|
|
|
|
|
|
const disableFeatureStrategiesProdGuard = () => {
|
|
|
|
localStorage.setItem(
|
|
|
|
'useFeatureStrategyProdGuardSettings:v2',
|
2023-10-02 14:25:46 +02:00
|
|
|
JSON.stringify({ hide: true }),
|
2023-04-04 10:46:28 +02:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export const runBefore = () => {
|
|
|
|
disableFeatureStrategiesProdGuard();
|
|
|
|
disableActiveSplashScreens();
|
|
|
|
};
|
|
|
|
|
|
|
|
export const login_UI = (
|
|
|
|
user = AUTH_USER,
|
2023-10-02 14:25:46 +02:00
|
|
|
password = AUTH_PASSWORD,
|
2023-04-04 10:46:28 +02:00
|
|
|
): Chainable<any> => {
|
|
|
|
return cy.session(user, () => {
|
|
|
|
cy.visit('/');
|
|
|
|
cy.wait(1500);
|
|
|
|
cy.get("[data-testid='LOGIN_EMAIL_ID']").type(user);
|
|
|
|
|
|
|
|
if (AUTH_PASSWORD) {
|
|
|
|
cy.get("[data-testid='LOGIN_PASSWORD_ID']").type(password);
|
|
|
|
}
|
|
|
|
|
|
|
|
cy.get("[data-testid='LOGIN_BUTTON']").click();
|
|
|
|
|
|
|
|
// Wait for the login redirect to complete.
|
|
|
|
cy.get("[data-testid='HEADER_USER_AVATAR']");
|
|
|
|
|
|
|
|
if (document.querySelector("[data-testid='CLOSE_SPLASH']")) {
|
|
|
|
cy.get("[data-testid='CLOSE_SPLASH']").click();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
export const createFeature_UI = (
|
|
|
|
name: string,
|
|
|
|
shouldWait?: boolean,
|
2023-10-02 14:25:46 +02:00
|
|
|
project?: string,
|
2023-04-04 10:46:28 +02:00
|
|
|
): Chainable<any> => {
|
|
|
|
const projectName = project || 'default';
|
refactor: isolate tests (#5433)
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,
});
});
};
```
2023-11-27 13:55:44 +01:00
|
|
|
cy.visit(`/projects/${project}`);
|
2023-04-04 10:46:28 +02:00
|
|
|
cy.get('[data-testid=NAVIGATE_TO_CREATE_FEATURE').click();
|
|
|
|
|
|
|
|
cy.intercept('POST', `/api/admin/projects/${projectName}/features`).as(
|
2023-10-02 14:25:46 +02:00
|
|
|
'createFeature',
|
2023-04-04 10:46:28 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
cy.wait(300);
|
|
|
|
|
|
|
|
cy.get("[data-testid='CF_NAME_ID'").type(name);
|
|
|
|
cy.get("[data-testid='CF_DESC_ID'").type('hello-world');
|
|
|
|
if (!shouldWait) return cy.get("[data-testid='CF_CREATE_BTN_ID']").click();
|
|
|
|
else cy.get("[data-testid='CF_CREATE_BTN_ID']").click();
|
|
|
|
return cy.wait('@createFeature');
|
|
|
|
};
|
|
|
|
|
|
|
|
export const createProject_UI = (
|
|
|
|
projectName: string,
|
2023-10-02 14:25:46 +02:00
|
|
|
defaultStickiness: string,
|
2023-04-04 10:46:28 +02:00
|
|
|
): Chainable<any> => {
|
|
|
|
cy.get('[data-testid=NAVIGATE_TO_CREATE_PROJECT').click();
|
|
|
|
|
|
|
|
cy.intercept('POST', `/api/admin/projects`).as('createProject');
|
|
|
|
|
|
|
|
cy.get("[data-testid='PROJECT_ID_INPUT']").type(projectName);
|
|
|
|
cy.get("[data-testid='PROJECT_NAME_INPUT']").type(projectName);
|
|
|
|
cy.get("[id='stickiness-select']")
|
|
|
|
.first()
|
|
|
|
.click()
|
|
|
|
.get(`[data-testid=SELECT_ITEM_ID-${defaultStickiness}`)
|
|
|
|
.first()
|
|
|
|
.click();
|
|
|
|
cy.get("[data-testid='CREATE_PROJECT_BTN']").click();
|
|
|
|
cy.wait('@createProject');
|
|
|
|
return cy.visit(`/projects/${projectName}`);
|
|
|
|
};
|
|
|
|
|
|
|
|
export const createSegment_UI = (segmentName: string): Chainable<any> => {
|
|
|
|
cy.get("[data-testid='NAVIGATE_TO_CREATE_SEGMENT']").click();
|
2023-05-08 10:16:18 +02:00
|
|
|
|
|
|
|
cy.intercept('POST', '/api/admin/segments').as('createSegment');
|
2023-04-04 10:46:28 +02:00
|
|
|
|
|
|
|
cy.get("[data-testid='SEGMENT_NAME_ID']").type(segmentName);
|
|
|
|
cy.get("[data-testid='SEGMENT_DESC_ID']").type('hello-world');
|
|
|
|
cy.get("[data-testid='SEGMENT_NEXT_BTN_ID']").click();
|
|
|
|
cy.get("[data-testid='SEGMENT_CREATE_BTN_ID']").click();
|
2023-05-08 10:16:18 +02:00
|
|
|
return cy.wait('@createSegment');
|
2023-04-04 10:46:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
export const deleteSegment_UI = (segmentName: string): Chainable<any> => {
|
|
|
|
cy.get(`[data-testid='SEGMENT_DELETE_BTN_ID_${segmentName}']`).click();
|
|
|
|
|
|
|
|
cy.get("[data-testid='SEGMENT_DIALOG_NAME_ID']").type(segmentName);
|
|
|
|
return cy.get("[data-testid='DIALOGUE_CONFIRM_ID'").click();
|
|
|
|
};
|
|
|
|
|
|
|
|
export const addFlexibleRolloutStrategyToFeature_UI = (
|
2023-10-02 14:25:46 +02:00
|
|
|
options: AddStrategyOptions,
|
2023-04-04 10:46:28 +02:00
|
|
|
): Chainable<any> => {
|
|
|
|
const { featureToggleName, project, environment, stickiness } = options;
|
|
|
|
const projectName = project || 'default';
|
|
|
|
const env = environment || 'development';
|
|
|
|
const defaultStickiness = stickiness || 'default';
|
|
|
|
|
refactor: isolate tests (#5433)
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,
});
});
};
```
2023-11-27 13:55:44 +01:00
|
|
|
cy.visit(`/projects/${projectName}/features/${featureToggleName}`);
|
2023-05-08 10:16:18 +02:00
|
|
|
|
2023-04-04 10:46:28 +02:00
|
|
|
cy.intercept(
|
|
|
|
'POST',
|
|
|
|
`/api/admin/projects/${projectName}/features/${featureToggleName}/environments/development/strategies`,
|
2023-10-02 14:25:46 +02:00
|
|
|
(req) => {
|
2023-04-04 10:46:28 +02:00
|
|
|
expect(req.body.name).to.equal('flexibleRollout');
|
|
|
|
expect(req.body.parameters.groupId).to.equal(featureToggleName);
|
|
|
|
expect(req.body.parameters.stickiness).to.equal(defaultStickiness);
|
|
|
|
expect(req.body.parameters.rollout).to.equal('50');
|
|
|
|
|
|
|
|
if (ENTERPRISE) {
|
|
|
|
expect(req.body.constraints.length).to.equal(1);
|
|
|
|
} else {
|
|
|
|
expect(req.body.constraints.length).to.equal(0);
|
|
|
|
}
|
|
|
|
|
2023-10-02 14:25:46 +02:00
|
|
|
req.continue((res) => {
|
2023-04-04 10:46:28 +02:00
|
|
|
strategyId = res.body.id;
|
|
|
|
});
|
2023-10-02 14:25:46 +02:00
|
|
|
},
|
2023-04-04 10:46:28 +02:00
|
|
|
).as('addStrategyToFeature');
|
|
|
|
|
|
|
|
cy.visit(
|
2023-10-02 14:25:46 +02:00
|
|
|
`/projects/${projectName}/features/${featureToggleName}/strategies/create?environmentId=${env}&strategyName=flexibleRollout`,
|
2023-04-04 10:46:28 +02:00
|
|
|
);
|
|
|
|
cy.wait(500);
|
|
|
|
// Takes a bit to load the screen - this will wait until it finds it or fail
|
|
|
|
cy.get('[data-testid=FLEXIBLE_STRATEGY_STICKINESS_ID]');
|
|
|
|
if (ENTERPRISE) {
|
|
|
|
cy.get('[data-testid=ADD_CONSTRAINT_ID]').click();
|
|
|
|
cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
|
|
|
|
}
|
|
|
|
cy.get(`[data-testid=STRATEGY_FORM_SUBMIT_ID]`).first().click();
|
2023-05-08 10:16:18 +02:00
|
|
|
return cy.wait('@addStrategyToFeature');
|
2023-04-04 10:46:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
export const updateFlexibleRolloutStrategy_UI = (
|
|
|
|
featureToggleName: string,
|
2023-10-02 14:25:46 +02:00
|
|
|
projectName?: string,
|
2023-04-04 10:46:28 +02:00
|
|
|
) => {
|
|
|
|
const project = projectName || 'default';
|
|
|
|
cy.visit(
|
2023-10-02 14:25:46 +02:00
|
|
|
`/projects/${project}/features/${featureToggleName}/strategies/edit?environmentId=development&strategyId=${strategyId}`,
|
2023-04-04 10:46:28 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
cy.wait(500);
|
|
|
|
|
|
|
|
cy.get('[data-testid=FLEXIBLE_STRATEGY_STICKINESS_ID]')
|
|
|
|
.first()
|
|
|
|
.click()
|
|
|
|
.get('[data-testid=SELECT_ITEM_ID-sessionId')
|
|
|
|
.first()
|
|
|
|
.click();
|
|
|
|
|
|
|
|
cy.wait(500);
|
|
|
|
cy.get('[data-testid=FLEXIBLE_STRATEGY_GROUP_ID]')
|
|
|
|
.first()
|
|
|
|
.clear()
|
|
|
|
.type('new-group-id');
|
|
|
|
|
|
|
|
cy.intercept(
|
|
|
|
'PUT',
|
|
|
|
`/api/admin/projects/${project}/features/${featureToggleName}/environments/*/strategies/${strategyId}`,
|
2023-10-02 14:25:46 +02:00
|
|
|
(req) => {
|
2023-04-04 10:46:28 +02:00
|
|
|
expect(req.body.parameters.groupId).to.equal('new-group-id');
|
|
|
|
expect(req.body.parameters.stickiness).to.equal('sessionId');
|
|
|
|
expect(req.body.parameters.rollout).to.equal('50');
|
|
|
|
|
|
|
|
if (ENTERPRISE) {
|
|
|
|
expect(req.body.constraints.length).to.equal(1);
|
|
|
|
} else {
|
|
|
|
expect(req.body.constraints.length).to.equal(0);
|
|
|
|
}
|
|
|
|
|
2023-10-02 14:25:46 +02:00
|
|
|
req.continue((res) => {
|
2023-04-04 10:46:28 +02:00
|
|
|
expect(res.statusCode).to.equal(200);
|
|
|
|
});
|
2023-10-02 14:25:46 +02:00
|
|
|
},
|
2023-04-04 10:46:28 +02:00
|
|
|
).as('updateStrategy');
|
2023-05-08 10:16:18 +02:00
|
|
|
|
|
|
|
cy.get(`[data-testid=STRATEGY_FORM_SUBMIT_ID]`).first().click();
|
2023-04-04 10:46:28 +02:00
|
|
|
return cy.wait('@updateStrategy');
|
|
|
|
};
|
|
|
|
|
|
|
|
export const deleteFeatureStrategy_UI = (
|
|
|
|
featureToggleName: string,
|
|
|
|
shouldWait?: boolean,
|
2023-10-02 14:25:46 +02:00
|
|
|
projectName?: string,
|
2023-04-04 10:46:28 +02:00
|
|
|
): Chainable<any> => {
|
|
|
|
const project = projectName || 'default';
|
|
|
|
|
|
|
|
cy.intercept(
|
|
|
|
'DELETE',
|
|
|
|
`/api/admin/projects/${project}/features/${featureToggleName}/environments/*/strategies/${strategyId}`,
|
2023-10-02 14:25:46 +02:00
|
|
|
(req) => {
|
|
|
|
req.continue((res) => {
|
2023-04-04 10:46:28 +02:00
|
|
|
expect(res.statusCode).to.equal(200);
|
|
|
|
});
|
2023-10-02 14:25:46 +02:00
|
|
|
},
|
2023-04-04 10:46:28 +02:00
|
|
|
).as('deleteUserStrategy');
|
|
|
|
cy.visit(`/projects/${project}/features/${featureToggleName}`);
|
|
|
|
cy.get('[data-testid=FEATURE_ENVIRONMENT_ACCORDION_development]').click();
|
2023-06-28 10:38:21 +02:00
|
|
|
cy.get('[data-testid=STRATEGY_REMOVE_MENU_BTN]').first().click();
|
2023-05-08 10:16:18 +02:00
|
|
|
cy.get('[data-testid=STRATEGY_FORM_REMOVE_ID]').first().click();
|
2023-04-04 10:46:28 +02:00
|
|
|
if (!shouldWait) return cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
|
|
|
|
else cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
|
|
|
|
return cy.wait('@deleteUserStrategy');
|
|
|
|
};
|
|
|
|
|
|
|
|
export const addUserIdStrategyToFeature_UI = (
|
|
|
|
featureToggleName: string,
|
2023-10-02 14:25:46 +02:00
|
|
|
projectName: string,
|
2023-04-04 10:46:28 +02:00
|
|
|
): Chainable<any> => {
|
|
|
|
const project = projectName || 'default';
|
|
|
|
cy.visit(
|
2023-10-02 14:25:46 +02:00
|
|
|
`/projects/${project}/features/${featureToggleName}/strategies/create?environmentId=development&strategyName=userWithId`,
|
2023-04-04 10:46:28 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
if (ENTERPRISE) {
|
|
|
|
cy.get('[data-testid=ADD_CONSTRAINT_ID]').click();
|
|
|
|
cy.get('[data-testid=CONSTRAINT_AUTOCOMPLETE_ID]')
|
|
|
|
.type('{downArrow}'.repeat(1))
|
|
|
|
.type('{enter}');
|
|
|
|
cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
|
|
|
|
}
|
|
|
|
|
|
|
|
cy.get('[data-testid=STRATEGY_INPUT_LIST]')
|
|
|
|
.type('user1')
|
|
|
|
.type('{enter}')
|
|
|
|
.type('user2')
|
|
|
|
.type('{enter}');
|
|
|
|
cy.get('[data-testid=ADD_TO_STRATEGY_INPUT_LIST]').click();
|
2023-05-08 10:16:18 +02:00
|
|
|
|
2023-04-04 10:46:28 +02:00
|
|
|
cy.intercept(
|
|
|
|
'POST',
|
|
|
|
`/api/admin/projects/default/features/${featureToggleName}/environments/*/strategies`,
|
2023-10-02 14:25:46 +02:00
|
|
|
(req) => {
|
2023-04-04 10:46:28 +02:00
|
|
|
expect(req.body.name).to.equal('userWithId');
|
|
|
|
|
|
|
|
expect(req.body.parameters.userIds.length).to.equal(11);
|
|
|
|
|
|
|
|
if (ENTERPRISE) {
|
|
|
|
expect(req.body.constraints.length).to.equal(1);
|
|
|
|
} else {
|
|
|
|
expect(req.body.constraints.length).to.equal(0);
|
|
|
|
}
|
|
|
|
|
2023-10-02 14:25:46 +02:00
|
|
|
req.continue((res) => {
|
2023-04-04 10:46:28 +02:00
|
|
|
strategyId = res.body.id;
|
|
|
|
});
|
2023-10-02 14:25:46 +02:00
|
|
|
},
|
2023-04-04 10:46:28 +02:00
|
|
|
).as('addStrategyToFeature');
|
|
|
|
|
|
|
|
cy.get(`[data-testid=STRATEGY_FORM_SUBMIT_ID]`).first().click();
|
2023-05-08 10:16:18 +02:00
|
|
|
return cy.wait('@addStrategyToFeature');
|
2023-04-04 10:46:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
export const addVariantsToFeature_UI = (
|
|
|
|
featureToggleName: string,
|
|
|
|
variants: Array<string>,
|
2023-10-02 14:25:46 +02:00
|
|
|
projectName: string,
|
2023-04-04 10:46:28 +02:00
|
|
|
) => {
|
|
|
|
const project = projectName || 'default';
|
|
|
|
cy.visit(`/projects/${project}/features/${featureToggleName}/variants`);
|
|
|
|
cy.wait(1000);
|
|
|
|
cy.intercept(
|
|
|
|
'PATCH',
|
|
|
|
`/api/admin/projects/${project}/features/${featureToggleName}/environments/development/variants`,
|
2023-10-02 14:25:46 +02:00
|
|
|
(req) => {
|
2023-04-04 10:46:28 +02:00
|
|
|
variants.forEach((variant, index) => {
|
|
|
|
expect(req.body[index].op).to.equal('add');
|
|
|
|
expect(req.body[index].path).to.equal(`/${index}`);
|
|
|
|
expect(req.body[index].value.name).to.equal(variant);
|
|
|
|
expect(req.body[index].value.weight).to.equal(
|
2023-10-02 14:25:46 +02:00
|
|
|
1000 / variants.length,
|
2023-04-04 10:46:28 +02:00
|
|
|
);
|
|
|
|
});
|
2023-10-02 14:25:46 +02:00
|
|
|
},
|
2023-04-04 10:46:28 +02:00
|
|
|
).as('variantCreation');
|
|
|
|
|
|
|
|
cy.get('[data-testid=ADD_VARIANT_BUTTON]').first().click();
|
|
|
|
cy.wait(500);
|
|
|
|
variants.forEach((variant, index) => {
|
|
|
|
cy.get('[data-testid=VARIANT_NAME_INPUT]').eq(index).type(variant);
|
|
|
|
index + 1 < variants.length &&
|
|
|
|
cy.get('[data-testid=MODAL_ADD_VARIANT_BUTTON]').first().click();
|
|
|
|
});
|
|
|
|
|
|
|
|
cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').first().click();
|
|
|
|
return cy.wait('@variantCreation');
|
|
|
|
};
|
|
|
|
|
|
|
|
export const deleteVariant_UI = (
|
|
|
|
featureToggleName: string,
|
|
|
|
variant: string,
|
2023-10-02 14:25:46 +02:00
|
|
|
projectName?: string,
|
2023-04-04 10:46:28 +02:00
|
|
|
): Chainable<any> => {
|
|
|
|
const project = projectName || 'default';
|
|
|
|
cy.visit(`/projects/${project}/features/${featureToggleName}/variants`);
|
|
|
|
cy.get('[data-testid=EDIT_VARIANTS_BUTTON]').click();
|
|
|
|
cy.wait(300);
|
|
|
|
cy.get(`[data-testid=VARIANT_DELETE_BUTTON_${variant}]`).first().click();
|
|
|
|
|
|
|
|
cy.intercept(
|
|
|
|
'PATCH',
|
|
|
|
`/api/admin/projects/${project}/features/${featureToggleName}/environments/development/variants`,
|
2023-10-02 14:25:46 +02:00
|
|
|
(req) => {
|
2023-04-04 10:46:28 +02:00
|
|
|
expect(req.body[0].op).to.equal('remove');
|
|
|
|
expect(req.body[0].path).to.equal('/1');
|
|
|
|
expect(req.body[1].op).to.equal('replace');
|
|
|
|
expect(req.body[1].path).to.equal('/0/weight');
|
|
|
|
expect(req.body[1].value).to.equal(1000);
|
2023-10-02 14:25:46 +02:00
|
|
|
},
|
2023-04-04 10:46:28 +02:00
|
|
|
).as('delete');
|
|
|
|
|
|
|
|
cy.get('[data-testid=DIALOGUE_CONFIRM_ID]').click();
|
|
|
|
return cy.wait('@delete');
|
|
|
|
};
|
|
|
|
|
|
|
|
export const logout_UI = (): Chainable<any> => {
|
|
|
|
return cy.visit('/logout');
|
|
|
|
};
|