diff --git a/.changeset/funny-snails-promise.md b/.changeset/funny-snails-promise.md new file mode 100644 index 000000000000..bdd74a60b1e9 --- /dev/null +++ b/.changeset/funny-snails-promise.md @@ -0,0 +1,10 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/livechat": patch +--- + +livechat `setDepartment` livechat api fixes: +- Changing department didn't reflect on the registration form in real time +- Changing the department mid conversation didn't transfer the chat +- Depending on the state of the department, it couldn't be set as default + diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat-api.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat-api.spec.ts index bf77840446dc..278df11f16cb 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat-api.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-livechat-api.spec.ts @@ -212,7 +212,6 @@ test.describe('OC - Livechat API', () => { test.skip(!IS_EE, 'Enterprise Only'); // Tests that requires interaction from an agent or more let poAuxContext: { page: Page; poHomeOmnichannel: HomeOmnichannel }; - let poAuxContext2: { page: Page; poHomeOmnichannel: HomeOmnichannel }; let poLiveChat: OmnichannelLiveChatEmbedded; let page: Page; let agent: Awaited>; @@ -247,18 +246,12 @@ test.describe('OC - Livechat API', () => { await poAuxContext.poHomeOmnichannel.sidenav.switchStatus('online'); } - if (testInfo.title === 'OC - Livechat API - setDepartment') { - const { page: pageCtx2 } = await createAuxContext(browser, Users.user2); - poAuxContext2 = { page: pageCtx2, poHomeOmnichannel: new HomeOmnichannel(pageCtx) }; - } - await page.goto('/packages/rocketchat_livechat/assets/demo.html'); }); test.afterEach(async () => { await poAuxContext.page.close(); await page.close(); - await poAuxContext2?.page.close(); await pageContext?.close(); }); @@ -316,41 +309,100 @@ test.describe('OC - Livechat API', () => { }); }); - test('OC - Livechat API - setDepartment', async () => { - const [departmentA, departmentB] = departments.map(({ data }) => data); - const registerGuestVisitor = { - name: faker.person.firstName(), - email: faker.internet.email(), - token: faker.string.uuid(), - department: departmentA._id, - }; + test.describe('OC - Livechat API - setDepartment', () => { + let poAuxContext2: { page: Page; poHomeOmnichannel: HomeOmnichannel }; - // Start Chat - await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget()); - await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible(); + test.beforeEach(async ({ browser }) => { + const { page: pageCtx2 } = await createAuxContext(browser, Users.user2); + poAuxContext2 = { page: pageCtx2, poHomeOmnichannel: new HomeOmnichannel(pageCtx2) }; + }); - await poLiveChat.page.evaluate( - (registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor), - registerGuestVisitor, - ); + test.afterEach(async () => { + await poAuxContext2.page.close(); + }); - await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible(); + test('setDepartment - Called during ongoing conversation', async () => { + const [departmentA, departmentB] = departments.map(({ data }) => data); + const registerGuestVisitor = { + name: faker.person.firstName(), + email: faker.internet.email(), + token: faker.string.uuid(), + department: departmentA._id, + }; - await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); - await poLiveChat.btnSendMessageToOnlineAgent.click(); + // Start Chat + await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget()); + await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible(); - await test.step('Expect registered guest to be in dep1', async () => { - await poAuxContext.poHomeOmnichannel.sidenav.openChat(registerGuestVisitor.name); + await poLiveChat.page.evaluate( + (registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor), + registerGuestVisitor, + ); + + await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible(); + + await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); + await poLiveChat.btnSendMessageToOnlineAgent.click(); + + await test.step('Expect registered guest to be in dep1', async () => { + await poAuxContext.poHomeOmnichannel.sidenav.openChat(registerGuestVisitor.name); + await expect(poAuxContext.poHomeOmnichannel.content.channelHeader).toContainText(registerGuestVisitor.name); + }); + + const depId = departmentB._id; + + await test.step('Expect chat not be transferred', async () => { + await poLiveChat.page.evaluate((depId) => window.RocketChat.livechat.setDepartment(depId), depId); + + await poAuxContext2.page.locator('role=navigation >> role=button[name=Search]').click(); + await poAuxContext2.page.locator('role=search >> role=searchbox').fill(registerGuestVisitor.name); + await expect( + poAuxContext2.page.locator(`role=search >> role=listbox >> role=link >> text="${registerGuestVisitor.name}"`), + ).not.toBeVisible(); + }); + + await test.step('Expect registered guest to still be in dep1', async () => { + await poAuxContext.poHomeOmnichannel.sidenav.openChat(registerGuestVisitor.name); + await expect(poAuxContext.poHomeOmnichannel.content.channelHeader).toContainText(registerGuestVisitor.name); + }); }); - const depId = departmentB._id; + test('setDepartment - Called before conversation', async () => { + const departmentB = departments[1].data; + const registerGuestVisitor = { + name: faker.person.firstName(), + email: faker.internet.email(), + }; + + const depId = departmentB._id; - await test.step('Expect setDepartment to change a guest department', async () => { await poLiveChat.page.evaluate((depId) => window.RocketChat.livechat.setDepartment(depId), depId); - }); - await test.step('Expect registered guest to be in dep2', async () => { - await poAuxContext2.poHomeOmnichannel.sidenav.openChat(registerGuestVisitor.name); + await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget()); + await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible(); + + await poLiveChat.sendMessage(registerGuestVisitor, false); + + await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor'); + await poLiveChat.btnSendMessageToOnlineAgent.click(); + + await test.step('Expect registered guest to be in dep2', async () => { + await poAuxContext2.page.locator('role=navigation >> role=button[name=Search]').click(); + await poAuxContext2.page.locator('role=search >> role=searchbox').fill(registerGuestVisitor.name); + await poAuxContext2.page.locator(`role=search >> role=listbox >> role=link >> text="${registerGuestVisitor.name}"`).click(); + await poAuxContext2.page.locator('role=main').waitFor(); + await poAuxContext2.page.locator('role=main >> role=heading[level=1]').waitFor(); + await expect(poAuxContext2.page.locator('role=main >> .rcx-skeleton')).toHaveCount(0); + await expect(poAuxContext2.page.locator('role=main >> role=list')).not.toHaveAttribute('aria-busy', 'true'); + }); + + await test.step('Expect registered guest not to be in dep1', async () => { + await poAuxContext.page.locator('role=navigation >> role=button[name=Search]').click(); + await poAuxContext.page.locator('role=search >> role=searchbox').fill(registerGuestVisitor.name); + await expect( + poAuxContext.page.locator(`role=search >> role=listbox >> role=link >> text="${registerGuestVisitor.name}"`), + ).not.toBeVisible(); + }); }); }); diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-livechat-embedded.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-livechat-embedded.ts index ec7015c4a11b..79d1e9532677 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel-livechat-embedded.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-livechat-embedded.ts @@ -121,10 +121,10 @@ export class OmnichannelLiveChatEmbedded { public async sendMessage(liveChatUser: { name: string; email: string }, isOffline = true): Promise { const buttonLabel = isOffline ? 'Send' : 'Start chat'; - await this.inputName.type(liveChatUser.name); - await this.inputEmail.type(liveChatUser.email); + await this.inputName.fill(liveChatUser.name); + await this.inputEmail.fill(liveChatUser.email); if (isOffline) { - await this.textAreaMessage.type('any_message'); + await this.textAreaMessage.fill('any_message'); await this.btnSendMessage(buttonLabel).click(); return this.btnFinishOfflineMessage().click(); } diff --git a/packages/livechat/src/components/Form/CustomFields/index.tsx b/packages/livechat/src/components/Form/CustomFields/index.tsx index 8c037974e115..3bca3375a972 100644 --- a/packages/livechat/src/components/Form/CustomFields/index.tsx +++ b/packages/livechat/src/components/Form/CustomFields/index.tsx @@ -2,6 +2,7 @@ import type { Control, FieldErrors, FieldValues } from 'react-hook-form'; import { Controller } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; +import type { RegisterFormValues } from '../../../routes/Register'; import { FormField } from '../FormField'; import { SelectInput } from '../SelectInput'; import { TextInput } from '../TextInput'; @@ -19,7 +20,7 @@ export type CustomField = { type RenderCustomFieldsProps = { customFields: CustomField[]; loading: boolean; - control: Control; + control: Control; errors: FieldErrors; }; diff --git a/packages/livechat/src/components/Form/SelectInput/index.tsx b/packages/livechat/src/components/Form/SelectInput/index.tsx index c785445b3021..3646b0147f3f 100644 --- a/packages/livechat/src/components/Form/SelectInput/index.tsx +++ b/packages/livechat/src/components/Form/SelectInput/index.tsx @@ -1,6 +1,5 @@ import type { ComponentChild, Ref } from 'preact'; import type { TargetedEvent } from 'preact/compat'; -import { useState } from 'preact/hooks'; import type { JSXInternal } from 'preact/src/jsx'; import { createClassName } from '../../../helpers/createClassName'; @@ -38,38 +37,26 @@ export const SelectInput = ({ value, ref, }: SelectInputProps) => { - const [internalValue, setInternalValue] = useState(value); - const SelectOptions = Array.from(options).map(({ value, label }, key) => ( )); - const handleChange = (event: TargetedEvent) => { - onChange(event); - - if (event.defaultPrevented) { - return; - } - - setInternalValue((event.target as HTMLSelectElement)?.value); - }; - return (