diff --git a/.travis.yml b/.travis.yml
index 75f78ed20..40e48596d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,16 +17,14 @@ install:
- npm install
- npm link
- popd
- - git clone --branch=develop --depth 1 https://github.com/enactjs/enact ../enact
+ - git clone --branch=feature/NXT-9751 --depth 1 https://github.com/enactjs/enact ../enact
- pushd ../enact
- - npm uninstall @enact/ui-test-utils --prefix packages/i18n
- npm install
- npm run lerna exec -- --ignore enact-sampler --concurrency 1 -- npm --no-package-lock install
- npm run lerna exec -- --ignore enact-sampler --concurrency 1 -- npm --no-package-lock link
- npm run interlink
- popd
- rm -fr node_modules/@enact
- - npm uninstall @enact/ui-test-utils
- npm install
- enact link
script:
diff --git a/tests/ui/apps/Loader/Loader-View.js b/tests/ui/apps/Loader/Loader-View.js
new file mode 100644
index 000000000..33de22835
--- /dev/null
+++ b/tests/ui/apps/Loader/Loader-View.js
@@ -0,0 +1,40 @@
+import I18nDecorator, {I18nContextDecorator} from '@enact/i18n/I18nDecorator';
+import Text from '@enact/i18n/Text';
+
+let urlLocale;
+let urlSync = false;
+if (typeof window !== 'undefined') {
+ const url = new URL(document.location);
+ urlLocale = url.searchParams.get('locale');
+ urlSync = url.searchParams.get('sync') === 'true';
+}
+
+const AppBase = ({loaded, locale, rtl, ...rest}) => (
+
+ {document.location.href}
+
+ {loaded ? 'Loaded' : 'Not Loaded'}
+
+
+ {locale}
+
+
+ {rtl ? 'RTL' : 'LTR'}
+
+
+ test
+
+
+);
+
+const App = I18nDecorator(
+ {sync: urlSync},
+ I18nContextDecorator(
+ {loadedProp: 'loaded', localeProp: 'locale', rtlProp: 'rtl'},
+ AppBase
+ )
+);
+
+const app = () => ;
+
+export default app;
diff --git a/tests/ui/apps/Loader/resources/ar/strings.json b/tests/ui/apps/Loader/resources/ar/strings.json
new file mode 100644
index 000000000..5ef2b7b5b
--- /dev/null
+++ b/tests/ui/apps/Loader/resources/ar/strings.json
@@ -0,0 +1,3 @@
+{
+ "test": "test-ar"
+}
\ No newline at end of file
diff --git a/tests/ui/apps/Loader/resources/en/strings.json b/tests/ui/apps/Loader/resources/en/strings.json
new file mode 100644
index 000000000..b91c93dd0
--- /dev/null
+++ b/tests/ui/apps/Loader/resources/en/strings.json
@@ -0,0 +1,3 @@
+{
+ "test": "test-en"
+}
\ No newline at end of file
diff --git a/tests/ui/apps/Loader/resources/ilibmanifest.json b/tests/ui/apps/Loader/resources/ilibmanifest.json
new file mode 100644
index 000000000..e877310fc
--- /dev/null
+++ b/tests/ui/apps/Loader/resources/ilibmanifest.json
@@ -0,0 +1,3 @@
+{
+ "files": ["ar/strings.json", "en/strings.json"]
+}
\ No newline at end of file
diff --git a/tests/ui/specs/Loader/Loader-specs.js b/tests/ui/specs/Loader/Loader-specs.js
new file mode 100644
index 000000000..a59abad10
--- /dev/null
+++ b/tests/ui/specs/Loader/Loader-specs.js
@@ -0,0 +1,88 @@
+const Page = require('./LoaderPage');
+
+// When running these tests manually in the browser, be sure to clear localStorage via dev tools or
+// with window.localStorage.clear() to ensure that resources are loaded via the network and not the
+// localStorage cache
+describe('Loader', () => {
+ describe('sync', () => {
+ it('should load', async () => {
+ await Page.open({sync: true, locale: 'ar-SA'});
+
+ expect(await Page.loadState()).toBe('Loaded');
+ });
+
+ it('should have specified locale', async () => {
+ await Page.open({sync: true, locale: 'ar-SA'});
+
+ expect(await Page.locale()).toBe('ar-SA');
+ });
+
+ it('should have LTR text direction', async () => {
+ await Page.open({sync: true, locale: 'en-US'});
+
+ expect(await Page.textDirection()).toBe('LTR');
+ });
+
+ it('should have RTL text direction', async () => {
+ await Page.open({sync: true, locale: 'ar-SA'});
+
+ expect(await Page.textDirection()).toBe('RTL');
+ });
+
+ it('should have translated text for en-US', async () => {
+ await Page.open({sync: true, locale: 'en-US'});
+
+ expect(await Page.text()).toBe('test-en');
+ });
+
+ it('should have translated text for ar-SA', async () => {
+ await Page.open({sync: true, locale: 'ar-SA'});
+
+ expect(await Page.text()).toBe('test-ar');
+ });
+ });
+
+ describe('async', function () {
+ it('should load', async () => {
+ await Page.open({sync: false, locale: 'ar-SA'});
+
+ browser.waitUntil(() => Page.loadState() === 'Loaded', 500);
+ expect(await Page.loadState()).toBe('Loaded');
+ });
+
+ it('should have specified locale', async () => {
+ await Page.open({sync: false, locale: 'ar-SA'});
+
+ browser.waitUntil(() => Page.loadState() === 'Loaded', 500);
+ expect(await Page.locale()).toBe('ar-SA');
+ });
+
+ it('should have LTR text direction', async () => {
+ await Page.open({sync: false, locale: 'en-US'});
+
+ browser.waitUntil(() => Page.loadState() === 'Loaded', 500);
+ expect(await Page.textDirection()).toBe('LTR');
+ });
+
+ it('should have RTL text direction', async () => {
+ await Page.open({sync: false, locale: 'ar-SA'});
+
+ browser.waitUntil(() => Page.loadState() === 'Loaded', 500);
+ expect(await Page.textDirection()).toBe('RTL');
+ });
+
+ it('should have translated text for en-US', async () => {
+ await Page.open({sync: false, locale: 'en-US'});
+
+ browser.waitUntil(() => Page.loadState() === 'Loaded', 500);
+ expect(await Page.text()).toBe('test-en');
+ });
+
+ it('should have translated text for ar-SA', async () => {
+ await Page.open({sync: false, locale: 'ar-SA'});
+
+ browser.waitUntil(() => Page.loadState() === 'Loaded', 500);
+ expect(await Page.text()).toBe('test-ar');
+ });
+ });
+});
diff --git a/tests/ui/specs/Loader/LoaderPage.js b/tests/ui/specs/Loader/LoaderPage.js
new file mode 100644
index 000000000..104665de0
--- /dev/null
+++ b/tests/ui/specs/Loader/LoaderPage.js
@@ -0,0 +1,34 @@
+'use strict';
+const {Page} = require('@enact/ui-test-utils/utils');
+
+class LoaderPage extends Page {
+ constructor () {
+ super();
+ this.title = 'Loader Test';
+ }
+
+ async loadState () {
+ return await browser.$('#loaded').getText();
+ }
+
+ async locale () {
+ return await browser.$('#locale').getText();
+ }
+
+ async textDirection () {
+ return await browser.$('#dir').getText();
+ }
+
+ async text () {
+ return await browser.$('#text').getText();
+ }
+
+ async open (opts) {
+ const urlExtra = Object.keys(opts).reduce((v, key) => {
+ return v + encodeURIComponent(key) + '=' + encodeURIComponent(opts[key]) + '&';
+ }, '?');
+ await super.open('Loader-View', urlExtra);
+ }
+}
+
+module.exports = new LoaderPage();