Skip to content

Commit

Permalink
Implement the capability to change the language between tests (#873)
Browse files Browse the repository at this point in the history
* pipe language through to xcrun launch

* Revert "pipe language through to xcrun launch"

This reverts commit 797a3dd.

* pipe the thing through for real this time

* pass the other arg

* remove console.logs

* pass language prop to android

* remove a newline

* fix unit tests

* add a unit test

* re-apply changes and fix unit tests

* add a locale option

* update unit test

* 100% test coverage

* temp rename unneeded test files

* temp use iphone 6

* install and link react-native-device-info

* add boilerplate for a language screen

* render current country and locale in test app

* add e2e test

* add language UI to language screen

* update e2e

* Revert "install and link react-native-device-info"

This reverts commit 9ee6c55.

* e2e test is passing

* clean up language screen

* temp switch emulator

* add language support to test app for android

* Revert "add language support to test app for android"

This reverts commit 6b1b446.

* add android support to language screen

* update android API

* Revert "temp switch emulator"

This reverts commit 971fb09.

* Revert "temp use iphone 6"

This reverts commit c11308d.

* Revert "temp rename unneeded test files"

This reverts commit 9de21e5.

* uncomment some tests

* comment out the not cross-plat test

* update docs
  • Loading branch information
luisnaranjo733 authored and LeoNatan committed Sep 29, 2018
1 parent ebec396 commit 50ee994
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 20 deletions.
2 changes: 1 addition & 1 deletion detox/src/devices/Device.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Device {
}
}

const processId = await this.deviceDriver.launchApp(this._deviceId, _bundleId, this._prepareLaunchArgs(baseLaunchArgs));
const processId = await this.deviceDriver.launchApp(this._deviceId, _bundleId, this._prepareLaunchArgs(baseLaunchArgs), params.languageAndLocale);
this._processes[_bundleId] = processId;

