Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 83 additions & 13 deletions frontend/e2e/helpers/e2e-helpers.playwright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,21 @@ export class E2EHelpers {
}
}

// Add a remote config override for a segment picked by name, so other
// segments in a shared project can't shift the selection
async addSegmentOverrideConfigForSegment(segmentName: string, value: string) {
await this.click(byId('segment_overrides'));
// E2E builds render the select's options as plain links
await this.page
.locator('[data-test^="select-segment-option-"]')
.filter({ hasText: segmentName })
.first()
.click();
await this.waitForElementVisible(this.segmentOverrideValueField(0));
await this.setText(this.segmentOverrideValueField(0), value);
await this.click(byId('segment-override-toggle-0'));
}

// Add segment override for remote configs
async addSegmentOverrideConfig(index: number, value: string | number | boolean, selectionIndex: number = 0) {
await this.openSegmentOverride(index, selectionIndex);
Expand Down Expand Up @@ -1126,12 +1141,24 @@ export class E2EHelpers {
}

// Create a change request from feature modal
async createChangeRequest(title: string, description: string) {
async createChangeRequest(
title: string,
description: string,
{
openWith = '#update-feature-btn',
scheduleFor,
}: {
// The tab's save button, e.g. '#update-feature-segments-btn'
openWith?: string;
// A date in the picker's format, e.g. 'January 1, 2099 12:00 PM'
scheduleFor?: string;
} = {},
) {
log(`Create change request: ${title}`);

// Click the update/create change request button
// When four-eyes is enabled, this button says "Create Change Request"
await this.click('#update-feature-btn');
await this.click(openWith);
await this.page.waitForTimeout(1000);

// Fill in title using placeholder
Expand All @@ -1143,17 +1170,26 @@ export class E2EHelpers {
const descField = this.page.locator('textarea[placeholder="Add an optional description..."]');
await descField.fill(description);

// The date picker needs to be set - click on it to trigger current date/time
// Find the date input and click it
const dateInput = this.page.locator('.react-datepicker__input-container input').first();
await dateInput.click();
await this.page.waitForTimeout(300);

// Click "Now" or today's date to set it
// The datepicker should appear - click on today
const todayButton = this.page.locator('.react-datepicker__today-button, .react-datepicker__day--today').first();
await todayButton.click();
await this.page.waitForTimeout(500);
if (scheduleFor) {
const scheduleField = this.page.getByLabel('Schedule Change');
await scheduleField.fill(scheduleFor);
await scheduleField.press('Enter');
await this.page
.getByText('This change will be scheduled to go live at')
.waitFor({ state: 'visible' });
} else {
// The date picker needs to be set - click on it to trigger current date/time
// Find the date input and click it
const dateInput = this.page.locator('.react-datepicker__input-container input').first();
await dateInput.click();
await this.page.waitForTimeout(300);

// Click "Now" or today's date to set it
// The datepicker should appear - click on today
const todayButton = this.page.locator('.react-datepicker__today-button, .react-datepicker__day--today').first();
await todayButton.click();
await this.page.waitForTimeout(500);
}

// Click create/save button - look for enabled button
const saveButton = this.page.locator('button').filter({ hasText: /Save|Create/ }).filter({ hasNotText: 'Cancel' }).last();
Expand All @@ -1167,6 +1203,40 @@ export class E2EHelpers {
await this.waitForToast();
}

// Save an open change request's edits from its feature modal
async updateChangeRequest() {
log('Update change request');
await this.click('#update-feature-btn');
const updateButton = this.page.getByRole('button', { name: 'Update', exact: true });
await updateButton.waitFor({ state: 'visible' });
await this.waitForToastsToClear();
await updateButton.click();
// Checked before it clears, as waitForToast waits for it to go
await expect(
this.page.locator('.toast-message').filter({ hasText: /Updated Change Request #\d+/ }),
).toBeVisible({ timeout: LONG_TIMEOUT });
await this.waitForToastsToClear();
}

// Open the feature modal for an unapproved change request, from its page
async editChangeRequest() {
log('Edit change request');
await this.page.getByRole('button', { name: 'Edit', exact: true }).click();
await this.waitForElementVisible('#create-feature-modal');
}

// Enable feature versioning for the current environment
async enableFeatureVersioning() {
log('Enable feature versioning');
await this.click('#env-settings-link');
await this.click(byId('enable-versioning'));
await this.click('#confirm-btn-yes');
// Feature versioning takes up to a minute to enable on the backend
await this.waitForElementVisible(byId('feature-versioning-enabled'));
await this.waitForElementVisible('.toast-message');
await this.waitForToastsToClear();
}

// Open a change request from the list
async openChangeRequest(index: number = 0) {
log(`Open change request at index ${index}`);
Expand Down
176 changes: 176 additions & 0 deletions frontend/e2e/tests/change-request-test.pw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,180 @@ test.describe('Change Request Tests', () => {

log('Change request test completed successfully')
})

test('A segment override change request in a versioned environment opens on its Segment Overrides diff @enterprise', async ({
page,
}) => {
const {
addSegmentOverrideConfigForSegment,
closeModal,
createChangeRequest,
createEnvironment,
createRemoteConfig,
createSegment,
enableChangeRequests,
enableFeatureVersioning,
gotoChangeRequests,
gotoFeature,
gotoFeatures,
gotoProject,
gotoSegments,
login,
openChangeRequest,
} = createHelpers(page)
const flagsmith = await getFlagsmith()
if (
!flagsmith.hasFeature('segment_change_requests') ||
!flagsmith.hasFeature('feature_versioning')
) {
log('Skipping versioned change request test, feature not enabled.')
test.skip()
return
}

const segmentName = 'cr_versioned_segment'
const featureName = 'cr_versioned_feature'

log('Login as admin')
await login(E2E_USER, PASSWORD)
await gotoProject(E2E_TEST_PROJECT)

log('Create a versioned environment that requires change requests')
await page.click('text="Create Environment"')
await createEnvironment('CR_Versioned_Env')
await enableFeatureVersioning()
await enableChangeRequests(1)

log('Create the segment and feature')
await gotoSegments()
await createSegment(segmentName, [
{ name: 'plan', operator: 'EQUAL', value: 'pro' },
])
await createRemoteConfig({ name: featureName, value: 'initial_value' })

log('Create a change request that only overrides the segment')
await gotoFeatures()
await gotoFeature(featureName)
await addSegmentOverrideConfigForSegment(segmentName, 'pro_value')
await createChangeRequest('Override for pro plan', 'Segment override only', {
openWith: '#update-feature-segments-btn',
})
await closeModal()

log('The diff opens on Segment Overrides, the only tab with changes')
await gotoChangeRequests()
await openChangeRequest(0)
await expect(
page.getByRole('button', { name: /^Segment Overrides/ }),
).toHaveClass(/tab-active/)
})

test('An open change request can be edited from its page @enterprise', async ({
page,
}) => {
const {
closeModal,
createChangeRequest,
createEnvironment,
createRemoteConfig,
editChangeRequest,
enableChangeRequests,
featureValueField,
gotoChangeRequests,
gotoFeature,
gotoFeatures,
gotoProject,
login,
openChangeRequest,
setText,
updateChangeRequest,
} = createHelpers(page)
const flagsmith = await getFlagsmith()
if (!flagsmith.hasFeature('segment_change_requests')) {
log('Skipping change request edit test, feature not enabled.')
test.skip()
return
}

const featureName = 'cr_edit_feature'

log('Login as admin')
await login(E2E_USER, PASSWORD)
await gotoProject(E2E_TEST_PROJECT)

log('Create an environment that requires change requests')
await page.click('text="Create Environment"')
await createEnvironment('CR_Edit_Env')
await enableChangeRequests(1)
await createRemoteConfig({ name: featureName, value: 'initial_value' })

log('Create a change request')
await gotoFeatures()
await gotoFeature(featureName)
await setText(featureValueField(), 'first_value')
await createChangeRequest('Change the value', 'First version')
await closeModal()

log('Edit it from its page')
await gotoChangeRequests()
await openChangeRequest(0)
await editChangeRequest()
await setText(featureValueField(), 'second_value')
await updateChangeRequest()

log('The change request now carries the edited value')
await expect(page.getByText('second_value').first()).toBeVisible()
})

test('A change request scheduled for later shows as a scheduled change @enterprise', async ({
page,
}) => {
const {
closeModal,
createChangeRequest,
createEnvironment,
createRemoteConfig,
enableChangeRequests,
featureValueField,
gotoChangeRequests,
gotoFeature,
gotoFeatures,
gotoProject,
login,
openChangeRequest,
setText,
} = createHelpers(page)
const flagsmith = await getFlagsmith()
if (!flagsmith.hasFeature('segment_change_requests')) {
log('Skipping scheduled change request test, feature not enabled.')
test.skip()
return
}

const featureName = 'cr_schedule_feature'

log('Login as admin')
await login(E2E_USER, PASSWORD)
await gotoProject(E2E_TEST_PROJECT)

log('Create an environment that requires change requests')
await page.click('text="Create Environment"')
await createEnvironment('CR_Schedule_Env')
await enableChangeRequests(1)
await createRemoteConfig({ name: featureName, value: 'initial_value' })

log('Create a change request scheduled for 2099')
await gotoFeatures()
await gotoFeature(featureName)
await setText(featureValueField(), 'scheduled_value')
await createChangeRequest('Scheduled value', 'Goes live in 2099', {
scheduleFor: 'January 1, 2099 12:00 PM',
})
await closeModal()

log('Its page shows it as a scheduled change')
await gotoChangeRequests()
await openChangeRequest(0)
await expect(page.getByText(/go live at 1st Jan 2099/)).toBeVisible()
})
})
Loading