snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
import type { Locator } from '@playwright/test';
import { FlexTab } from './flextab';
export class EditAdminRoomFlexTab extends FlexTab {
constructor(locator: Locator) {
super(locator);
}
get roomNameInput(): Locator {
return this.root.locator('input[name="roomName"]');
}
get privateLabel(): Locator {
return this.root.locator(`label >> text=Private`);
}
get privateInput(): Locator {
return this.root.locator('input[name="roomType"]');
}
get roomOwnerInput(): Locator {
return this.root.locator('input[name="roomOwner"]');
}
get archivedLabel(): Locator {
return this.root.locator('label >> text=Archived');
}
get archivedInput(): Locator {
return this.root.locator('input[name="archived"]');
}
get favoriteLabel(): Locator {
return this.root.locator('label >> text=Favorite');
}
get favoriteInput(): Locator {
return this.root.locator('input[name="favorite"]');
}
get defaultLabel(): Locator {
return this.root.locator('label >> text=Default');
}
get defaultInput(): Locator {
return this.root.locator('input[name="isDefault"]');
}
}

View File

@@ -0,0 +1,19 @@
import type { Page } from '@playwright/test';
import { FlexTab } from './flextab';
export class AddEmojiFlexTab extends FlexTab {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Add New Emoji' }));
}
}
export class EditEmojiFlexTab extends FlexTab {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Custom Emoji Info' }));
}
async delete() {
await this.btnDelete.click();
}
}

View File

@@ -0,0 +1,28 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
export class AutoTranslateFlexTab extends FlexTab {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Auto-Translate' }));
}
get checkboxAutomaticTranslation(): Locator {
return this.root.getByRole('checkbox', { name: 'Automatic Translation' });
}
get textAutomaticTranslation(): Locator {
return this.root.getByText('Automatic Translation', { exact: true });
}
get textEncryptedRoomCallout(): Locator {
return this.root.getByText('Automatic translation not available');
}
async setAutomaticTranslation(enabled: boolean): Promise<void> {
if ((await this.checkboxAutomaticTranslation.isChecked()) === enabled) {
return;
}
await this.textAutomaticTranslation.click();
}
}

View File

@@ -0,0 +1,83 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
import { Listbox } from '../listbox';
import { ConfirmDeleteRoomModal, ConfirmRemoveModal } from '../modals';
import { Modal } from '../modals/modal';
class AddExistingChannelModal extends Modal {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Add Existing Channels' }));
}
get inputChannels(): Locator {
return this.root.getByRole('textbox');
}
get btnAdd(): Locator {
return this.root.getByRole('button', { name: 'Add', exact: true });
}
async confirmAdd() {
await this.btnAdd.click();
await this.waitForDismissal();
}
}
export class ChannelsFlexTab extends FlexTab {
readonly confirmRemoveModal: ConfirmRemoveModal;
readonly confirmDeleteModal: ConfirmDeleteRoomModal;
readonly addExistingChannelModal: AddExistingChannelModal;
readonly listbox: Listbox;
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Team Channels' }));
this.confirmRemoveModal = new ConfirmRemoveModal(page.getByRole('dialog', { name: 'Are you sure?' }));
this.confirmDeleteModal = new ConfirmDeleteRoomModal(page);
this.addExistingChannelModal = new AddExistingChannelModal(page);
this.listbox = new Listbox(page);
}
get btnAddExisting(): Locator {
return this.root.getByRole('button', { name: 'Add Existing', exact: true });
}
get btnCreateNew(): Locator {
return this.root.getByRole('button', { name: 'Create new', exact: true });
}
get channelsList(): Locator {
return this.root.getByRole('list');
}
getListboxOption(name: string): Locator {
return this.listbox.getOption(name);
}
channelOption(name: string) {
return this.root.locator('li', { hasText: name });
}
async openChannelOptionMoreActions(name: string) {
await this.channelOption(name).hover();
await this.channelOption(name).getByRole('button', { name: 'More' }).click();
}
async confirmRemoveChannel() {
return this.confirmRemoveModal.confirmRemove();
}
async confirmDeleteRoom() {
return this.confirmDeleteModal.confirmDelete();
}
async addExistingChannel(name: string) {
await this.btnAddExisting.click();
await this.addExistingChannelModal.inputChannels.fill(name);
await this.listbox.selectOption(name);
await this.addExistingChannelModal.confirmAdd();
}
}

View File

