Add ICU message format support
[chromium-blink-merge.git] / ui / views / controls / styled_label_unittest.cc
blob6efcd669391128c9ff790d42cebfb94da13e6068
1 // Copyright 2013 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 <string>
7 #include "base/basictypes.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/skia/include/core/SkColor.h"
12 #include "ui/gfx/font_list.h"
13 #include "ui/views/border.h"
14 #include "ui/views/controls/link.h"
15 #include "ui/views/controls/styled_label.h"
16 #include "ui/views/controls/styled_label_listener.h"
17 #include "ui/views/test/views_test_base.h"
18 #include "ui/views/widget/widget.h"
20 using base::ASCIIToUTF16;
22 namespace views {
24 class StyledLabelTest : public ViewsTestBase, public StyledLabelListener {
25 public:
26 StyledLabelTest() {}
27 ~StyledLabelTest() override {}
29 // StyledLabelListener implementation.
30 void StyledLabelLinkClicked(const gfx::Range& range,
31 int event_flags) override {}
33 protected:
34 StyledLabel* styled() { return styled_.get(); }
36 void InitStyledLabel(const std::string& ascii_text) {
37 styled_.reset(new StyledLabel(ASCIIToUTF16(ascii_text), this));
38 styled_->set_owned_by_client();
41 int StyledLabelContentHeightForWidth(int w) {
42 return styled_->GetHeightForWidth(w) - styled_->GetInsets().height();
45 private:
46 scoped_ptr<StyledLabel> styled_;
48 DISALLOW_COPY_AND_ASSIGN(StyledLabelTest);
51 TEST_F(StyledLabelTest, NoWrapping) {
52 const std::string text("This is a test block of text");
53 InitStyledLabel(text);
54 Label label(ASCIIToUTF16(text));
55 const gfx::Size label_preferred_size = label.GetPreferredSize();
56 EXPECT_EQ(label_preferred_size.height(),
57 StyledLabelContentHeightForWidth(label_preferred_size.width() * 2));
60 TEST_F(StyledLabelTest, TrailingWhitespaceiIgnored) {
61 const std::string text("This is a test block of text ");
62 InitStyledLabel(text);
64 styled()->SetBounds(0, 0, 1000, 1000);
65 styled()->Layout();
67 ASSERT_EQ(1, styled()->child_count());
68 ASSERT_EQ(std::string(Label::kViewClassName),
69 styled()->child_at(0)->GetClassName());
70 EXPECT_EQ(ASCIIToUTF16("This is a test block of text"),
71 static_cast<Label*>(styled()->child_at(0))->text());
74 TEST_F(StyledLabelTest, RespectLeadingWhitespace) {
75 const std::string text(" This is a test block of text");
76 InitStyledLabel(text);
78 styled()->SetBounds(0, 0, 1000, 1000);
79 styled()->Layout();
81 ASSERT_EQ(1, styled()->child_count());
82 ASSERT_EQ(std::string(Label::kViewClassName),
83 styled()->child_at(0)->GetClassName());
84 EXPECT_EQ(ASCIIToUTF16(" This is a test block of text"),
85 static_cast<Label*>(styled()->child_at(0))->text());
88 TEST_F(StyledLabelTest, FirstLineNotEmptyWhenLeadingWhitespaceTooLong) {
89 const std::string text(" a");
90 InitStyledLabel(text);
92 Label label(ASCIIToUTF16(text));
93 gfx::Size label_preferred_size = label.GetPreferredSize();
95 styled()->SetBounds(0, 0, label_preferred_size.width() / 2, 1000);
96 styled()->Layout();
98 ASSERT_EQ(1, styled()->child_count());
99 ASSERT_EQ(std::string(Label::kViewClassName),
100 styled()->child_at(0)->GetClassName());
101 EXPECT_EQ(ASCIIToUTF16("a"),
102 static_cast<Label*>(styled()->child_at(0))->text());
105 TEST_F(StyledLabelTest, BasicWrapping) {
106 const std::string text("This is a test block of text");
107 InitStyledLabel(text);
108 Label label(ASCIIToUTF16(text.substr(0, text.size() * 2 / 3)));
109 gfx::Size label_preferred_size = label.GetPreferredSize();
110 EXPECT_EQ(label_preferred_size.height() * 2,
111 StyledLabelContentHeightForWidth(label_preferred_size.width()));
113 // Also respect the border.
114 styled()->SetBorder(Border::CreateEmptyBorder(3, 3, 3, 3));
115 styled()->SetBounds(
118 styled()->GetInsets().width() + label_preferred_size.width(),
119 styled()->GetInsets().height() + 2 * label_preferred_size.height());
120 styled()->Layout();
121 ASSERT_EQ(2, styled()->child_count());
122 EXPECT_EQ(3, styled()->child_at(0)->x());
123 EXPECT_EQ(3, styled()->child_at(0)->y());
124 EXPECT_EQ(styled()->height() - 3, styled()->child_at(1)->bounds().bottom());
127 TEST_F(StyledLabelTest, WrapLongWords) {
128 const std::string text("ThisIsTextAsASingleWord");
129 InitStyledLabel(text);
130 Label label(ASCIIToUTF16(text.substr(0, text.size() * 2 / 3)));
131 gfx::Size label_preferred_size = label.GetPreferredSize();
132 EXPECT_EQ(label_preferred_size.height() * 2,
133 StyledLabelContentHeightForWidth(label_preferred_size.width()));
135 styled()->SetBounds(
136 0, 0, styled()->GetInsets().width() + label_preferred_size.width(),
137 styled()->GetInsets().height() + 2 * label_preferred_size.height());
138 styled()->Layout();
140 ASSERT_EQ(2, styled()->child_count());
141 ASSERT_EQ(gfx::Point(), styled()->bounds().origin());
142 EXPECT_EQ(gfx::Point(), styled()->child_at(0)->bounds().origin());
143 EXPECT_EQ(gfx::Point(0, styled()->height() / 2),
144 styled()->child_at(1)->bounds().origin());
146 EXPECT_FALSE(static_cast<Label*>(styled()->child_at(0))->text().empty());
147 EXPECT_FALSE(static_cast<Label*>(styled()->child_at(1))->text().empty());
148 EXPECT_EQ(ASCIIToUTF16(text),
149 static_cast<Label*>(styled()->child_at(0))->text() +
150 static_cast<Label*>(styled()->child_at(1))->text());
153 TEST_F(StyledLabelTest, CreateLinks) {
154 const std::string text("This is a test block of text.");
155 InitStyledLabel(text);
157 // Without links, there should be no focus border.
158 EXPECT_TRUE(styled()->GetInsets().empty());
160 // Now let's add some links.
161 styled()->AddStyleRange(gfx::Range(0, 1),
162 StyledLabel::RangeStyleInfo::CreateForLink());
163 styled()->AddStyleRange(gfx::Range(1, 2),
164 StyledLabel::RangeStyleInfo::CreateForLink());
165 styled()->AddStyleRange(gfx::Range(10, 11),
166 StyledLabel::RangeStyleInfo::CreateForLink());
167 styled()->AddStyleRange(gfx::Range(12, 13),
168 StyledLabel::RangeStyleInfo::CreateForLink());
170 // Now there should be a focus border because there are non-empty Links.
171 EXPECT_FALSE(styled()->GetInsets().empty());
173 // Verify layout creates the right number of children.
174 styled()->SetBounds(0, 0, 1000, 1000);
175 styled()->Layout();
176 EXPECT_EQ(7, styled()->child_count());
179 TEST_F(StyledLabelTest, DontBreakLinks) {
180 const std::string text("This is a test block of text, ");
181 const std::string link_text("and this should be a link");
182 InitStyledLabel(text + link_text);
183 styled()->AddStyleRange(
184 gfx::Range(text.size(), text.size() + link_text.size()),
185 StyledLabel::RangeStyleInfo::CreateForLink());
187 Label label(ASCIIToUTF16(text + link_text.substr(0, link_text.size() / 2)));
188 gfx::Size label_preferred_size = label.GetPreferredSize();
189 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width());
190 EXPECT_EQ(label_preferred_size.height() * 2,
191 pref_height - styled()->GetInsets().height());
193 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height);
194 styled()->Layout();
195 ASSERT_EQ(2, styled()->child_count());
196 // The label has no focus border while the link (and thus overall styled
197 // label) does, so the label should be inset by the width of the focus border.
198 EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(0)->x());
199 EXPECT_EQ(0, styled()->child_at(1)->x());
202 TEST_F(StyledLabelTest, StyledRangeWithDisabledLineWrapping) {
203 const std::string text("This is a test block of text, ");
204 const std::string unbreakable_text("and this should not be broken");
205 InitStyledLabel(text + unbreakable_text);
206 StyledLabel::RangeStyleInfo style_info;
207 style_info.disable_line_wrapping = true;
208 styled()->AddStyleRange(
209 gfx::Range(text.size(), text.size() + unbreakable_text.size()),
210 style_info);
212 Label label(ASCIIToUTF16(
213 text + unbreakable_text.substr(0, unbreakable_text.size() / 2)));
214 gfx::Size label_preferred_size = label.GetPreferredSize();
215 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width());
216 EXPECT_EQ(label_preferred_size.height() * 2,
217 pref_height - styled()->GetInsets().height());
219 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height);
220 styled()->Layout();
221 ASSERT_EQ(2, styled()->child_count());
222 EXPECT_EQ(0, styled()->child_at(0)->x());
223 EXPECT_EQ(0, styled()->child_at(1)->x());
226 TEST_F(StyledLabelTest, StyledRangeUnderlined) {
227 const std::string text("This is a test block of text, ");
228 const std::string underlined_text("and this should be undelined");
229 InitStyledLabel(text + underlined_text);
230 StyledLabel::RangeStyleInfo style_info;
231 style_info.font_style = gfx::Font::UNDERLINE;
232 styled()->AddStyleRange(
233 gfx::Range(text.size(), text.size() + underlined_text.size()),
234 style_info);
236 styled()->SetBounds(0, 0, 1000, 1000);
237 styled()->Layout();
239 ASSERT_EQ(2, styled()->child_count());
240 ASSERT_EQ(std::string(Label::kViewClassName),
241 styled()->child_at(1)->GetClassName());
242 EXPECT_EQ(
243 gfx::Font::UNDERLINE,
244 static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle());
247 TEST_F(StyledLabelTest, StyledRangeBold) {
248 const std::string bold_text(
249 "This is a block of text whose style will be set to BOLD in the test");
250 const std::string text(" normal text");
251 InitStyledLabel(bold_text + text);
253 StyledLabel::RangeStyleInfo style_info;
254 style_info.font_style = gfx::Font::BOLD;
255 styled()->AddStyleRange(gfx::Range(0, bold_text.size()), style_info);
257 // Calculate the bold text width if it were a pure label view, both with bold
258 // and normal style.
259 Label label(ASCIIToUTF16(bold_text));
260 const gfx::Size normal_label_size = label.GetPreferredSize();
261 label.SetFontList(label.font_list().DeriveWithStyle(gfx::Font::BOLD));
262 const gfx::Size bold_label_size = label.GetPreferredSize();
264 ASSERT_GE(bold_label_size.width(), normal_label_size.width());
266 // Set the width so |bold_text| doesn't fit on a single line with bold style,
267 // but does with normal font style.
268 int styled_width = (normal_label_size.width() + bold_label_size.width()) / 2;
269 int pref_height = styled()->GetHeightForWidth(styled_width);
271 // Sanity check that |bold_text| with normal font style would fit on a single
272 // line in a styled label with width |styled_width|.
273 StyledLabel unstyled(ASCIIToUTF16(bold_text), this);
274 unstyled.SetBounds(0, 0, styled_width, pref_height);
275 unstyled.Layout();
276 EXPECT_EQ(1, unstyled.child_count());
278 styled()->SetBounds(0, 0, styled_width, pref_height);
279 styled()->Layout();
281 ASSERT_EQ(3, styled()->child_count());
283 // The bold text should be broken up into two parts.
284 ASSERT_EQ(std::string(Label::kViewClassName),
285 styled()->child_at(0)->GetClassName());
286 EXPECT_EQ(
287 gfx::Font::BOLD,
288 static_cast<Label*>(styled()->child_at(0))->font_list().GetFontStyle());
289 ASSERT_EQ(std::string(Label::kViewClassName),
290 styled()->child_at(1)->GetClassName());
291 EXPECT_EQ(
292 gfx::Font::BOLD,
293 static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle());
294 ASSERT_EQ(std::string(Label::kViewClassName),
295 styled()->child_at(2)->GetClassName());
296 EXPECT_EQ(
297 gfx::Font::NORMAL,
298 static_cast<Label*>(styled()->child_at(2))->font_list().GetFontStyle());
300 // The second bold part should start on a new line.
301 EXPECT_EQ(0, styled()->child_at(0)->x());
302 EXPECT_EQ(0, styled()->child_at(1)->x());
303 EXPECT_EQ(styled()->child_at(1)->bounds().right(),
304 styled()->child_at(2)->x());
307 TEST_F(StyledLabelTest, Color) {
308 const std::string text_red("RED");
309 const std::string text_link("link");
310 const std::string text("word");
311 InitStyledLabel(text_red + text_link + text);
313 StyledLabel::RangeStyleInfo style_info_red;
314 style_info_red.color = SK_ColorRED;
315 styled()->AddStyleRange(gfx::Range(0, text_red.size()), style_info_red);
317 StyledLabel::RangeStyleInfo style_info_link =
318 StyledLabel::RangeStyleInfo::CreateForLink();
319 styled()->AddStyleRange(gfx::Range(text_red.size(),
320 text_red.size() + text_link.size()),
321 style_info_link);
323 styled()->SetBounds(0, 0, 1000, 1000);
324 styled()->Layout();
326 Widget* widget = new Widget();
327 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
328 widget->Init(params);
329 View* container = new View();
330 widget->SetContentsView(container);
331 container->AddChildView(styled());
333 // Obtain the default text color for a label.
334 Label* label = new Label(ASCIIToUTF16(text));
335 container->AddChildView(label);
336 const SkColor kDefaultTextColor = label->enabled_color();
338 // Obtain the default text color for a link;
339 Link* link = new Link(ASCIIToUTF16(text_link));
340 container->AddChildView(link);
341 const SkColor kDefaultLinkColor = link->enabled_color();
343 EXPECT_EQ(SK_ColorRED,
344 static_cast<Label*>(styled()->child_at(0))->enabled_color());
345 EXPECT_EQ(kDefaultLinkColor,
346 static_cast<Label*>(styled()->child_at(1))->enabled_color());
347 EXPECT_EQ(kDefaultTextColor,
348 static_cast<Label*>(styled()->child_at(2))->enabled_color());
350 // Test adjusted color readability.
351 styled()->SetDisplayedOnBackgroundColor(SK_ColorBLACK);
352 styled()->Layout();
353 label->SetBackgroundColor(SK_ColorBLACK);
355 const SkColor kAdjustedTextColor = label->enabled_color();
356 EXPECT_NE(kAdjustedTextColor, kDefaultTextColor);
357 EXPECT_EQ(kAdjustedTextColor,
358 static_cast<Label*>(styled()->child_at(2))->enabled_color());
360 widget->CloseNow();
363 TEST_F(StyledLabelTest, StyledRangeWithTooltip) {
364 const std::string text("This is a test block of text, ");
365 const std::string tooltip_text("this should have a tooltip,");
366 const std::string normal_text(" this should not have a tooltip, ");
367 const std::string link_text("and this should be a link");
369 const size_t tooltip_start = text.size();
370 const size_t link_start =
371 text.size() + tooltip_text.size() + normal_text.size();
373 InitStyledLabel(text + tooltip_text + normal_text + link_text);
374 StyledLabel::RangeStyleInfo tooltip_style;
375 tooltip_style.tooltip = ASCIIToUTF16("tooltip");
376 styled()->AddStyleRange(
377 gfx::Range(tooltip_start, tooltip_start + tooltip_text.size()),
378 tooltip_style);
379 styled()->AddStyleRange(gfx::Range(link_start, link_start + link_text.size()),
380 StyledLabel::RangeStyleInfo::CreateForLink());
382 // Break line inside the range with the tooltip.
383 Label label(ASCIIToUTF16(
384 text + tooltip_text.substr(0, tooltip_text.size() - 3)));
385 gfx::Size label_preferred_size = label.GetPreferredSize();
386 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width());
387 EXPECT_EQ(label_preferred_size.height() * 3,
388 pref_height - styled()->GetInsets().height());
390 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height);
391 styled()->Layout();
393 EXPECT_EQ(label_preferred_size.width(), styled()->width());
395 ASSERT_EQ(5, styled()->child_count());
396 // The labels have no focus border while the link (and thus overall styled
397 // label) does, so the labels should be inset by the width of the focus
398 // border.
399 EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(0)->x());
400 EXPECT_EQ(styled()->child_at(0)->bounds().right(),
401 styled()->child_at(1)->x());
402 EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(2)->x());
403 EXPECT_EQ(styled()->child_at(2)->bounds().right(),
404 styled()->child_at(3)->x());
405 EXPECT_EQ(0, styled()->child_at(4)->x());
407 base::string16 tooltip;
408 EXPECT_TRUE(
409 styled()->child_at(1)->GetTooltipText(gfx::Point(1, 1), &tooltip));
410 EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip);
411 EXPECT_TRUE(
412 styled()->child_at(2)->GetTooltipText(gfx::Point(1, 1), &tooltip));
413 EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip);
416 TEST_F(StyledLabelTest, SetBaseFontList) {
417 const std::string text("This is a test block of text.");
418 InitStyledLabel(text);
419 std::string font_name("arial");
420 gfx::Font font(font_name, 30);
421 styled()->SetBaseFontList(gfx::FontList(font));
422 Label label(ASCIIToUTF16(text), gfx::FontList(font));
424 styled()->SetBounds(0,
426 label.GetPreferredSize().width(),
427 label.GetPreferredSize().height());
429 // Make sure we have the same sizing as a label.
430 EXPECT_EQ(label.GetPreferredSize().height(), styled()->height());
431 EXPECT_EQ(label.GetPreferredSize().width(), styled()->width());
434 TEST_F(StyledLabelTest, LineHeight) {
435 const std::string text("one");
436 InitStyledLabel(text);
437 int default_height = styled()->GetHeightForWidth(100);
438 const std::string newline_text("one\ntwo\nthree");
439 InitStyledLabel(newline_text);
440 styled()->SetLineHeight(18);
441 EXPECT_EQ(18 * 2 + default_height, styled()->GetHeightForWidth(100));
444 TEST_F(StyledLabelTest, HandleEmptyLayout) {
445 const std::string text("This is a test block of text.");
446 InitStyledLabel(text);
447 styled()->Layout();
448 EXPECT_EQ(0, styled()->child_count());
451 TEST_F(StyledLabelTest, CacheSize) {
452 const int preferred_height = 50;
453 const int preferred_width = 100;
454 const std::string text("This is a test block of text.");
455 const base::string16 another_text(base::ASCIIToUTF16(
456 "This is a test block of text. This text is much longer than previous"));
458 InitStyledLabel(text);
460 // we should be able to calculate height without any problem
461 // no controls should be created
462 int precalculated_height = styled()->GetHeightForWidth(preferred_width);
463 EXPECT_LT(0, precalculated_height);
464 EXPECT_EQ(0, styled()->child_count());
466 styled()->SetBounds(0, 0, preferred_width, preferred_height);
467 styled()->Layout();
469 // controls should be created after layout
470 // height should be the same as precalculated
471 int real_height = styled()->GetHeightForWidth(styled()->width());
472 View* first_child_after_layout = styled()->has_children() ?
473 styled()->child_at(0) : nullptr;
474 EXPECT_LT(0, styled()->child_count());
475 EXPECT_LT(0, real_height);
476 EXPECT_EQ(real_height, precalculated_height);
478 // another call to Layout should not kill and recreate all controls
479 styled()->Layout();
480 View* first_child_after_second_layout = styled()->has_children() ?
481 styled()->child_at(0) : nullptr;
482 EXPECT_EQ(first_child_after_layout, first_child_after_second_layout);
484 // if text is changed:
485 // layout should be recalculated
486 // all controls should be recreated
487 styled()->SetText(another_text);
488 int updated_height = styled()->GetHeightForWidth(styled()->width());
489 EXPECT_NE(updated_height, real_height);
490 View* first_child_after_text_update = styled()->has_children() ?
491 styled()->child_at(0) : nullptr;
492 EXPECT_NE(first_child_after_text_update, first_child_after_layout);
495 } // namespace views