test: Add playwright main usage test
Some checks failed
Deploy Web Apps / deploy (push) Has been cancelled
Queue Release Build / prepare (push) Successful in 21s
Queue Release Build / build-linux (push) Successful in 27m44s
Queue Release Build / build-windows (push) Successful in 32m16s
Queue Release Build / finalize (push) Successful in 1m54s
Some checks failed
Deploy Web Apps / deploy (push) Has been cancelled
Queue Release Build / prepare (push) Successful in 21s
Queue Release Build / build-linux (push) Successful in 27m44s
Queue Release Build / build-windows (push) Successful in 32m16s
Queue Release Build / finalize (push) Successful in 1m54s
This commit is contained in:
143
e2e/pages/chat-messages.page.ts
Normal file
143
e2e/pages/chat-messages.page.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import {
|
||||
expect,
|
||||
type Locator,
|
||||
type Page
|
||||
} from '@playwright/test';
|
||||
|
||||
export type ChatDropFilePayload = {
|
||||
name: string;
|
||||
mimeType: string;
|
||||
base64: string;
|
||||
};
|
||||
|
||||
export class ChatMessagesPage {
|
||||
readonly composer: Locator;
|
||||
readonly composerInput: Locator;
|
||||
readonly sendButton: Locator;
|
||||
readonly typingIndicator: Locator;
|
||||
readonly gifButton: Locator;
|
||||
readonly gifPicker: Locator;
|
||||
readonly messageItems: Locator;
|
||||
|
||||
constructor(private page: Page) {
|
||||
this.composer = page.locator('app-chat-message-composer');
|
||||
this.composerInput = page.getByPlaceholder('Type a message...');
|
||||
this.sendButton = page.getByRole('button', { name: 'Send message' });
|
||||
this.typingIndicator = page.locator('app-typing-indicator');
|
||||
this.gifButton = page.getByRole('button', { name: 'Search KLIPY GIFs' });
|
||||
this.gifPicker = page.getByRole('dialog', { name: 'KLIPY GIF picker' });
|
||||
this.messageItems = page.locator('[data-message-id]');
|
||||
}
|
||||
|
||||
async waitForReady(): Promise<void> {
|
||||
await expect(this.composerInput).toBeVisible({ timeout: 30_000 });
|
||||
}
|
||||
|
||||
async sendMessage(content: string): Promise<void> {
|
||||
await this.waitForReady();
|
||||
await this.composerInput.fill(content);
|
||||
await this.sendButton.click();
|
||||
}
|
||||
|
||||
async typeDraft(content: string): Promise<void> {
|
||||
await this.waitForReady();
|
||||
await this.composerInput.fill(content);
|
||||
}
|
||||
|
||||
async clearDraft(): Promise<void> {
|
||||
await this.waitForReady();
|
||||
await this.composerInput.fill('');
|
||||
}
|
||||
|
||||
async attachFiles(files: ChatDropFilePayload[]): Promise<void> {
|
||||
await this.waitForReady();
|
||||
|
||||
await this.composerInput.evaluate((element, payloads: ChatDropFilePayload[]) => {
|
||||
const dataTransfer = new DataTransfer();
|
||||
|
||||
for (const payload of payloads) {
|
||||
const binary = atob(payload.base64);
|
||||
const bytes = new Uint8Array(binary.length);
|
||||
|
||||
for (let index = 0; index < binary.length; index++) {
|
||||
bytes[index] = binary.charCodeAt(index);
|
||||
}
|
||||
|
||||
dataTransfer.items.add(new File([bytes], payload.name, { type: payload.mimeType }));
|
||||
}
|
||||
|
||||
element.dispatchEvent(new DragEvent('drop', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
dataTransfer
|
||||
}));
|
||||
}, files);
|
||||
}
|
||||
|
||||
async openGifPicker(): Promise<void> {
|
||||
await this.waitForReady();
|
||||
await this.gifButton.click();
|
||||
await expect(this.gifPicker).toBeVisible({ timeout: 10_000 });
|
||||
}
|
||||
|
||||
async selectFirstGif(): Promise<void> {
|
||||
const gifCard = this.gifPicker.getByRole('button', { name: /click to select/i }).first();
|
||||
|
||||
await expect(gifCard).toBeVisible({ timeout: 10_000 });
|
||||
await gifCard.click();
|
||||
}
|
||||
|
||||
getMessageItemByText(text: string): Locator {
|
||||
return this.messageItems.filter({
|
||||
has: this.page.getByText(text, { exact: false })
|
||||
}).last();
|
||||
}
|
||||
|
||||
getMessageImageByAlt(altText: string): Locator {
|
||||
return this.page.locator(`[data-message-id] img[alt="${altText}"]`).last();
|
||||
}
|
||||
|
||||
async expectMessageImageLoaded(altText: string): Promise<void> {
|
||||
const image = this.getMessageImageByAlt(altText);
|
||||
|
||||
await expect(image).toBeVisible({ timeout: 20_000 });
|
||||
await expect.poll(async () =>
|
||||
image.evaluate((element) => {
|
||||
const img = element as HTMLImageElement;
|
||||
|
||||
return img.complete && img.naturalWidth > 0 && img.naturalHeight > 0;
|
||||
}), {
|
||||
timeout: 20_000,
|
||||
message: `Image ${altText} should fully load in chat`
|
||||
}).toBe(true);
|
||||
}
|
||||
|
||||
getEmbedCardByTitle(title: string): Locator {
|
||||
return this.page.locator('app-chat-link-embed').filter({
|
||||
has: this.page.getByText(title, { exact: true })
|
||||
}).last();
|
||||
}
|
||||
|
||||
async editOwnMessage(originalText: string, updatedText: string): Promise<void> {
|
||||
const messageItem = this.getMessageItemByText(originalText);
|
||||
const editButton = messageItem.locator('button:has(ng-icon[name="lucideEdit"])').first();
|
||||
const editTextarea = this.page.locator('textarea.edit-textarea').first();
|
||||
const saveButton = this.page.locator('button:has(ng-icon[name="lucideCheck"])').first();
|
||||
|
||||
await expect(messageItem).toBeVisible({ timeout: 15_000 });
|
||||
await messageItem.hover();
|
||||
await editButton.click();
|
||||
await expect(editTextarea).toBeVisible({ timeout: 10_000 });
|
||||
await editTextarea.fill(updatedText);
|
||||
await saveButton.click();
|
||||
}
|
||||
|
||||
async deleteOwnMessage(text: string): Promise<void> {
|
||||
const messageItem = this.getMessageItemByText(text);
|
||||
const deleteButton = messageItem.locator('button:has(ng-icon[name="lucideTrash2"])').first();
|
||||
|
||||
await expect(messageItem).toBeVisible({ timeout: 15_000 });
|
||||
await messageItem.hover();
|
||||
await deleteButton.click();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user