@@ -0,0 +1,17 @@
import type { Locator } from '@playwright/test';
import { FlexTab } from './flextab';
export class DeviceInfoFlexTab extends FlexTab {
constructor(locator: Locator) {
super(locator);
}
get btnLogoutDevice(): Locator {
return this.root.getByRole('button', { name: 'Log out device' });
}
getDeviceInfoId(deviceId: string): Locator {
return this.root.getByText(deviceId, { exact: true });
}
}

View File

@@ -0,0 +1,33 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
export class OmnichannelEditContactFlexTab extends FlexTab {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'contact' }));
}
get inputEmail(): Locator {
return this.root.locator('input[name="emails.0.address"]');
}
get inputPhone(): Locator {
return this.root.locator('input[name="phones.0.phoneNumber"]');
}
get inputContactManager(): Locator {
return this.root.locator('input[name=contactManager]');
}
get btnAddEmail(): Locator {
return this.root.locator('role=button[name="Add email"]');
}
get btnAddPhone(): Locator {
return this.root.locator('role=button[name="Add phone"]');
}
getErrorMessage(message: string): Locator {
return this.root.locator(`role=alert >> text="${message}"`);
}
}

View File

@@ -0,0 +1,114 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
import { Listbox } from '../listbox';
export class EditRoomFlexTab extends FlexTab {
constructor(locator: Locator) {
super(locator);
}
get inputTopic(): Locator {
return this.root.getByRole('textbox', { name: 'Topic' });
}
get inputAnnouncement(): Locator {
return this.root.getByRole('textbox', { name: 'Announcement' });
}
get inputDescription(): Locator {
return this.root.getByRole('textbox', { name: 'Description' });
}
get checkboxReadOnly(): Locator {
return this.root.locator('label', { hasText: 'Read-only' });
}
get calloutRetentionPolicy(): Locator {
return this.root.getByRole('alert', { name: 'Retention policy warning callout' });
}
get advancedSettingsAccordion(): Locator {
return this.root.getByRole('button', { name: 'Advanced settings' });
}
get pruneAccordion(): Locator {
return this.root.getByRole('button', { name: 'Prune', exact: true });
}
getMaxAgeLabel(maxAge = '30') {
return this.root.getByText(`Maximum message age in days (default: ${maxAge})`);
}
get inputRetentionMaxAge(): Locator {
return this.root.locator('input[name="retentionMaxAge"]');
}
get checkboxPruneMessages(): Locator {
return this.root.locator('label', { hasText: 'Automatically prune old messages' });
}
get checkboxOverrideGlobalRetention(): Locator {
return this.root.locator('label', { hasText: 'Override global retention policy' });
}
get checkboxIgnoreThreads(): Locator {
return this.root.locator('label', { hasText: 'Do not prune Threads' });
}
get checkboxChannels(): Locator {
return this.root.locator('label', { hasText: 'Channels' });
}
get checkboxDiscussions(): Locator {
return this.root.locator('label', { hasText: 'Discussions' });
}
async toggleSidepanelItems() {
await this.checkboxChannels.click();
await this.checkboxDiscussions.click();
}
}
export class EditTeamFlexTab extends EditRoomFlexTab {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Edit team' }));
}
}
export class OmnichannelEditRoomFlexTab extends EditRoomFlexTab {
private readonly tagsListbox: Listbox;
private readonly slaListbox: Listbox;
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Edit Room' }));
this.tagsListbox = new Listbox(page);
this.slaListbox = new Listbox(page, 'SLA Policy');
}
get inputSLAPolicy(): Locator {
return this.root.getByRole('button', { name: 'SLA Policy', exact: true });
}
optionTag(name: string): Locator {
return this.tagsListbox.getOption(name);
}
async selectTag(name: string) {
await this.tagsListbox.selectOption(name);
}
async selectSLA(name: string) {
await this.inputSLAPolicy.click();
await this.slaListbox.selectOption(name, true);
}
get inputTags(): Locator {
return this.root.getByRole('textbox', { name: 'Select an option' });
}
getRoomCustomField(label: string): Locator {
return this.root.getByRole('group', { name: 'Custom Fields' }).getByLabel(label);
}
}

View File

@@ -0,0 +1,53 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
export class EditUserFlexTab extends FlexTab {
constructor(page: Page) {
super(page.getByRole('dialog'));
}
get btnAddUser(): Locator {
return this.root.locator('role=button[name="Add user"]');
}
get btnSaveUser(): Locator {
return this.root.locator('role=button[name="Save user"]');
}
get inputUserName(): Locator {
return this.root.getByRole('textbox', { name: 'Username', exact: true });
}
get inputEmail(): Locator {
return this.root.getByRole('textbox', { name: 'Email', exact: true });
}
get inputSetManually(): Locator {
return this.root.locator('//label[text()="Set manually"]');
}
get inputPassword(): Locator {
return this.root.getByPlaceholder('Password', { exact: true });
}
get inputConfirmPassword(): Locator {
return this.root.getByPlaceholder('Confirm password', { exact: true });
}
get joinDefaultChannels(): Locator {
return this.root.locator('//label[text()="Join default channels"]');
}
get chipUserRole(): Locator {
return this.root.getByRole('button', { name: 'user', exact: true });
}
get setupSmtpLink(): Locator {
return this.root.getByRole('link', { name: 'Set up SMTP' });
}
getCustomField(fieldName: string): Locator {
return this.root.getByRole('textbox', { name: fieldName, exact: true });
}
}

View File

@@ -0,0 +1,87 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
export class ExportMessagesFlexTab extends FlexTab {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Export Messages' }));
}
async exposeMethods() {
await this.method.click();
}
async setMethod(optionName: string) {
await this.exposeMethods();
await this.root.page().getByRole('option', { name: optionName }).click();
}
async exposeOutputFormats() {
await this.outputFormat.click();
}
async setOutputFormat(optionName: string) {
await this.exposeOutputFormats();
await this.root.page().getByRole('option', { name: optionName }).click();
}
getMethodOptionByName(name: string) {
return this.root.page().getByRole('option', { name });
}
getOutputFormatOptionByName(name: string) {
return this.root.page().getByRole('option', { name });
}
async selectAllMessages() {
await this.root
.page()
.getByRole('button', { name: /Select \d+ messages/ })
.click();
}
async downloadMessages() {
const [download] = await Promise.all([this.root.page().waitForEvent('download'), this.downloadButton.click()]);
return download;
}
async send() {
await this.sendButton.click();
}
async setAdditionalEmail(email: string) {
await this.inputAdditionalEmails.fill(email);
}
getMessageCheckbox(messageText: string): Locator {
return this.root.page().getByRole('listitem').filter({ hasText: messageText }).getByRole('checkbox');
}
get inputUsers() {
return this.root.getByRole('combobox', { name: 'To users' });
}
get inputAdditionalEmails() {
return this.root.getByRole('textbox', { name: 'To additional emails' });
}
get method() {
return this.root.getByTestId('export-messages-method');
}
get outputFormat() {
return this.root.page().getByTestId('export-messages-output-format');
}
get downloadButton() {
return this.root.getByRole('button', { name: 'Download', exact: true });
}
get sendButton() {
return this.root.getByRole('button', { name: 'Send', exact: true });
}
get clearSelectionButton() {
return this.root.page().getByRole('button', { name: 'Clear selection' });
}
}

View File

@@ -0,0 +1,31 @@
import type { Page } from '@playwright/test';
import { FlexTab } from './flextab';
import { MenuMore } from '../menu';
import { ConfirmDeleteModal } from '../modals';
export class FilesFlexTab extends FlexTab {
readonly menu: MenuMore;
readonly confirmDeleteModal: ConfirmDeleteModal;
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Files' }));
this.menu = new MenuMore(page);
this.confirmDeleteModal = new ConfirmDeleteModal(page.getByRole('dialog', { name: 'Are you sure?' }));
}
get fileList() {
return this.root.getByRole('list', { name: 'Files list' });
}
getFileByName(name: string) {
return this.fileList.getByRole('listitem').filter({ hasText: name });
}
async deleteFile(name: string) {
await this.getFileByName(name).getByRole('button', { name: 'More' }).click();
await this.menu.selectMenuItem('Delete');
await this.confirmDeleteModal.confirmDelete();
}
}

View File

@@ -0,0 +1,56 @@
import type { Locator } from '@playwright/test';
import { expect } from '../../../utils/test';
export abstract class FlexTab {
/**
* @param root should be protected, but for now there are a lot of tests relying on accessing it directly, * so we need to keep it public until we can refactor those tests
*/
constructor(public root: Locator) {}
waitForDisplay() {
return expect(this.root).toBeVisible();
}
waitForDismissal() {
return expect(this.root).not.toBeVisible();
}
private get btnClose() {
return this.root.getByRole('button', { name: 'Close', exact: true });
}
get inputName() {
return this.root.getByRole('textbox', { name: 'Name', exact: true });
}
get btnSave() {
return this.root.getByRole('button', { name: 'Save', exact: true });
}
get btnCancel() {
return this.root.getByRole('button', { name: 'Cancel', exact: true });
}
get btnDelete() {
return this.root.getByRole('button', { name: 'Delete', exact: true });
}
get btnReset() {
return this.root.getByRole('button', { name: 'Reset', exact: true });
}
errorMessage(message: string): Locator {
return this.root.locator('[role="alert"]', { hasText: message });
}
async close() {
await this.btnClose.click();
await this.waitForDismissal();
}
async save() {
await this.btnSave.click();
await this.waitForDismissal();
}
}

View File

@@ -0,0 +1,17 @@
export * from './auto-translate-flextab';
export * from './admin-flextab-emoji';
export * from './admin-edit-room-flextab';
export * from './channels-flextab';
export * from './device-info-flextab';
export * from './edit-room-flextab';
export * from './edit-user-flextab';
export * from './edit-contact-flextab';
export * from './export-messages-flextab';
export * from './files-flextab';
export * from './members-flextab';
export * from './notification-preferences-flextab';
export * from './prune-messages-flextab';
export * from './room-info-flextab';
export * from './threads-flextab';
export * from './search-messages-flextab';
export * from './user-info-flextab';

View File

@@ -0,0 +1,179 @@
import type { Page } from '@playwright/test';
import { FlexTab } from './flextab';
import { Listbox } from '../listbox';
import { MenuMore } from '../menu';
import { ConfirmRemoveModal } from '../modals';
import { UserInfoFlexTab } from './user-info-flextab';
import { Modal } from '../modals/modal';
export class ConfirmMuteModal extends Modal {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Are you sure' }));
}
private get btnMute() {
return this.root.getByRole('button', { name: 'Yes, mute user!' });
}
async confirmMute() {
await this.btnMute.click();
await this.waitForDismissal();
}
}
export class RemoveTeamMemberModal extends Modal {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Removing Member' }));
}
get description() {
return this.root.getByText('Select the channels you want the user to be removed from');
}
channel(channelName: string) {
return this.root.getByText(channelName);
}
channelCheckbox(channelName: string) {
return this.root.getByRole('row', { name: channelName }).getByRole('checkbox');
}
async selectChannel(channelName: string) {
await this.channel(channelName).click();
}
private get btnContinue() {
return this.root.getByRole('button', { name: 'Continue' });
}
async continue() {
await this.btnContinue.click();
await this.waitForDismissal();
}
}
class AddUsersFlexTab extends FlexTab {
readonly listbox: Listbox;
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Add users' }));
this.listbox = new Listbox(page);
}
private btnAddUsers() {
return this.root.getByRole('button', { name: 'Add users' });
}
async addUser(username: string) {
await this.root.getByRole('combobox', { name: 'Choose users' }).pressSequentially(username);
await this.listbox.selectOption(username);
await this.btnAddUsers().click();
await this.waitForDismissal();
}
}
export class MembersFlexTab extends FlexTab {
readonly listbox: Listbox;
readonly removeModal: ConfirmRemoveModal;
readonly removeTeamMemberModal: RemoveTeamMemberModal;
readonly menu: MenuMore;
readonly confirmMuteModal: ConfirmMuteModal;
readonly addUsersFlexTab: AddUsersFlexTab;
readonly userInfo: UserInfoFlexTab;
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Members' }));
this.removeModal = new ConfirmRemoveModal(page.getByRole('dialog', { name: 'Confirmation', exact: true }));
this.removeTeamMemberModal = new RemoveTeamMemberModal(page);
this.listbox = new Listbox(page);
this.menu = new MenuMore(page);
this.confirmMuteModal = new ConfirmMuteModal(page);
this.addUsersFlexTab = new AddUsersFlexTab(page);
this.userInfo = new UserInfoFlexTab(page);
}
memberOption(username: string) {
return this.root.locator('li', { hasText: username });
}
getMenuItemAction(action: string) {
return this.menu.getMenuItem(action);
}
async openMemberInfo(username: string) {
await this.memberOption(username).click();
}
async openMoreActions() {
await this.root.getByRole('button', { name: 'More' }).click();
}
async openMemberOptionMoreActions(username: string) {
await this.memberOption(username).hover();
await this.memberOption(username).getByRole('button', { name: 'More' }).click();
}
async addUser(username: string) {
await this.root.getByRole('button', { name: 'Add' }).click();
await this.addUsersFlexTab.addUser(username);
}
private get btnInviteLink() {
return this.root.getByRole('button', { name: 'Invite Link' });
}
async inviteUser() {
await this.btnInviteLink.click();
}
async muteUser(username: string) {
await this.openMemberOptionMoreActions(username);
await this.menu.selectMenuItem('Mute user');
await this.confirmMuteModal.confirmMute();
}
async unmuteUser(username: string) {
await this.openMemberOptionMoreActions(username);
await this.menu.selectMenuItem('Unmute user');
}
async setUserAsModerator(username: string) {
await this.openMemberOptionMoreActions(username);
await this.menu.selectMenuItem('Set as moderator');
}
async setUserAsOwner(username: string) {
await this.openMemberOptionMoreActions(username);
await this.menu.selectMenuItem('Set as owner');
}
async showAllUsers() {
await this.root.getByRole('button', { name: 'Online' }).click();
await this.listbox.selectOption('All');
}
private async ignoreUserAction(action: string, username: string) {
await this.openMemberInfo(username);
await this.userInfo.openMoreActions();
await this.userInfo.menu.selectMenuItem(action);
}
async ignoreUser(username: string) {
await this.ignoreUserAction('Ignore', username);
}
async unignoreUser(username: string) {
await this.ignoreUserAction('Unignore', username);
}
async confirmRemoveUser() {
return this.removeModal.confirmRemove();
}
}

