mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-09 20:50:12 +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.
100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {generateTotp} from '@support/utils/totp';
|
|
|
|
import client from './client';
|
|
import {getResponseFromError} from './common';
|
|
import User from './user';
|
|
|
|
// ****************************************************************
|
|
// MFA
|
|
// See https://api.mattermost.com/#tag/users
|
|
//
|
|
// Exported API function should have the following:
|
|
// - documented using JSDoc
|
|
// - meaningful description
|
|
// - match the referenced API endpoints
|
|
// - parameter/s defined by `@param`
|
|
// - return value defined by `@return`
|
|
// ****************************************************************
|
|
|
|
/**
|
|
* Generate an MFA secret for the current user.
|
|
* See https://api.mattermost.com/#operation/GenerateMfaSecret
|
|
* @param {string} baseUrl - the base server URL
|
|
* @return {Object} returns {secret, qrCode} on success or {error, status} on error
|
|
*/
|
|
export const apiGenerateMfaSecret = async (baseUrl: string): Promise<any> => {
|
|
try {
|
|
const response = await client.post(`${baseUrl}/api/v4/users/me/mfa/generate`);
|
|
|
|
return {secret: response.data.secret, qrCode: response.data.qr_code};
|
|
} catch (err) {
|
|
return getResponseFromError(err);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Activate MFA for the current user using a TOTP code.
|
|
* See https://api.mattermost.com/#operation/UpdateUserMfa
|
|
* @param {string} baseUrl - the base server URL
|
|
* @param {string} code - the TOTP code generated from the secret
|
|
* @return {Object} returns {status} on success or {error, status} on error
|
|
*/
|
|
export const apiActivateMfa = async (baseUrl: string, code: string): Promise<any> => {
|
|
try {
|
|
const response = await client.put(
|
|
`${baseUrl}/api/v4/users/me/mfa`,
|
|
{activate: true, code},
|
|
);
|
|
|
|
return {status: response.status};
|
|
} catch (err) {
|
|
return getResponseFromError(err);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Enable MFA for a user and return the base32 secret so the caller can
|
|
* compute TOTP codes for the in-app login flow.
|
|
*
|
|
* MFA secret generation/activation are current-user endpoints, so this logs in
|
|
* as the target user (switching the shared client session), enables MFA, and
|
|
* leaves the session as the target user. Callers that still need the admin
|
|
* session must re-login afterwards.
|
|
*
|
|
* @param {string} baseUrl - the base server URL
|
|
* @param {Object} user - user object with `username` and `password`
|
|
* @return {Object} returns {secret} on success or {error, status} on error
|
|
*/
|
|
export const apiEnableMfaForUser = async (baseUrl: string, user: {username: string; password: string}): Promise<any> => {
|
|
const loginResult = await User.apiLogin(baseUrl, user);
|
|
if (loginResult.error) {
|
|
return loginResult;
|
|
}
|
|
|
|
const generateResult = await apiGenerateMfaSecret(baseUrl);
|
|
if (generateResult.error) {
|
|
return generateResult;
|
|
}
|
|
|
|
const {secret} = generateResult;
|
|
const code = generateTotp(secret);
|
|
|
|
const activateResult = await apiActivateMfa(baseUrl, code);
|
|
if (activateResult.error) {
|
|
return activateResult;
|
|
}
|
|
|
|
return {secret};
|
|
};
|
|
|
|
export const Mfa = {
|
|
apiActivateMfa,
|
|
apiEnableMfaForUser,
|
|
apiGenerateMfaSecret,
|
|
};
|
|
|
|
export default Mfa;
|