diff --git a/e2e-tests/playwright/lib/src/ui/components/channels/post_dot_menu.ts b/e2e-tests/playwright/lib/src/ui/components/channels/post_dot_menu.ts index 6c8b20b22c3..7df116840d6 100644 --- a/e2e-tests/playwright/lib/src/ui/components/channels/post_dot_menu.ts +++ b/e2e-tests/playwright/lib/src/ui/components/channels/post_dot_menu.ts @@ -21,6 +21,7 @@ export default class PostDotMenu { readonly copyTextMenuItem; readonly deleteMenuItem; readonly flagMessageMenuItem; + readonly showTranslationMenuItem; constructor(container: Locator) { this.container = container; @@ -42,6 +43,7 @@ export default class PostDotMenu { this.copyTextMenuItem = getMenuItem('Copy Text'); this.deleteMenuItem = getMenuItem('Delete'); this.flagMessageMenuItem = getMenuItem('Quarantine for Review'); + this.showTranslationMenuItem = getMenuItem('Show translation'); } async toBeVisible() { diff --git a/e2e-tests/playwright/specs/functional/channels/autotranslation/autotranslation.spec.ts b/e2e-tests/playwright/specs/functional/channels/autotranslation/autotranslation.spec.ts index 7b9738ddf40..f2f4cd1064a 100644 --- a/e2e-tests/playwright/specs/functional/channels/autotranslation/autotranslation.spec.ts +++ b/e2e-tests/playwright/specs/functional/channels/autotranslation/autotranslation.spec.ts @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import { + ChannelsPost, disableChannelAutotranslation, enableAutotranslationConfig, enableChannelAutotranslation, @@ -834,95 +835,98 @@ test( }, ); -// Skipped due to flaky race condition - see https://github.com/mattermost/mattermost/pull/35443 -// test( -// 'message actions include Show translation', -// { -// tag: ['@autotranslation'], -// }, -// async ({pw}) => { -// const {adminClient, user, userClient, team} = await pw.initSetup(); -// -// const license = await adminClient.getClientLicenseOld(); -// test.skip( -// !hasAutotranslationLicense(license.SkuShortName), -// 'Skipping test - server does not have Entry or Advanced license', -// ); -// const translationUrl = process.env.TRANSLATION_SERVICE_URL || 'http://localhost:3010'; -// await enableAutotranslationConfig(adminClient, { -// mockBaseUrl: translationUrl, -// targetLanguages: ['en', 'es'], -// }); -// -// const channelName = `autotranslation-dotmenu-${await getRandomId()}`; -// const created = await adminClient.createChannel({ -// team_id: team.id, -// name: channelName, -// display_name: 'Dot Menu Show Translation Test', -// type: 'O', -// }); -// await enableChannelAutotranslation(adminClient, created.id); -// await adminClient.addToChannel(user.id, created.id); -// await setUserChannelAutotranslation(userClient, created.id, true); -// -// const poster = await pw.random.user('poster'); -// const createdPoster = await adminClient.createUser(poster, '', ''); -// await adminClient.addToTeam(team.id, createdPoster.id); -// await adminClient.addToChannel(createdPoster.id, created.id); -// const {client: posterClient} = await pw.makeClient({ -// username: poster.username, -// password: poster.password, -// }); -// if (!posterClient) throw new Error('Failed to create poster client'); -// -// // Create a second poster to show translation indicator (only visible with multiple users) -// const poster2 = await pw.random.user('poster2'); -// const createdPoster2 = await adminClient.createUser(poster2, '', ''); -// await adminClient.addToTeam(team.id, createdPoster2.id); -// await adminClient.addToChannel(createdPoster2.id, created.id); -// const {client: posterClient2} = await pw.makeClient({ -// username: poster2.username, -// password: poster2.password, -// }); -// if (!posterClient2) throw new Error('Failed to create second poster client'); -// -// // Set Spanish source to ensure translation happens -// await setMockSourceLanguage(translationUrl, 'es'); -// // Post Spanish message that's long enough for reliable detection -// await posterClient.createPost({ -// channel_id: created.id, -// message: 'Este mensaje es para probar el menú de acciones con la opción de mostrar traducción automática', -// user_id: createdPoster.id, -// }); -// // Second user posts a message so the first user's translation indicator appears -// await posterClient2.createPost({ -// channel_id: created.id, -// message: 'Segundo usuario con mensaje más largo para mejor detección de idioma', -// user_id: createdPoster2.id, -// }); -// -// const {channelsPage, page} = await pw.testBrowser.login(user); -// await channelsPage.goto(team.name, channelName); -// await channelsPage.toBeVisible(); -// -// // * Find post with message text and wait for translation before opening dot menu -// const messagePost = channelsPage.centerView.container -// .locator('[id^="post_"]') -// .filter({hasText: 'Este mensaje es para probar el menú de acciones'}); -// await messagePost.waitFor({state: 'visible', timeout: 15000}); -// -// // Wait for mock translation to be applied before opening the menu -// // (mock appends "[translated to en]"; Show translation only appears after translation) -// await expect(messagePost.getByText(/\[translated to en\]/i)).toBeVisible({timeout: 15000}); -// -// await messagePost.hover(); -// // Click the "more" (three dots) button to open the action menu -// await messagePost.locator('.post-menu').getByRole('button', {name: 'more'}).click(); -// -// const showTranslationItem = page.getByRole('menuitem', {name: 'Show translation'}); -// await expect(showTranslationItem).toBeVisible({timeout: 10000}); -// }, -// ); +test( + 'message actions include Show translation', + { + tag: ['@autotranslation'], + }, + async ({pw}) => { + const {adminClient, user, userClient, team} = await pw.initSetup(); + + const license = await adminClient.getClientLicenseOld(); + test.skip( + !hasAutotranslationLicense(license.SkuShortName), + 'Skipping test - server does not have Entry or Advanced license', + ); + const translationUrl = process.env.TRANSLATION_SERVICE_URL || 'http://localhost:3010'; + await enableAutotranslationConfig(adminClient, { + mockBaseUrl: translationUrl, + targetLanguages: ['en', 'es'], + }); + + const channelName = `autotranslation-dotmenu-${await getRandomId()}`; + const created = await adminClient.createChannel({ + team_id: team.id, + name: channelName, + display_name: 'Dot Menu Show Translation Test', + type: 'O', + }); + await enableChannelAutotranslation(adminClient, created.id); + await adminClient.addToChannel(user.id, created.id); + await setUserChannelAutotranslation(userClient, created.id, true); + + const poster = await pw.random.user('poster'); + const createdPoster = await adminClient.createUser(poster, '', ''); + await adminClient.addToTeam(team.id, createdPoster.id); + await adminClient.addToChannel(createdPoster.id, created.id); + const {client: posterClient} = await pw.makeClient({ + username: poster.username, + password: poster.password, + }); + if (!posterClient) throw new Error('Failed to create poster client'); + + // Create a second poster to show translation indicator (only visible with multiple users) + const poster2 = await pw.random.user('poster2'); + const createdPoster2 = await adminClient.createUser(poster2, '', ''); + await adminClient.addToTeam(team.id, createdPoster2.id); + await adminClient.addToChannel(createdPoster2.id, created.id); + const {client: posterClient2} = await pw.makeClient({ + username: poster2.username, + password: poster2.password, + }); + if (!posterClient2) throw new Error('Failed to create second poster client'); + + // Set Spanish source to ensure translation happens + await setMockSourceLanguage(translationUrl, 'es'); + // Post Spanish message that's long enough for reliable detection + await posterClient.createPost({ + channel_id: created.id, + message: 'Este mensaje es para probar el menú de acciones con la opción de mostrar traducción automática', + user_id: createdPoster.id, + }); + // Second user posts a message so the first user's translation indicator appears + await posterClient2.createPost({ + channel_id: created.id, + message: 'Segundo usuario con mensaje más largo para mejor detección de idioma', + user_id: createdPoster2.id, + }); + + const {channelsPage, page} = await pw.testBrowser.login(user); + await channelsPage.goto(team.name, channelName); + await channelsPage.toBeVisible(); + + // * Find the target post and wait for its translation before opening the menu + const messagePost = channelsPage.centerView.container + .getByTestId('postView') + .filter({hasText: 'Este mensaje es para probar el menú de acciones'}); + await messagePost.waitFor({state: 'visible', timeout: 15000}); + await expect(messagePost.getByText(/\[translated to en\]/i)).toBeVisible({timeout: 15000}); + + // * Open dot menu using the established hover → wait → click pattern + const post = new ChannelsPost(messagePost); + await post.hover(); + await post.postMenu.toBeVisible(); + await post.postMenu.dotMenuButton.click(); + + // Move mouse away so it doesn't hover over Remind and trigger its submenu. + // The submenu's MUI portal sets aria-hidden on the main menu, breaking getByRole. + await page.mouse.move(0, 0); + await channelsPage.postDotMenu.toBeVisible(); + + // * Verify the "Show translation" menu item is present + await expect(channelsPage.postDotMenu.showTranslationMenuItem).toBeVisible({timeout: 10000}); + }, +); test( 'any user can disable and enable again autotranslation for themselves in a channel',