1// Copyright (c) 2011 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#import <Cocoa/Cocoa.h>
6
7#import "base/memory/scoped_nsobject.h"
8#import "base/string_util.h"
9#include "base/utf_string_conversions.h"
10#import "chrome/app/chrome_command_ids.h"  // For translate menu command ids.
11#import "chrome/browser/translate/translate_infobar_delegate.h"
12#import "chrome/browser/ui/cocoa/browser_test_helper.h"
13#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
14#import "chrome/browser/ui/cocoa/infobars/infobar.h"
15#import "chrome/browser/ui/cocoa/translate/translate_infobar_base.h"
16#import "chrome/browser/ui/cocoa/translate/before_translate_infobar_controller.h"
17#import "content/browser/site_instance.h"
18#import "content/browser/tab_contents/tab_contents.h"
19#import "testing/gmock/include/gmock/gmock.h"
20#import "testing/gtest/include/gtest/gtest.h"
21#import "testing/platform_test.h"
22
23namespace {
24
25// All states the translate toolbar can assume.
26TranslateInfoBarDelegate::Type kTranslateToolbarStates[] = {
27  TranslateInfoBarDelegate::BEFORE_TRANSLATE,
28  TranslateInfoBarDelegate::AFTER_TRANSLATE,
29  TranslateInfoBarDelegate::TRANSLATING,
30  TranslateInfoBarDelegate::TRANSLATION_ERROR
31};
32
33class MockTranslateInfoBarDelegate : public TranslateInfoBarDelegate {
34 public:
35  MockTranslateInfoBarDelegate(TranslateInfoBarDelegate::Type type,
36                               TranslateErrors::Type error,
37                               TabContents* contents)
38      : TranslateInfoBarDelegate(type, error, contents, "en", "es"){
39    // Start out in the "Before Translate" state.
40    type_ = type;
41
42  }
43
44  virtual string16 GetDisplayNameForLocale(const std::string& language_code) {
45    return ASCIIToUTF16("Foo");
46  }
47
48  virtual bool IsLanguageBlacklisted() {
49    return false;
50  }
51
52  virtual bool IsSiteBlacklisted() {
53    return false;
54  }
55
56  virtual bool ShouldAlwaysTranslate() {
57    return false;
58  }
59
60  MOCK_METHOD0(Translate, void());
61  MOCK_METHOD0(RevertTranslation, void());
62  MOCK_METHOD0(TranslationDeclined, void());
63  MOCK_METHOD0(ToggleLanguageBlacklist, void());
64  MOCK_METHOD0(ToggleSiteBlacklist, void());
65  MOCK_METHOD0(ToggleAlwaysTranslate, void());
66};
67
68class TranslationInfoBarTest : public CocoaTest {
69 public:
70  BrowserTestHelper browser_helper_;
71  scoped_ptr<TabContents> tab_contents;
72  scoped_ptr<MockTranslateInfoBarDelegate> infobar_delegate;
73  scoped_nsobject<TranslateInfoBarControllerBase> infobar_controller;
74
75 public:
76  // Each test gets a single Mock translate delegate for the lifetime of
77  // the test.
78  virtual void SetUp() {
79    CocoaTest::SetUp();
80   tab_contents.reset(
81        new TabContents(browser_helper_.profile(),
82                        NULL,
83                        MSG_ROUTING_NONE,
84                        NULL,
85                        NULL));
86    CreateInfoBar();
87  }
88
89  void CreateInfoBar() {
90    CreateInfoBar(TranslateInfoBarDelegate::BEFORE_TRANSLATE);
91  }
92
93  void CreateInfoBar(TranslateInfoBarDelegate::Type type) {
94    TranslateErrors::Type error = TranslateErrors::NONE;
95    if (type == TranslateInfoBarDelegate::TRANSLATION_ERROR)
96      error = TranslateErrors::NETWORK;
97    infobar_delegate.reset(
98        new MockTranslateInfoBarDelegate(type, error, tab_contents.get()));
99    [[infobar_controller view] removeFromSuperview];
100    scoped_ptr<InfoBar> infobar(
101        static_cast<InfoBarDelegate*>(infobar_delegate.get())->CreateInfoBar());
102    infobar_controller.reset(
103        reinterpret_cast<TranslateInfoBarControllerBase*>(
104            infobar->controller()));
105    // We need to set the window to be wide so that the options button
106    // doesn't overlap the other buttons.
107    [test_window() setContentSize:NSMakeSize(2000, 500)];
108    [[infobar_controller view] setFrame:NSMakeRect(0, 0, 2000, 500)];
109    [[test_window() contentView] addSubview:[infobar_controller view]];
110  }
111};
112
113// Check that we can instantiate a Translate Infobar correctly.
114TEST_F(TranslationInfoBarTest, Instantiate) {
115  CreateInfoBar();
116  ASSERT_TRUE(infobar_controller.get());
117}
118
119// Check that clicking the Translate button calls Translate().
120TEST_F(TranslationInfoBarTest, TranslateCalledOnButtonPress) {
121  CreateInfoBar();
122
123  EXPECT_CALL(*infobar_delegate, Translate()).Times(1);
124  [infobar_controller ok:nil];
125}
126
127// Check that clicking the "Retry" button calls Translate() when we're
128// in the error mode - http://crbug.com/41315 .
129TEST_F(TranslationInfoBarTest, TranslateCalledInErrorMode) {
130  CreateInfoBar(TranslateInfoBarDelegate::TRANSLATION_ERROR);
131
132  EXPECT_CALL(*infobar_delegate, Translate()).Times(1);
133
134  [infobar_controller ok:nil];
135}
136
137// Check that clicking the "Show Original button calls RevertTranslation().
138TEST_F(TranslationInfoBarTest, RevertCalledOnButtonPress) {
139  CreateInfoBar();
140
141  EXPECT_CALL(*infobar_delegate, RevertTranslation()).Times(1);
142  [infobar_controller showOriginal:nil];
143}
144
145// Check that items in the options menu are hooked up correctly.
146TEST_F(TranslationInfoBarTest, OptionsMenuItemsHookedUp) {
147  EXPECT_CALL(*infobar_delegate, Translate())
148    .Times(0);
149
150  [infobar_controller rebuildOptionsMenu:NO];
151  NSMenu* optionsMenu = [infobar_controller optionsMenu];
152  NSArray* optionsMenuItems = [optionsMenu itemArray];
153
154  EXPECT_EQ(7U, [optionsMenuItems count]);
155
156  // First item is the options menu button's title, so there's no need to test
157  // that the target on that is setup correctly.
158  for (NSUInteger i = 1; i < [optionsMenuItems count]; ++i) {
159    NSMenuItem* item = [optionsMenuItems objectAtIndex:i];
160    if (![item isSeparatorItem])
161      EXPECT_EQ([item target], infobar_controller.get());
162  }
163  NSMenuItem* alwaysTranslateLanguateItem = [optionsMenuItems objectAtIndex:1];
164  NSMenuItem* neverTranslateLanguateItem = [optionsMenuItems objectAtIndex:2];
165  NSMenuItem* neverTranslateSiteItem = [optionsMenuItems objectAtIndex:3];
166  // Separator at 4.
167  NSMenuItem* reportBadLanguageItem = [optionsMenuItems objectAtIndex:5];
168  NSMenuItem* aboutTranslateItem = [optionsMenuItems objectAtIndex:6];
169
170  {
171    EXPECT_CALL(*infobar_delegate, ToggleAlwaysTranslate())
172    .Times(1);
173    [infobar_controller optionsMenuChanged:alwaysTranslateLanguateItem];
174  }
175
176  {
177    EXPECT_CALL(*infobar_delegate, ToggleLanguageBlacklist())
178    .Times(1);
179    [infobar_controller optionsMenuChanged:neverTranslateLanguateItem];
180  }
181
182  {
183    EXPECT_CALL(*infobar_delegate, ToggleSiteBlacklist())
184    .Times(1);
185    [infobar_controller optionsMenuChanged:neverTranslateSiteItem];
186  }
187
188  {
189    // Can't mock these effectively, so just check that the tag is set
190    // correctly.
191    EXPECT_EQ(IDC_TRANSLATE_REPORT_BAD_LANGUAGE_DETECTION,
192              [reportBadLanguageItem tag]);
193    EXPECT_EQ(IDC_TRANSLATE_OPTIONS_ABOUT, [aboutTranslateItem tag]);
194  }
195}
196
197// Check that selecting a new item from the "Source Language" popup in "before
198// translate" mode doesn't trigger a translation or change state.
199// http://crbug.com/36666
200TEST_F(TranslationInfoBarTest, Bug36666) {
201  EXPECT_CALL(*infobar_delegate, Translate())
202    .Times(0);
203
204  CreateInfoBar();
205  int arbitrary_index = 2;
206  [infobar_controller sourceLanguageModified:arbitrary_index];
207  EXPECT_CALL(*infobar_delegate, Translate())
208    .Times(0);
209}
210
211// Check that the infobar lays itself out correctly when instantiated in
212// each of the states.
213// http://crbug.com/36895
214TEST_F(TranslationInfoBarTest, Bug36895) {
215  EXPECT_CALL(*infobar_delegate, Translate())
216    .Times(0);
217
218  for (size_t i = 0; i < arraysize(kTranslateToolbarStates); ++i) {
219    CreateInfoBar(kTranslateToolbarStates[i]);
220    EXPECT_TRUE(
221        [infobar_controller verifyLayout]) << "Layout wrong, for state #" << i;
222  }
223}
224
225// Verify that the infobar shows the "Always translate this language" button
226// after doing 3 translations.
227TEST_F(TranslationInfoBarTest, TriggerShowAlwaysTranslateButton) {
228  TranslatePrefs translate_prefs(browser_helper_.profile()->GetPrefs());
229  translate_prefs.ResetTranslationAcceptedCount("en");
230  for (int i = 0; i < 4; ++i) {
231    translate_prefs.IncrementTranslationAcceptedCount("en");
232  }
233  CreateInfoBar(TranslateInfoBarDelegate::BEFORE_TRANSLATE);
234  BeforeTranslateInfobarController* controller =
235      (BeforeTranslateInfobarController*)infobar_controller.get();
236  EXPECT_TRUE([[controller alwaysTranslateButton] superview] !=  nil);
237  EXPECT_TRUE([[controller neverTranslateButton] superview] == nil);
238}
239
240// Verify that the infobar shows the "Never translate this language" button
241// after denying 3 translations.
242TEST_F(TranslationInfoBarTest, TriggerShowNeverTranslateButton) {
243  TranslatePrefs translate_prefs(browser_helper_.profile()->GetPrefs());
244  translate_prefs.ResetTranslationDeniedCount("en");
245  for (int i = 0; i < 4; ++i) {
246    translate_prefs.IncrementTranslationDeniedCount("en");
247  }
248  CreateInfoBar(TranslateInfoBarDelegate::BEFORE_TRANSLATE);
249  BeforeTranslateInfobarController* controller =
250      (BeforeTranslateInfobarController*)infobar_controller.get();
251  EXPECT_TRUE([[controller alwaysTranslateButton] superview] == nil);
252  EXPECT_TRUE([[controller neverTranslateButton] superview] != nil);
253}
254
255} // namespace
256