Skip to content

Commit

Permalink
test: Add permissions and roles e2e tests (#4172)
Browse files Browse the repository at this point in the history
Co-authored-by: kyle-ssg <[email protected]>
Co-authored-by: Matthew Elwell <[email protected]>
Co-authored-by: Kim Gustyr <[email protected]>
  • Loading branch information
4 people authored Jan 21, 2025
1 parent 638e97d commit 4702aaa
Show file tree
Hide file tree
Showing 30 changed files with 785 additions and 247 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/.reusable-docker-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ on:
required: false
default: ubuntu-latest
secrets:
gcr-token:
GCR_TOKEN:
description: A token to use for logging into Github Container Registry. If not provided, login does not occur.
required: false
SLACK_TOKEN:
description: A token to use uploading test failures to slack.
required: false

jobs:
run-e2e:
Expand All @@ -43,7 +46,7 @@ jobs:
id-token: write

env:
GCR_TOKEN: ${{ secrets.gcr-token }}
GCR_TOKEN: ${{ secrets.GCR_TOKEN }}

steps:
- name: Cloning repo
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/platform-docker-build-test-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ jobs:
concurrency: 2
- tests: versioning
concurrency: 1
- tests: organisation-permission environment-permission project-permission roles
concurrency: 1

docker-publish-api:
needs: [docker-build-api, run-e2e-tests]
Expand Down
24 changes: 23 additions & 1 deletion .github/workflows/platform-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ jobs:
concurrency: ${{ matrix.args.concurrency }}
tests: ${{ matrix.args.tests }}
secrets:
gcr-token: ${{ needs.permissions-check.outputs.can-write == 'true' && secrets.GITHUB_TOKEN || '' }}
GCR_TOKEN: ${{ needs.permissions-check.outputs.can-write == 'true' && secrets.GITHUB_TOKEN || '' }}
SLACK_TOKEN: ${{ needs.permissions-check.outputs.can-write == 'true' && secrets.SLACK_TOKEN || '' }}

strategy:
matrix:
Expand All @@ -154,3 +155,24 @@ jobs:
concurrency: 2
- tests: versioning
concurrency: 1

run-e2e-tests-private-cloud:
if: needs.permissions-check.outputs.can-write == 'true' && !cancelled()
needs: [permissions-check, docker-build-private-cloud, docker-build-e2e]
uses: ./.github/workflows/.reusable-docker-e2e-tests.yml
with:
runs-on: ${{ matrix.runs-on }}
e2e-image: ${{ needs.docker-build-e2e.outputs.image }}
api-image: ${{ needs.docker-build-private-cloud.outputs.image }}
concurrency: ${{ matrix.args.concurrency }}
tests: ${{ matrix.args.tests }}
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 || '' }}

strategy:
matrix:
runs-on: [ubuntu-latest, ARM64-2c]
args:
- tests: organisation-permission environment-permission project-permission roles
concurrency: 1
4 changes: 2 additions & 2 deletions api/e2etests/e2e_seed_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
CREATE_PROJECT,
MANAGE_USER_GROUPS,
)
from organisations.subscriptions.constants import SCALE_UP
from organisations.subscriptions.constants import ENTERPRISE
from projects.models import Project, UserProjectPermission
from users.models import FFAdminUser, UserPermissionGroup

Expand Down Expand Up @@ -129,7 +129,7 @@ def seed_data() -> None:
]
# Upgrade organisation seats
Subscription.objects.filter(organisation__in=org_admin.organisations.all()).update(
max_seats=8, plan=SCALE_UP, subscription_id="test_subscription_id"
max_seats=8, plan=ENTERPRISE, subscription_id="test_subscription_id"
)

# Create projects and environments
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from rest_framework.test import APIClient

from organisations.models import Subscription
from organisations.subscriptions.constants import SCALE_UP
from organisations.subscriptions.constants import ENTERPRISE
from users.models import FFAdminUser


Expand Down Expand Up @@ -43,7 +43,7 @@ def test_e2e_teardown(settings, db) -> None:
organisation__in=e2e_user.organisations.all()
):
assert subscription.max_seats == 8
assert subscription.plan == SCALE_UP
assert subscription.plan == ENTERPRISE
assert subscription.subscription_id == "test_subscription_id"