View File

@@ -0,0 +1,41 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
import { Listbox } from '../listbox';
export class NotificationPreferencesFlexTab extends FlexTab {
readonly listbox: Listbox;
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Notifications Preferences' }));
this.listbox = new Listbox(page);
}
getPreferenceByDevice(device: string): Locator {
return this.root.locator(`//div[@id="${device}Alert"]`);
}
async selectDropdownById(text: string): Promise<void> {
await this.root.locator(`//div[@id="${text}"]`).click();
}
async selectOptionByLabel(text: string): Promise<void> {
await this.listbox.selectOption(text);
}
async selectDevice(text: string): Promise<void> {
await this.root.getByRole('button', { name: text }).click();
}
async updateDevicePreference(device: string): Promise<void> {
await this.selectDevice(device);
await this.selectDropdownById(`${device}Alert`);
await this.selectOptionByLabel('Mentions');
}
async updateAllNotificationPreferences(): Promise<void> {
await this.updateDevicePreference('Desktop');
await this.updateDevicePreference('Mobile');
await this.updateDevicePreference('Email');
}
}

View File

@@ -0,0 +1,37 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
import { Modal } from '../modals/modal';
class ConfirmPruneMessageModal extends Modal {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Are you sure?' }));
}
async pruneConfirm(): Promise<void> {
await this.root.getByRole('button', { name: 'Yes, prune them!' }).click();
await this.waitForDismissal();
}
}
export class PruneMessagesFlexTab extends FlexTab {
readonly confirmPruneModal: ConfirmPruneMessageModal;
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Prune Messages' }));
this.confirmPruneModal = new ConfirmPruneMessageModal(page);
}
get labelDoNotPrunePinned(): Locator {
return this.root.locator('label', { hasText: 'Do not prune pinned messages' });
}
get labelFilesOnly(): Locator {
return this.root.locator('label', { hasText: 'Only remove the attached files, keep messages' });
}
async prune(): Promise<void> {
await this.root.getByRole('button', { name: 'Prune' }).click();
await this.confirmPruneModal.pruneConfirm();
}
}

View File

@@ -0,0 +1,124 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
import { MenuMore } from '../menu';
import { ConfirmDeleteTeamModal, ConfirmDeleteRoomModal } from '../modals';
import { Modal } from '../modals/modal';
class ConfirmLeaveRoomModal extends Modal {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Confirmation', exact: true }));
}
private get btnLeave() {
return this.root.getByRole('button', { name: 'Leave', exact: true });
}
async confirmLeave() {
await this.btnLeave.click();
await this.waitForDismissal();
}
}
export class RoomInfoFlexTab extends FlexTab {
readonly menu: MenuMore;
readonly confirmLeaveModal: ConfirmLeaveRoomModal;
readonly confirmDeleteModal: ConfirmDeleteRoomModal;
constructor(root: Locator) {
super(root);
this.menu = new MenuMore(root.page());
this.confirmLeaveModal = new ConfirmLeaveRoomModal(root.page());
this.confirmDeleteModal = new ConfirmDeleteRoomModal(root.page());
}
get btnEdit(): Locator {
return this.root.getByRole('button', { name: 'Edit' });
}
get btnLeave(): Locator {
return this.root.getByRole('button', { name: 'Leave' });
}
get btnMore(): Locator {
return this.root.getByRole('button', { name: 'More' });
}
get optionDelete(): Locator {
return this.menu.getMenuItem('Delete');
}
async leaveRoom() {
await this.btnLeave.click();
await this.confirmLeaveModal.confirmLeave();
}
async deleteRoom() {
await this.btnMore.click();
await this.menu.selectMenuItem('Delete');
await this.confirmDeleteModal.confirmDelete();
}
}
class ConfirmConvertIntoChannelModal extends Modal {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Confirmation', exact: true }));
}
private get btnConvert() {
return this.root.getByRole('button', { name: 'Convert', exact: true });
}
async confirmConvert() {
await this.btnConvert.click();
await this.waitForDismissal();
}
}
export class TeamInfoFlexTab extends RoomInfoFlexTab {
readonly confirmDeleteTeamModal: ConfirmDeleteTeamModal;
readonly confirmConvertIntoChannelModal: ConfirmConvertIntoChannelModal;
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Team info' }));
this.confirmDeleteTeamModal = new ConfirmDeleteTeamModal(page);
this.confirmConvertIntoChannelModal = new ConfirmConvertIntoChannelModal(page);
}
async deleteTeam() {
await this.btnMore.click();
await this.menu.selectMenuItem('Delete');
return this.confirmDeleteTeamModal.confirmDelete();
}
async convertIntoChannel() {
await this.btnMore.click();
await this.menu.selectMenuItem('Convert to Channel');
await this.confirmConvertIntoChannelModal.confirmConvert();
}
}
export class OmnichannelRoomInfoFlexTab extends RoomInfoFlexTab {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Room Information' }));
}
getInfo(value: string): Locator {
return this.root.locator(`span >> text="${value}"`);
}
getLabel(label: string): Locator {
return this.root.locator(`div >> text="${label}"`);
}
getInfoByLabel(label: string): Locator {
return this.root.getByLabel(label);
}
getTagInfoByLabel(label: string): Locator {
return this.root.getByRole('list', { name: 'Tags' }).getByText(label, { exact: true });
}
}

View File

@@ -0,0 +1,26 @@
import type { Page } from '@playwright/test';
import { FlexTab } from './flextab';
export class SearchMessagesFlexTab extends FlexTab {
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'Search Messages' }));
}
async search(text: string, { global = false }: { global?: boolean } = {}) {
if (global) {
await this.root.getByText('Global search').click();
}
await this.root.getByPlaceholder('Search Messages').fill(text);
}
async getResultItem(messageText: string) {
return this.root.getByRole('listitem', { name: messageText });
}
async jumpToMessage(messageText: string) {
const message = (await this.getResultItem(messageText)).first();
await message.hover();
await message.getByRole('button', { name: 'Jump to message' }).click();
}
}

View File

@@ -0,0 +1,21 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
export class ThreadsFlexTab extends FlexTab {
constructor(page: Page) {
super(page.getByRole('dialog'));
}
private get listThreadMessages(): Locator {
return this.root.getByRole('list', { name: 'Thread message list' });
}
get lastThreadMessage(): Locator {
return this.listThreadMessages.locator('[data-qa-type="message"]').last().locator('[data-qa-type="message-body"]');
}
getThreadMessageByText(messageText: string): Locator {
return this.listThreadMessages.getByRole('listitem').getByText(messageText);
}
}

View File

@@ -0,0 +1,33 @@
import type { Locator, Page } from '@playwright/test';
import { FlexTab } from './flextab';
import { MenuMore } from '../menu';
export class UserInfoFlexTab extends FlexTab {
readonly menu: MenuMore;
constructor(page: Page) {
super(page.getByRole('dialog', { name: 'User Info' }));
this.menu = new MenuMore(page);
}
get btnEdit(): Locator {
return this.root.getByRole('button', { name: 'Edit' });
}
get btnMoreActions(): Locator {
return this.root.getByRole('button', { name: 'More' });
}
get menuItemDeleteUser(): Locator {
return this.menu.root.getByRole('menuitem', { name: 'Delete' });
}
get username(): Locator {
return this.root.getByLabel('Username');
}
async openMoreActions() {
await this.btnMoreActions.click();
}
}