oobe_localization_browsertest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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.
4
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_document.h"
12#include "chrome/browser/chromeos/login/login_display_host_impl.h"
13#include "chrome/browser/chromeos/login/login_wizard.h"
14#include "chrome/browser/chromeos/login/test/js_checker.h"
15#include "chrome/common/pref_names.h"
16#include "chrome/test/base/in_process_browser_test.h"
17#include "chromeos/system/statistics_provider.h"
18#include "content/public/browser/notification_service.h"
19#include "content/public/browser/web_contents.h"
20#include "content/public/test/browser_test_utils.h"
21#include "content/public/test/test_utils.h"
22
23namespace base {
24class TaskRunner;
25}
26
27namespace chromeos {
28
29namespace {
30
31// OOBE constants.
32const char* kLocaleSelect = "language-select";
33const char* kKeyboardSelect = "keyboard-select";
34
35const char* kUSLayout = "xkb:us::eng";
36
37}
38
39namespace system {
40
41// Custom StatisticsProvider that will return each set of region settings.
42class FakeStatisticsProvider : public StatisticsProvider {
43 public:
44  virtual ~FakeStatisticsProvider() {}
45
46  void set_locale(const std::string& locale) {
47    initial_locale_ = locale;
48  }
49
50  void set_keyboard_layout(const std::string& keyboard_layout) {
51    keyboard_layout_ = keyboard_layout;
52  }
53
54 private:
55  // StatisticsProvider overrides.
56  virtual void StartLoadingMachineStatistics(
57      const scoped_refptr<base::TaskRunner>& file_task_runner,
58      bool load_oem_manifest) OVERRIDE {
59  }
60
61  // Populates the named machine statistic for initial_locale and
62  // keyboard_layout only.
63  virtual bool GetMachineStatistic(const std::string& name,
64                                   std::string* result) OVERRIDE {
65    if (name == "initial_locale")
66      *result = initial_locale_;
67    else if (name == "keyboard_layout")
68      *result = keyboard_layout_;
69    else
70      return false;
71
72    return true;
73  }
74
75  virtual bool GetMachineFlag(const std::string& name, bool* result) OVERRIDE {
76    return false;
77  }
78
79  virtual void Shutdown() OVERRIDE {
80  }
81
82  std::string initial_locale_;
83  std::string keyboard_layout_;
84};
85
86}  // namespace system
87
88class OobeLocalizationTest : public InProcessBrowserTest {
89 public:
90  OobeLocalizationTest();
91
92  // Verifies that the comma-separated |values| corresponds with the first
93  // values in |select_id|, optionally checking for an options group label after
94  // the first set of options.
95  void VerifyInitialOptions(const char* select_id,
96                            const char* values,
97                            bool check_separator);
98
99  // Verifies that |value| exists in |select_id|.
100  void VerifyOptionExists(const char* select_id, const char* value);
101
102  // Dumps OOBE select control (language or keyboard) to string.
103  std::string DumpOptions(const char* select_id);
104
105 protected:
106  // Runs the test for the given locale and keyboard layout.
107  void RunLocalizationTest(const std::string& initial_locale,
108                           const std::string& keyboard_layout,
109                           const std::string& expected_locale,
110                           const std::string& expected_keyboard_layout,
111                           const std::string& expected_keyboard_select_control);
112
113 private:
114  scoped_ptr<system::FakeStatisticsProvider> statistics_provider_;
115  test::JSChecker checker;
116
117  DISALLOW_COPY_AND_ASSIGN(OobeLocalizationTest);
118};
119
120OobeLocalizationTest::OobeLocalizationTest() {
121  statistics_provider_.reset(new system::FakeStatisticsProvider());
122  // Set the instance returned by GetInstance() for testing.
123  system::StatisticsProvider::SetTestProvider(statistics_provider_.get());
124}
125
126void OobeLocalizationTest::VerifyInitialOptions(const char* select_id,
127                                                const char* values,
128                                                bool check_separator) {
129  const std::string expression = base::StringPrintf(
130      "(function () {\n"
131      "  var select = document.querySelector('#%s');\n"
132      "  if (!select)\n"
133      "    return false;\n"
134      "  var values = '%s'.split(',');\n"
135      "  var correct = select.selectedIndex == 0;\n"
136      "  for (var i = 0; i < values.length && correct; i++) {\n"
137      "    if (select.options[i].value != values[i])\n"
138      "      correct = false;\n"
139      "  }\n"
140      "  if (%d && correct)\n"
141      "    correct = select.children[values.length].tagName === 'OPTGROUP';\n"
142      "  return correct;\n"
143      "})()", select_id, values, check_separator);
144  ASSERT_TRUE(checker.GetBool(expression)) << expression;
145}
146
147void OobeLocalizationTest::VerifyOptionExists(const char* select_id,
148                                              const char* value) {
149  const std::string expression = base::StringPrintf(
150      "(function () {\n"
151      "  var select = document.querySelector('#%s');\n"
152      "  if (!select)\n"
153      "    return false;\n"
154      "  for (var i = 0; i < select.options.length; i++) {\n"
155      "    if (select.options[i].value == '%s')\n"
156      "      return true;\n"
157      "  }\n"
158      "  return false;\n"
159      "})()", select_id, value);
160  ASSERT_TRUE(checker.GetBool(expression)) << expression;
161}
162
163std::string OobeLocalizationTest::DumpOptions(const char* select_id) {
164  const std::string expression = base::StringPrintf(
165      "\n"
166      "(function () {\n"
167      "  var selector = '#%s';\n"
168      "  var divider = ',';\n"
169      "  var select = document.querySelector(selector);\n"
170      "  if (!select)\n"
171      "    return 'document.querySelector(' + selector + ') failed.';\n"
172      "  var dumpOptgroup = function(group) {\n"
173      "    var result = '';\n"
174      "    for (var i = 0; i < group.children.length; i++) {\n"
175      "      if (i > 0) {\n"
176      "        result += divider;\n"
177      "      }\n"
178      "      if (group.children[i].value) {\n"
179      "        result += group.children[i].value;\n"
180      "      } else {\n"
181      "        result += '__NO_VALUE__';\n"
182      "      }\n"
183      "    }\n"
184      "    return result;\n"
185      "  };\n"
186      "  var result = '';\n"
187      "  var children = select.children;\n"
188      "  for (var i = 0; i < children.length; i++) {\n"
189      "    if (i > 0) {\n"
190      "      result += divider;\n"
191      "    }\n"
192      "    if (children[i].value) {\n"
193      "      result += children[i].value;\n"
194      "    } else if (children[i].tagName === 'OPTGROUP') {\n"
195      "      result += '[' + dumpOptgroup(children[i]) + ']';\n"
196      "    } else {\n"
197      "      result += '__NO_VALUE__';\n"
198      "    }\n"
199      "  }\n"
200      "  return result;\n"
201      "})()\n",
202      select_id);
203  return checker.GetString(expression);
204}
205
206void OobeLocalizationTest::RunLocalizationTest(
207    const std::string& initial_locale,
208    const std::string& keyboard_layout,
209    const std::string& expected_locale,
210    const std::string& expected_keyboard_layout,
211    const std::string& expected_keyboard_select_control) {
212  statistics_provider_->set_locale(initial_locale);
213  statistics_provider_->set_keyboard_layout(keyboard_layout);
214
215  // Initialize StartupCustomizationDocument with fake statistics provider.
216  StartupCustomizationDocument::GetInstance()->Init(
217      statistics_provider_.get());
218
219  // Bring up the OOBE network screen.
220  chromeos::ShowLoginWizard(chromeos::WizardController::kNetworkScreenName);
221  content::WindowedNotificationObserver(
222      chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
223      content::NotificationService::AllSources()).Wait();
224
225  checker.set_web_contents(static_cast<chromeos::LoginDisplayHostImpl*>(
226                           chromeos::LoginDisplayHostImpl::default_host())->
227                           GetOobeUI()->web_ui()->GetWebContents());
228
229  VerifyInitialOptions(kLocaleSelect, expected_locale.c_str(), true);
230  VerifyInitialOptions(kKeyboardSelect,
231                       expected_keyboard_layout.c_str(),
232                       false);
233
234  // Make sure we have a fallback keyboard.
235  VerifyOptionExists(kKeyboardSelect, kUSLayout);
236
237  // Note, that sort order is locale-specific, but is unlikely to change.
238  // Especially for keyboard layouts.
239  EXPECT_EQ(expected_keyboard_select_control, DumpOptions(kKeyboardSelect));
240
241  // Shut down the display host.
242  chromeos::LoginDisplayHostImpl::default_host()->Finalize();
243  base::MessageLoopForUI::current()->RunUntilIdle();
244
245  // Clear the locale pref so the statistics provider is pinged next time.
246  g_browser_process->local_state()->SetString(prefs::kApplicationLocale,
247                                              std::string());
248}
249
250IN_PROC_BROWSER_TEST_F(OobeLocalizationTest, NetworkScreenNonLatin) {
251  // For a non-Latin keyboard layout like Russian, we expect to see the US
252  // keyboard.
253  RunLocalizationTest("ru", "xkb:ru::rus",
254                      "ru", kUSLayout,
255                      "xkb:us::eng");
256
257  RunLocalizationTest("ru", "xkb:us::eng,xkb:ru::rus",
258                      "ru", kUSLayout,
259                      "xkb:us::eng");
260
261  // IMEs do not load at OOBE, so we just expect to see the (Latin) Japanese
262  // keyboard.
263  RunLocalizationTest("ja", "xkb:jp::jpn",
264                      "ja", "xkb:jp::jpn",
265                      "xkb:jp::jpn,[xkb:us::eng]");
266}
267
268IN_PROC_BROWSER_TEST_F(OobeLocalizationTest, NetworkScreenKeyboardLayout) {
269  // We don't use the Icelandic locale but the Icelandic keyboard layout
270  // should still be selected when specified as the default.
271  RunLocalizationTest("en-US", "xkb:is::ice",
272                      "en-US", "xkb:is::ice",
273                      "xkb:is::ice,["
274                          "xkb:us::eng,xkb:us:intl:eng,xkb:us:altgr-intl:eng,"
275                          "xkb:us:dvorak:eng,xkb:us:colemak:eng]");
276}
277
278IN_PROC_BROWSER_TEST_F(OobeLocalizationTest, NetworkScreenFullLatin) {
279  // French Swiss keyboard.
280  RunLocalizationTest("fr", "xkb:ch:fr:fra",
281                      "fr", "xkb:ch:fr:fra",
282                      "xkb:ch:fr:fra,["
283                          "xkb:fr::fra,xkb:be::fra,xkb:ca::fra,"
284                          "xkb:ca:multix:fra,xkb:us::eng]");
285
286  // German Swiss keyboard.
287  RunLocalizationTest("de", "xkb:ch::ger",
288                      "de", "xkb:ch::ger",
289                      "xkb:ch::ger,["
290                          "xkb:de::ger,xkb:de:neo:ger,xkb:be::ger,xkb:us::eng"
291                      "]");
292}
293
294IN_PROC_BROWSER_TEST_F(OobeLocalizationTest, NetworkScreenMultipleLocales) {
295  RunLocalizationTest("es,en-US,nl", "xkb:be::nld",
296                      "es,en-US,nl", "xkb:be::nld",
297                      "xkb:be::nld,[xkb:es::spa,xkb:latam::spa,xkb:us::eng]");
298
299  RunLocalizationTest("ru,de", "xkb:ru::rus",
300                      "ru,de", kUSLayout,
301                      "xkb:us::eng");
302}
303
304}  // namespace chromeos
305