Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / chromeos / login / oobe_localization_browsertest.cc
blob4d0efe3aef67eef34e46754f97999fea9da8377e
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/message_loop/message_loop.h"
6 #include "base/prefs/pref_service.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/task_runner.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/chromeos/customization/customization_document.h"
12 #include "chrome/browser/chromeos/input_method/input_method_util.h"
13 #include "chrome/browser/chromeos/login/login_manager_test.h"
14 #include "chrome/browser/chromeos/login/login_wizard.h"
15 #include "chrome/browser/chromeos/login/screens/network_screen.h"
16 #include "chrome/browser/chromeos/login/test/js_checker.h"
17 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
18 #include "chrome/browser/chromeos/login/wizard_controller.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/test/base/in_process_browser_test.h"
21 #include "chromeos/system/fake_statistics_provider.h"
22 #include "chromeos/system/statistics_provider.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/test/browser_test_utils.h"
26 #include "content/public/test/test_utils.h"
27 #include "ui/base/ime/chromeos/extension_ime_util.h"
28 #include "ui/base/ime/chromeos/input_method_manager.h"
29 #include "ui/base/ime/chromeos/input_method_whitelist.h"
31 namespace base {
32 class TaskRunner;
35 namespace chromeos {
37 namespace {
39 // OOBE constants.
40 const char kLocaleSelect[] = "language-select";
41 const char kKeyboardSelect[] = "keyboard-select";
43 const char kUSLayout[] = "xkb:us::eng";
45 class LanguageListWaiter : public NetworkScreen::Observer {
46 public:
47 explicit LanguageListWaiter(base::RunLoop& loop)
48 : network_screen_(
49 NetworkScreen::Get(WizardController::default_controller())),
50 loop_(loop) {
51 network_screen_->AddObserver(this);
52 CheckLanguageList();
55 ~LanguageListWaiter() override { network_screen_->RemoveObserver(this); }
57 // NetworkScreen::Observer implementation:
58 void OnLanguageListReloaded() override { CheckLanguageList(); }
60 private:
61 void CheckLanguageList() {
62 if (network_screen_->GetLanguageList())
63 loop_.Quit();
66 NetworkScreen* network_screen_;
67 base::RunLoop& loop_;
70 } // namespace
72 struct LocalizationTestParams {
73 const char* initial_locale;
74 const char* keyboard_layout;
75 const char* expected_locale;
76 const char* expected_keyboard_layout;
77 const char* expected_keyboard_select_control;
78 } const oobe_localization_test_parameters[] = {
79 // ------------------ Non-Latin setup
80 // For a non-Latin keyboard layout like Russian, we expect to see the US
81 // keyboard.
82 {"ru", "xkb:ru::rus", "ru", kUSLayout, "xkb:us::eng"},
83 {"ru", "xkb:us::eng,xkb:ru::rus", "ru", kUSLayout, "xkb:us::eng"},
85 // IMEs do not load at OOBE, so we just expect to see the (Latin) Japanese
86 // keyboard.
87 {"ja", "xkb:jp::jpn", "ja", "xkb:jp::jpn", "xkb:jp::jpn,[xkb:us::eng]"},
89 // We don't use the Icelandic locale but the Icelandic keyboard layout
90 // should still be selected when specified as the default.
91 {"en-US",
92 "xkb:is::ice",
93 "en-US",
94 "xkb:is::ice",
95 "xkb:is::ice,[xkb:us::eng,xkb:us:intl:eng,xkb:us:altgr-intl:eng,"
96 "xkb:us:dvorak:eng,xkb:us:dvp:eng,xkb:us:colemak:eng]"},
97 // ------------------ Full Latin setup
98 // French Swiss keyboard.
99 {"fr",
100 "xkb:ch:fr:fra",
101 "fr",
102 "xkb:ch:fr:fra",
103 "xkb:ch:fr:fra,[xkb:fr::fra,xkb:be::fra,xkb:ca::fra,"
104 "xkb:ca:multix:fra,xkb:us::eng]"},
106 // German Swiss keyboard.
107 {"de",
108 "xkb:ch::ger",
109 "de",
110 "xkb:ch::ger",
111 "xkb:ch::ger,[xkb:de::ger,xkb:de:neo:ger,xkb:be::ger,xkb:us::eng]"},
113 // NetworkScreenMultipleLocales
114 {"es,en-US,nl",
115 "xkb:be::nld",
116 "es,en-US,nl",
117 "xkb:be::nld",
118 "xkb:be::nld,[xkb:es::spa,xkb:latam::spa,xkb:us::eng]"},
120 {"ru,de", "xkb:ru::rus", "ru,de", kUSLayout, "xkb:us::eng"},
122 // TODO(alemate/michaelpg): Figure out why these tests are failing
123 // and re-enable them. crbug.com/422702.
124 // ------------------ Regional Locales
125 // Syntetic example to test correct merging of different locales.
126 // {"fr-CH,it-CH,de-CH",
127 // "xkb:fr::fra,xkb:it::ita,xkb:de::ger",
128 // "fr-CH,it-CH,de-CH",
129 // "xkb:fr::fra",
130 // "xkb:fr::fra,xkb:it::ita,xkb:de::ger,[xkb:be::fra,xkb:ca::fra,"
131 // "xkb:ch:fr:fra,xkb:ca:multix:fra,xkb:us::eng]"},
133 // Another syntetic example. Check that british keyboard is available.
134 // {"en-AU",
135 // "xkb:us::eng",
136 // "en-AU",
137 // "xkb:us::eng",
138 // "xkb:us::eng,[xkb:gb:extd:eng,xkb:gb:dvorak:eng]"},
141 class OobeLocalizationTest
142 : public LoginManagerTest,
143 public testing::WithParamInterface<const LocalizationTestParams*> {
144 public:
145 OobeLocalizationTest();
147 // Verifies that the comma-separated |values| corresponds with the first
148 // values in |select_id|, optionally checking for an options group label after
149 // the first set of options.
150 bool VerifyInitialOptions(const char* select_id,
151 const char* values,
152 bool check_separator);
154 // Verifies that |value| exists in |select_id|.
155 bool VerifyOptionExists(const char* select_id, const char* value);
157 // Dumps OOBE select control (language or keyboard) to string.
158 std::string DumpOptions(const char* select_id);
160 protected:
161 // Runs the test for the given locale and keyboard layout.
162 void RunLocalizationTest();
164 void WaitUntilJSIsReady() {
165 LoginDisplayHostImpl* host = static_cast<LoginDisplayHostImpl*>(
166 LoginDisplayHostImpl::default_host());
167 if (!host)
168 return;
169 chromeos::OobeUI* oobe_ui = host->GetOobeUI();
170 if (!oobe_ui)
171 return;
172 base::RunLoop run_loop;
173 const bool oobe_ui_ready = oobe_ui->IsJSReady(run_loop.QuitClosure());
174 if (!oobe_ui_ready)
175 run_loop.Run();
178 private:
179 system::ScopedFakeStatisticsProvider fake_statistics_provider_;
180 test::JSChecker checker;
182 DISALLOW_COPY_AND_ASSIGN(OobeLocalizationTest);
185 OobeLocalizationTest::OobeLocalizationTest() : LoginManagerTest(false) {
186 fake_statistics_provider_.SetMachineStatistic("initial_locale",
187 GetParam()->initial_locale);
188 fake_statistics_provider_.SetMachineStatistic("keyboard_layout",
189 GetParam()->keyboard_layout);
192 bool OobeLocalizationTest::VerifyInitialOptions(const char* select_id,
193 const char* values,
194 bool check_separator) {
195 const std::string expression = base::StringPrintf(
196 "(function () {\n"
197 " var select = document.querySelector('#%s');\n"
198 " if (!select)\n"
199 " return false;\n"
200 " var values = '%s'.split(',');\n"
201 " var correct = select.selectedIndex == 0;\n"
202 " for (var i = 0; i < values.length && correct; i++) {\n"
203 " if (select.options[i].value != values[i])\n"
204 " correct = false;\n"
205 " }\n"
206 " if (%d && correct)\n"
207 " correct = select.children[values.length].tagName === 'OPTGROUP';\n"
208 " return correct;\n"
209 "})()", select_id, values, check_separator);
210 const bool execute_status = checker.GetBool(expression);
211 EXPECT_TRUE(execute_status) << expression;
212 return execute_status;
215 bool OobeLocalizationTest::VerifyOptionExists(const char* select_id,
216 const char* value) {
217 const std::string expression = base::StringPrintf(
218 "(function () {\n"
219 " var select = document.querySelector('#%s');\n"
220 " if (!select)\n"
221 " return false;\n"
222 " for (var i = 0; i < select.options.length; i++) {\n"
223 " if (select.options[i].value == '%s')\n"
224 " return true;\n"
225 " }\n"
226 " return false;\n"
227 "})()", select_id, value);
228 const bool execute_status = checker.GetBool(expression);
229 EXPECT_TRUE(execute_status) << expression;
230 return execute_status;
233 std::string OobeLocalizationTest::DumpOptions(const char* select_id) {
234 const std::string expression = base::StringPrintf(
235 "\n"
236 "(function () {\n"
237 " var selector = '#%s';\n"
238 " var divider = ',';\n"
239 " var select = document.querySelector(selector);\n"
240 " if (!select)\n"
241 " return 'document.querySelector(' + selector + ') failed.';\n"
242 " var dumpOptgroup = function(group) {\n"
243 " var result = '';\n"
244 " for (var i = 0; i < group.children.length; i++) {\n"
245 " if (i > 0) {\n"
246 " result += divider;\n"
247 " }\n"
248 " if (group.children[i].value) {\n"
249 " result += group.children[i].value;\n"
250 " } else {\n"
251 " result += '__NO_VALUE__';\n"
252 " }\n"
253 " }\n"
254 " return result;\n"
255 " };\n"
256 " var result = '';\n"
257 " if (select.selectedIndex != 0) {\n"
258 " result += '(selectedIndex=' + select.selectedIndex + \n"
259 " ', selected \"' + select.options[select.selectedIndex].value +\n"
260 " '\")';\n"
261 " }\n"
262 " var children = select.children;\n"
263 " for (var i = 0; i < children.length; i++) {\n"
264 " if (i > 0) {\n"
265 " result += divider;\n"
266 " }\n"
267 " if (children[i].value) {\n"
268 " result += children[i].value;\n"
269 " } else if (children[i].tagName === 'OPTGROUP') {\n"
270 " result += '[' + dumpOptgroup(children[i]) + ']';\n"
271 " } else {\n"
272 " result += '__NO_VALUE__';\n"
273 " }\n"
274 " }\n"
275 " return result;\n"
276 "})()\n",
277 select_id);
278 return checker.GetString(expression);
281 std::string TranslateXKB2Extension(const std::string& src) {
282 std::string result(src);
283 // Modifies the expected keyboard select control options for the new
284 // extension based xkb id.
285 size_t pos = 0;
286 std::string repl_old = "xkb:";
287 std::string repl_new =
288 extension_ime_util::GetInputMethodIDByEngineID("xkb:");
289 while ((pos = result.find(repl_old, pos)) != std::string::npos) {
290 result.replace(pos, repl_old.length(), repl_new);
291 pos += repl_new.length();
293 return result;
296 void OobeLocalizationTest::RunLocalizationTest() {
297 const std::string initial_locale(GetParam()->initial_locale);
298 const std::string keyboard_layout(GetParam()->keyboard_layout);
299 const std::string expected_locale(GetParam()->expected_locale);
300 const std::string expected_keyboard_layout(
301 GetParam()->expected_keyboard_layout);
302 const std::string expected_keyboard_select_control(
303 GetParam()->expected_keyboard_select_control);
305 const std::string expected_keyboard_select =
306 TranslateXKB2Extension(expected_keyboard_select_control);
309 base::RunLoop loop;
310 LanguageListWaiter waiter(loop);
311 loop.Run();
314 WaitUntilJSIsReady();
316 const std::string first_language =
317 expected_locale.substr(0, expected_locale.find(','));
318 bool done = false;
319 const std::string waiting_script = base::StringPrintf(
320 "var screenElement = document.getElementById('language-select');"
321 "function SendReplyIfAcceptEnabled() {"
322 " if ($('language-select').value != '%s')"
323 " return false;"
324 " domAutomationController.send(true);"
325 " observer.disconnect();"
326 " return true;"
328 "var observer = new MutationObserver(SendReplyIfAcceptEnabled);"
329 "if (!SendReplyIfAcceptEnabled()) {"
330 " var options = { attributes: true };"
331 " observer.observe(screenElement, options);"
332 "}",
333 first_language.c_str());
335 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
336 static_cast<chromeos::LoginDisplayHostImpl*>(
337 chromeos::LoginDisplayHostImpl::default_host())
338 ->GetOobeUI()
339 ->web_ui()
340 ->GetWebContents(),
341 waiting_script,
342 &done));
344 checker.set_web_contents(static_cast<chromeos::LoginDisplayHostImpl*>(
345 chromeos::LoginDisplayHostImpl::default_host())->
346 GetOobeUI()->web_ui()->GetWebContents());
348 if (!VerifyInitialOptions(kLocaleSelect, expected_locale.c_str(), true)) {
349 LOG(ERROR) << "Actual value of " << kLocaleSelect << ":\n"
350 << DumpOptions(kLocaleSelect);
352 if (!VerifyInitialOptions(
353 kKeyboardSelect,
354 TranslateXKB2Extension(expected_keyboard_layout).c_str(),
355 false)) {
356 LOG(ERROR) << "Actual value of " << kKeyboardSelect << ":\n"
357 << DumpOptions(kKeyboardSelect);
360 // Make sure we have a fallback keyboard.
361 if (!VerifyOptionExists(kKeyboardSelect,
362 extension_ime_util::GetInputMethodIDByEngineID(
363 kUSLayout).c_str())) {
364 LOG(ERROR) << "Actual value of " << kKeyboardSelect << ":\n"
365 << DumpOptions(kKeyboardSelect);
368 // Note, that sort order is locale-specific, but is unlikely to change.
369 // Especially for keyboard layouts.
370 EXPECT_EQ(expected_keyboard_select, DumpOptions(kKeyboardSelect));
372 // Shut down the display host.
373 chromeos::LoginDisplayHostImpl::default_host()->Finalize();
374 base::MessageLoopForUI::current()->RunUntilIdle();
376 // Clear the locale pref so the statistics provider is pinged next time.
377 g_browser_process->local_state()->SetString(prefs::kApplicationLocale,
378 std::string());
381 IN_PROC_BROWSER_TEST_P(OobeLocalizationTest, LocalizationTest) {
382 RunLocalizationTest();
385 INSTANTIATE_TEST_CASE_P(
386 StructSequence,
387 OobeLocalizationTest,
388 testing::Range(&oobe_localization_test_parameters[0],
389 &oobe_localization_test_parameters[arraysize(
390 oobe_localization_test_parameters)]));
391 } // namespace chromeos