diff --git a/.changeset/tags-save-updatedat.md b/.changeset/tags-save-updatedat.md new file mode 100644 index 0000000000000..49f0df776542d --- /dev/null +++ b/.changeset/tags-save-updatedat.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixed saving a livechat tag failing with "Invalid response" — the model mutated its return value with `_updatedAt`, which the endpoint response schema rejects diff --git a/apps/meteor/ee/server/models/raw/LivechatTag.ts b/apps/meteor/ee/server/models/raw/LivechatTag.ts index 555b32dcf89b9..729ff8d4097d1 100644 --- a/apps/meteor/ee/server/models/raw/LivechatTag.ts +++ b/apps/meteor/ee/server/models/raw/LivechatTag.ts @@ -37,7 +37,8 @@ export class LivechatTagRaw extends BaseRaw implements ILivechatTa _id = (await this.insertOne(record)).insertedId; } - return Object.assign(record, { _id }); + // updateOne/insertOne mutate `record` by injecting `_updatedAt` (setUpdatedAt), so rebuild the declared shape + return { _id, name, description, numDepartments: departments.length, departments }; } // REMOVE diff --git a/apps/meteor/playwright.config.ts b/apps/meteor/playwright.config.ts index 1b27badd2e2d3..2326797e16b7d 100644 --- a/apps/meteor/playwright.config.ts +++ b/apps/meteor/playwright.config.ts @@ -22,6 +22,7 @@ export default { outputDir: 'tests/e2e/.playwright', reporter: [ ['list'], + process.env.GITHUB_ACTIONS === 'true' && ['github'], process.env.REPORTER_ROCKETCHAT_REPORT === 'true' && [ './reporters/rocketchat.ts', { diff --git a/apps/meteor/tests/e2e/e2e-encryption/e2ee-file-encryption.spec.ts b/apps/meteor/tests/e2e/e2e-encryption/e2ee-file-encryption.spec.ts index 7e3d2eca348cb..6c052e7ee0bf8 100644 --- a/apps/meteor/tests/e2e/e2e-encryption/e2ee-file-encryption.spec.ts +++ b/apps/meteor/tests/e2e/e2e-encryption/e2ee-file-encryption.spec.ts @@ -50,6 +50,18 @@ test.describe('E2EE File Encryption', () => { await page.goto(`/group/${group.name}`); await page.locator('#main-content').waitFor(); await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible(); + await expect + .poll( + () => + page.evaluate(async (rid) => { + // eslint-disable-next-line import-x/no-absolute-path + const { e2e } = require('/client/lib/e2ee/rocketchat.e2e.ts') as typeof import('../../../client/lib/e2ee/rocketchat.e2e'); + const room = await e2e.getInstanceByRoomId(rid); + return room?.getState(); + }, group._id), + { message: 'expect room encryption key to be ready before sending messages' }, + ) + .toBe('READY'); }); test.afterEach(async ({ api }) => { diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-agents.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-agents.spec.ts index 6eaf7f731a251..b7604b1c749d1 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-agents.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-agents.spec.ts @@ -81,7 +81,7 @@ test.describe.serial('OC - Manage Agents', () => { await test.step('expect update "user1" information', async () => { await poOmnichannelAgents.editAgent.selectStatus('Not Available'); await poOmnichannelAgents.editAgent.selectDepartment(department.data.name); - await poOmnichannelAgents.editAgent.btnSave.click(); + await poOmnichannelAgents.editAgent.save(); }); await test.step('expect removing "user1" via sidebar', async () => { diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-business-hours.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-business-hours.spec.ts index bc425f209299b..b9955ec4de292 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-business-hours.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-business-hours.spec.ts @@ -94,6 +94,7 @@ test.describe('OC - Business Hours', () => { await poOmnichannelBusinessHours.table.findRowByName(BHName).click(); await poOmnichannelBusinessHours.selectDepartment(department2.data.name); await poOmnichannelBusinessHours.btnSave.click(); + await expect(poOmnichannelBusinessHours.btnSave).not.toBeVisible(); }); await test.step('expect department to be in the chosen departments list', async () => { @@ -108,6 +109,7 @@ test.describe('OC - Business Hours', () => { await poOmnichannelBusinessHours.table.findRowByName(BHName).click(); await poOmnichannelBusinessHours.selectDepartment(department2.data.name); await poOmnichannelBusinessHours.btnSave.click(); + await expect(poOmnichannelBusinessHours.btnSave).not.toBeVisible(); }); await test.step('expect department to not be in the chosen departments list', async () => { diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-tags.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-tags.spec.ts index dd70cd34b4f0d..8b57f9872c6f2 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-tags.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-tags.spec.ts @@ -97,7 +97,8 @@ test.describe('OC - Manage Tags', () => { await poOmnichannelTags.table.findRowByName(tag.name).click(); await expect(poOmnichannelTags.editTag.root).toBeVisible(); await poOmnichannelTags.editTag.selectDepartment(department2.data.name); - await poOmnichannelTags.editTag.btnSave.click(); + await expect(poOmnichannelTags.editTag.findSelectedDepartment(department2.data.name)).toBeVisible(); + await poOmnichannelTags.editTag.save(); }); await test.step('expect department to be in the chosen departments list', async () => { @@ -113,7 +114,8 @@ test.describe('OC - Manage Tags', () => { await poOmnichannelTags.table.findRowByName(tag.name).click(); await expect(poOmnichannelTags.editTag.root).toBeVisible(); await poOmnichannelTags.editTag.selectDepartment(department2.data.name); - await poOmnichannelTags.editTag.btnSave.click(); + await expect(poOmnichannelTags.editTag.findSelectedDepartment(department2.data.name)).not.toBeVisible(); + await poOmnichannelTags.editTag.save(); }); await test.step('expect department to not be in the chosen departments list', async () => { diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel/omnichannel-tags.ts b/apps/meteor/tests/e2e/page-objects/omnichannel/omnichannel-tags.ts index a718a29e03e1c..f1523013ae796 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel/omnichannel-tags.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel/omnichannel-tags.ts @@ -22,6 +22,10 @@ class OmnichannelEditTagFlexTab extends FlexTab { await this.inputDepartments.fill(name); await this.listbox.selectOption(name); } + + findSelectedDepartment(name: string) { + return this.root.getByLabel('Departments').getByRole('option', { name }); + } } class OmnichannelTagsTable extends Table { diff --git a/apps/meteor/tests/e2e/threads.spec.ts b/apps/meteor/tests/e2e/threads.spec.ts index 2ab155aa5aecb..b010c79899a52 100644 --- a/apps/meteor/tests/e2e/threads.spec.ts +++ b/apps/meteor/tests/e2e/threads.spec.ts @@ -54,7 +54,9 @@ test.describe.serial('Threads', () => { await test.step('open threads contextual bar when clicked on thread preview', async () => { await poHomeChannel.content.lastThreadMessagePreviewText.click(); await expect(page).toHaveURL(/.*thread/); - await expect(poHomeChannel.content.lastUserThreadMessage).toContainText('This is a thread message also sent in channel'); + await expect(poHomeChannel.content.lastUserThreadMessage).toContainText('This is a thread message also sent in channel', { + timeout: 15_000, + }); }); await expect(page).not.toHaveURL(/[?&]msg=/); @@ -64,7 +66,9 @@ test.describe.serial('Threads', () => { test('expect not to close thread contextual bar when performing some action', async ({ page }) => { await poHomeChannel.content.lastThreadMessagePreviewText.click(); await expect(page).toHaveURL(/.*thread/); - await expect(poHomeChannel.content.lastUserThreadMessage).toContainText('This is a thread message also sent in channel'); + await expect(poHomeChannel.content.lastUserThreadMessage).toContainText('This is a thread message also sent in channel', { + timeout: 15_000, + }); await poHomeChannel.content.openLastThreadMessageMenu(); await page.locator('role=menuitem[name="Copy text"]').click(); diff --git a/apps/meteor/tests/end-to-end/api/rooms.ts b/apps/meteor/tests/end-to-end/api/rooms.ts index aa13edd3afa87..af7de96de5e50 100644 --- a/apps/meteor/tests/end-to-end/api/rooms.ts +++ b/apps/meteor/tests/end-to-end/api/rooms.ts @@ -2764,6 +2764,17 @@ describe('[Rooms]', () => { ]), ); + const expectSubscriptionFieldToEqual = async (field: 'name' | 'fname', expected: string) => { + for (let attempt = 0; ; attempt++) { + const { body } = await request.get(api('subscriptions.getOne')).set(credentials).query({ roomId }); + if (body.subscription?.[field] === expected || attempt >= 20) { + expect(body.subscription?.[field]).to.equal(expected); + return; + } + await sleep(250); + } + }; + it('should update group name if user changes username', async () => { await updateSetting('UI_Use_Real_Name', false); await request @@ -2776,18 +2787,7 @@ describe('[Rooms]', () => { }, }); - // need to wait for the username update finish - await sleep(300); - - await request - .get(api('subscriptions.getOne')) - .set(credentials) - .query({ roomId }) - .send() - .expect((res) => { - const { subscription } = res.body; - expect(subscription.name).to.equal(`changed.username.${testUser.username},${testUser2.username}`); - }); + await expectSubscriptionFieldToEqual('name', `changed.username.${testUser.username},${testUser2.username}`); }); describe('use real name', () => { @@ -2810,18 +2810,7 @@ describe('[Rooms]', () => { }, }); - // need to wait for the name update finish - await sleep(300); - - await request - .get(api('subscriptions.getOne')) - .set(credentials) - .query({ roomId }) - .send() - .expect((res) => { - const { subscription } = res.body; - expect(subscription.fname).to.equal(`changed.name.${testUser.username}, ${testUser2.name}`); - }); + await expectSubscriptionFieldToEqual('fname', `changed.name.${testUser.username}, ${testUser2.name}`); }); }); });