From 6b06634ed9cfe278c66efe09cffb0dc77dd4ccb1 Mon Sep 17 00:00:00 2001 From: Anthony Coughlin <33689325+antowaddle@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:00:46 +0000 Subject: [PATCH] Cypress e2e Project Permissions Tests (#3460) * Jira Task - RHOAIENG-14369. Migrated ODS-2194 to Cypress * Abstracted project checker logic, changed fixture file names and contents --------- Co-authored-by: Noam Manos --- .../dataScienceProject.yaml | 4 - .../testProjectAdminPermissions.yaml | 2 + .../testProjectContributorPermissions.yaml | 2 + .../testProjectCreation.yaml | 4 + .../testProjectAdminPermissions.cy.ts | 76 ++++++++++++++++++ .../testProjectContributorPermissions.cy.ts | 78 +++++++++++++++++++ .../testProjectCreation.cy.ts | 26 +++---- .../src/__tests__/cypress/cypress/types.ts | 8 +- .../cypress/cypress/utils/dataLoader.ts | 11 +++ .../cypress/cypress/utils/projectChecker.ts | 28 +++++++ 10 files changed, 218 insertions(+), 21 deletions(-) delete mode 100644 frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/dataScienceProject.yaml create mode 100644 frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectAdminPermissions.yaml create mode 100644 frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectContributorPermissions.yaml create mode 100644 frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectCreation.yaml create mode 100644 frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectAdminPermissions.cy.ts create mode 100644 frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectContributorPermissions.cy.ts create mode 100644 frontend/src/__tests__/cypress/cypress/utils/dataLoader.ts create mode 100644 frontend/src/__tests__/cypress/cypress/utils/projectChecker.ts diff --git a/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/dataScienceProject.yaml b/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/dataScienceProject.yaml deleted file mode 100644 index 33c7a170b4..0000000000 --- a/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/dataScienceProject.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# testProjectCreation.cy.ts Test Data # -dsProjectName: "Cypress Test Project 1" -dsProjectDescription: "Cypress Test project 1 description." -dsOCProjectName: "cypress-test-project-1" \ No newline at end of file diff --git a/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectAdminPermissions.yaml b/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectAdminPermissions.yaml new file mode 100644 index 0000000000..408d338fa1 --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectAdminPermissions.yaml @@ -0,0 +1,2 @@ +# testProjectAdminPermissions.cy.ts Test Data # +projectPermissionResourceName: "cypress-user-perm-test" \ No newline at end of file diff --git a/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectContributorPermissions.yaml b/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectContributorPermissions.yaml new file mode 100644 index 0000000000..0c8de1223c --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectContributorPermissions.yaml @@ -0,0 +1,2 @@ +# testProjectContributorPermissions.cy.ts Test Data # +projectContributorResourceName: "cypress-user-perm-contr-test" \ No newline at end of file diff --git a/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectCreation.yaml b/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectCreation.yaml new file mode 100644 index 0000000000..363a32ed32 --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/fixtures/e2e/dataScienceProjects/testProjectCreation.yaml @@ -0,0 +1,4 @@ +# testProjectCreation.cy.ts Test Data # +projectDisplayName: "Cypress Test Project" +projectDescription: "Cypress Test project description." +projectResourceName: "cypress-test-project" diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectAdminPermissions.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectAdminPermissions.cy.ts new file mode 100644 index 0000000000..dc39216355 --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectAdminPermissions.cy.ts @@ -0,0 +1,76 @@ +import type { DataScienceProjectData } from '~/__tests__/cypress/cypress/types'; +import { projectDetails, projectListPage } from '~/__tests__/cypress/cypress/pages/projects'; +import { permissions } from '~/__tests__/cypress/cypress/pages/permissions'; +import { ADMIN_USER, TEST_USER } from '~/__tests__/cypress/cypress/utils/e2eUsers'; +import { loadFixture } from '~/__tests__/cypress/cypress/utils/dataLoader'; +import { createCleanProject } from '~/__tests__/cypress/cypress/utils/projectChecker'; +import { deleteOpenShiftProject } from '~/__tests__/cypress/cypress/utils/oc_commands/project'; + +describe('Verify that users can provide admin project permissions to non-admin users', () => { + let testData: DataScienceProjectData; + let projectName: string; + + // Setup: Load test data and ensure clean state + before(() => { + return loadFixture('e2e/dataScienceProjects/testProjectAdminPermissions.yaml') + .then((fixtureData: DataScienceProjectData) => { + testData = fixtureData; + projectName = testData.projectPermissionResourceName; + if (!projectName) { + throw new Error('Project name is undefined or empty in the loaded fixture'); + } + cy.log(`Loaded project name: ${projectName}`); + return createCleanProject(projectName); + }) + .then(() => { + cy.log(`Project ${projectName} confirmed to be created and verified successfully`); + }); + }); + after(() => { + // Delete provisioned Project + if (projectName) { + cy.log(`Deleting Project ${projectName} after the test has finished.`); + deleteOpenShiftProject(projectName); + } + }); + + it('Verify that user can be added as an Admin for a Project', () => { + // Authentication and navigation + cy.step('Log into the application'); + cy.visitWithLogin('/', ADMIN_USER); + + // Project navigation, add user and provide admin permissions + cy.step( + `Navigate to the Project list tab and search for ${testData.projectPermissionResourceName}`, + ); + projectListPage.navigate(); + projectListPage.filterProjectByName(testData.projectPermissionResourceName); + projectListPage.findProjectLink(testData.projectPermissionResourceName).click(); + projectDetails.findSectionTab('permissions').click(); + + cy.step('Assign admin user Project Permissions'); + permissions.findAddUserButton().click(); + permissions.getUserTable().findAddInput().type(TEST_USER.USERNAME); + permissions + .getUserTable() + .selectPermission(TEST_USER.USERNAME, 'Admin Edit the project and manage user access'); + + cy.step( + `Save the user and validate that ${TEST_USER.USERNAME} has been saved with admin permissions`, + ); + permissions.getUserTable().findSaveNewButton().should('exist').and('be.visible').click(); + cy.contains(TEST_USER.USERNAME).should('exist'); + }); + it('Verify that user can access the created project with Admin rights', () => { + // Authentication and navigation + cy.step(`Log into the application with ${TEST_USER.USERNAME}`); + cy.visitWithLogin('/', TEST_USER); + + // Project navigation and validate permissions tab is accessible + cy.step('Verify that the user has access to the created project and can access Permissions'); + projectListPage.navigate(); + projectListPage.filterProjectByName(testData.projectPermissionResourceName); + projectListPage.findProjectLink(testData.projectPermissionResourceName).click(); + projectDetails.findSectionTab('permissions').click(); + }); +}); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectContributorPermissions.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectContributorPermissions.cy.ts new file mode 100644 index 0000000000..b195df0057 --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectContributorPermissions.cy.ts @@ -0,0 +1,78 @@ +import { deleteOpenShiftProject } from '~/__tests__/cypress/cypress/utils/oc_commands/project'; +import { projectDetails, projectListPage } from '~/__tests__/cypress/cypress/pages/projects'; +import type { DataScienceProjectData } from '~/__tests__/cypress/cypress/types'; +import { permissions } from '~/__tests__/cypress/cypress/pages/permissions'; +import { ADMIN_USER, TEST_USER } from '~/__tests__/cypress/cypress/utils/e2eUsers'; +import { loadFixture } from '~/__tests__/cypress/cypress/utils/dataLoader'; +import { createCleanProject } from '~/__tests__/cypress/cypress/utils/projectChecker'; + +describe('Verify that users can provide contributor project permissions to non-admin users', () => { + let testData: DataScienceProjectData; + let projectName: string; + + // Setup: Load test data and ensure clean state + before(() => { + return loadFixture('e2e/dataScienceProjects/testProjectContributorPermissions.yaml') + .then((fixtureData: DataScienceProjectData) => { + testData = fixtureData; + projectName = testData.projectContributorResourceName; + if (!projectName) { + throw new Error('Project name is undefined or empty in the loaded fixture'); + } + cy.log(`Loaded project name: ${projectName}`); + return createCleanProject(projectName); + }) + .then(() => { + cy.log(`Project ${projectName} confirmed to be created and verified successfully`); + }); + }); + after(() => { + // Delete provisioned Project + if (projectName) { + cy.log(`Deleting Project ${projectName} after the test has finished.`); + deleteOpenShiftProject(projectName); + } + }); + + it('Verify that user can be added as a Contributor for a Project', () => { + // Authentication and navigation + cy.step('Log into the application'); + cy.visitWithLogin('/', ADMIN_USER); + + // Project navigation, add user and provide contributor permissions + cy.step( + `Navigate to the Project list tab and search for ${testData.projectContributorResourceName}`, + ); + projectListPage.navigate(); + projectListPage.filterProjectByName(testData.projectContributorResourceName); + projectListPage.findProjectLink(testData.projectContributorResourceName).click(); + projectDetails.findSectionTab('permissions').click(); + + cy.step('Assign contributor user Project Permissions'); + permissions.findAddUserButton().click(); + permissions.getUserTable().findAddInput().type(TEST_USER.USERNAME); + cy.debug(); + permissions + .getUserTable() + .selectPermission(TEST_USER.USERNAME, 'Contributor View and edit the project components'); + + cy.step( + `Save the user and validate that ${TEST_USER.USERNAME} has been saved with Contributor permissions`, + ); + permissions.getUserTable().findSaveNewButton().should('exist').and('be.visible').click(); + cy.contains(TEST_USER.USERNAME).should('exist'); + }); + it('Verify that user can access the created project as a Contributor', () => { + // Authentication and navigation + cy.step(`Log into the application with ${TEST_USER.USERNAME}`); + cy.visitWithLogin('/', TEST_USER); + + // Project navigation and validate permissions tab is accessible + cy.step('Verify that the user has access to the created project but cannot access Permissions'); + projectListPage.navigate(); + projectListPage.filterProjectByName(testData.projectContributorResourceName); + projectListPage.findProjectLink(testData.projectContributorResourceName).click(); + cy.log('Attempting to find permissions tab which should not be visible'); + projectDetails.findSectionTab('permissions').should('not.exist'); + }); +}); diff --git a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectCreation.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectCreation.cy.ts index 42e0a08d53..c96fd04fbb 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectCreation.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/e2e/dataScienceProjects/testProjectCreation.cy.ts @@ -18,10 +18,10 @@ describe('Verify Data Science Project - Creation and Deletion', () => { // Setup: Load test data and ensure clean state before(() => { return cy - .fixture('e2e/dataScienceProjects/dataScienceProject.yaml', 'utf8') + .fixture('e2e/dataScienceProjects/testProjectCreation.yaml', 'utf8') .then((yamlContent: string) => { testData = yaml.load(yamlContent) as DataScienceProjectData; - const projectName = testData.dsOCProjectName; + const projectName = testData.projectResourceName; if (!projectName) { throw new Error('Project name is undefined or empty'); @@ -30,7 +30,7 @@ describe('Verify Data Science Project - Creation and Deletion', () => { return verifyOpenShiftProjectExists(projectName); }) .then((exists: boolean) => { - const projectName = testData.dsOCProjectName; + const projectName = testData.projectResourceName; // Clean up existing project if it exists if (exists) { cy.log(`Project ${projectName} exists. Deleting before test.`); @@ -55,20 +55,18 @@ describe('Verify Data Science Project - Creation and Deletion', () => { // Input project details cy.step('Enter valid project information'); - createProjectModal.k8sNameDescription.findDisplayNameInput().type(testData.dsProjectName); - createProjectModal.k8sNameDescription - .findDescriptionInput() - .type(testData.dsProjectDescription); + createProjectModal.k8sNameDescription.findDisplayNameInput().type(testData.projectDisplayName); + createProjectModal.k8sNameDescription.findDescriptionInput().type(testData.projectDescription); // Submit project creation cy.step('Save the project'); createProjectModal.findSubmitButton().click(); // Verify project creation - cy.step(`Verify that the project ${testData.dsProjectName} has been created`); - cy.url().should('include', `/projects/${testData.dsOCProjectName}`); - projectDetails.verifyProjectName(testData.dsProjectName); - projectDetails.verifyProjectDescription(testData.dsProjectDescription); + cy.step(`Verify that the project ${testData.projectDisplayName} has been created`); + cy.url().should('include', `/projects/${testData.projectResourceName}`); + projectDetails.verifyProjectName(testData.projectDisplayName); + projectDetails.verifyProjectDescription(testData.projectDescription); // Initiate project deletion cy.step('Deleting the project - clicking actions'); @@ -78,12 +76,12 @@ describe('Verify Data Science Project - Creation and Deletion', () => { // Confirm project deletion cy.step('Entering project details for deletion'); deleteModal.shouldBeOpen(); - deleteModal.findInput().type(testData.dsProjectName); + deleteModal.findInput().type(testData.projectDisplayName); deleteModal.findSubmitButton().should('be.enabled').click(); // Verify project deletion - cy.step(`Verify that the project ${testData.dsProjectName} has been deleted`); - projectListPage.filterProjectByName(testData.dsProjectName); + cy.step(`Verify that the project ${testData.projectDisplayName} has been deleted`); + projectListPage.filterProjectByName(testData.projectDisplayName); projectListPage.findEmptyResults(); }); }); diff --git a/frontend/src/__tests__/cypress/cypress/types.ts b/frontend/src/__tests__/cypress/cypress/types.ts index 7fbd56738b..04e5c69280 100644 --- a/frontend/src/__tests__/cypress/cypress/types.ts +++ b/frontend/src/__tests__/cypress/cypress/types.ts @@ -87,9 +87,11 @@ export type TestConfig = { }; export type DataScienceProjectData = { - dsProjectName: string; - dsProjectDescription: string; - dsOCProjectName: string; + projectDisplayName: string; + projectDescription: string; + projectResourceName: string; + projectPermissionResourceName: string; + projectContributorResourceName: string; }; export type NimServingResponse = { diff --git a/frontend/src/__tests__/cypress/cypress/utils/dataLoader.ts b/frontend/src/__tests__/cypress/cypress/utils/dataLoader.ts new file mode 100644 index 0000000000..485d9ecab2 --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/utils/dataLoader.ts @@ -0,0 +1,11 @@ +import yaml from 'js-yaml'; +import type { DataScienceProjectData } from '~/__tests__/cypress/cypress/types'; + +// Load fixture function that returns a specific type +export const loadFixture = (fixturePath: string): Cypress.Chainable => { + return cy.fixture(fixturePath, 'utf8').then((yamlContent: string) => { + const data = yaml.load(yamlContent) as DataScienceProjectData; + + return data; + }); +}; diff --git a/frontend/src/__tests__/cypress/cypress/utils/projectChecker.ts b/frontend/src/__tests__/cypress/cypress/utils/projectChecker.ts new file mode 100644 index 0000000000..8857427500 --- /dev/null +++ b/frontend/src/__tests__/cypress/cypress/utils/projectChecker.ts @@ -0,0 +1,28 @@ +import { + verifyOpenShiftProjectExists, + deleteOpenShiftProject, + createOpenShiftProject, +} from '~/__tests__/cypress/cypress/utils/oc_commands/project'; + +export const createAndVerifyProject = (projectName: string): void => { + createOpenShiftProject(projectName).then((result) => { + expect(result.code).to.equal(0); + }); + + verifyOpenShiftProjectExists(projectName).then((exists) => { + if (!exists) { + throw new Error(`Expected project ${projectName} to exist, but it does not.`); + } + }); +}; + +export const createCleanProject = (projectName: string): void => { + verifyOpenShiftProjectExists(projectName).then((exists) => { + if (exists) { + cy.log(`Project ${projectName} already exists. Deleting it.`); + deleteOpenShiftProject(projectName); + } + cy.log(`Creating project ${projectName}`); + createAndVerifyProject(projectName); + }); +};