mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-06 10:50:13 +00:00
Closes manual release-test gaps for mobile (Detox + Maestro), automating new test cases and hardening flaky/CI-only failures uncovered along the way. ### New coverage automated - **MM-T416** offline/online post-retry (Android via airplane mode; iOS ultimately marked unsupported — see below) - **MM-T1194 / MM-T1193** custom ToS accept/decline survives relaunch - **MM-T3251** status change via account UI (cross-user visibility) - **MM-T510** channel-wide/keyword mention delivery — later expanded to a full 5-channel mention matrix with exact server-side counts - **MM-T3261 (_1/_2)** Report a Problem external hand-off (browser link mode, email mode) - **MM-T2940** iOS managed-config (MDM) pre-fill/autoconnect - **MM-T3271/3272** opening the app from a push notification (synthetic notification payloads, background/closed states) - Un-skipped several tests that turned out to already pass (stale skips) ### Root-caused failing tests - **Android SQLite corruption**: traced to native Kotlin DB connections not opening in WAL mode while JSI did (post #10109) — journal-mode mismatch corrupted the store on push-triggered writes. Fixed properly with a **WAL patch** (#10117) rather than the original destructive "wipe app database" workaround, which was reverted. - **Fabric crash** (`addViewAt`/reparent) on `SlideUpPanelItem` taps — root-caused to view flattening racing sheet mounts; fixed with `collapsable={false}` (went through a revert/re-revert once CI reproduced the crash without it). - **Search race**: stale in-flight search results could overwrite a cleared search box — fixed with request-id tagging. - **iOS offline detection**: multiple iterations (AAAA sampling → Cloudflare-prefix check → final decision) landed on refusing iOS offline simulation outright, since all E2E servers are Cloudflare-fronted/anycast and the address space can't be enumerated/blocked. - **MM-T3261 config patch** intermittently silently ignored by the server — root-caused to `ReportAProblemType` being environment-managed on PR Spinwicks (config PATCH returns 200 but is silently dropped); now detected and reported as a skip with proven cause, not a failure. - **MM-T416 failed-post timing**: multiple rounds of tightening the wait bound to match the client's actual retry/backoff/timeout ceiling (ended at ~150s, derived from OkHttp/retry-interceptor math, not guessed). - **MM-T5603 upload wait**: raised to match the client's real 180s upload timeout. - **Classification banner suite**: fixed to verify config state directly instead of trusting a PATCH reply that the cloud sometimes drops. ### CI/reporting infra fixes - Dedicated Maestro steps (MM-T3261, MM-T67856_4) weren't feeding into the merged JUnit report and couldn't gate pass/fail — fixed report merging/naming so they now count. - Android Maestro logcat capture added (previously iOS-only diagnostics). - Jest retry logic no longer discards passing results from a shard killed by timeout. - Sharding raised 24→28. - Removed dead Detox Pilot/Playbooks scaffolding (unused, uncovered by CI). ### Cleanup - Comment/docs pass stripping narrative session-archaeology from long explanatory comments, keeping only load-bearing facts and CI references.
221 lines
8.1 KiB
TypeScript
221 lines
8.1 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
import React, {useCallback, useEffect, useState} from 'react';
|
|
import {useIntl} from 'react-intl';
|
|
import {View, Text, ScrollView, Platform} from 'react-native';
|
|
import {SafeAreaView} from 'react-native-safe-area-context';
|
|
|
|
import {savePreference} from '@actions/remote/preference';
|
|
import Button from '@components/button';
|
|
import MenuDivider from '@components/menu_divider';
|
|
import SettingOption from '@components/settings/option';
|
|
import {Preferences, Screens} from '@constants';
|
|
import {DEFAULT_REPORT_A_PROBLEM_EMAIL} from '@constants/report_a_problem';
|
|
import {useServerUrl} from '@context/server';
|
|
import {useTheme} from '@context/theme';
|
|
import useAndroidHardwareBackHandler from '@hooks/android_back_handler';
|
|
import {navigateBack} from '@screens/navigation';
|
|
import {logDebug} from '@utils/log';
|
|
import {emailLogs, getDefaultReportAProblemLink, shareLogs} from '@utils/share_logs';
|
|
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
|
import {typography} from '@utils/typography';
|
|
import {tryOpenURL} from '@utils/url';
|
|
|
|
import AppLogs from './app_logs';
|
|
import CopyMetadata from './copy_metadata';
|
|
import {getCommonStyleSheet} from './styles';
|
|
|
|
import type {ReportAProblemMetadata} from '@typings/screens/report_a_problem';
|
|
|
|
type Props = {
|
|
reportAProblemMail?: string;
|
|
reportAProblemLink?: string;
|
|
siteName?: string;
|
|
allowDownloadLogs: boolean;
|
|
attachLogsEnabled: boolean;
|
|
currentUserId: string;
|
|
reportAProblemType?: string;
|
|
isFreeEdition: boolean;
|
|
metadata: ReportAProblemMetadata;
|
|
}
|
|
|
|
const getStyleSheet = makeStyleSheetFromTheme((theme) => ({
|
|
...getCommonStyleSheet(theme),
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: theme.centerChannelBg,
|
|
paddingVertical: Platform.select({ios: 20, default: 0}),
|
|
gap: 20,
|
|
},
|
|
body: {
|
|
flex: 1,
|
|
},
|
|
content: {
|
|
gap: 16,
|
|
paddingHorizontal: 20,
|
|
},
|
|
detailsTitle: {
|
|
...typography('Heading', 200, 'SemiBold'),
|
|
color: theme.centerChannelColor,
|
|
},
|
|
detailsSection: {
|
|
gap: 8,
|
|
},
|
|
buttonContainer: {
|
|
paddingHorizontal: 20,
|
|
marginTop: 'auto',
|
|
borderTopWidth: 1,
|
|
borderColor: changeOpacity(theme.centerChannelColor, 0.08),
|
|
width: '100%',
|
|
paddingTop: 20,
|
|
marginBottom: Platform.select({ios: 20, default: 0}),
|
|
},
|
|
}));
|
|
|
|
const ReportProblem = ({
|
|
reportAProblemMail,
|
|
reportAProblemLink,
|
|
siteName,
|
|
allowDownloadLogs,
|
|
attachLogsEnabled,
|
|
currentUserId,
|
|
reportAProblemType,
|
|
isFreeEdition,
|
|
metadata,
|
|
}: Props) => {
|
|
const theme = useTheme();
|
|
const styles = getStyleSheet(theme);
|
|
const intl = useIntl();
|
|
const serverUrl = useServerUrl();
|
|
const [attachLogsToggle, setAttachLogsToggle] = useState(attachLogsEnabled);
|
|
|
|
useEffect(() => {
|
|
setAttachLogsToggle(attachLogsEnabled);
|
|
}, [attachLogsEnabled]);
|
|
|
|
const handleToggleAttachLogs = useCallback(async (value: boolean) => {
|
|
setAttachLogsToggle(value);
|
|
const {error} = await savePreference(serverUrl, [{
|
|
user_id: currentUserId,
|
|
category: Preferences.CATEGORIES.ADVANCED_SETTINGS,
|
|
name: Preferences.ATTACH_APP_LOGS,
|
|
value: String(value),
|
|
}]);
|
|
if (error) {
|
|
setAttachLogsToggle(!value);
|
|
}
|
|
}, [serverUrl, currentUserId]);
|
|
|
|
const handleReport = useCallback(async () => {
|
|
switch (reportAProblemType) {
|
|
case 'email':
|
|
await emailLogs(metadata, siteName, reportAProblemMail, !allowDownloadLogs);
|
|
return;
|
|
case 'link': {
|
|
let linkToUse = reportAProblemLink;
|
|
if (!linkToUse) {
|
|
logDebug('Report a problem link is not set');
|
|
linkToUse = getDefaultReportAProblemLink(!isFreeEdition);
|
|
}
|
|
tryOpenURL(linkToUse);
|
|
return;
|
|
}
|
|
case 'default': {
|
|
if (isFreeEdition) {
|
|
tryOpenURL(getDefaultReportAProblemLink(false));
|
|
} else {
|
|
await emailLogs(metadata, siteName, DEFAULT_REPORT_A_PROBLEM_EMAIL, !allowDownloadLogs);
|
|
}
|
|
return;
|
|
}
|
|
case 'hidden':
|
|
return;
|
|
}
|
|
|
|
// Old servers where reportAProblemType is not defined
|
|
if (!reportAProblemLink) {
|
|
await shareLogs(metadata, siteName, undefined, false);
|
|
return;
|
|
}
|
|
|
|
tryOpenURL(reportAProblemLink);
|
|
}, [reportAProblemType, reportAProblemLink, reportAProblemMail, metadata, siteName, allowDownloadLogs, isFreeEdition]);
|
|
|
|
useAndroidHardwareBackHandler(Screens.REPORT_PROBLEM, navigateBack);
|
|
|
|
const descriptionText = allowDownloadLogs ? intl.formatMessage({
|
|
id: 'screen.report_problem.details.description',
|
|
defaultMessage: 'When reporting a problem, share the metadata and app logs given below to help troubleshoot your problem faster',
|
|
}) : intl.formatMessage({
|
|
id: 'screen.report_problem.details.description_without_logs',
|
|
defaultMessage: 'When reporting a problem, share the metadata given below to help troubleshoot your problem faster',
|
|
});
|
|
|
|
return (
|
|
<SafeAreaView
|
|
style={styles.container}
|
|
testID='report_problem.screen'
|
|
>
|
|
<View style={styles.body}>
|
|
<ScrollView contentContainerStyle={styles.content}>
|
|
<View style={styles.detailsSection}>
|
|
<Text style={styles.detailsTitle}>
|
|
{intl.formatMessage({
|
|
id: 'screen.report_problem.details.title',
|
|
defaultMessage: 'Troubleshooting details',
|
|
})}
|
|
</Text>
|
|
<Text style={styles.bodyText}>
|
|
{descriptionText}
|
|
</Text>
|
|
</View>
|
|
<MenuDivider/>
|
|
<CopyMetadata
|
|
metadata={metadata}
|
|
componentId={Screens.REPORT_PROBLEM}
|
|
/>
|
|
{allowDownloadLogs && (
|
|
<>
|
|
<MenuDivider/>
|
|
<SettingOption
|
|
label={intl.formatMessage({
|
|
id: 'screen.report_problem.enable_log_attachments',
|
|
defaultMessage: 'Enable app log attachments',
|
|
})}
|
|
description={intl.formatMessage({
|
|
id: 'screen.report_problem.enable_log_attachments.description',
|
|
defaultMessage: 'Show an option to attach app logs in the file attachment menu',
|
|
})}
|
|
selected={attachLogsToggle}
|
|
type='toggle'
|
|
action={handleToggleAttachLogs}
|
|
testID='report_problem.enable_log_attachments'
|
|
/>
|
|
<MenuDivider/>
|
|
<AppLogs/>
|
|
</>
|
|
)}
|
|
</ScrollView>
|
|
</View>
|
|
{reportAProblemType !== 'hidden' && (
|
|
<View style={styles.buttonContainer}>
|
|
<Button
|
|
theme={theme}
|
|
text={intl.formatMessage({
|
|
id: 'screen.report_problem.button',
|
|
defaultMessage: 'Report a problem',
|
|
})}
|
|
onPress={handleReport}
|
|
iconName='open-in-new'
|
|
size='lg'
|
|
isIconOnTheRight={true}
|
|
testID='report_problem.report.button'
|
|
/>
|
|
</View>
|
|
)}
|
|
</SafeAreaView>
|
|
);
|
|
};
|
|
|
|
export default ReportProblem;
|