diff --git a/.gitignore b/.gitignore index 873e221..c2d81f3 100644 --- a/.gitignore +++ b/.gitignore @@ -86,3 +86,4 @@ $RECYCLE.BIN/ /playwright-report/ /blob-report/ /playwright/.cache/ +/downloads/ diff --git a/playwright/PageObjects/ChatsElements/CombinedSelector.ts b/playwright/PageObjects/ChatsElements/CombinedSelector.ts new file mode 100644 index 0000000..9888030 --- /dev/null +++ b/playwright/PageObjects/ChatsElements/CombinedSelector.ts @@ -0,0 +1,40 @@ +import MainPage from "../MainPage"; +import { type Locator, type Page } from "@playwright/test"; + +export class CombinedSelector extends MainPage { + readonly combinedSelector: Locator; + readonly emojiContainer: Locator; + readonly footerTabs: Locator; + readonly footerTabsEmojiButton: Locator; + readonly footerTabsGifButton: Locator; + readonly footerTabsStickerButton: Locator; + readonly giphySelector: Locator; + readonly stickerSelector: Locator; + + constructor(public readonly page: Page) { + super(page); + this.combinedSelector = this.page.getByTestId("combined-selector"); + this.emojiContainer = this.page.getByTestId("emoji-container"); + this.footerTabs = this.page.getByTestId(".pill-tabs"); + this.footerTabsEmojiButton = this.page.getByTestId("button-Emojis"); + this.footerTabsGifButton = this.page.getByTestId("button-GIFs"); + this.footerTabsStickerButton = this.page.getByTestId("button-Stickers"); + this.giphySelector = this.page.getByTestId("giphy-selector"); + this.stickerSelector = this.page.getByTestId("sticker-selector"); + } + + async goToEmojisTab() { + await this.footerTabsEmojiButton.click(); + await this.emojiContainer.waitFor({ state: "visible" }); + } + + async goToGifsTab() { + await this.footerTabsGifButton.click(); + await this.giphySelector.waitFor({ state: "visible" }); + } + + async goToStickersTab() { + await this.footerTabsStickerButton.click(); + await this.stickerSelector.waitFor({ state: "visible" }); + } +} diff --git a/playwright/PageObjects/ChatsElements/EmojiPicker.ts b/playwright/PageObjects/ChatsElements/EmojiPicker.ts new file mode 100644 index 0000000..4c755b6 --- /dev/null +++ b/playwright/PageObjects/ChatsElements/EmojiPicker.ts @@ -0,0 +1,124 @@ +import { CombinedSelector } from "./CombinedSelector"; +import { type Locator, type Page, expect } from "@playwright/test"; + +export class EmojiPicker extends CombinedSelector { + readonly categoryNav: Locator; + readonly categoryNavLink: Locator; + readonly emojiSection: Locator; + readonly emojiSectionEmojis: Locator; + readonly emojiSectionLabel: Locator; + readonly emojiSectionList: Locator; + readonly emojiContainerSearchInput: Locator; + readonly emojiContainerSizeLabel: Locator; + readonly emojiContainerSizeRangeSelector: Locator; + readonly emojiContainerSizeRangeSelectorInput: Locator; + readonly emojiContainerSizeSection: Locator; + readonly emojiSelector: Locator; + readonly frequentlyUsedSection: Locator; + readonly skinToneSelector: Locator; + readonly skinToneSelectorButton: Locator; + + constructor(public readonly page: Page) { + super(page); + this.categoryNav = this.page.getByTestId("emoji-category-nav"); + this.emojiContainerSearchInput = this.page.getByTestId( + "emoji-container-search-input", + ); + this.emojiContainerSizeLabel = this.page.getByTestId( + "emoji-container-size-label", + ); + this.emojiContainerSizeRangeSelector = this.page + .getByTestId("emoji-container-size-section") + .getByTestId("range-selector"); + this.emojiContainerSizeRangeSelectorInput = this.page + .getByTestId("emoji-container-size-section") + .getByTestId("range-selector") + .getByTestId("range-selector-input"); + this.emojiContainerSizeSection = this.page.getByTestId( + "emoji-container-size-section", + ); + this.emojiSelector = this.page.getByTestId("emoji-selector"); + this.frequentlyUsedSection = this.page.getByTestId( + "frequently-used-section", + ); + this.skinToneSelector = this.page.getByTestId("skin-tone-selector"); + this.skinToneSelectorButton = this.page.getByTestId( + "skin-tone-selector-button", + ); + } + + async changeSkinToneEmoji(index: number) { + await this.skinToneSelector.click(); + await this.page.locator(".skin-tone-popup").waitFor({ state: "visible" }); + const skinToneButton = this.page + .getByTestId("skin-tone-selector-button") + .nth(index); + await skinToneButton.click(); + await this.page.locator(".skin-tone-popup").waitFor({ state: "detached" }); + } + + async changeEmojiSizeView(size: string) { + await this.emojiContainerSizeRangeSelectorInput.fill(size); + } + + async navigateThroughEmojiCategories(category: string) { + const locator = this.page.getByTestId("category-link-" + category); + await locator.click(); + const section = this.page.getByTestId(category + "-section"); + await section.waitFor({ state: "visible" }); + } + + async searchEmoji(emoji: string) { + await this.emojiContainerSearchInput.fill(emoji); + } + + async selectEmoji(emoji: string) { + await this.page + .getByTestId("emoji-container") + .locator("span") + .filter({ + hasText: emoji, + }) + .click(); + } + + async validateEmojiCategories(expectedCategories: string[]) { + // Define an array to store section names + const sectionNames = []; + + // Select all section elements (assuming they have a data-cy attribute ending with '-section') + const sections = await this.page.$$(`section[data-cy$="-section"]`); + + // Loop through each section and find its corresponding label + for (const section of sections) { + // Find the label within the current section that ends with '-label' + const label = await section.$(`label[data-cy$="-label"]`); + if (label) { + const sectionName = await label.textContent(); + sectionNames.push(sectionName.trim()); + } + } + expect(sectionNames).toEqual(expectedCategories); + } + + async validateNumberOfEmojisPerSection( + section: string, + expectedNumber: number, + ) { + const emojisCount = await this.page + .getByTestId(section + "-section") + .locator("span") + .count(); + expect(emojisCount).toEqual(expectedNumber); + } + + async validateSingleEmojiSize(emoji: string, expectedSize: string) { + const emojiLocator = this.page + .getByTestId("emoji-container") + .locator("span") + .filter({ + hasText: emoji, + }); + await expect(emojiLocator).toHaveCSS("font-size", expectedSize); + } +} diff --git a/playwright/PageObjects/ChatsElements/GifPicker.ts b/playwright/PageObjects/ChatsElements/GifPicker.ts new file mode 100644 index 0000000..f22ecc0 --- /dev/null +++ b/playwright/PageObjects/ChatsElements/GifPicker.ts @@ -0,0 +1,11 @@ +import MainPage from "../MainPage"; +import { type Locator, type Page } from "@playwright/test"; + +export class GifPicker extends MainPage { + readonly combinedSelector: Locator; + + constructor(public readonly page: Page) { + super(page); + this.combinedSelector = this.page.getByTestId("combined-selector"); + } +} diff --git a/playwright/PageObjects/ChatsElements/StickerPicker.ts b/playwright/PageObjects/ChatsElements/StickerPicker.ts new file mode 100644 index 0000000..c578a12 --- /dev/null +++ b/playwright/PageObjects/ChatsElements/StickerPicker.ts @@ -0,0 +1,11 @@ +import MainPage from "../MainPage"; +import { type Locator, type Page } from "@playwright/test"; + +export class StickerPicker extends MainPage { + readonly combinedSelector: Locator; + + constructor(public readonly page: Page) { + super(page); + this.combinedSelector = this.page.getByTestId("combined-selector"); + } +} diff --git a/playwright/PageObjects/ChatsMain.ts b/playwright/PageObjects/ChatsMain.ts index 49a1600..112c455 100644 --- a/playwright/PageObjects/ChatsMain.ts +++ b/playwright/PageObjects/ChatsMain.ts @@ -61,6 +61,7 @@ export class ChatsMainPage extends MainPage { readonly fileEmbedName: Locator; readonly fileEmbedShareButton: Locator; readonly fileEmbedSize: Locator; + readonly gifPickerButton: Locator; readonly imageEmbedContainer: Locator; readonly imageEmbedDownloadButton: Locator; readonly imageEmbedFile: Locator; @@ -103,6 +104,7 @@ export class ChatsMainPage extends MainPage { readonly sectionAddSomeone: Locator; readonly scrollToBottomButton: Locator; readonly statusIndicator: Locator; + readonly stickerPickerButton: Locator; readonly textChatMessage: Locator; readonly topbar: Locator; readonly uploadFileInput: Locator; @@ -236,7 +238,9 @@ export class ChatsMainPage extends MainPage { .getByRole("textbox"); this.emojiButton = this.page.locator('[data-cy^="button-emoji-"]'); this.emojiGroup = this.page.getByTestId("emoji-group"); - this.emojiPickerButton = this.page.getByTestId("button-emoji-picker"); + this.emojiPickerButton = this.page.getByTestId( + "button-chatbar-emoji-picker", + ); this.fileEmbed = this.page.getByTestId("file-embed"); this.fileEmbedAddToFilesButton = this.fileEmbed.getByTestId( "file-embed-add-to-files-button", @@ -250,6 +254,7 @@ export class ChatsMainPage extends MainPage { "file-embed-share-button", ); this.fileEmbedSize = this.fileEmbed.getByTestId("file-embed-size"); + this.gifPickerButton = this.page.getByTestId("button-chatbar-gif-picker"); this.imageEmbedContainer = this.page.getByTestId("image-embed-container"); this.imageEmbedDownloadButton = this.imageEmbedContainer.getByTestId( "image-embed-download-button", @@ -335,6 +340,9 @@ export class ChatsMainPage extends MainPage { .locator(".scroll-to-bottom") .locator("button"); this.sectionAddSomeone = this.page.getByTestId("section-add-someone"); + this.stickerPickerButton = this.page.getByTestId( + "button-chatbar-sticker-picker", + ); this.textChatMessage = this.page.getByTestId("text-chat-message"); this.topbar = this.page.getByTestId("topbar"); this.uploadFileInput = this.chatbar.locator('input[type="file"]'); @@ -1093,4 +1101,18 @@ export class ChatsMainPage extends MainPage { await expect(modalPreviewImageContainer).toBeVisible(); await expect(modalPreviewImage).toBeVisible(); } + + // Emojis, GIFs and Stickers methods + + async openEmojiPicker() { + await this.emojiPickerButton.click(); + } + + async openGifPicker() { + await this.gifPickerButton.click(); + } + + async openStickerPicker() { + await this.stickerPickerButton.click(); + } } diff --git a/playwright/specs/03-friends-two-instances.spec.ts b/playwright/specs/03-friends-two-instances.spec.ts index afa246c..de69d82 100644 --- a/playwright/specs/03-friends-two-instances.spec.ts +++ b/playwright/specs/03-friends-two-instances.spec.ts @@ -7,6 +7,7 @@ import type { BrowserContext, Locator, Page } from "@playwright/test"; import { faker } from "@faker-js/faker"; import { SettingsProfile } from "playwright/PageObjects/Settings/SettingsProfile"; import { SettingsMessages } from "playwright/PageObjects/Settings/SettingsMessages"; +import { EmojiPicker } from "playwright/PageObjects/ChatsElements/EmojiPicker"; const username = "ChatUserA"; const usernameTwo = "ChatUserB"; @@ -1511,7 +1512,7 @@ test.describe("Two instances tests - Friends and Chats", () => { await chatsMainPageFirst.closeImagePreview(); }); - test("Sending and receiving emojis, gifs and stickers tests", async ({ + test("B66 - Sending and receiving emojis and emoji picker tests", async ({ firstUserContext, secondUserContext, }) => { @@ -1534,56 +1535,109 @@ test.describe("Two instances tests - Friends and Chats", () => { page1, ); - let fileLocations = [ - "playwright/assets/logo.jpg", - "playwright/assets/test.txt", - ]; - - await chatsMainPageSecond.uploadFiles(fileLocations); - await chatsMainPageSecond.validateFilePreviews(fileLocations); - await chatsMainPageSecond.sendMessage("bunch of files"); - - // Validate file sent is displayed on local side - await chatsMainPageSecond.validateFileEmbedInChat("test.txt", "14 B", true); + await chatsMainPageSecond.openEmojiPicker(); + const emojiPickerSecond = new EmojiPicker(page2); + await emojiPickerSecond.selectEmoji("😂"); + await chatsMainPageSecond.buttonChatbarSendMessage.click(); - // Validate image sent is displayed on local side - await chatsMainPageSecond.validateImageEmbedInChat( - "logo.jpg", - "7.75 kB", - true, + // Validate emoji sent is displayed on local and remote sides + await expect(chatsMainPageSecond.messageBubbleContent.last()).toHaveText( + "😂", ); - - // Validate file received is displayed in chat on remote side - await chatsMainPageFirst.validateFileEmbedInChat("test.txt", "14 B", false); - - // Validate image received is displayed in chat on remote side - await chatsMainPageFirst.validateImageEmbedInChat( - "logo.jpg", - "7.75 kB", - false, + await expect(chatsMainPageFirst.messageBubbleContent.last()).toHaveText( + "😂", ); - // B53 - User can download media from chat by clicking download - // Download last files sent and received - await chatsMainPageSecond.downloadFileLastMessage("file", true); - await chatsMainPageSecond.validateDownloadedFile("test.txt"); - await chatsMainPageFirst.downloadFileLastMessage("file", false); - await chatsMainPageFirst.validateDownloadedFile("test.txt"); - - // Download last images sent and received - await chatsMainPageSecond.downloadFileLastMessage("image", true); - await chatsMainPageSecond.validateDownloadedFile("logo.jpg"); - await chatsMainPageFirst.downloadFileLastMessage("image", false); - await chatsMainPageFirst.validateDownloadedFile("logo.jpg"); + // Change skin tone of emojis + await chatsMainPageSecond.openEmojiPicker(); + await emojiPickerSecond.changeSkinToneEmoji(2); + await emojiPickerSecond.selectEmoji("🖐🏾"); + await chatsMainPageSecond.buttonChatbarSendMessage.click(); - // B52 - User should be able to click on image in chat to see image preview - await chatsMainPageSecond.openImagePreviewLastImageSent(); - await chatsMainPageSecond.validateImagePreviewIsVisible(); - await chatsMainPageSecond.closeImagePreview(); - - await chatsMainPageFirst.openImagePreviewLastImageReceived(); - await chatsMainPageFirst.validateImagePreviewIsVisible(); - await chatsMainPageFirst.closeImagePreview(); + // Validate emoji sent is displayed on local and remote sides + await expect(chatsMainPageSecond.messageBubbleContent.last()).toHaveText( + "🖐🏾", + ); + await expect(chatsMainPageFirst.messageBubbleContent.last()).toHaveText( + "🖐🏾", + ); + + // Change emoji size in emojis container view + await chatsMainPageSecond.openEmojiPicker(); + await emojiPickerSecond.changeEmojiSizeView("16"); + await emojiPickerSecond.validateSingleEmojiSize("😀", "16px"); + await emojiPickerSecond.changeEmojiSizeView("45"); + await emojiPickerSecond.validateSingleEmojiSize("😀", "45px"); + await emojiPickerSecond.changeEmojiSizeView("30"); + await emojiPickerSecond.validateSingleEmojiSize("😀", "30px"); + + // Validate emoji categories displayed in emoji container + const emojiCategories = [ + "Frequently Used", + "smileys and emotion", + "people and body", + "animals and nature", + "food and drink", + "travel and places", + "activities", + "objects", + "symbols", + "flags", + ]; + await emojiPickerSecond.validateEmojiCategories(emojiCategories); + + // Validate number of emojis per category + await emojiPickerSecond.validateNumberOfEmojisPerSection( + "frequently-used", + 2, + ); + await emojiPickerSecond.validateNumberOfEmojisPerSection( + "smileys-and-emotion", + 168, + ); + await emojiPickerSecond.validateNumberOfEmojisPerSection( + "people-and-body", + 367, + ); + await emojiPickerSecond.validateNumberOfEmojisPerSection( + "animals-and-nature", + 153, + ); + await emojiPickerSecond.validateNumberOfEmojisPerSection( + "food-and-drink", + 135, + ); + await emojiPickerSecond.validateNumberOfEmojisPerSection( + "travel-and-places", + 218, + ); + await emojiPickerSecond.validateNumberOfEmojisPerSection("activities", 84); + await emojiPickerSecond.validateNumberOfEmojisPerSection("objects", 261); + await emojiPickerSecond.validateNumberOfEmojisPerSection("symbols", 223); + await emojiPickerSecond.validateNumberOfEmojisPerSection("flags", 269); + + // Validate user can navigate through all categories of emojis + await emojiPickerSecond.navigateThroughEmojiCategories( + "smileys-and-emotion", + ); + await emojiPickerSecond.navigateThroughEmojiCategories("people-and-body"); + await emojiPickerSecond.navigateThroughEmojiCategories( + "animals-and-nature", + ); + await emojiPickerSecond.navigateThroughEmojiCategories("food-and-drink"); + await emojiPickerSecond.navigateThroughEmojiCategories("travel-and-places"); + await emojiPickerSecond.navigateThroughEmojiCategories("activities"); + await emojiPickerSecond.navigateThroughEmojiCategories("objects"); + await emojiPickerSecond.navigateThroughEmojiCategories("symbols"); + await emojiPickerSecond.navigateThroughEmojiCategories("flags"); + + // Validate user can navigate through tabs in emoji picker + await emojiPickerSecond.goToGifsTab(); + await emojiPickerSecond.goToStickersTab(); + await emojiPickerSecond.goToEmojisTab(); + + // Search for emojis in emoji picker + await emojiPickerSecond.searchEmoji("mexico"); }); });