minor(#16099): when adding team give user option to configure the isJoinable field (#16722)

* minor(#16099): when adding team give user option to configure the isJoinable field

* chore: add helper text for public team field

* test: add test for create team

* fix: playwright test
This commit is contained in:
Sachin Chaurasiya 2024-06-20 18:46:59 +05:30 committed by GitHub
parent cd5d8dfa06
commit 870bb8c3f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 291 additions and 1 deletions

View File

@ -0,0 +1,78 @@
/*
* 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 test, { expect } from '@playwright/test';
import { GlobalSettingOptions } from '../../constant/settings';
import { redirectToHomePage } from '../../utils/common';
import { settingClick } from '../../utils/sidebar';
import { createTeam, hardDeleteTeam } from '../../utils/team';
// use the admin user to login
test.use({ storageState: 'playwright/.auth/admin.json' });
test.describe('Teams Page', () => {
test.beforeEach('Visit Home Page', async ({ page }) => {
await redirectToHomePage(page);
});
test('Create a new public team', async ({ page }) => {
await settingClick(page, GlobalSettingOptions.TEAMS);
await page.waitForSelector('[data-testid="add-team"]');
await page.getByTestId('add-team').click();
const publicTeam = await createTeam(page, true);
await page.getByRole('link', { name: publicTeam.displayName }).click();
await page
.getByTestId('team-details-collapse')
.getByTestId('manage-button')
.click();
await expect(page.locator('button[role="switch"]')).toHaveAttribute(
'aria-checked',
'true'
);
await page.click('body'); // Equivalent to clicking outside
await hardDeleteTeam(page);
});
test('Create a new private team', async ({ page }) => {
await settingClick(page, GlobalSettingOptions.TEAMS);
await page.waitForSelector('[data-testid="add-team"]');
await page.getByTestId('add-team').click();
const publicTeam = await createTeam(page);
await page.getByRole('link', { name: publicTeam.displayName }).click();
await page
.getByTestId('team-details-collapse')
.getByTestId('manage-button')
.click();
await expect(page.locator('button[role="switch"]')).toHaveAttribute(
'aria-checked',
'false'
);
await page.click('body'); // Equivalent to clicking outside
await hardDeleteTeam(page);
});
});

View File

@ -0,0 +1,80 @@
/*
* 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, Page } from '@playwright/test';
import { uuid } from './common';
export const createTeam = async (page: Page, isPublic?: boolean) => {
const teamData = {
name: `pwteam-${uuid()}`,
displayName: `PW ${uuid()}`,
email: `pwteam${uuid()}@example.com`,
description: 'This is a PW team',
};
await page.waitForSelector('[role="dialog"].ant-modal');
await expect(page.locator('[role="dialog"].ant-modal')).toBeVisible();
await page.fill('[data-testid="name"]', teamData.name);
await page.fill('[data-testid="display-name"]', teamData.displayName);
await page.fill('[data-testid="email"]', teamData.email);
if (isPublic) {
await page.getByTestId('isJoinable-switch-button').click();
}
await page
.locator('.toastui-editor-md-container > .toastui-editor > .ProseMirror')
.isVisible();
await page
.locator('.toastui-editor-md-container > .toastui-editor > .ProseMirror')
.fill(teamData.description);
const createTeamResponse = page.waitForResponse('/api/v1/teams');
await page.locator('button[type="submit"]').click();
await createTeamResponse;
return teamData;
};
export const hardDeleteTeam = async (page: Page) => {
await page
.getByTestId('team-details-collapse')
.getByTestId('manage-button')
.click();
await page.getByTestId('delete-button').click();
await page.waitForSelector('[role="dialog"].ant-modal');
await expect(page.locator('[role="dialog"].ant-modal')).toBeVisible();
await page.click('[data-testid="hard-delete-option"]');
await page.check('[data-testid="hard-delete"]');
await page.fill('[data-testid="confirmation-text-input"]', 'DELETE');
const deleteResponse = page.waitForResponse(
'/api/v1/teams/*?hardDelete=true&recursive=true'
);
await page.click('[data-testid="confirm-button"]');
await deleteResponse;
await expect(page.locator('.Toastify__toast-body')).toHaveText(
/deleted successfully!/
);
await page.click('.Toastify__close-button');
};

View File

@ -10,7 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { render } from '@testing-library/react';
import { act, fireEvent, render } from '@testing-library/react';
import React from 'react';
import { TeamType } from '../../generated/entity/teams/team';
import AddTeamForm from './AddTeamForm';
@ -38,5 +38,114 @@ describe('AddTeamForm component', () => {
expect(getByTestId('email')).toBeInTheDocument();
expect(getByTestId('editor')).toBeInTheDocument();
expect(getByTestId('team-selector')).toBeInTheDocument();
expect(getByTestId('isJoinable-switch-button')).toBeInTheDocument();
});
it('should call onCancel function when cancel button is clicked', () => {
const { getByText } = render(
<AddTeamForm
visible
isLoading={false}
parentTeamType={TeamType.Organization}
onCancel={mockCancel}
onSave={mockSave}
/>
);
getByText('Cancel').click();
expect(mockCancel).toHaveBeenCalled();
});
it('should call onSave function when save button is clicked', async () => {
const { getByText, getByTestId } = render(
<AddTeamForm
visible
isLoading={false}
parentTeamType={TeamType.Organization}
onCancel={mockCancel}
onSave={mockSave}
/>
);
// input name
const nameInput = getByTestId('name');
await act(async () => {
fireEvent.change(nameInput, { target: { value: 'test' } });
});
// input displayName
const displayNameInput = getByTestId('display-name');
await act(async () => {
fireEvent.change(displayNameInput, { target: { value: 'Test Team' } });
});
// input email
const emailInput = getByTestId('email');
await act(async () => {
fireEvent.change(emailInput, { target: { value: 'testteam@gmail.com' } });
});
// make team joinable
const isJoinableSwitch = getByTestId('isJoinable-switch-button');
await act(async () => {
fireEvent.click(isJoinableSwitch);
});
// save form
const saveButton = getByText('label.save');
await act(async () => {
fireEvent.click(saveButton);
});
expect(mockSave).toHaveBeenCalledWith({
name: 'test',
displayName: 'Test Team',
email: 'testteam@gmail.com',
description: '',
isJoinable: true,
teamType: 'Group',
});
});
it('should call onSave function when save button is clicked with isJoinable default value', async () => {
const { getByText, getByTestId } = render(
<AddTeamForm
visible
isLoading={false}
parentTeamType={TeamType.Organization}
onCancel={mockCancel}
onSave={mockSave}
/>
);
// input name
const nameInput = getByTestId('name');
await act(async () => {
fireEvent.change(nameInput, { target: { value: 'test' } });
});
// input displayName
const displayNameInput = getByTestId('display-name');
await act(async () => {
fireEvent.change(displayNameInput, { target: { value: 'Test Team' } });
});
// save form
const saveButton = getByText('label.save');
await act(async () => {
fireEvent.click(saveButton);
});
expect(mockSave).toHaveBeenCalledWith({
name: 'test',
displayName: 'Test Team',
email: undefined,
description: '',
isJoinable: false,
teamType: 'Group',
});
});
});

View File

@ -21,7 +21,13 @@ import { EditorContentRef } from '../../components/common/RichTextEditor/RichTex
import { VALIDATION_MESSAGES } from '../../constants/constants';
import { ENTITY_NAME_REGEX } from '../../constants/regex.constants';
import { Team, TeamType } from '../../generated/entity/teams/team';
import {
FieldProp,
FieldTypes,
FormItemLayout,
} from '../../interface/FormUtils.interface';
import { getTeams } from '../../rest/teamsAPI';
import { getField } from '../../utils/formUtils';
import { getTeamOptionsFromType } from '../../utils/TeamUtils';
import { showErrorToast } from '../../utils/ToastUtils';
import { AddTeamFormType } from './AddTeamForm.interface';
@ -65,6 +71,19 @@ const AddTeamForm: React.FC<AddTeamFormType> = ({
}
};
const isJoinableField: FieldProp = {
name: 'isJoinable',
label: t('label.public-team'),
type: FieldTypes.SWITCH,
required: false,
props: {
'data-testid': 'isJoinable-switch-button',
},
id: 'isJoinable-switch-button',
formItemLayout: FormItemLayout.HORIZONTAL,
helperText: t('message.access-to-collaborate'),
};
useEffect(() => {
if (visible) {
fetchAllTeams();
@ -82,6 +101,7 @@ const AddTeamForm: React.FC<AddTeamFormType> = ({
type: 'primary',
htmlType: 'submit',
}}
okText={t('label.save')}
open={visible}
title={t('label.add-entity', { entity: t('label.team') })}
width={750}
@ -90,6 +110,7 @@ const AddTeamForm: React.FC<AddTeamFormType> = ({
id="add-team-form"
initialValues={{
teamType: TeamType.Group,
isJoinable: false,
}}
layout="vertical"
name="add-team-nest-messages"
@ -166,6 +187,7 @@ const AddTeamForm: React.FC<AddTeamFormType> = ({
placeholder={t('message.select-team')}
/>
</Form.Item>
{getField(isJoinableField)}
<Form.Item
label={t('label.description')}
name="description"

View File

@ -268,6 +268,7 @@ const TeamsPage = () => {
teamType: data.teamType as TeamType,
parents: fqn ? [selectedTeam.id] : undefined,
email: data.email || undefined,
isJoinable: data.isJoinable,
};
const res = await createTeam(teamData);
if (res) {