flotilla/e2e/specs/emoji-picker.spec.ts
2026-09-25 14:56:50 -07:00

160 lines
5.2 KiB
TypeScript

import {HOUR} from "@welshman/lib"
import type {Locator, Page} from "@playwright/test"
import {composer, emojiButton, expect, message, roomPath, test, users} from "../harness"
const openReactionPicker = async (page: Page, opener: Locator) => {
await opener.click()
const picker = page.locator("emoji-picker").filter({visible: true})
await expect(picker).toHaveCount(1)
return picker
}
const openComposerEmojiPicker = async (page: Page) => {
await page.getByRole("button", {name: "Add emoji"}).click()
const shell = page.locator('[data-component="EmojiPicker"]').filter({visible: true})
await expect(shell).toHaveCount(1)
return shell
}
const searchPicker = async (picker: Locator, annotation: string) => {
await picker.locator("input.search").fill(annotation)
await expect(picker.getByRole("option", {name: new RegExp(annotation)}).first()).toBeVisible()
}
test("Tippy reaction picker shows gestures and flags without CDN fetches", async ({seed, as}) => {
const scenario = await seed(({relay, user, at}) => {
const space = relay("space")
space.room("general", {name: "General"})
space.join(user.alice, "general")
space.join(user.bob, "general")
space.message(user.bob, "general", "coverage check", at(2, HOUR))
})
const alice = await as(users.alice, roomPath(scenario.space("space").url, "general"))
const cdnRequests: string[] = []
await alice.context().route(/jsdelivr|skypack|fonts\.googleapis|fonts\.gstatic/i, route => {
cdnRequests.push(route.request().url())
return route.continue()
})
const opener = emojiButton(message(alice, "coverage check"))
const picker = await openReactionPicker(alice, opener)
await searchPicker(picker, "heart hands")
await searchPicker(picker, "flag: India")
await alice.keyboard.press("Escape")
expect(cdnRequests).toEqual([])
})
test("reaction picker preserves skin tone", async ({seed, as}) => {
const scenario = await seed(({relay, user, at}) => {
const space = relay("space")
space.room("general", {name: "General"})
space.join(user.alice, "general")
space.join(user.bob, "general")
space.message(user.bob, "general", "skin tone check", at(2, HOUR))
})
const alice = await as(users.alice, roomPath(scenario.space("space").url, "general"))
const opener = emojiButton(message(alice, "skin tone check"))
const picker = await openReactionPicker(alice, opener)
await picker.getByRole("button", {name: /Choose a skin tone/}).click()
await picker.getByRole("option", {name: "Medium-Dark"}).click()
await searchPicker(picker, "thumbs up")
await picker
.getByRole("option", {name: /thumbs up/})
.first()
.click()
await expect(message(alice, "skin tone check").getByRole("button", {name: "👍🏾"})).toBeVisible()
})
test("composer inserts emoji and kaomoji at the caret and closes the popover", async ({
seed,
as,
}) => {
const scenario = await seed(({relay, user}) => {
const space = relay("space")
space.room("general", {name: "General"})
space.join(user.alice, "general")
})
const alice = await as(users.alice, roomPath(scenario.space("space").url, "general"))
const editor = composer(alice)
await editor.click()
await editor.pressSequentially("Hello world")
await editor.evaluate(node => {
const paragraph = node.querySelector("p")
const textNode = paragraph?.firstChild
if (!(textNode instanceof Text)) {
return
}
const range = document.createRange()
range.setStart(textNode, "Hello ".length)
range.collapse(true)
const selection = window.getSelection()
selection?.removeAllRanges()
selection?.addRange(range)
})
const shell = await openComposerEmojiPicker(alice)
const picker = shell.locator("emoji-picker")
await picker.locator("input.search").fill("grinning face")
await picker
.getByRole("option", {name: /grinning face/})
.first()
.click()
await expect(shell).toHaveCount(0)
await expect(editor).toBeFocused()
await expect(editor).toHaveText("Hello 😀world")
const kaomojiShell = await openComposerEmojiPicker(alice)
await kaomojiShell.getByRole("button", {name: "Kaomoji", exact: true}).click()
await kaomojiShell.getByRole("button", {name: "Shrug", exact: true}).click()
await expect(kaomojiShell).toHaveCount(0)
await expect(editor).toBeFocused()
await expect(editor).toContainText("¯\\_(ツ)_/¯")
})
test("Emoji → Kaomoji → Emoji still selects emoji", async ({seed, as}) => {
const scenario = await seed(({relay, user}) => {
const space = relay("space")
space.room("general", {name: "General"})
space.join(user.alice, "general")
})
const alice = await as(users.alice, roomPath(scenario.space("space").url, "general"))
const shell = await openComposerEmojiPicker(alice)
await shell.getByRole("button", {name: "Kaomoji", exact: true}).click()
await expect(shell.getByRole("button", {name: "Shrug", exact: true})).toBeVisible()
await shell.getByRole("button", {name: "Emoji", exact: true}).click()
const picker = shell.locator("emoji-picker")
await picker.locator("input.search").fill("party popper")
await picker.getByRole("option", {name: /party popper/}).click()
await expect(composer(alice)).toContainText("🎉")
})