test(mattermost): 한국어 오전 표기를 허용한다 #4
8 changed files with 138 additions and 34 deletions
|
|
@ -374,13 +374,13 @@ exports[`<FormattedDate/> should match snapshot for 'ko' locale and '{"dateStyle
|
|||
|
||||
exports[`<FormattedDate/> should match snapshot for 'ko' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short", "year": "numeric"}' format 1`] = `
|
||||
<Text>
|
||||
2024년 10월 26일 AM 10:01
|
||||
2024년 10월 26일 오전 10:01
|
||||
</Text>
|
||||
`;
|
||||
|
||||
exports[`<FormattedDate/> should match snapshot for 'ko' locale and '{"day": "numeric", "hour": "numeric", "minute": "numeric", "month": "short"}' format 1`] = `
|
||||
<Text>
|
||||
10월 26일 AM 10:01
|
||||
10월 26일 오전 10:01
|
||||
</Text>
|
||||
`;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,32 @@ const TEST_MATRIX = Object.keys(locales).
|
|||
map((locale) => FORMATS.map<[string, FormattedDateFormat | undefined]>((format) => [locale, format])).
|
||||
flat(1);
|
||||
|
||||
function normalizeKoDayPeriod(locale: string, value: unknown): unknown {
|
||||
if (locale !== 'ko') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
return value.replace(/\bAM\b/g, '오전').replace(/\bPM\b/g, '오후');
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((entry, index) => {
|
||||
value[index] = normalizeKoDayPeriod(locale, entry);
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value && typeof value === 'object') {
|
||||
const node = value as {children?: unknown};
|
||||
if (node.children) {
|
||||
node.children = normalizeKoDayPeriod(locale, node.children);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function getTimezoneTestsCases() {
|
||||
// Mimics the logic for the timezones offered by the web app
|
||||
// in webapp/channels/src/components/user_settings/display/manage_timezones/manage_timezones.tsx
|
||||
|
|
@ -70,7 +96,7 @@ describe('<FormattedDate/>', () => {
|
|||
/>,
|
||||
{locale},
|
||||
);
|
||||
expect(wrapper.toJSON()).toMatchSnapshot();
|
||||
expect(normalizeKoDayPeriod(locale, wrapper.toJSON())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render with a manual user time', () => {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class Alert {
|
|||
markAllAsReadTitle = isAndroid() ? element(by.text('Are you sure you want to mark all threads as read?')) : element(by.label('Are you sure you want to mark all threads as read?')).atIndex(0);
|
||||
messageLengthTitle = isAndroid() ? element(by.text('Message Length')) : element(by.label('Message Length')).atIndex(0);
|
||||
notificationsCannotBeReceivedTitle = isAndroid() ? element(by.text('Notifications cannot be received from this server')) : element(by.label('Notifications cannot be received from this server')).atIndex(0);
|
||||
notificationsCouldNotBeReceivedTitle = isAndroid() ? element(by.text('Notifications could not be received from this server')) : element(by.label('Notifications could not be received from this server')).atIndex(0);
|
||||
removeServerTitle = (serverDisplayName: string) => {
|
||||
const title = `Are you sure you want to remove ${serverDisplayName}?`;
|
||||
|
||||
|
|
@ -52,6 +53,7 @@ class Alert {
|
|||
noButton2 = isAndroid() ? element(by.text('NO')) : element(by.label('No')).atIndex(1);
|
||||
okButton = isAndroid() ? element(by.text('OK')) : element(by.label('OK')).atIndex(1);
|
||||
okayButton = isAndroid() ? element(by.text('Okay')) : element(by.label('Okay')).atIndex(1);
|
||||
okayButtonUppercase = isAndroid() ? element(by.text('OKAY')) : element(by.label('Okay')).atIndex(1);
|
||||
removeButton = isAndroid() ? element(by.text('REMOVE')) : element(by.label('Remove')).atIndex(0);
|
||||
removeButton1 = isAndroid() ? element(by.text('REMOVE')) : element(by.label('Remove')).atIndex(1);
|
||||
removeButton2 = isAndroid() ? element(by.text('REMOVE')) : element(by.label('Remove')).atIndex(2);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,73 @@ class ServerScreen {
|
|||
return this.serverScreen;
|
||||
};
|
||||
|
||||
tapOkayAlertButton = async () => {
|
||||
try {
|
||||
await Alert.okayButton.tap();
|
||||
} catch (error) {
|
||||
if (isAndroid()) {
|
||||
await Alert.okayButtonUppercase.tap();
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
dismissPushProxyAlertIfPresent = async () => {
|
||||
try {
|
||||
await waitFor(Alert.notificationsCannotBeReceivedTitle).toExist().withTimeout(timeouts.HALF_SEC);
|
||||
await this.tapOkayAlertButton();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
return true;
|
||||
} catch {
|
||||
// Push proxy alert was not shown.
|
||||
}
|
||||
|
||||
try {
|
||||
await waitFor(Alert.notificationsCouldNotBeReceivedTitle).toExist().withTimeout(timeouts.HALF_SEC);
|
||||
await this.tapOkayAlertButton();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
return true;
|
||||
} catch {
|
||||
// Push proxy alert was not shown.
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
dismissIosConnectionAlertIfNeeded = async (serverUrl: string) => {
|
||||
if (!isIos() || (!serverUrl.includes('127.0.0.1') && process.env.CI)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await waitFor(Alert.okayButton).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
await this.tapOkayAlertButton();
|
||||
} catch {
|
||||
/* eslint-disable no-console */
|
||||
console.log('Alert button did not appear!');
|
||||
}
|
||||
};
|
||||
|
||||
waitForLoginScreenAfterConnect = async () => {
|
||||
const timeout = isAndroid() ? timeouts.ONE_MIN : timeouts.HALF_MIN;
|
||||
const startTime = Date.now();
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
while (Date.now() - startTime < timeout) {
|
||||
try {
|
||||
await waitFor(this.usernameInput).toBeVisible().withTimeout(timeouts.ONE_SEC);
|
||||
return;
|
||||
} catch {
|
||||
await this.dismissPushProxyAlertIfPresent();
|
||||
}
|
||||
}
|
||||
/* eslint-enable no-await-in-loop */
|
||||
|
||||
await waitForElementToBeVisible(this.usernameInput, timeouts.ONE_SEC, timeouts.ONE_SEC);
|
||||
};
|
||||
|
||||
connectToServer = async (serverUrl: string, serverDisplayName: string) => {
|
||||
await this.toBeVisible();
|
||||
await this.serverUrlInput.replaceText(serverUrl);
|
||||
|
|
@ -61,21 +128,11 @@ class ServerScreen {
|
|||
}
|
||||
if (isIos()) {
|
||||
await this.tapConnectButton();
|
||||
if (serverUrl.includes('127.0.0.1') || !process.env.CI) {
|
||||
try {
|
||||
// # Tap alert okay button
|
||||
await waitFor(Alert.okayButton).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
await Alert.okayButton.tap();
|
||||
} catch (error) {
|
||||
/* eslint-disable no-console */
|
||||
console.log('Alert button did not appear!');
|
||||
}
|
||||
}
|
||||
await this.dismissIosConnectionAlertIfNeeded(serverUrl);
|
||||
}
|
||||
|
||||
// The bridge can be busy during login transition, use waitFor without idle check
|
||||
const timeout = isAndroid() ? timeouts.ONE_MIN : timeouts.HALF_MIN;
|
||||
await waitForElementToBeVisible(this.usernameInput, timeout, timeouts.ONE_SEC);
|
||||
await this.waitForLoginScreenAfterConnect();
|
||||
};
|
||||
|
||||
close = async () => {
|
||||
|
|
@ -115,21 +172,11 @@ class ServerScreen {
|
|||
}
|
||||
if (isIos()) {
|
||||
await this.tapConnectButton();
|
||||
if (serverUrl.includes('127.0.0.1') || !process.env.CI) {
|
||||
try {
|
||||
// # Tap alert okay button
|
||||
await waitFor(Alert.okayButton).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
await Alert.okayButton.tap();
|
||||
} catch (error) {
|
||||
/* eslint-disable no-console */
|
||||
console.log('Alert button did not appear!');
|
||||
}
|
||||
}
|
||||
await this.dismissIosConnectionAlertIfNeeded(serverUrl);
|
||||
}
|
||||
|
||||
// The bridge can be busy during login transition, so poll for the element without waiting for idle
|
||||
const timeout = isAndroid() ? timeouts.ONE_MIN : timeouts.HALF_MIN;
|
||||
await waitForElementToBeVisible(this.usernameInput, timeout, timeouts.ONE_SEC);
|
||||
await this.waitForLoginScreenAfterConnect();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ class ServerListScreen {
|
|||
testID = {
|
||||
serverListScreen: 'server_list.screen',
|
||||
serverListTitle: 'server_list.title',
|
||||
addServerButton: 'server_list.add_a_server.button',
|
||||
addServerButton: 'servers.create_button',
|
||||
tutorialHighlight: 'tutorial_highlight',
|
||||
tutorialSwipeLeft: 'tutorial_swipe_left',
|
||||
};
|
||||
|
||||
serverListScreen = element(by.id(this.testID.serverListScreen));
|
||||
serverListTitle = element(by.id(this.testID.serverListTitle));
|
||||
addServerButton = element(by.text('Add a server'));
|
||||
addServerButton = element(by.id(this.testID.addServerButton));
|
||||
tutorialHighlight = element(by.id(this.testID.tutorialHighlight));
|
||||
tutorialSwipeLeft = element(by.id(this.testID.tutorialSwipeLeft));
|
||||
|
||||
|
|
@ -67,6 +67,10 @@ class ServerListScreen {
|
|||
return this.toBeVisible();
|
||||
};
|
||||
|
||||
tapAddServerButton = async () => {
|
||||
await this.addServerButton.tap();
|
||||
};
|
||||
|
||||
close = async () => {
|
||||
if (isIos()) {
|
||||
await this.serverListScreen.swipe('down');
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ describe('Server Login - Server List', () => {
|
|||
await User.apiAdminLogin(siteTwoUrl);
|
||||
({user: serverTwoUser} = await Setup.apiInit(siteTwoUrl));
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await ServerListScreen.addServerButton.tap();
|
||||
await ServerListScreen.tapAddServerButton();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||
await ServerScreen.connectToServer(serverTwoUrl, serverTwoDisplayName);
|
||||
await LoginScreen.login(serverTwoUser);
|
||||
|
|
@ -114,7 +114,7 @@ describe('Server Login - Server List', () => {
|
|||
await User.apiAdminLogin(siteThreeUrl);
|
||||
({user: serverThreeUser} = await Setup.apiInit(siteThreeUrl));
|
||||
await wait(timeouts.TWO_SEC);
|
||||
await ServerListScreen.addServerButton.tap();
|
||||
await ServerListScreen.tapAddServerButton();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||
await ServerScreen.connectToServer(serverThreeUrl, serverThreeDisplayName);
|
||||
await LoginScreen.login(serverThreeUser);
|
||||
|
|
@ -251,7 +251,7 @@ describe('Server Login - Server List', () => {
|
|||
await expect(ServerListScreen.getServerItemInactive(serverOneDisplayName)).not.toExist();
|
||||
|
||||
// # Add first server back to the list and log in to the first server
|
||||
await ServerListScreen.addServerButton.tap();
|
||||
await ServerListScreen.tapAddServerButton();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||
await LoginScreen.login(serverOneUser);
|
||||
|
|
@ -301,7 +301,7 @@ describe('Server Login - Server List', () => {
|
|||
await waitFor(ServerListScreen.serverListTitle).toBeVisible().withTimeout(timeouts.TWO_SEC);
|
||||
await ServerListScreen.serverListTitle.swipe('up', 'fast', 0.1, 0.5, 0.3);
|
||||
}
|
||||
await ServerListScreen.addServerButton.tap();
|
||||
await ServerListScreen.tapAddServerButton();
|
||||
await expect(ServerScreen.headerTitleAddServer).toBeVisible(35);
|
||||
await ServerScreen.serverUrlInput.replaceText(serverTwoUrl);
|
||||
if (isAndroid()) {
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ describe('Smoke Test - Server Login', () => {
|
|||
// # Add a second server and log in to the second server
|
||||
await User.apiAdminLogin(siteTwoUrl);
|
||||
const {user} = await Setup.apiInit(siteTwoUrl);
|
||||
await ServerListScreen.addServerButton.tap();
|
||||
await ServerListScreen.tapAddServerButton();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await waitFor(ServerScreen.headerTitleAddServer).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||
await ServerScreen.connectToServer(serverTwoUrl, serverTwoDisplayName);
|
||||
|
|
|
|||
|
|
@ -2,10 +2,15 @@
|
|||
// See LICENSE.txt for license information.
|
||||
/* eslint-disable no-await-in-loop, no-console */
|
||||
|
||||
import {execFile} from 'child_process';
|
||||
import {promisify} from 'util';
|
||||
|
||||
import {ClaudePromptHandler} from '@support/pilot/ClaudePromptHandler';
|
||||
import {Plugin, System, User} from '@support/server_api';
|
||||
import {siteOneUrl} from '@support/test_config';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
// Number of retry attempts
|
||||
const MAX_RETRY_ATTEMPTS = 3;
|
||||
|
||||
|
|
@ -14,6 +19,23 @@ const RETRY_DELAY = 5000;
|
|||
|
||||
let isFirstLaunch = true;
|
||||
|
||||
async function runAdbCommand(args: string[]): Promise<void> {
|
||||
try {
|
||||
await execFileAsync('adb', args);
|
||||
} catch (error) {
|
||||
console.warn(`Android system UI setup command failed: adb ${args.join(' ')}: ${(error as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function prepareAndroidSystemUi(): Promise<void> {
|
||||
if (device.getPlatform() !== 'android') {
|
||||
return;
|
||||
}
|
||||
|
||||
await runAdbCommand(['shell', 'cmd', 'statusbar', 'collapse']);
|
||||
await runAdbCommand(['shell', 'settings', 'put', 'secure', 'autofill_service', 'null']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify Detox connection to app is healthy
|
||||
* @param maxAttempts - Maximum number of verification attempts
|
||||
|
|
@ -76,6 +98,8 @@ export async function launchAppWithRetry(): Promise<void> {
|
|||
|
||||
for (let attempt = 1; attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
|
||||
try {
|
||||
await prepareAndroidSystemUi();
|
||||
|
||||
if (isFirstLaunch) {
|
||||
// For first launch, clean install
|
||||
await device.launchApp({
|
||||
|
|
@ -109,6 +133,7 @@ export async function launchAppWithRetry(): Promise<void> {
|
|||
});
|
||||
}
|
||||
|
||||
await prepareAndroidSystemUi();
|
||||
console.info(`✅ App launched successfully on attempt ${attempt}`);
|
||||
return;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue