diff --git a/frontend/.github/workflows/e2e.project-access.yml b/frontend/.github/workflows/e2e.project-access.yml
new file mode 100644
index 0000000000..f2dfff64df
--- /dev/null
+++ b/frontend/.github/workflows/e2e.project-access.yml
@@ -0,0 +1,25 @@
+name: e2e:project-access
+# https://docs.github.com/en/actions/reference/events-that-trigger-workflows
+on: [deployment_status]
+jobs:
+ e2e:
+ # only runs this job on successful deploy
+ if: github.event_name == 'deployment_status' && github.event.deployment_status.state == 'success'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: |
+ echo "$GITHUB_CONTEXT"
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Run Cypress
+ uses: cypress-io/github-action@v2
+ with:
+ env: AUTH_USER=admin,AUTH_PASSWORD=unleash4all
+ config: baseUrl=${{ github.event.deployment_status.target_url }}
+ record: true
+ spec: cypress/integration/projects/access/project-access.spec.ts
+ env:
+ CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
diff --git a/frontend/cypress/integration/projects/access/project-access.spec.ts b/frontend/cypress/integration/projects/access/project-access.spec.ts
new file mode 100644
index 0000000000..cd9039449e
--- /dev/null
+++ b/frontend/cypress/integration/projects/access/project-access.spec.ts
@@ -0,0 +1,147 @@
+///
+
+import {
+ PA_ASSIGN_BUTTON_ID,
+ PA_ASSIGN_CREATE_ID,
+ PA_EDIT_BUTTON_ID,
+ PA_REMOVE_BUTTON_ID,
+ PA_ROLE_ID,
+ PA_USERS_GROUPS_ID,
+ PA_USERS_GROUPS_TITLE_ID,
+} from '../../../../src/utils/testIds';
+
+export {};
+const baseUrl = Cypress.config().baseUrl;
+const randomId = String(Math.random()).split('.')[1];
+const groupAndProjectName = `group-e2e-${randomId}`;
+const userName = `user-e2e-${randomId}`;
+const groupIds: any[] = [];
+const userIds: any[] = [];
+
+// Disable all active splash pages by visiting them.
+const disableActiveSplashScreens = () => {
+ cy.visit(`/splash/operators`);
+};
+
+describe('project-access', () => {
+ before(() => {
+ disableActiveSplashScreens();
+ cy.login();
+ for (let i = 1; i <= 2; i++) {
+ const name = `${i}-${userName}`;
+ cy.request('POST', `${baseUrl}/api/admin/user-admin`, {
+ name: name,
+ email: `${name}@test.com`,
+ sendEmail: false,
+ rootRole: 3,
+ })
+ .as(name)
+ .then(response => {
+ const id = response.body.id;
+ userIds.push(id);
+ cy.request('POST', `${baseUrl}/api/admin/groups`, {
+ name: `${i}-${groupAndProjectName}`,
+ users: [{ user: { id: id } }],
+ }).then(response => {
+ const id = response.body.id;
+ groupIds.push(id);
+ });
+ });
+ }
+ cy.request('POST', `${baseUrl}/api/admin/projects`, {
+ id: groupAndProjectName,
+ name: groupAndProjectName,
+ });
+ });
+
+ after(() => {
+ userIds.forEach(id =>
+ cy.request('DELETE', `${baseUrl}/api/admin/user-admin/${id}`)
+ );
+ groupIds.forEach(id =>
+ cy.request('DELETE', `${baseUrl}/api/admin/groups/${id}`)
+ );
+
+ cy.request(
+ 'DELETE',
+ `${baseUrl}/api/admin/projects/${groupAndProjectName}`
+ );
+ });
+
+ beforeEach(() => {
+ cy.login();
+ cy.visit(`/projects/${groupAndProjectName}/access`);
+ if (document.querySelector("[data-testid='CLOSE_SPLASH']")) {
+ cy.get("[data-testid='CLOSE_SPLASH']").click();
+ }
+ });
+
+ it('can assign permissions to user', () => {
+ cy.get(`[data-testid='${PA_ASSIGN_BUTTON_ID}']`).click();
+
+ cy.intercept(
+ 'POST',
+ `/api/admin/projects/${groupAndProjectName}/role/4/access`
+ ).as('assignAccess');
+
+ cy.get(`[data-testid='${PA_USERS_GROUPS_ID}']`).click();
+ cy.contains(`1-${userName}`).click();
+ cy.get(`[data-testid='${PA_USERS_GROUPS_TITLE_ID}']`).click();
+ cy.get(`[data-testid='${PA_ROLE_ID}']`).click();
+ cy.contains('full control over the project').click({ force: true });
+
+ cy.get(`[data-testid='${PA_ASSIGN_CREATE_ID}']`).click();
+ cy.wait('@assignAccess');
+ cy.contains(`1-${userName}`);
+ });
+
+ it('can assign permissions to group', () => {
+ cy.get(`[data-testid='${PA_ASSIGN_BUTTON_ID}']`).click();
+
+ cy.intercept(
+ 'POST',
+ `/api/admin/projects/${groupAndProjectName}/role/4/access`
+ ).as('assignAccess');
+
+ cy.get(`[data-testid='${PA_USERS_GROUPS_ID}']`).click();
+ cy.contains(`1-${groupAndProjectName}`).click({ force: true });
+ cy.get(`[data-testid='${PA_USERS_GROUPS_TITLE_ID}']`).click();
+ cy.get(`[data-testid='${PA_ROLE_ID}']`).click();
+ cy.contains('full control over the project').click({ force: true });
+
+ cy.get(`[data-testid='${PA_ASSIGN_CREATE_ID}']`).click();
+ cy.wait('@assignAccess');
+ cy.contains(`1-${groupAndProjectName}`);
+ });
+
+ it('can edit role', () => {
+ cy.get(`[data-testid='${PA_EDIT_BUTTON_ID}']`).first().click();
+
+ cy.intercept(
+ 'PUT',
+ `/api/admin/projects/${groupAndProjectName}/groups/${groupIds[0]}/roles/5`
+ ).as('editAccess');
+
+ cy.get(`[data-testid='${PA_ROLE_ID}']`).click();
+ cy.contains('within a project are allowed').click({ force: true });
+
+ cy.get(`[data-testid='${PA_ASSIGN_CREATE_ID}']`).click();
+ cy.wait('@editAccess');
+ cy.get("td span:contains('Owner')").should('have.length', 2);
+ cy.get("td span:contains('Member')").should('have.length', 1);
+ });
+
+ it('can remove access', () => {
+ cy.get(`[data-testid='${PA_REMOVE_BUTTON_ID}']`).first().click();
+
+ cy.intercept(
+ 'DELETE',
+ `/api/admin/projects/${groupAndProjectName}/groups/${groupIds[0]}/roles/5`
+ ).as('removeAccess');
+
+ cy.contains("Yes, I'm sure").click();
+
+ cy.wait('@removeAccess');
+ cy.contains(`1-${groupAndProjectName} has been removed from project`);
+ });
+});
diff --git a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx
index c5765402b4..8b0144468f 100644
--- a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx
+++ b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectAccessAssign.tsx
@@ -27,6 +27,12 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import { ProjectRoleDescription } from './ProjectRoleDescription/ProjectRoleDescription';
import { useNavigate } from 'react-router-dom';
import { GO_BACK } from 'constants/navigate';
+import {
+ PA_ASSIGN_CREATE_ID,
+ PA_ROLE_ID,
+ PA_USERS_GROUPS_ID,
+ PA_USERS_GROUPS_TITLE_ID,
+} from 'utils/testIds';
const StyledForm = styled('form')(() => ({
display: 'flex',
@@ -282,11 +288,14 @@ export const ProjectAccessAssign = ({
>
-
+
Select the {entityType}