diff --git a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/TeamsHierarchy.spec.ts b/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/TeamsHierarchy.spec.ts deleted file mode 100644 index b4ad6bb0f23..00000000000 --- a/openmetadata-ui/src/main/resources/ui/cypress/e2e/Features/TeamsHierarchy.spec.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2022 Collate. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - interceptURL, - uuid, - verifyResponseStatusCode, -} from '../../common/common'; -import { addTeam } from '../../common/Utils/Teams'; -import { GlobalSettingOptions } from '../../constants/settings.constant'; - -const buTeamName = `bu-${uuid()}`; -const divTeamName = `div-${uuid()}`; -const depTeamName = `dep-${uuid()}`; -const grpTeamName = `grp-${uuid()}`; -const teamNames = [buTeamName, divTeamName, depTeamName, grpTeamName]; - -const getTeam = (teamName: string) => { - return { - name: teamName, - displayName: teamName, - teamType: 'BusinessUnit', - description: `Team ${teamName} Description`, - ownername: 'admin', - email: 'team@gmail.com', - }; -}; - -describe( - 'Add nested teams and test TeamsSelectable', - { tags: 'Settings' }, - () => { - beforeEach(() => { - cy.login(); - - interceptURL('GET', '/api/v1/teams/name/*', 'getOrganization'); - interceptURL('GET', '/api/v1/permissions/team/name/*', 'getPermissions'); - - cy.settingClick(GlobalSettingOptions.TEAMS); - - verifyResponseStatusCode('@getOrganization', 200); - }); - - it('Add teams', () => { - verifyResponseStatusCode('@getPermissions', 200); - teamNames.forEach((teamName, index) => { - addTeam(getTeam(teamName), index, true); - verifyResponseStatusCode('@getOrganization', 200); - - // asserting the added values - cy.get('table').find('.ant-table-row').contains(teamName).click(); - verifyResponseStatusCode('@getOrganization', 200); - verifyResponseStatusCode('@getPermissions', 200); - }); - }); - - it('Check hierarchy in Add User page', () => { - // Clicking on users - cy.settingClick(GlobalSettingOptions.USERS); - - cy.get('[data-testid="add-user"]').should('be.visible').click(); - - // Enter team name - cy.get('[data-testid="team-select"] .ant-select-selector') - .should('exist') - .scrollIntoView() - .should('be.visible') - .click() - .type(buTeamName); - - teamNames.forEach((teamName) => { - cy.get('.ant-tree-select-dropdown').should('contain', teamName); - }); - - teamNames.forEach((teamName) => { - cy.get('[data-testid="team-select"] .ant-select-selector') - .should('exist') - .scrollIntoView() - .should('be.visible') - .click() - .type(teamName); - cy.get('.ant-tree-select-dropdown').should('contain', teamName); - }); - }); - } -); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/TeamsHierarchy.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/TeamsHierarchy.spec.ts new file mode 100644 index 00000000000..0f40bc75eca --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/TeamsHierarchy.spec.ts @@ -0,0 +1,144 @@ +/* + * Copyright 2024 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { expect, test } from '@playwright/test'; +import { DELETE_TERM } from '../../constant/common'; +import { GlobalSettingOptions } from '../../constant/settings'; +import { + redirectToHomePage, + toastNotification, + uuid, +} from '../../utils/common'; +import { settingClick } from '../../utils/sidebar'; +import { addTeamHierarchy, getNewTeamDetails } from '../../utils/team'; + +// use the admin user to login +test.use({ storageState: 'playwright/.auth/admin.json' }); + +test.describe.configure({ mode: 'serial' }); + +const businessTeamName = `business-${uuid()}`; +const divisionTeamName = `division-${uuid()}`; +const departmentTeamName = `department-${uuid()}`; +const groupTeamName = `group-${uuid()}`; +const teamNames = [ + businessTeamName, + divisionTeamName, + departmentTeamName, + groupTeamName, +]; + +test.describe('Add Nested Teams and Test TeamsSelectable', () => { + test.slow(true); + + test.beforeEach(async ({ page }) => { + await redirectToHomePage(page); + + const getOrganizationResponse = page.waitForResponse( + '/api/v1/teams/name/*' + ); + const permissionResponse = page.waitForResponse( + '/api/v1/permissions/team/name/*' + ); + + await settingClick(page, GlobalSettingOptions.TEAMS); + await permissionResponse; + await getOrganizationResponse; + }); + + test('Add teams in hierarchy', async ({ page }) => { + for (const [index, teamName] of teamNames.entries()) { + const getOrganizationResponse = page.waitForResponse( + '/api/v1/teams/name/*' + ); + await addTeamHierarchy(page, getNewTeamDetails(teamName), index, true); + await getOrganizationResponse; + + // Asserting the added values + const permissionResponse = page.waitForResponse( + '/api/v1/permissions/team/name/*' + ); + await page.getByRole('link', { name: teamName }).click(); + await permissionResponse; + } + }); + + test('Check hierarchy in Add User page', async ({ page }) => { + // Clicking on users + await settingClick(page, GlobalSettingOptions.USERS); + + // Click on add user button + await page.locator('[data-testid="add-user"]').click(); + + // Enter team name + const teamSelect = page.locator( + '[data-testid="team-select"] .ant-select-selector' + ); + await teamSelect.click(); + await teamSelect.type(businessTeamName); + + for (const teamName of teamNames) { + const dropdown = page.locator('.ant-tree-select-dropdown'); + + await expect(dropdown).toContainText(teamName); + } + + for (const teamName of teamNames) { + await expect(teamSelect).toBeVisible(); + + await teamSelect.click(); + await teamSelect.type(teamName); + + await expect(page.locator('.ant-tree-select-dropdown')).toContainText( + teamName + ); + } + }); + + test('Delete Parent Team', async ({ page }) => { + await settingClick(page, GlobalSettingOptions.TEAMS); + + await page.getByRole('link', { name: businessTeamName }).click(); + + await page.click('[data-testid="manage-button"]'); + + await page.click('[data-testid="delete-button-title"]'); + + await expect(page.locator('.ant-modal-header')).toContainText( + businessTeamName + ); + + await page.click(`[data-testid="hard-delete-option"]`); + + await expect(page.locator('[data-testid="confirm-button"]')).toBeDisabled(); + + await page + .locator('[data-testid="confirmation-text-input"]') + .fill(DELETE_TERM); + + const deleteResponse = page.waitForResponse( + `/api/v1/teams/*?hardDelete=true&recursive=true` + ); + + await expect( + page.locator('[data-testid="confirm-button"]') + ).not.toBeDisabled(); + + await page.click('[data-testid="confirm-button"]'); + await deleteResponse; + + await toastNotification( + page, + `"${businessTeamName}" deleted successfully!` + ); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts index 479421401bb..10cf7db398e 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/team.ts @@ -11,7 +11,10 @@ * limitations under the License. */ import { expect, Page } from '@playwright/test'; -import { uuid } from './common'; +import { descriptionBox, uuid } from './common'; +import { validateFormNameFieldInput } from './form'; + +const TEAM_TYPES = ['Department', 'Division', 'Group']; export const createTeam = async (page: Page, isPublic?: boolean) => { const teamData = { @@ -78,3 +81,130 @@ export const hardDeleteTeam = async (page: Page) => { await page.click('.Toastify__close-button'); }; + +export const getNewTeamDetails = (teamName: string) => { + return { + name: teamName, + displayName: teamName, + teamType: 'BusinessUnit', + description: `Team ${teamName} Description`, + ownername: 'admin', + email: 'team@gmail.com', + }; +}; + +const getTeamType = ( + currentTeam: string +): { + childTeamType: string; + teamTypeOptions: typeof TEAM_TYPES; +} => { + switch (currentTeam) { + case 'BusinessUnit': + return { + childTeamType: 'Division', + teamTypeOptions: TEAM_TYPES, + }; + + case 'Division': + return { + childTeamType: 'Department', + teamTypeOptions: TEAM_TYPES, + }; + + case 'Department': + return { + childTeamType: 'Group', + teamTypeOptions: ['Department', 'Group'], + }; + } + + return { + childTeamType: '', + teamTypeOptions: [], + }; +}; + +const checkTeamTypeOptions = async (page: Page, type: string) => { + const teamTypeOptions = getTeamType(type)?.teamTypeOptions; + if (teamTypeOptions) { + for (const teamType of teamTypeOptions) { + const teamTypeElement = page.locator( + `.ant-select-dropdown [title="${teamType}"]` + ); + + await expect(teamTypeElement).toBeVisible(); + } + } +}; + +export const selectTeamHierarchy = async (page: Page, index: number) => { + if (index > 0) { + const teamTypeElement = page.locator('[data-testid="team-type"]'); + const text = await teamTypeElement.innerText(); + checkTeamTypeOptions(page, text); + const childTeamType = getTeamType(text).childTeamType; + await page + .locator(`.ant-select-dropdown [title="${childTeamType}"]`) + .click(); + } else { + checkTeamTypeOptions(page, 'BusinessUnit'); + await page.locator(`.ant-select-dropdown [title='BusinessUnit']`).click(); + } +}; + +export const addTeamHierarchy = async ( + page: Page, + teamDetails: { + name: string; + displayName?: string; + teamType: string; + description: string; + ownername?: string; + email: string; + updatedName?: string; + username?: string; + userId?: string; + assetname?: string; + updatedEmail?: string; + }, + index?: number, + isHierarchy = false +) => { + const getTeamsResponse = page.waitForResponse('/api/v1/teams*'); + + // Fetching the add button and clicking on it + if (index && index > 0) { + await page.click('[data-testid="add-placeholder-button"]'); + } else { + await page.click('[data-testid="add-team"]'); + } + + await getTeamsResponse; + + // Entering team details + await validateFormNameFieldInput({ + page, + value: teamDetails.name, + fieldName: 'Name', + fieldSelector: '[data-testid="name"]', + errorDivSelector: '#add-team-nest-messages_name_help', + }); + + await page.fill('[data-testid="display-name"]', teamDetails.name); + await page.fill('[data-testid="email"]', teamDetails.email); + await page.click('[data-testid="team-selector"]'); + + if (isHierarchy) { + await selectTeamHierarchy(page, index ?? 0); + } else { + await page.click(`.ant-select-dropdown [title="${teamDetails.teamType}"]`); + } + + await page.fill(descriptionBox, teamDetails.description); + + // Saving the created team + const saveTeamResponse = page.waitForResponse('/api/v1/teams'); + await page.click('[form="add-team-form"]'); + await saveTeamResponse; +};