Expand Down
1 change: 1 addition & 0 deletions frontend/common/stores/organisation-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ store.dispatcherIndex = Dispatcher.register(store, (payload) => {
controller.invalidateInviteLink(action.link)
break
case Actions.LOGOUT:
store.model = null
store.id = null
break
default:
Expand Down
3 changes: 3 additions & 0 deletions frontend/common/stores/project-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ store.dispatcherIndex = Dispatcher.register(store, (payload) => {
case Actions.EDIT_PROJECT:
controller.editProject(action.id, action.project)
break
case Actions.LOGOUT:
store.model = null
break
default:
}
})
Expand Down
7 changes: 7 additions & 0 deletions frontend/e2e/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
const E2E_EMAIL_DOMAIN = 'flagsmithe2etestdomain.io'
export const E2E_SIGN_UP_USER = `e2e_signup_user@${E2E_EMAIL_DOMAIN}`
export const E2E_USER = `e2e_user@${E2E_EMAIL_DOMAIN}`
export const E2E_NON_ADMIN_USER = `e2e_user@${E2E_EMAIL_DOMAIN}`
//e2e_non_admin_user_with_project_permissions@flagsmithe2etestdomain.io
export const E2E_NON_ADMIN_USER_WITH_ORG_PERMISSIONS = `e2e_non_admin_user_with_org_permissions@${E2E_EMAIL_DOMAIN}`
export const E2E_NON_ADMIN_USER_WITH_PROJECT_PERMISSIONS = `e2e_non_admin_user_with_project_permissions@${E2E_EMAIL_DOMAIN}`
export const E2E_NON_ADMIN_USER_WITH_PROJECT_READ_PERMISSIONS = `e2e_non_admin_user_with_project_read_permissions@${E2E_EMAIL_DOMAIN}`
export const E2E_NON_ADMIN_USER_WITH_ENV_PERMISSIONS = `e2e_non_admin_user_with_env_permissions@${E2E_EMAIL_DOMAIN}`
export const E2E_NON_ADMIN_USER_WITH_A_ROLE = `e2e_non_admin_user_with_a_role@${E2E_EMAIL_DOMAIN}`
export const E2E_CHANGE_MAIL = `e2e_change_email@${E2E_EMAIL_DOMAIN}`
export const PASSWORD = 'Str0ngp4ssw0rd!'
107 changes: 106 additions & 1 deletion frontend/e2e/helpers.cafe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,24 @@ export const waitForElementVisible = async (selector: string) => {
.ok(`waitForElementVisible(${selector})`, { timeout: LONG_TIMEOUT })
}

export const waitForElementNotClickable = async (selector: string) => {
logUsingLastSection(`Waiting element visible ${selector}`)
await t
.expect(Selector(selector).visible)
.ok(`waitForElementVisible(${selector})`, { timeout: LONG_TIMEOUT })
await t
.expect(Selector(selector).hasAttribute('disabled')).ok()
}

export const waitForElementClickable = async (selector: string) => {
logUsingLastSection(`Waiting element visible ${selector}`)
await t
.expect(Selector(selector).visible)
.ok(`waitForElementVisible(${selector})`, { timeout: LONG_TIMEOUT })
await t
.expect(Selector(selector).hasAttribute('disabled')).notOk()
}

export const logResults = async (requests: LoggedRequest[], t) => {
if (!t.testRun?.errs?.length) {
log('Finished without errors')
Expand Down Expand Up @@ -88,6 +106,17 @@ export const click = async (selector: string) => {
.click(selector)
}

export const clickByText = async (text:string, element = 'button') => {
logUsingLastSection(`Click by text ${text} ${element}`)
const selector = Selector(element).withText(text);
await t
.scrollIntoView(selector)
.expect(Selector(selector).hasAttribute('disabled'))
.notOk('ready for testing', { timeout: 5000 })
.hover(selector)
.click(selector)
}

export const gotoSegments = async () => {
await click('#segments-link')
}
Expand All @@ -102,6 +131,28 @@ export const getLogger = () =>
stringifyResponseBody: true,
})

export const createRole = async (roleName: string, index: number, users: number[]) => {
await click(byId('tab-item-roles'))
await click(byId('create-role'))
await setText(byId('role-name'), roleName)
await click(byId('save-role'))
await click(byId(`role-${index}`))
await click(byId('members-tab'))
await click(byId('assigned-users'))
for (const userId of users) {
await click(byId(`assignees-list-item-${userId}`))
}
await closeModal()
}


export const editRoleMembers = async (index:number)=>{
await click(byId('tab-item-roles'))
await click(byId('create-role'))
await setText(byId('role-name'), roleName)
await click(byId('save-role'))
}

export const gotoTraits = async () => {
await click('#features-link')
await click('#users-link')
Expand Down Expand Up @@ -219,6 +270,12 @@ export const saveFeatureSegments = async () => {
await waitForElementNotExist('#create-feature-modal')
}

export const createEnvironment = async (name:string) => {
await setText('[name="envName"]', name)
await click('#create-env-btn')
await waitForElementVisible(byId(`switch-environment-${name.toLowerCase()}-active`))
}

export const goToUser = async (index: number) => {
await click('#features-link')
await click('#users-link')
Expand Down Expand Up @@ -257,7 +314,7 @@ export const login = async (email: string, password: string) => {
await click('#login-btn')
await waitForElementVisible('#project-manage-widget')
}
export const logout = async (t) => {
export const logout = async () => {
await click('#account-settings-link')
await click('#logout-link')
await waitForElementVisible('#login-page')
Expand Down Expand Up @@ -413,6 +470,14 @@ export const toggleFeature = async (index: number, toValue: boolean) => {
)
}

export const setUserPermissions = async (index: number, toValue: boolean) => {
await click(byId(`feature-switch-${index}${toValue ? '-off' : 'on'}`))
await click('#confirm-toggle-feature-btn')
await waitForElementVisible(
byId(`feature-switch-${index}${toValue ? '-on' : 'off'}`),
)
}

export const setSegmentRule = async (
ruleIndex: number,
orIndex: number,
Expand Down Expand Up @@ -478,4 +543,44 @@ export const refreshUntilElementVisible = async (selector: string, maxRetries=20
return t.scrollIntoView(element)
}

const permissionsMap = {
'CREATE_PROJECT': 'organisation',
'MANAGE_USERS': 'organisation',
'MANAGE_USER_GROUPS': 'organisation',
'VIEW_PROJECT': 'project',
'CREATE_ENVIRONMENT': 'project',
'DELETE_FEATURE': 'project',
'CREATE_FEATURE': 'project',
'MANAGE_SEGMENTS': 'project',
'VIEW_AUDIT_LOG': 'project',
'VIEW_ENVIRONMENT': 'environment',
'UPDATE_FEATURE_STATE': 'environment',
'MANAGE_IDENTITIES': 'environment',
'CREATE_CHANGE_REQUEST': 'environment',
'APPROVE_CHANGE_REQUEST': 'environment',
'VIEW_IDENTITIES': 'environment',
'MANAGE_SEGMENT_OVERRIDES': 'environment',
'MANAGE_TAGS': 'project',
} as const;


export const setUserPermission = async (email: string, permission: keyof typeof permissionsMap | 'ADMIN', entityName:string|null, entityLevel?: 'project'|'environment'|'organisation', parentName?: string) => {
await click(byId('users-and-permissions'))
await click(byId(`user-${email}`))
const level = permissionsMap[permission] || entityLevel
await click(byId(`${level}-permissions-tab`))
if(parentName) {
await clickByText(parentName, 'a')
}
if(entityName) {
await click(byId(`permissions-${entityName.toLowerCase()}`))
}
if(permission==='ADMIN') {
await click(byId(`admin-switch-${level}`))
} else {
await click(byId(`permission-switch-${permission}`))
}
await closeModal()
}

export default {}
13 changes: 9 additions & 4 deletions frontend/e2e/index.cafe.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@ createTestCafe()
testcafe = tc;
await new Promise((resolve) => {
process.env.PORT = 3000;
server = fork('./api/index');
server.on('message', () => {
resolve();
});
console.log(process.env.E2E_LOCAL)
if(process.env.E2E_LOCAL) {
resolve()
} else {
server = fork('./api/index');
server.on('message', () => {
resolve();
});
}
});
const runner = testcafe.createRunner()
const args = process.argv.splice(2).map(value => value.toLowerCase());
Expand Down
21 changes: 20 additions & 1 deletion frontend/e2e/init.cafe.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import projectTest from './tests/project-test'
import { testSegment1, testSegment2, testSegment3 } from './tests/segment-test'
import initialiseTests from './tests/initialise-tests'
import flagTests from './tests/flag-tests'
import versioningTests from './tests/versioning-tests';
import versioningTests from './tests/versioning-tests'
import organisationPermissionTest from './tests/organisation-permission-test'
import projectPermissionTest from './tests/project-permission-test'
import environmentPermissionTest from './tests/environment-permission-test'
import rolesTest from './tests/roles-test'

require('dotenv').config()

Expand Down Expand Up @@ -124,3 +128,18 @@ test('Versioning', async () => {
await versioningTests()
await logout()
})

test('Organisation-permission', async () => {
await organisationPermissionTest()
await logout()
})

test('Project-permission', async () => {
await projectPermissionTest()
await logout()
})

test('Environment-permission', async () => {
await environmentPermissionTest()
await logout()
})
Loading

0 comments on commit 4702aaa

Please sign in to comment.