Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .github/workflows/.reusable-docker-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ on:
description: The concurrent number of browsers to be used on testing
required: false
default: 16
pg-max-connections:
type: number
description: Postgres max_connections for the E2E database. Raise above the default of 100 for high-concurrency runs (e.g. private-cloud) to avoid connection exhaustion.
required: false
default: 100
runs-on:
type: string
description: The runner label to use. Defaults to `depot-ubuntu-latest`
Expand Down Expand Up @@ -115,6 +120,7 @@ jobs:
E2E_IMAGE: ${{ inputs.e2e-image }}
E2E_CONCURRENCY: ${{ inputs.concurrency }}
E2E_RETRIES: 2
PG_MAX_CONNECTIONS: ${{ inputs.pg-max-connections }}
VISUAL_REGRESSION: ${{ inputs.visual-regression && '1' || '' }}
VISUAL_REGRESSION_ARGS: ${{ inputs.visual-regression-update && '--update-snapshots' || '' }}
SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/platform-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ jobs:
api-image: ${{ needs.docker-build-private-cloud.outputs.image }}
args: --grep "@oss|@enterprise"
visual-regression: ${{ matrix.runs-on == 'depot-ubuntu-latest-16' }}
# TODO: identify whether the E2E leaks connections, leading to the connection bloat
pg-max-connections: 200
secrets:
GCR_TOKEN: ${{ needs.permissions-check.outputs.can-write == 'true' && secrets.GITHUB_TOKEN || '' }}
SLACK_TOKEN: ${{ needs.permissions-check.outputs.can-write == 'true' && secrets.SLACK_TOKEN || '' }}
Expand Down
1 change: 1 addition & 0 deletions frontend/docker-compose-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ version: '3'
services:
db:
image: docker.io/library/postgres:15-alpine
command: postgres -c max_connections=${PG_MAX_CONNECTIONS:-100}
environment:
POSTGRES_PASSWORD: password
POSTGRES_DB: flagsmith
Expand Down
33 changes: 31 additions & 2 deletions frontend/e2e/helpers/e2e-helpers.playwright.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Page, expect } from '@playwright/test';
import { LONG_TIMEOUT, byId, log, logUsingLastSection, getFlagsmith } from './utils.playwright';
import { LONG_TIMEOUT, SHORT_TIMEOUT, byId, log, logUsingLastSection, getFlagsmith } from './utils.playwright';

// Re-export for backwards compatibility
export { LONG_TIMEOUT, byId, log, logUsingLastSection, getFlagsmith };
export { LONG_TIMEOUT, SHORT_TIMEOUT, byId, log, logUsingLastSection, getFlagsmith };


export type MultiVariate = { value: string; weight: number };
Expand Down Expand Up @@ -40,6 +40,26 @@ export class E2EHelpers {
});
}

// Asserts a navbar link is reachable. A link can either be shown inline, or
// collapsed into the OverflowNav "more" menu when the navbar runs out of
// horizontal space (e.g. when a feature flag adds an extra item). Try inline
// first; if it isn't visible, open the overflow menu and retry. Only fails if
// the link is reachable via neither.
async waitForNavElementVisible(selector: string) {
logUsingLastSection(`Waiting nav element visible (inline or overflow) ${selector}`);
const element = this.page.locator(selector).first();
try {
await element.waitFor({ state: 'visible', timeout: SHORT_TIMEOUT });
return;
} catch {
const overflowButton = this.page.locator(byId('overflow-nav-button')).first();
if (await overflowButton.isVisible()) {
await overflowButton.click();
}
await element.waitFor({ state: 'visible', timeout: LONG_TIMEOUT });
}
}

async waitForElementNotClickable(selector: string) {
logUsingLastSection(`Waiting element not clickable ${selector}`);
const element = this.page.locator(selector).first();
Expand Down Expand Up @@ -898,11 +918,20 @@ export class E2EHelpers {
if (entityName) {
await this.click(byId(`permissions-${entityName.toLowerCase()}`));
}
// Wait for the permission save (POST/PUT) to commit before closing, so a later read can't race the grant.
const savePromise = this.page.waitForResponse(
(res) =>
res.url().includes('/user-permissions/') &&
['POST', 'PUT'].includes(res.request().method()) &&
res.ok(),
{ timeout: LONG_TIMEOUT },
);
if (permission === 'ADMIN') {
await this.click(byId(`admin-switch-${level}`));
} else {
await this.click(byId(`permission-switch-${permission}`));
}
await savePromise;
await this.closeModal();
}

Expand Down
1 change: 1 addition & 0 deletions frontend/e2e/helpers/utils.playwright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IFlagsmith } from '@flagsmith/flagsmith/types';
import Project from '../../common/project';

export const LONG_TIMEOUT = 20000;
export const SHORT_TIMEOUT = 5000;

export const byId = (id: string) => `[data-test="${id}"]`;

Expand Down
3 changes: 2 additions & 1 deletion frontend/e2e/tests/project-permission-test.pw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ test.describe('Project Permission Tests', () => {
waitForElementNotExist,
waitForElementVisible,
waitForFeatureSwitch,
waitForNavElementVisible,
waitForPageFullyLoaded,
} = createHelpers(page);

Expand Down Expand Up @@ -84,7 +85,7 @@ test.describe('Project Permission Tests', () => {
log('User with ADMIN permissions can set project settings')
await login(E2E_NON_ADMIN_USER_WITH_PROJECT_PERMISSIONS, PASSWORD)
await gotoProject(PROJECT_NAME)
await waitForElementVisible('#project-settings-link')
await waitForNavElementVisible('#project-settings-link')
await logout()
log('Remove user as project ADMIN')
await login(E2E_USER, PASSWORD)
Expand Down
1 change: 1 addition & 0 deletions frontend/web/components/navigation/OverflowNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const OverflowNav: FC<OverflowNavProps> = ({
style={{ height: buttonWidth, width: buttonWidth }}
onClick={() => setOpen(!open)}
theme='secondary'
data-test='overflow-nav-button'
className='d-flex align-items-center justify-content-center m-0 p-0'
>
<IonIcon className='fs-small' icon={icon} />
Expand Down
Loading