blob: 51159c9d8698d65cdc3daabcbe8fb94cdd3813c3 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/renderer/i18n_hooks_delegate.h"
#include "base/strings/stringprintf.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/message_bundle.h"
#include "extensions/renderer/bindings/api_binding_test_util.h"
#include "extensions/renderer/native_extension_bindings_system.h"
#include "extensions/renderer/native_extension_bindings_system_test_base.h"
#include "extensions/renderer/script_context.h"
namespace extensions {
using I18nHooksDelegateTest = NativeExtensionBindingsSystemUnittest;
// NOTE(devlin): This test lives in //chrome (rather than //extensions) since
// the feature is defined at the chrome level (in
// chrome/common/extensions/api/i18n.json). However, all the custom bindings
// for i18n live at the //extensions level. We should move these to all be in
// the same location.
TEST_F(I18nHooksDelegateTest, TestI18nGetMessage) {
scoped_refptr<const Extension> extension = ExtensionBuilder("foo").Build();
RegisterExtension(extension);
v8::HandleScope handle_scope(isolate());
v8::Local<v8::Context> context = MainContext();
ScriptContext* script_context = CreateScriptContext(
context, extension.get(), Feature::BLESSED_EXTENSION_CONTEXT);
script_context->set_url(extension->url());
bindings_system()->UpdateBindingsForContext(script_context);
// In practice, messages will be retrieved from the browser process on first
// request. Since this is a unittest, pre-populate the message bundle.
L10nMessagesMap messages = {
{"simple", "simple message"},
{"one_placeholder", "placeholder $1 end"},
{"multi_placeholders", "placeholder $1 and $2 end"}};
GetExtensionToL10nMessagesMap()->emplace(extension->id(), messages);
auto run_get_message = [context](const char* args) {
SCOPED_TRACE(args);
constexpr char kRunGetMessageFunction[] =
"(function() { return chrome.i18n.getMessage(%s); })";
v8::Local<v8::Function> function = FunctionFromString(
context, base::StringPrintf(kRunGetMessageFunction, args));
v8::Local<v8::Value> result = RunFunction(function, context, 0, nullptr);
return V8ToString(result, context);
};
// Simple tests.
EXPECT_EQ(R"("simple message")", run_get_message("'simple'"));
EXPECT_EQ(R"("placeholder foo end")",
run_get_message("'one_placeholder', 'foo'"));
EXPECT_EQ(R"("placeholder foo end")",
run_get_message("'one_placeholder', ['foo']"));
EXPECT_EQ(R"("placeholder foo and bar end")",
run_get_message("'multi_placeholders', ['foo', 'bar']"));
// We place the somewhat-arbitrary (but documented) limit of 9 substitutions
// on the call.
EXPECT_EQ("undefined",
run_get_message("'one_placeholder',"
"['one', 'two', 'three', 'four', 'five', 'six',"
" 'seven', 'eight', 'nine', 'ten']"));
// Oddities. All of these should probably behave differently. These tests are
// more for documentation than for desirable functionality.
// Non-string values passed in the array of placeholders will be implicitly
// converted to strings...
EXPECT_EQ(R"("placeholder [object Object] end")",
run_get_message("'one_placeholder', [{}]"));
// ... While non-string values passed as a single placeholder are silently
// ignored.
EXPECT_EQ(R"("placeholder end")", run_get_message("'one_placeholder', {}"));
// And values can throw errors (which are silently caught) in string
// conversions, in which case the value is silently ignored.
EXPECT_EQ(R"("placeholder end")",
run_get_message("'one_placeholder',"
"[{toString() { throw new Error('haha'); } }]"));
EXPECT_EQ("undefined",
run_get_message("'one_placeholder',"
"(function() {"
" var x = [];"
" Object.defineProperty(x, 0, {"
" get() { throw new Error('haha'); }"
" });"
" return x;"
" })()"));
EXPECT_EQ(
R"("placeholder foo end")",
run_get_message("'one_placeholder',"
"[{toString() { throw new Error('haha'); } }, 'foo']"));
}
} // namespace extensions