-
Notifications
You must be signed in to change notification settings - Fork 562
feat: cohort synchronisation keys and provider connect modals #8420
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
1ae05b3
feat: cohort sync key management in environment settings
Zaimwa9 bd96a29
feat: connect cohort provider modals for Mixpanel and Amplitude
Zaimwa9 1f8d0a8
test: cohort synchronisation keys e2e coverage
Zaimwa9 5dba9df
fix: address cohort sync keys review feedback
Zaimwa9 29fbf47
fix: modal router crash, dual permission gate and absolute provider URLs
Zaimwa9 7f71765
fix: amplitude connect step matches destination workflow without URL
Zaimwa9 7260b79
fix: dual permission tooltip copy and scoped e2e key revocation
Zaimwa9 cb70c79
fix: cohort keys list header layout
Zaimwa9 114b160
fix: rename environment settings tab to Cohorts and remove extra webh…
Zaimwa9 919e332
fix: cohort key modal polish, identities env pre-fill and env-scoped …
Zaimwa9 27d36ff
fix: surface sync key query errors instead of empty state
Zaimwa9 fd696c6
fix: tighten show-once warning spacing
Zaimwa9 4d2225f
fix: use Link component for docs and settings links
Zaimwa9 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| import { test, expect } from '../test-setup' | ||
| import { | ||
| byId, | ||
| log, | ||
| createHelpers, | ||
| getFlagsmith, | ||
| LONG_TIMEOUT, | ||
| } from '../helpers' | ||
| import { E2E_USER, PASSWORD, E2E_TEST_PROJECT } from '../config' | ||
|
|
||
| const COHORT_PROVIDERS = ['amplitude', 'mixpanel'] as const | ||
|
|
||
| type CohortProvider = (typeof COHORT_PROVIDERS)[number] | ||
|
|
||
| type SegmentSourceFlagEntry = { | ||
| active?: boolean | ||
| name?: string | ||
| visible?: boolean | ||
| } | ||
|
|
||
| // Mirrors `getSegmentSources` in CreateSegmentSourcesModal: only an entry that | ||
| // is visible AND active opens the connect modal (and shows the environment tab). | ||
| const getActiveCohortProviders = (config: unknown): CohortProvider[] => { | ||
| if (!Array.isArray(config)) { | ||
| return [] | ||
| } | ||
| return (config as SegmentSourceFlagEntry[]) | ||
| .filter( | ||
| (entry) => | ||
| COHORT_PROVIDERS.includes(entry?.name as CohortProvider) && | ||
| entry?.visible !== false && | ||
| entry?.active === true, | ||
| ) | ||
| .map((entry) => entry.name as CohortProvider) | ||
| } | ||
|
|
||
| test.describe('Cohort Synchronisation Keys Tests', () => { | ||
| test('Cohort synchronisation keys can be created from a provider connection and revoked in Environment Settings @oss', async ({ | ||
| page, | ||
| }) => { | ||
| const { | ||
| click, | ||
| gotoProject, | ||
| gotoSegments, | ||
| login, | ||
| setText, | ||
| waitForElementVisible, | ||
| waitForModalToClose, | ||
| } = createHelpers(page) | ||
|
|
||
| const flagsmith = await getFlagsmith() | ||
| const activeProviders = flagsmith.hasFeature( | ||
| 'create_segment_with_external_sources', | ||
| ) | ||
| ? getActiveCohortProviders( | ||
| flagsmith.getValue('create_segment_with_external_sources', { | ||
| fallback: null, | ||
| json: true, | ||
| }), | ||
| ) | ||
| : [] | ||
| test.skip( | ||
| activeProviders.length === 0, | ||
| 'No active Amplitude or Mixpanel entry in `create_segment_with_external_sources`, so the cohort synchronisation UI is unreachable', | ||
| ) | ||
|
|
||
| // With both providers active each key goes through a different provider; | ||
| // with one, the second key goes through its "Create a new key" state. | ||
| const firstProvider = activeProviders[0] | ||
| const secondProvider = activeProviders[1] ?? activeProviders[0] | ||
|
|
||
| const runId = Date.now() | ||
| const keyOne = `e2e key one ${runId}` | ||
| const keyTwo = `e2e key two ${runId}` | ||
| const keyThree = `e2e key three ${runId}` | ||
|
|
||
| const connectModal = page.locator('.connect-cohort-provider') | ||
| const envSelect = connectModal.locator(byId('connect-provider-env-select')) | ||
|
|
||
| const openConnectModal = async (provider: CohortProvider) => { | ||
| await click(byId('show-create-segment-btn')) | ||
| await click(byId(`segment-source-${provider}`)) | ||
| await waitForElementVisible(byId('connect-provider-done')) | ||
| await expect(envSelect).toBeVisible({ timeout: LONG_TIMEOUT }) | ||
| // The modal defaults to the alphabetically first environment; assert it | ||
| // resolved so both keys are created against the same environment. | ||
| const label = (await envSelect.innerText()).trim() | ||
| expect(label).not.toBe('') | ||
| expect(label).not.toBe('Select an Environment') | ||
| return label | ||
| } | ||
|
|
||
| const createKeyInConnectModal = async (name: string) => { | ||
| // Step 1 renders either the create form or the existing-keys state, | ||
| // depending on whether this environment already has keys. | ||
| await connectModal | ||
| .locator( | ||
| `${byId('connect-provider-key-name')}, ${byId( | ||
| 'connect-provider-new-key', | ||
| )}`, | ||
| ) | ||
| .first() | ||
| .waitFor({ state: 'visible', timeout: LONG_TIMEOUT }) | ||
| const newKeyButton = connectModal.locator( | ||
| byId('connect-provider-new-key'), | ||
| ) | ||
| if (await newKeyButton.isVisible()) { | ||
| await click(byId('connect-provider-new-key')) | ||
| } | ||
| await setText(byId('connect-provider-key-name'), name) | ||
| await click(byId('connect-provider-create-key')) | ||
| await expect( | ||
| connectModal.locator(byId('connect-provider-key-value')), | ||
| ).toHaveValue(/.+/, { timeout: LONG_TIMEOUT }) | ||
| await click(byId('connect-provider-done')) | ||
| await waitForModalToClose() | ||
| } | ||
|
|
||
| log('Login') | ||
| await login(E2E_USER, PASSWORD) | ||
| await gotoProject(E2E_TEST_PROJECT) | ||
| await gotoSegments() | ||
|
|
||
| log(`Create the first key while connecting ${firstProvider}`) | ||
| const environmentLabel = await openConnectModal(firstProvider) | ||
| await createKeyInConnectModal(keyOne) | ||
|
|
||
| log(`Create the second key while connecting ${secondProvider}`) | ||
| expect(await openConnectModal(secondProvider)).toBe(environmentLabel) | ||
| await waitForElementVisible(byId('connect-provider-new-key')) | ||
| await expect(connectModal).toContainText(keyOne) | ||
| // This link carries the environment the modal is working against, so | ||
| // following it guarantees we inspect the keys we just created. | ||
| const settingsLink = connectModal.locator( | ||
| 'a[href*="tab=cohorts"]', | ||
| ) | ||
| await expect(settingsLink).toBeVisible() | ||
| const settingsHref = (await settingsLink.getAttribute('href')) ?? '' | ||
| expect(settingsHref).not.toBe('') | ||
| await createKeyInConnectModal(keyTwo) | ||
|
|
||
| log('Open the Cohort Synchronisation tab in Environment Settings') | ||
| await page.goto(settingsHref) | ||
| await waitForElementVisible('#cohort-sync-keys-list') | ||
| const keysList = page.locator('#cohort-sync-keys-list') | ||
| await expect(keysList).toContainText(keyOne) | ||
| await expect(keysList).toContainText(keyTwo) | ||
|
|
||
| log('Create a third key from Environment Settings') | ||
| await click(byId('create-cohort-sync-key')) | ||
| await setText(byId('cohort-sync-key-name'), keyThree) | ||
| await click(byId('cohort-sync-key-create')) | ||
| await expect(page.locator(byId('cohort-sync-key-value'))).toHaveValue( | ||
| /.+/, | ||
| { timeout: LONG_TIMEOUT }, | ||
| ) | ||
| await click(byId('cohort-sync-key-done')) | ||
| await waitForModalToClose() | ||
| await expect(keysList).toContainText(keyThree) | ||
|
|
||
| log('Revoke the keys created by this test') | ||
| for (const name of [keyOne, keyTwo, keyThree]) { | ||
| const row = keysList.locator('.list-item').filter({ hasText: name }) | ||
| await row.locator('[aria-label^="Revoke "]').click() | ||
| await click('#confirm-btn-yes') | ||
| await expect(row).toHaveCount(0, { timeout: LONG_TIMEOUT }) | ||
| } | ||
|
|
||
| log('Verify the test keys are gone') | ||
| for (const name of [keyOne, keyTwo, keyThree]) { | ||
| await expect(page.getByText(name, { exact: true })).toHaveCount(0) | ||
| } | ||
| }) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.