mirror of
https://github.com/mattermost/mattermost-mobile.git
synced 2026-11-05 18:38:54 +00:00
* Expo-Router partial migration * Full migration to ExpoRouter * i18n * add tests * Merge branch 'main' into expo-router * align package-lock.json * i18n * feedback review * feedback review * android 16kb patch * Refactored emoji picker keyboard using a state machine * set android launch_screen in drawables instead of layout * no conditional hooks * remove duplicate call in test * address feedback * fix tests and address more feedback * small corrections * small corrections * small corrections * fix draft test * fix draft test 2 * remove navigateToScreen in draff test * fix nit in draft test * remove doc file * Feedback review * fix snapshots * improve test coverage * fix i18n * Upgrade t o RN 0.83, other dependencies and new architecture (#9737) * library hardware-keyboard android new arch support fix * library rnshare android new arch support fix * library rnutils android new arch support fix * library secure-pdf-viewer add new arch support * Enable new arch on iOS and Android * update emm and network client with new-arch * update intune to use new-arch * performance improvements * fix tests * fixes post input on new-arch * fix android emoji picker * remove doc * run tests in parallel * upgrade to React Native 0.83.9, Expo 55, React 19, and New Architecture Bumps core dependencies to their latest versions: - React Native 0.77.3 → 0.83.9 (New Architecture enabled) - React 18.3.1 → 19.2.5 - Expo SDK 52 → 55 - TypeScript 5.8.3 → 6.0.3 - Node.js 22.16.0 → 24.15.0 - react-native-reanimated 3.17.3 → 4.3.0 - Various other dependency upgrades Migrates iOS AppDelegate from Objective-C to Swift, updates Android build tooling and Gradle wrapper, refreshes all patches and font assets, and adapts app code, tests, and CI workflows throughout for API changes introduced by these upgrades. * remove unecessary dismissKeyboard in notifications * feedback review * update intune to use new-arch * update @mattermost deps * missing i18n string * additional fixes * update fastlane
150 lines
5.7 KiB
TypeScript
150 lines
5.7 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import DatabaseManager from '@database/manager';
|
|
import {renderWithEverything, waitFor} from '@test/intl-test-helper';
|
|
import TestHelper from '@test/test_helper';
|
|
|
|
import WatermarkScreenExport from './index';
|
|
|
|
import type {Database} from '@nozbe/watermelondb';
|
|
|
|
// Portal renders children into a named host declared in _layout.tsx.
|
|
// In unit tests there is no host, so we render children directly.
|
|
jest.mock('@gorhom/portal', () => {
|
|
const React = require('react');
|
|
const {View} = require('react-native');
|
|
return {
|
|
Portal: ({children}: {children: React.ReactNode}) => React.createElement(View, {testID: 'portal'}, children),
|
|
};
|
|
});
|
|
|
|
// FullWindowOverlay is a native component — render children passthrough in tests.
|
|
jest.mock('react-native-screens', () => {
|
|
const React = require('react');
|
|
return {
|
|
FullWindowOverlay: ({children}: {children: React.ReactNode}) => React.createElement(React.Fragment, null, children),
|
|
};
|
|
});
|
|
|
|
describe('WatermarkScreen', () => {
|
|
let database: Database;
|
|
const serverUrl = 'http://www.someserver.com';
|
|
|
|
beforeAll(async () => {
|
|
const server = await TestHelper.setupServerDatabase(serverUrl);
|
|
await DatabaseManager.updateServerIdentifier(serverUrl, 'someidentifier');
|
|
await DatabaseManager.setActiveServerDatabase(serverUrl);
|
|
database = server.database;
|
|
});
|
|
|
|
afterAll(() => {
|
|
DatabaseManager.destroyServerDatabase(serverUrl);
|
|
});
|
|
|
|
it('should render nothing when watermark is disabled', async () => {
|
|
const {queryByTestId} = renderWithEverything(
|
|
<WatermarkScreenExport/>,
|
|
{database, serverUrl},
|
|
);
|
|
|
|
// Give observables time to resolve — the portal should never appear.
|
|
await waitFor(() => {
|
|
expect(queryByTestId('portal')).toBeNull();
|
|
});
|
|
});
|
|
|
|
it('should render watermark text containing username and server URL when enabled', async () => {
|
|
// Enable the watermark via the system config in the DB.
|
|
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
|
await operator.handleConfigs({
|
|
configs: [{id: 'ExperimentalEnableWatermark', value: 'true'}],
|
|
configsToDelete: [],
|
|
prepareRecordsOnly: false,
|
|
});
|
|
|
|
const {getAllByText, unmount} = renderWithEverything(
|
|
<WatermarkScreenExport/>,
|
|
{database, serverUrl},
|
|
);
|
|
|
|
// The watermark container is hidden from accessibility intentionally;
|
|
// use includeHiddenElements to still find the text in tests.
|
|
await waitFor(() => {
|
|
expect(getAllByText(/www\.someserver\.com/, {includeHiddenElements: true}).length).toBeGreaterThan(0);
|
|
});
|
|
|
|
// Unmount before resetting config to avoid act() warnings from post-unmount observable updates.
|
|
unmount();
|
|
await operator.handleConfigs({
|
|
configs: [{id: 'ExperimentalEnableWatermark', value: 'false'}],
|
|
configsToDelete: [],
|
|
prepareRecordsOnly: false,
|
|
});
|
|
});
|
|
|
|
it('should render multiple copies of the watermark text for the tiled grid pattern when enabled', async () => {
|
|
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
|
await operator.handleConfigs({
|
|
configs: [{id: 'ExperimentalEnableWatermark', value: 'true'}],
|
|
configsToDelete: [],
|
|
prepareRecordsOnly: false,
|
|
});
|
|
|
|
const {getAllByText, unmount} = renderWithEverything(
|
|
<WatermarkScreenExport/>,
|
|
{database, serverUrl},
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(getAllByText(/www\.someserver\.com/, {includeHiddenElements: true}).length).toBeGreaterThan(1);
|
|
});
|
|
|
|
unmount();
|
|
await operator.handleConfigs({
|
|
configs: [{id: 'ExperimentalEnableWatermark', value: 'false'}],
|
|
configsToDelete: [],
|
|
prepareRecordsOnly: false,
|
|
});
|
|
});
|
|
|
|
it('should render watermark text with username, server URL, date and time when enabled', async () => {
|
|
const {operator} = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
|
|
await operator.handleConfigs({
|
|
configs: [{id: 'ExperimentalEnableWatermark', value: 'true'}],
|
|
configsToDelete: [],
|
|
prepareRecordsOnly: false,
|
|
});
|
|
|
|
// Freeze Date so the watermark's "now" is deterministic.
|
|
// Only fake Date — keep all timer APIs real so WatermelonDB observables resolve normally.
|
|
jest.useFakeTimers({
|
|
now: new Date('2026-04-14T10:30:00'),
|
|
doNotFake: ['nextTick', 'setImmediate', 'clearImmediate', 'setInterval', 'clearInterval', 'setTimeout', 'clearTimeout', 'queueMicrotask'],
|
|
});
|
|
|
|
let unmount: () => void;
|
|
try {
|
|
const username = TestHelper.basicUser!.username;
|
|
const rendered = renderWithEverything(
|
|
<WatermarkScreenExport/>,
|
|
{database, serverUrl},
|
|
);
|
|
unmount = rendered.unmount;
|
|
|
|
const expectedText = `${username} ${serverUrl} 4/14/2026 10:30 AM`;
|
|
await waitFor(() => {
|
|
expect(rendered.getAllByText(expectedText, {includeHiddenElements: true}).length).toBeGreaterThan(0);
|
|
});
|
|
} finally {
|
|
unmount!();
|
|
jest.useRealTimers();
|
|
await operator.handleConfigs({
|
|
configs: [{id: 'ExperimentalEnableWatermark', value: 'false'}],
|
|
configsToDelete: [],
|
|
prepareRecordsOnly: false,
|
|
});
|
|
}
|
|
});
|
|
});
|