I'm writing acceptance tests for an Ember component which allows the user to toggle their i18n settings via a dropdown provided by the ember-select-list
package. I'm also using the ember-i18n
package for the i18n settings. The dropdown component looks like this:
import Ember from 'ember';
export default Ember.Component.extend({
i18n: Ember.inject.service(),
languagePreference: Ember.inject.service(),
classNames: ['language-dropdown'],
languageOptions: Ember.computed('i18n.locale', function() {
let i18n = this.get('i18n');
return i18n.get('locales').map((item) => {
return {
id: item,
name: i18n.t(`languages.${item}`)
};
});
}),
onFinish: null,
actions: {
selectLanguage(i18n) {
this.get('languagePreference').setUserI18nPreference(i18n);
this.get('onFinish')();
}
}
});
The language-preference
service looks like this:
import Ember from 'ember';
export default Ember.Service.extend({
i18n: Ember.inject.service(),
config: Ember.inject.service(),
setSessionI18nPreference() {
let setSessionI18n = (results) => {
let { preferences, userName } = results;
if (userName && preferences[userName]) {
this.set('i18n.locale', preferences[userName].i18n);
}
};
this._fetchUserPreferencesDB().then(setSessionI18n);
},
setUserI18nPreference(i18n) {
let setUserI18n = (results) => {
let { preferences, userName } = results;
if (preferences[userName] === undefined) {
preferences[userName] = {};
}
preferences[userName].i18n = i18n;
this.get('config.configDB').put(preferences);
this.set('i18n.locale', i18n);
};
this._fetchUserPreferencesDB().then(setUserI18n);
},
_fetchUserPreferencesDB() {
let configDB = this.get('config.configDB');
let userName;
return configDB.get('current_user').then((user) => {
userName = this._fetchUsername(user);
let preferences = configDB.get('preferences');
return Ember.RSVP.hash({ userName, preferences });
}).catch((err) => {
console.log(err);
if (err.status === 404) {
this._initPreferencesDB(userName, 'en');
}
});
},
_fetchUsername(user) {
switch (typeof user.value) {
case 'string':
return user.value;
case 'object':
return user.value.name;
default:
return undefined;
}
},
_initPreferencesDB(username, i18n) {
let doc = {
_id: 'preferences'
};
if (username != undefined) {
doc[username] = {
i18n: i18n || 'en'
};
}
this.get('config.configDB').put(doc);
}
});
For the acceptance test, so far what I have is this:
import Ember from 'ember';
import { module, test } from 'qunit';
import startApp from 'hospitalrun/tests/helpers/start-app';
module('Acceptance | language dropdown', {
beforeEach() {
this.application = startApp();
},
afterEach() {
Ember.run(this.application, 'destroy');
}
});
test('setting a language preference persists after logout', (assert) => {
runWithPouchDump('default', () => {
authenticateUser({ name: 'foobar', prefix: 'p2' });
visit('/');
andThen(() => {
assert.equal(currentURL(), '/');
assert.equal(find('.view-current-title').text(), 'Welcome to HospitalRun!');
});
andThen(() => {
click('a.settings-trigger');
waitToAppear('.settings-nav');
// andThen(() => {
// select('.language-dropdown', 'French');
// andThen(() => {
// debugger;
// });
// });
});
});
});
This works fine and all my tests pass. But when I un-comment the block that's currently commented-out, and re-run the tests, I see multiple test failures, starting with a global error:
not ok 66 PhantomJS 2.1 - Global error: Error: Missing translation: navigation.subnav.textReplacements at http://localhost:7357/assets/tests.js, line 12058
---
Log: |
{ type: 'error',
text: 'Error: Missing translation: navigation.subnav.textReplacements at http://localhost:7357/assets/tests.js, line 12058\n' }
...
not ok 67 PhantomJS 2.1 - Acceptance | language dropdown: setting a language preference persists after logout
---
actual: >
null
expected: >
null
stack: >
http://localhost:7357/assets/tests.js:12058
message: >
Error: Missing translation: navigation.subnav.textReplacements
Log: |
...
The app works fine in my browser (the translation files are all in place and I can see the correct i18n output in the browser when toggling the i18n). The ember-i18n
docs say that it should work fine with tests that call startApp()
, which this test does. What's weird to me is that the commented-out line, when uncommented, breaks not just this test but tests in other files too. I'm guessing it's affecting some global (within the test process) i18n setting, and this setting is not getting torn down after this test finishes, but I can't be sure.
Code lives here- https://github.com/richiethomas/hospitalrun-frontend/tree/debug
The latest commit represents the failing test.
EDIT: When I put a debugger in the _fetchUserPreferencesDB
function inside language-preference
, I'm able to call this.get('i18n.locales')
and see the locales I expect, so I know the cause is not different locales in different environments (i.e. development
vs test
). I even called this.get('i18n').t('navigation.subnav.textReplacements')
, which is the same translation value that the failing test says is missing, and I get the correct translation. So I'm really not sure why the test fails.
So apparently my test was trying to use the German translations file (which didn't have the
navigation.subnav.textReplacements
key), even though my acceptance test specified French. I verified this by running the tests in the Chrome console, putting a debugger just before the failing line, and inspecting thethis.get('i18n._locale')
object'stranslations
attribute. I found they were all in German for some reason.So the mystery of the missing translation elements was really masking another mystery, which is why my acceptance test is defaulting to German even after PhantomJS has selected the French i18n option. But that's a topic for another post.