Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: improved agents e2e tests #31196

Merged
merged 2 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
const voipExtensionField = useUniqueId();

return (
<Contextualbar>
<Contextualbar data-qa-id='agent-edit-contextual-bar'>
<ContextualbarHeader>
<ContextualbarTitle>{t('Edit_User')}</ContextualbarTitle>
<ContextualbarClose onClick={() => router.navigate('/omnichannel/agents')} />
Expand All @@ -122,7 +122,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
<form id={formId} onSubmit={handleSubmit(handleSave)}>
{username && (
<Box display='flex' flexDirection='column' alignItems='center'>
<UserInfo.Avatar data-qa='AgentEdit-Avatar' username={username} />
<UserInfo.Avatar data-qa-id='agent-edit-avatar' username={username} />
</Box>
)}
<FieldGroup>
Expand All @@ -132,7 +132,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
<Controller
name='name'
control={control}
render={({ field }) => <TextInput id={nameField} data-qa='AgentEditTextInput-Name' {...field} readOnly />}
render={({ field }) => <TextInput id={nameField} data-qa-id='agent-edit-name' {...field} readOnly />}
/>
</FieldRow>
</Field>
Expand All @@ -145,7 +145,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
render={({ field }) => (
<TextInput
id={usernameField}
data-qa='AgentEditTextInput-Username'
data-qa-id='agent-edit-username'
{...field}
readOnly
addon={<Icon name='at' size='x20' />}
Expand All @@ -163,7 +163,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
render={({ field }) => (
<TextInput
id={emailField}
data-qa='AgentEditTextInput-Email'
data-qa-id='agent-edit-email'
{...field}
readOnly
addon={<Icon name='mail' size='x20' />}
Expand All @@ -181,7 +181,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
render={({ field }) => (
<MultiSelect
id={departmentsField}
data-qa='AgentEditTextInput-Departaments'
data-qa-id='agent-edit-departments'
options={departmentsOptions}
{...field}
placeholder={t('Select_an_option')}
Expand All @@ -199,7 +199,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
render={({ field }) => (
<Select
id={statusField}
data-qa='AgentEditTextInput-Status'
data-qa-id='agent-edit-status'
{...field}
options={statusOptions}
placeholder={t('Select_an_option')}
Expand All @@ -216,7 +216,7 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
<Controller
name='voipExtension'
control={control}
render={({ field }) => <TextInput id={voipExtensionField} {...field} data-qa='AgentEditTextInput-VoIP_Extension' />}
render={({ field }) => <TextInput id={voipExtensionField} {...field} data-qa-id='agent-edit-voip-extension' />}
/>
</FieldRow>
</Field>
Expand All @@ -227,10 +227,10 @@ const AgentEdit = ({ agentData, userDepartments, availableDepartments }: AgentEd
</ContextualbarScrollableContent>
<ContextualbarFooter>
<ButtonGroup stretch>
<Button data-qa='AgentEditButtonReset' type='reset' disabled={!isDirty} onClick={() => reset()}>
<Button data-qa-id='agent-edit-reset' type='reset' disabled={!isDirty} onClick={() => reset()}>
{t('Reset')}
</Button>
<Button form={formId} primary type='submit' data-qa='AgentEditButtonSave' disabled={!isDirty}>
<Button form={formId} primary type='submit' data-qa-id='agent-edit-save' disabled={!isDirty}>
{t('Save')}
</Button>
</ButtonGroup>
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/client/views/omnichannel/agents/AgentInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const AgentInfo = ({ uid }: AgentInfoProps) => {
const { username, statusLivechat, status: userStatus } = data?.user;

return (
<Contextualbar>
<Contextualbar data-qa-id='agent-info-contextual-bar'>
<ContextualbarHeader>
<ContextualbarTitle>{t('User_Info')}</ContextualbarTitle>
<ContextualbarClose onClick={() => router.navigate('/omnichannel/agents')} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type AgentInfoActionProps = {
} & Omit<HtmlHTMLAttributes<HTMLElement>, 'is'>;

const AgentInfoAction: FC<AgentInfoActionProps> = ({ icon, label, ...props }) => (
<Button icon={icon} data-qa={`AgentInfoAction-${label}`} title={label} {...props} mi={4}>
<Button icon={icon} data-qa={`agent-info-action-${label?.toLowerCase()}`} title={label} {...props} mi={4}>
{label}
</Button>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ const AgentsTable = () => {
)}
{isSuccess && data?.users.length > 0 && (
<>
<GenericTable aria-busy={filter !== debouncedFilter}>
<GenericTable aria-busy={filter !== debouncedFilter} data-qa-id='agents-table'>
<GenericTableHeader>{headers}</GenericTableHeader>
<GenericTableBody data-qa='GenericTableAgentInfoBody'>
{data?.users.map((user) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const AgentsTableRow = ({
const handleDelete = useRemoveAgent(_id);

return (
<GenericTableRow action onClick={() => router.navigate(`/omnichannel/agents/info/${_id}`)}>
<GenericTableRow data-qa-id={username} action onClick={() => router.navigate(`/omnichannel/agents/info/${_id}`)}>
<GenericTableCell>
<Box display='flex' alignItems='center'>
{username && <UserAvatar size={mediaQuery ? 'x28' : 'x40'} title={username} username={username} etag={avatarETag} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ export const useRemoveAgent = (uid: ILivechatAgent['_id']) => {
}
};

setModal(<GenericModal variant='danger' onConfirm={onDeleteAgent} onCancel={() => setModal()} confirmText={t('Delete')} />);
setModal(
<GenericModal
data-qa-id='remove-agent-modal'
variant='danger'
onConfirm={onDeleteAgent}
onCancel={() => setModal()}
confirmText={t('Delete')}
/>,
);
});

return handleDelete;
Expand Down
98 changes: 81 additions & 17 deletions apps/meteor/tests/e2e/omnichannel/omnichannel-agents.spec.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,108 @@
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelAgents } from '../page-objects';
import { createDepartment } from '../utils/omnichannel/departments';
import { test, expect } from '../utils/test';

test.use({ storageState: Users.admin.state });

test.describe.serial('omnichannel-agents', () => {
test.describe.serial('OC - Manage Agents', () => {
let poOmnichannelAgents: OmnichannelAgents;
let department: Awaited<ReturnType<typeof createDepartment>>;

// Create agent and department
test.beforeEach(async ({ api }) => {
department = await createDepartment(api);
});

// Create page object and redirect to home
test.beforeEach(async ({ page }) => {
poOmnichannelAgents = new OmnichannelAgents(page);

await page.goto('/omnichannel');
await poOmnichannelAgents.sidenav.linkAgents.click();
});

test('expect add "user1" as agent', async ({ page }) => {
await poOmnichannelAgents.inputUsername.type('user1');
await page.locator('role=option[name="user1"]').click();
await poOmnichannelAgents.btnAdd.click();
// Ensure that there is no leftover data even if test fails
test.afterEach(async ({ api }) => {
await await api.delete('/livechat/users/agent/user1');
await api.post('/settings/Omnichannel_enable_department_removal', { value: true }).then((res) => expect(res.status()).toBe(200));
await department.delete();
await api.post('/settings/Omnichannel_enable_department_removal', { value: false }).then((res) => expect(res.status()).toBe(200));
});

await poOmnichannelAgents.inputSearch.fill('user1');
await expect(poOmnichannelAgents.firstRowInTable).toBeVisible();
test('OC - Manage Agents - Add, search and remove using table', async ({ page }) => {
await test.step('expect "user1" be first ', async () => {
await poOmnichannelAgents.inputUsername.type('user');
await expect(page.locator('role=option[name="user1"]')).toContainText('user1');

await poOmnichannelAgents.inputUsername.fill('');
});

await test.step('expect add "user1" as agent', async () => {
await poOmnichannelAgents.selectUsername('user1');
await poOmnichannelAgents.btnAdd.click();

await poOmnichannelAgents.inputSearch.fill('user1');
await expect(poOmnichannelAgents.firstRowInTable).toBeVisible();
await expect(poOmnichannelAgents.firstRowInTable).toHaveText('user1');
});

await test.step('expect remove "user1" as agent', async () => {
await poOmnichannelAgents.inputSearch.fill('user1');
await poOmnichannelAgents.btnDeletefirstRowInTable.click();
await poOmnichannelAgents.btnModalRemove.click();

await poOmnichannelAgents.inputSearch.fill('user1');
await expect(poOmnichannelAgents.findRowByUsername('user1')).not.toBeVisible();
});
});

test('expect update "user1" status', async ({ page }) => {
test('OC - Manage Agents [CE]- Edit and Remove', async () => {
test.skip(IS_EE, 'Community Edition Only');

await poOmnichannelAgents.selectUsername('user1');
await poOmnichannelAgents.btnAdd.click();

await poOmnichannelAgents.inputSearch.fill('user1');
await poOmnichannelAgents.firstRowInTable.click();

await poOmnichannelAgents.btnEdit.click();
await poOmnichannelAgents.btnStatus.click();
await page.locator(`.rcx-option__content:has-text("Not available")`).click();
await poOmnichannelAgents.btnSave.click();

await test.step('expect max chats fields to be hidden', async () => {
await expect(poOmnichannelAgents.inputMaxChats).toBeHidden();
});

await test.step('expect update "user1" information', async () => {
await poOmnichannelAgents.selectStatus('Not available');
await poOmnichannelAgents.selectDepartment(department.data.name);
await poOmnichannelAgents.btnSave.click();
});

await test.step('expect removing "user1" via sidebar', async () => {
await poOmnichannelAgents.inputSearch.fill('user1');
await poOmnichannelAgents.firstRowInTable.click();
await poOmnichannelAgents.btnRemove.click();
});
});

test('expect remove "user1" as agent', async () => {
await poOmnichannelAgents.inputSearch.fill('user1');
await poOmnichannelAgents.btnDeletefirstRowInTable.click();
await poOmnichannelAgents.btnModalRemove.click();
test('OC - Manage Agents [EE] - Edit ', async () => {
test.skip(!IS_EE, 'Enterprise Only');

await poOmnichannelAgents.selectUsername('user1');
await poOmnichannelAgents.btnAdd.click();

await poOmnichannelAgents.inputSearch.fill('user1');
await expect(poOmnichannelAgents.firstRowInTable).toBeHidden();
await poOmnichannelAgents.findRowByUsername('user1').click();
await poOmnichannelAgents.btnEdit.click();

await test.step('expect max chats field to be visible', async () => {
await expect(poOmnichannelAgents.inputMaxChats).toBeVisible();
});

await test.step('expect update "user1" information', async () => {
await poOmnichannelAgents.inputMaxChats.click();
await poOmnichannelAgents.inputMaxChats.fill('2');
await poOmnichannelAgents.btnSave.click();
});
});
});
59 changes: 51 additions & 8 deletions apps/meteor/tests/e2e/page-objects/omnichannel-agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ export class OmnichannelAgents {

readonly sidenav: OmnichannelSidenav;

readonly editCtxBar: Locator;

readonly infoCtxBar: Locator;

constructor(page: Page) {
this.page = page;
this.sidenav = new OmnichannelSidenav(page);
this.editCtxBar = page.locator('[data-qa-id="agent-edit-contextual-bar"]');
this.infoCtxBar = page.locator('[data-qa-id="agent-info-contextual-bar"]');
}

get inputUsername(): Locator {
return this.page.locator('input').first();
return this.page.locator('[data-qa-id="UserAutoComplete"]');
}

get inputSearch(): Locator {
Expand All @@ -25,26 +31,63 @@ export class OmnichannelAgents {
}

get firstRowInTable() {
return this.page.locator('[data-qa="GenericTableAgentInfoBody"] .rcx-table__row--action .rcx-table__cell:first-child');
return this.page.locator('[data-qa-id="agents-table"] tr:first-child td:first-child');
}

get btnDeletefirstRowInTable() {
return this.page.locator('button[title="Remove"]');
return this.page.locator('[data-qa-id="agents-table"] tr:first-child').locator('role=button[name="Remove"]');
}

get modalRemoveAgent(): Locator {
return this.page.locator('[data-qa-id="remove-agent-modal"]');
}

get btnModalRemove(): Locator {
return this.page.locator('#modal-root dialog .rcx-modal__inner .rcx-modal__footer .rcx-button--danger');
return this.modalRemoveAgent.locator('role=button[name="Delete"]');
}

get btnEdit(): Locator {
return this.page.locator('[data-qa="AgentInfoAction-Edit"]');
return this.infoCtxBar.locator('[data-qa="agent-info-action-edit"]');
}

get btnStatus(): Locator {
return this.page.locator('[data-qa="AgentEditTextInput-Status"]');
get btnRemove(): Locator {
return this.infoCtxBar.locator('role=button[name="Remove"]');
}

get btnSave(): Locator {
return this.page.locator('[data-qa="AgentEditButtonSave"]');
return this.editCtxBar.locator('[data-qa-id="agent-edit-save"]');
}

get inputMaxChats(): Locator {
return this.editCtxBar.locator('input[name="maxNumberSimultaneousChat"]');
}

get inputStatus(): Locator {
return this.page.locator('[data-qa-id="agent-edit-status"]');
}

get inputDepartment(): Locator {
return this.editCtxBar.locator('[data-qa-id="agent-edit-departments"]');
}

async selectDepartment(name: string) {
await this.inputDepartment.click();
await this.inputDepartment.type(name);
await this.page.locator(`.rcx-option__content:has-text("${name}")`).click();
}

async selectStatus(status: string) {
await this.inputStatus.click();
await this.page.locator(`.rcx-option__content:has-text("${status}")`).click();
}

async selectUsername(username: string) {
await this.inputUsername.fill('');
await this.inputUsername.type(username);
await this.page.locator(`role=option[name="${username}"]`).click();
}

findRowByUsername(username: string) {
return this.page.locator(`[data-qa-id="${username}"]`);
}
}
Loading