await this.deviceDriver.waitUntilReady();
Expand Down
33 changes: 24 additions & 9 deletions detox/src/devices/Device.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,22 @@ describe('Device', () => {

expect(device.deviceDriver.launchApp).toHaveBeenCalledWith(device._deviceId,
device._bundleId,
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test"});
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test"}, undefined);
});

it('launchApp({languageAndLocale}) should launch app with a specific language/locale', async () => {
device = validDevice();

const languageAndLocale = {
language: 'es-MX',
locale: 'es-MX'
};

await device.launchApp({languageAndLocale});

expect(device.deviceDriver.launchApp).toHaveBeenCalledWith(device._deviceId,
device._bundleId,
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test"}, languageAndLocale);
});

it(`relaunchApp()`, async () => {
Expand All @@ -107,7 +122,7 @@ describe('Device', () => {
expect(device.deviceDriver.terminate).toHaveBeenCalled();
expect(device.deviceDriver.launchApp).toHaveBeenCalledWith(device._deviceId,
device._bundleId,
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test"});
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test"}, undefined);
});

it(`relaunchApp({newInstance: false}) should not terminate the app before launch`, async () => {
Expand Down Expand Up @@ -144,7 +159,7 @@ describe('Device', () => {
expect(device.deviceDriver.installApp).toHaveBeenCalled();
expect(device.deviceDriver.launchApp).toHaveBeenCalledWith(device._deviceId,
device._bundleId,
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test"});
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test"}, undefined);
});

it(`relaunchApp() without delete when reuse is enabled should not uninstall and install`, async () => {
Expand All @@ -158,7 +173,7 @@ describe('Device', () => {
expect(device.deviceDriver.installApp).not.toHaveBeenCalled();
expect(device.deviceDriver.launchApp).toHaveBeenCalledWith(device._deviceId,
device._bundleId,
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test"});
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test"}, undefined);
});

it(`relaunchApp() with url should send the url as a param in launchParams`, async () => {
Expand All @@ -167,7 +182,7 @@ describe('Device', () => {

expect(device.deviceDriver.launchApp).toHaveBeenCalledWith(device._deviceId,
device._bundleId,
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test", "-detoxURLOverride": "scheme://some.url"});
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test", "-detoxURLOverride": "scheme://some.url"}, undefined);
});

it(`relaunchApp() with url should send the url as a param in launchParams`, async () => {
Expand All @@ -179,7 +194,7 @@ describe('Device', () => {
{
"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test", "-detoxURLOverride": "scheme://some.url", "-detoxSourceAppOverride":
"sourceAppBundleId"
});
}, undefined);
});

it(`launchApp() with disableTouchIndicators should send a boolean switch as a param in launchParams`, async () => {
Expand All @@ -188,7 +203,7 @@ describe('Device', () => {

expect(device.deviceDriver.launchApp).toHaveBeenCalledWith(device._deviceId,
device._bundleId,
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test", "-detoxDisableTouchIndicators": true});
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test", "-detoxDisableTouchIndicators": true}, undefined);
});

it(`relaunchApp() with userNofitication should send the userNotification as a param in launchParams`, async () => {
Expand All @@ -200,7 +215,7 @@ describe('Device', () => {

expect(device.deviceDriver.launchApp).toHaveBeenCalledWith(device._deviceId,
device._bundleId,
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test", "-detoxUserNotificationDataURL": "url"});
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test", "-detoxUserNotificationDataURL": "url"}, undefined);
});

it(`relaunchApp() with url and userNofitication should throw`, async () => {
Expand Down Expand Up @@ -228,7 +243,7 @@ describe('Device', () => {

expect(device.deviceDriver.launchApp).toHaveBeenCalledWith(device._deviceId,
device._bundleId,
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test", "-arg1": "1", "-arg2": 2});
{"-detoxServer": "ws://localhost:8099", "-detoxSessionId": "test", "-arg1": "1", "-arg2": 2}, undefined);
});

it(`sendToHome() should pass to device driver`, async () => {
Expand Down
2 changes: 1 addition & 1 deletion detox/src/devices/drivers/AndroidDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class AndroidDriver extends DeviceDriverBase {
}
}

async launchApp(deviceId, bundleId, launchArgs) {
async launchApp(deviceId, bundleId, launchArgs, languageAndLocale) {
await this.emitter.emit('beforeLaunchApp', { deviceId, bundleId, launchArgs });

if (!this.instrumentationProcess) {
Expand Down
4 changes: 2 additions & 2 deletions detox/src/devices/drivers/SimulatorDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ class SimulatorDriver extends IosDriver {
await this._applesimutils.uninstall(deviceId, bundleId);
}

async launchApp(deviceId, bundleId, launchArgs) {
async launchApp(deviceId, bundleId, launchArgs, languageAndLocale) {
await this.emitter.emit('beforeLaunchApp', {bundleId, deviceId, launchArgs});
const pid = await this._applesimutils.launch(deviceId, bundleId, launchArgs);
const pid = await this._applesimutils.launch(deviceId, bundleId, launchArgs, languageAndLocale);
await this.emitter.emit('launchApp', {bundleId, deviceId, launchArgs, pid});

return pid;
Expand Down
18 changes: 13 additions & 5 deletions detox/src/devices/ios/AppleSimUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,12 @@ class AppleSimUtils {
}
}

async launch(udid, bundleId, launchArgs) {
async launch(udid, bundleId, launchArgs, languageAndLocale) {
const frameworkPath = await environment.getFrameworkPath();
const logsInfo = new LogsInfo(udid);
const args = this._joinLaunchArgs(launchArgs);

const result = await this._launchMagically(frameworkPath, logsInfo, udid, bundleId, args);
const result = await this._launchMagically(frameworkPath, logsInfo, udid, bundleId, args, languageAndLocale);
return this._parseLaunchId(result);
}

Expand Down Expand Up @@ -250,17 +250,25 @@ class AppleSimUtils {
return _.map(launchArgs, (v, k) => `${k} ${v}`).join(' ').trim();
}

async _launchMagically(frameworkPath, logsInfo, udid, bundleId, args) {
async _launchMagically(frameworkPath, logsInfo, udid, bundleId, args, languageAndLocale) {
const statusLogs = {
trying: `Launching ${bundleId}...`,
successful: `${bundleId} launched. The stdout and stderr logs were recreated, you can watch them with:\n` +
` tail -F ${logsInfo.absJoined}`
};

const launchBin = `/bin/cat /dev/null >${logsInfo.absStdout} 2>${logsInfo.absStderr} && ` +
let launchBin = `/bin/cat /dev/null >${logsInfo.absStdout} 2>${logsInfo.absStderr} && ` +
`SIMCTL_CHILD_DYLD_INSERT_LIBRARIES="${frameworkPath}/Detox" ` +
`/usr/bin/xcrun simctl launch --stdout=${logsInfo.simStdout} --stderr=${logsInfo.simStderr} ` +
`${udid} ${bundleId} --args ${args}`;
`${udid} ${bundleId} --args ${args}`;;

if (!!languageAndLocale && !!languageAndLocale.language) {
launchBin += ` -AppleLanguages "(${languageAndLocale.language})"`;
}

if (!!languageAndLocale && !!languageAndLocale.locale) {
launchBin += ` -AppleLocale ${languageAndLocale.locale}`;
}

return await exec.execWithRetriesAndLogs(launchBin, undefined, statusLogs, 1);
}
Expand Down
10 changes: 10 additions & 0 deletions detox/src/devices/ios/AppleSimUtils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,15 @@ describe('AppleSimUtils', () => {
expect(exec.execWithRetriesAndLogs.mock.calls).toMatchSnapshot();
});

it('appends language and locale flags', async () => {
const languageAndLocale = {
language: "es-MS",
locale: "en-US"
};
await uut.launch('udid', 'theBundleId', undefined, languageAndLocale);
expect(exec.execWithRetriesAndLogs.mock.calls).toMatchSnapshot();
});

it('concats args', async () => {
await uut.launch('udid', 'theBundleId', { 'foo': 'bar', 'bob': 'yourUncle' });
expect(exec.execWithRetriesAndLogs.mock.calls).toMatchSnapshot();
Expand Down Expand Up @@ -344,6 +353,7 @@ describe('AppleSimUtils', () => {
const result = await uut.launch('udid', 'theBundleId');
expect(result).toEqual(12345);
});

});

describe('sendToHome', () => {
Expand Down
15 changes: 15 additions & 0 deletions detox/src/devices/ios/__snapshots__/AppleSimUtils.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,21 @@ Array [
]
`;

exports[`AppleSimUtils launch appends language and locale flags 1`] = `
Array [
Array [
"/bin/cat /dev/null >/Users/detox/Library/Developer/CoreSimulator/Devices/udid/data/tmp/detox.last_launch_app_log.out 2>/Users/detox/Library/Developer/CoreSimulator/Devices/udid/data/tmp/detox.last_launch_app_log.err && SIMCTL_CHILD_DYLD_INSERT_LIBRARIES=\\"undefined/Detox\\" /usr/bin/xcrun simctl launch --stdout=/tmp/detox.last_launch_app_log.out --stderr=/tmp/detox.last_launch_app_log.err udid theBundleId --args -AppleLanguages \\"(es-MS)\\" -AppleLocale en-US",
undefined,
Object {
"successful": "theBundleId launched. The stdout and stderr logs were recreated, you can watch them with:
tail -F /Users/detox/Library/Developer/CoreSimulator/Devices/udid/data/tmp/detox.last_launch_app_log.{out,err}",
"trying": "Launching theBundleId...",
},
1,
],
]
`;

exports[`AppleSimUtils launch asks environment for frameworkPath 1`] = `
Array [
Array [
Expand Down
23 changes: 23 additions & 0 deletions detox/test/e2e/06.device.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ describe('Device', () => {
await expect(element(by.text('Hello!!!'))).toBeVisible();
});

// // Passing on iOS, not implemented on Android
// it('launchApp in a different language', async () => {
// let languageAndLocale = {
// language: "es-MX",
// locale: "es-MX"
// };

// await device.launchApp({newInstance: true, languageAndLocale});
// await element(by.text('Language')).tap();
// await expect(element(by.text(`Current locale: ${languageAndLocale.locale}`))).toBeVisible();
// await expect(element(by.text(`Current language: ${languageAndLocale.language}`))).toBeVisible();

// languageAndLocale = {
// language: "en-US",
// locale: "en-US"
// };

// await device.launchApp({newInstance: true, languageAndLocale});
// await element(by.text('Language')).tap();
// await expect(element(by.text(`Current locale: ${languageAndLocale.locale}`))).toBeVisible();
// await expect(element(by.text(`Current language: ${languageAndLocale.language}`))).toBeVisible();
// });

it('resetContentAndSettings() + install() + relaunch() - should tap successfully', async () => {
await device.resetContentAndSettings();
await device.installApp();
Expand Down
27 changes: 27 additions & 0 deletions detox/test/src/Screens/LanguageScreen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { Component } from 'react';
import { Text, View, NativeModules, Platform } from 'react-native';
import _ from 'lodash';

export default class LanguageScreen extends Component {
render() {

const locale = Platform.select({
ios: () => NativeModules.SettingsManager.settings.AppleLocale,
android: () => NativeModules.I18nManager.localeIdentifier
})();

const language = Platform.select({
ios: () => _.take(NativeModules.SettingsManager.settings.AppleLanguages, 1),
android: () => 'Unavailable'
})();

return (
<View style={{ flex: 1, paddingTop: 20, justifyContent: 'center', alignItems: 'center' }}>
<Text style={{ fontSize: 25, marginBottom: 30 }}>Current locale: {locale}</Text>
<Text style={{ fontSize: 25, marginBottom: 30 }}>
Current language: {language}
</Text>
</View>
);
}
}
6 changes: 4 additions & 2 deletions detox/test/src/Screens/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import NetworkScreen from './NetworkScreen';
import AnimationsScreen from './AnimationsScreen';
import LocationScreen from './LocationScreen';
import ShakeScreen from './ShakeScreen';
import DatePickerScreen from './DatePickerScreen'
import DatePickerScreen from './DatePickerScreen';
import LanguageScreen from './LanguageScreen';

export {
SanityScreen,
Expand All @@ -29,5 +30,6 @@ export {
AnimationsScreen,
LocationScreen,
ShakeScreen,
DatePickerScreen
DatePickerScreen,
LanguageScreen
};
1 change: 1 addition & 0 deletions detox/test/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class example extends Component {
<Text style={{fontSize: 20, marginBottom: 30}}>
Choose a test
</Text>
{this.renderScreenButton('Language', Screens.LanguageScreen)}
{this.renderScreenButton('Sanity', Screens.SanityScreen)}
{this.renderScreenButton('Matchers', Screens.MatchersScreen)}
{this.renderScreenButton('Actions', Screens.ActionsScreen)}
Expand Down
42 changes: 42 additions & 0 deletions docs/APIRef.DeviceObjectAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,48 @@ Disable touch indicators on iOS.
await device.launchApp({disableTouchIndicators: true});
```

##### 9. Launch with a specific language (iOS only)
Launch the app with a specific system language

Information about accepted values can be found [here](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html).

```js
await device.launchApp({
languageAndLocale: {
language: "es-MX",
locale: "es-MX"
}
});
```

With this API, you can run sets of e2e tests per language. For example:
```js
['es-MX', 'fr-FR', 'pt-BR'].forEach(locale => {
describe(`Test suite in ${locale}`, () => {

beforeAll(async () => {
await device.launchApp({
newInstance: true,
languageAndLocale: {
language: locale,
locale
}
});
});


it('Test A', () => {

})

it('Test B', () => {

})

});
});
```

### `device.relaunchApp(params)`
**Deprecated** Use `device.launchApp(params)` instead. This method is now calling `launchApp({newInstance: true})` for backwards compatibility, it will be removed in Detox 6.X.X.<Br>
Kill and relaunch the app defined in the current [`configuration`](APIRef.Configuration.md).
Expand Down

0 comments on commit 50ee994

Please sign in to comment.