mattermost-mobile-upstream/detox/e2e/support/ui/screen/thread_options.ts
yasser khan f3af9dbe99
Fix/detox hittest cluster (#10023)
fix(e2e): stabilize Detox and Maestro test suite

Restore main features, and harden the E2E harness against flaky UI, network, retry, synchronization, and classification-lock failures.

Stabilize channel creation, saved messages, bookmarks, search, deep links, keyboard dismissal, and post verification.
Bound network, transport, health-check, synchronization, and lock retries by elapsed time.
Fix iOS and Android Maestro/Detox races and flaky interaction flows.
Restore Session Attributes and deep-link fixes lost during merge.
Improve test isolation, global-setting cleanup, and Jest/TSIO reporting.
Enforce Maestro flow-header validation in local and CI checks.
2026-08-27 08:58:03 +05:30

90 lines
3.8 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {isIos, timeouts, wait, waitForElementToExist, waitForElementToNotExist, withSynchronizationDisabled} from '@support/utils';
import {expect, waitFor} from 'detox';
class ThreadOptionsScreen {
testID = {
threadOptionsScreen: 'thread_options.screen',
replyThreadOption: 'post_options.reply_post.option',
followThreadOption: 'post_options.follow_thread.option',
followingThreadOption: 'post_options.following_thread.option',
openInChannelOption: 'thread_options.open_in_channel.option',
markAsReadOption: 'thread_options.mark_as_read.option',
markAsUnreadOption: 'thread_options.mark_as_unread.option',
saveThreadOption: 'post_options.Save_post.option',
unsaveThreadOption: 'post_options.Unsave_post.option',
copyLinkOption: 'post_options.copy_permalink.option',
};
threadOptionsScreen = element(by.id(this.testID.threadOptionsScreen));
replyThreadOption = element(by.id(this.testID.replyThreadOption));
followThreadOption = element(by.id(this.testID.followThreadOption));
followingThreadOption = element(by.id(this.testID.followingThreadOption));
openInChannelOption = element(by.id(this.testID.openInChannelOption));
markAsReadOption = element(by.id(this.testID.markAsReadOption));
markAsUnreadOption = element(by.id(this.testID.markAsUnreadOption));
saveThreadOption = element(by.id(this.testID.saveThreadOption));
unsaveThreadOption = element(by.id(this.testID.unsaveThreadOption));
copyLinkOption = element(by.id(this.testID.copyLinkOption));
toBeVisible = async () => {
await waitForElementToExist(this.threadOptionsScreen, timeouts.TEN_SEC);
return threadOptionsScreen;
};
close = async () => {
if (isIos()) {
await this.threadOptionsScreen.swipe('down');
} else {
await device.pressBack();
}
await wait(timeouts.ONE_SEC);
await expect(this.threadOptionsScreen).not.toBeVisible();
await wait(timeouts.ONE_SEC);
};
// Same gorhom+Reanimated path as PostOptionsScreen.tapSheetRowIos: center taps miss
// the Save row on iOS 26, and Detox sync never idles so waitFor cannot see dismiss.
private tapSheetRowIos = async (option: Detox.NativeElement) => {
await withSynchronizationDisabled(async () => {
await waitForElementToExist(this.threadOptionsScreen, timeouts.TEN_SEC);
await waitForElementToExist(option, timeouts.TEN_SEC);
await option.tap({x: 1, y: 1});
try {
await waitForElementToNotExist(this.threadOptionsScreen, timeouts.FIVE_SEC);
} catch {
await option.tap({x: 1, y: 1});
await waitForElementToNotExist(this.threadOptionsScreen, timeouts.FIVE_SEC);
}
});
};
tapSaveThread = async () => {
if (isIos()) {
await this.tapSheetRowIos(this.saveThreadOption);
return;
}
await this.toBeVisible();
await waitFor(this.saveThreadOption).toExist().withTimeout(timeouts.FIVE_SEC);
await this.saveThreadOption.tap();
await waitFor(this.threadOptionsScreen).not.toExist().withTimeout(timeouts.TEN_SEC);
};
tapUnsaveThread = async () => {
if (isIos()) {
await this.tapSheetRowIos(this.unsaveThreadOption);
return;
}
await this.toBeVisible();
await waitFor(this.unsaveThreadOption).toExist().withTimeout(timeouts.FIVE_SEC);
await this.unsaveThreadOption.tap();
await waitFor(this.threadOptionsScreen).not.toExist().withTimeout(timeouts.TEN_SEC);
};
}
const threadOptionsScreen = new ThreadOptionsScreen();
export default threadOptionsScreen;