blob: 624c8831310055a4f9de4e4a1922c17eedbecaf1 [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: stevensr@google.com (Ryan Stevens)
#include "net/instaweb/rewriter/public/mobilize_rewrite_filter.h"
#include "base/logging.h"
#include "net/instaweb/public/global_constants.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/rewrite_test_base.h"
#include "net/instaweb/rewriter/public/server_context.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/mock_message_handler.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/statistics.h"
#include "pagespeed/kernel/base/stdio_file_system.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/html/html_element.h"
#include "pagespeed/kernel/html/html_node.h"
#include "pagespeed/kernel/html/html_parse_test_base.h"
#include "pagespeed/kernel/html/html_writer_filter.h"
#include "pagespeed/kernel/http/content_type.h"
#include "pagespeed/kernel/http/user_agent_matcher_test_base.h"
namespace net_instaweb {
namespace {
const char kTestDataDir[] = "/net/instaweb/rewriter/testdata/";
const char kOriginal[] = "mobilize_test.html";
const char kRewritten[] = "mobilize_test_output.html";
const char kPhoneNumber[] = "16175551212";
const int64 kConversionId = 42;
const char kPhoneConversionLabel[] = "HelloWorld";
const char kMobBeaconUrl[] = "/beacon";
GoogleString Styles(bool layout_mode) {
return StrCat(
"<link rel=\"stylesheet\" href=\"/psajs/mobilize_css.0.css\">",
(layout_mode
? "<link rel=\"stylesheet\" href=\"/psajs/mobilize_layout_css.0.css\">"
: ""));
}
GoogleString HeadAndViewport(bool layout_mode) {
return StrCat(
"<meta itemprop=\"telephone\" content=\"", kPhoneNumber, "\"/>",
(layout_mode ? ("<meta name='viewport' content='width=device-width'/>"
"<script src=\"/psajs/mobilize_xhr.0.js\"></script>")
: ""));
}
} // namespace
// Base class for doing our tests. Can access MobilizeRewriteFilter's private
// API.
class MobilizeRewriteFilterTest : public RewriteTestBase {
protected:
MobilizeRewriteFilterTest() {}
void CheckExpected(const GoogleString& expected) {
PrepareWrite();
EXPECT_STREQ(expected, output_buffer_);
}
virtual void SetUp() {
RewriteTestBase::SetUp();
options()->ClearSignatureForTesting();
options()->set_mob_always(true);
options()->set_mob_phone_number(kPhoneNumber);
options()->set_mob_conversion_id(kConversionId);
options()->set_mob_phone_conversion_label(kPhoneConversionLabel);
options()->set_mob_beacon_url(kMobBeaconUrl);
options()->set_mob_layout(LayoutMode());
options()->set_mob_nav(true);
server_context()->ComputeSignature(options());
SetHtmlMimetype(); // Don't wrap scripts in <![CDATA[ ]]>
filter_.reset(new MobilizeRewriteFilter(rewrite_driver()));
}
virtual void TearDown() {
RewriteTestBase::TearDown();
}
virtual bool LayoutMode() const { return true; }
virtual bool AddBody() const { return false; }
virtual bool AddHtmlTags() const { return false; }
void CheckVariable(const char* name, int value) {
Variable* var = rewrite_driver()->statistics()->FindVariable(name);
if (var == NULL) {
CHECK(false) << "Checked for a variable that doesn't exit.";
} else {
EXPECT_EQ(value, var->Get()) << name;
}
}
// Wrappers for MobilizeRewriteFilter private API.
void FilterAddStyle(HtmlElement* element) {
filter_->AddStyle(element);
}
MobileRole::Level FilterGetMobileRole(HtmlElement* element) {
return filter_->GetMobileRole(element);
}
void FilterSetAddedProgress(bool added) {
filter_->added_progress_ = added;
}
GoogleString ScriptsAtEndOfBody(StringPiece bg_color,
StringPiece fg_color) const {
return StrCat(
"<script src=\"/psajs/mobilize.0.js\"></script>"
"<script>window.psDebugMode=false;window.psNavMode=true;"
"window.psLabeledMode=false;window.psConfigMode=false;"
"window.psLayoutMode=",
BoolToString(LayoutMode()),
";window.psStaticJs=false;"
"window.psDeviceType='mobile';",
"window.psConversionId='", Integer64ToString(kConversionId),
"';window.psPhoneNumber='", kPhoneNumber,
"';window.psPhoneConversionLabel='", kPhoneConversionLabel,
"';window.psMobBackgroundColor=", bg_color,
";window.psMobForegroundColor=", fg_color, ";window.psMobBeaconUrl='",
kMobBeaconUrl, "';psStartMobilization();</script>");
}
GoogleString ScriptsAtEndOfBody() const {
return ScriptsAtEndOfBody("null", "null");
}
GoogleString Spacer() const {
return "<header id=\"psmob-header-bar\" class=\"psmob-hide\"></header>"
"<div id=\"psmob-spacer\"></div>";
}
GoogleString Scrim() const {
return
"<div id=\"ps-progress-scrim\" class=\"psProgressScrim\">"
"<a href=\"javascript:psRemoveProgressBar();\""
" id=\"ps-progress-remove\" id=\"ps-progress-show-log\">"
"Remove Progress Bar (doesn't stop mobilization)</a><br>"
"<a href=\"javascript:psSetDebugMode();\">"
"Show Debug Log In Progress Bar</a>"
"<div class=\"psProgressBar\">"
"<span id=\"ps-progress-span\" class=\"psProgressSpan\"></span></div>"
"<pre id=\"ps-progress-log\" class=\"psProgressLog\"/></div>";
}
scoped_ptr<MobilizeRewriteFilter> filter_;
private:
void PrepareWrite() {
SetupWriter();
html_parse()->ApplyFilter(html_writer_filter_.get());
}
DISALLOW_COPY_AND_ASSIGN(MobilizeRewriteFilterTest);
};
namespace {
// For testing private functions in isolation.
class MobilizeRewriteUnitTest : public MobilizeRewriteFilterTest {
protected:
MobilizeRewriteUnitTest() {}
virtual void SetUp() {
MobilizeRewriteFilterTest::SetUp();
static const char kUrl[] = "http://mob.rewrite.test/test.html";
ASSERT_TRUE(html_parse()->StartParse(kUrl));
}
virtual void TearDown() {
html_parse()->FinishParse();
MobilizeRewriteFilterTest::TearDown();
}
private:
DISALLOW_COPY_AND_ASSIGN(MobilizeRewriteUnitTest);
};
TEST_F(MobilizeRewriteUnitTest, AddStyle) {
HtmlElement* head = html_parse()->NewElement(NULL, HtmlName::kHead);
html_parse()->InsertNodeBeforeCurrent(head);
HtmlCharactersNode* content = html_parse()->NewCharactersNode(head, "123");
html_parse()->AppendChild(head, content);
CheckExpected("<head>123</head>");
FilterAddStyle(head);
CheckExpected(StrCat("<head>123", Styles(LayoutMode()), "</head>"));
}
TEST_F(MobilizeRewriteUnitTest, MobileRoleAttribute) {
HtmlElement* div = html_parse()->NewElement(NULL, HtmlName::kDiv);
html_parse()->AddAttribute(div, "data-mobile-role", "navigational");
// Add the new node to the parse tree so it will be deleted.
html_parse()->InsertNodeBeforeCurrent(div);
EXPECT_EQ(MobileRole::kNavigational,
FilterGetMobileRole(div));
}
TEST_F(MobilizeRewriteUnitTest, InvalidMobileRoleAttribute) {
HtmlElement* div = html_parse()->NewElement(NULL, HtmlName::kDiv);
html_parse()->AddAttribute(div, "data-mobile-role", "garbage");
// Add the new node to the parse tree so it will be deleted.
html_parse()->InsertNodeBeforeCurrent(div);
EXPECT_EQ(MobileRole::kInvalid,
FilterGetMobileRole(div));
}
TEST_F(MobilizeRewriteUnitTest, KeeperMobileRoleAttribute) {
HtmlElement* script = html_parse()->NewElement(NULL, HtmlName::kScript);
// Add the new node to the parse tree so it will be deleted.
html_parse()->InsertNodeBeforeCurrent(script);
EXPECT_EQ(MobileRole::kKeeper,
FilterGetMobileRole(script));
}
class MobilizeRewriteFunctionalTest : public MobilizeRewriteFilterTest {
protected:
MobilizeRewriteFunctionalTest() {}
virtual void SetUp() {
MobilizeRewriteFilterTest::SetUp();
rewrite_driver()->AppendUnownedPreRenderFilter(filter_.get());
// By default we *don't* add the progress bar scrim. This explicitly gets
// overridden in subclasses.
FilterSetAddedProgress(true);
SetCurrentUserAgent(
UserAgentMatcherTestBase::kAndroidChrome21UserAgent);
}
void HeadTest(const char* name,
StringPiece original_head, StringPiece expected_mid_head,
int deleted_elements, int keeper_blocks) {
GoogleString original =
StrCat("<head>\n", original_head, "\n</head>", Body());
GoogleString expected =
StrCat("<head>", HeadAndViewport(LayoutMode()), "\n", expected_mid_head,
"\n", Styles(LayoutMode()), "</head>", ExpectedBody());
ValidateExpected(name, original, expected);
CheckVariable(MobilizeRewriteFilter::kPagesMobilized, 1);
CheckVariable(MobilizeRewriteFilter::kKeeperBlocks, keeper_blocks);
CheckVariable(MobilizeRewriteFilter::kHeaderBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kNavigationalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kContentBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kMarginalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kDeletedElements, deleted_elements);
}
void BodyTest(const char* name,
StringPiece original_body, StringPiece expected_mid_body) {
// TODO(jmaessen): We should inject a head in these cases, possibly by
// requiring AddHeadFilter to run. We should also deal with the complete
// absence of a body tag.
GoogleString original =
StrCat("\n<body>\n", original_body, "\n</body>\n");
GoogleString expected =
StrCat("\n<body>", Spacer(), "\n", expected_mid_body, "\n",
ScriptsAtEndOfBody(), "</body>\n");
ValidateExpected(name, original, expected);
CheckVariable(MobilizeRewriteFilter::kPagesMobilized, 1);
}
void BodyUnchanged(const char* name, StringPiece body) {
BodyTest(name, body, body);
}
void KeeperTagsTest(const char* name, GoogleString keeper) {
BodyUnchanged(name, keeper);
CheckVariable(MobilizeRewriteFilter::kKeeperBlocks, 1);
CheckVariable(MobilizeRewriteFilter::kHeaderBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kNavigationalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kContentBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kMarginalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kDeletedElements, 0);
}
void TwoBodysTest(const char* name,
StringPiece first_body, StringPiece second_body) {
GoogleString original =
StrCat("\n<body>\n", first_body,
"\n</body>\n<body>\n", second_body, "\n</body>\n");
GoogleString expected =
StrCat("\n<body>", Spacer(), "\n", first_body, "\n</body>\n<body>\n",
second_body, "\n", ScriptsAtEndOfBody(), "</body>\n");
ValidateExpected(name, original, expected);
CheckVariable(MobilizeRewriteFilter::kPagesMobilized, 1);
}
GoogleString Body() const {
return "\n<body>\nhello, world!\n</body>\n";
}
GoogleString ExpectedBody() const {
return StrCat("\n<body>", Spacer(), "\nhello, world!\n",
ScriptsAtEndOfBody(), "</body>\n");
}
GoogleString ExpectedBody(StringPiece bg_color, StringPiece fg_color) const {
return StrCat("\n<body>", Spacer(), "\nhello, world!\n",
ScriptsAtEndOfBody(bg_color, fg_color), "</body>\n");
}
private:
DISALLOW_COPY_AND_ASSIGN(MobilizeRewriteFunctionalTest);
};
TEST_F(MobilizeRewriteFunctionalTest, AddStyleAndViewport) {
HeadTest("add_style_and_viewport", "", "", 0, 0);
}
TEST_F(MobilizeRewriteFunctionalTest, RemoveExistingViewport) {
HeadTest("remove_existing_viewport",
"<meta name='viewport' content='value' />", "", 1, 0);
}
TEST_F(MobilizeRewriteFunctionalTest, RemoveExistingViewportThatMatches) {
HeadTest("remove_existing_viewport",
"<meta name='viewport' content='width=device-width'/>", "", 1, 0);
}
TEST_F(MobilizeRewriteFunctionalTest, HeadUnmodified) {
const char kHeadTags[] =
"<meta name='keywords' content='cool,stuff'/>"
"<style>abcd</style>";
HeadTest("head_unmodified", kHeadTags, kHeadTags, 0, 1);
}
TEST_F(MobilizeRewriteFunctionalTest, HeadLinksUnmodified) {
const char kLink[] =
"<link rel='stylesheet' type='text/css' href='theme.css'>";
HeadTest("head_unmodified", kLink, kLink, 0, 1);
}
TEST_F(MobilizeRewriteFunctionalTest, EmptyBody) {
GoogleString expected = StrCat("<body>", Spacer(), ScriptsAtEndOfBody(),
"</body>");
ValidateExpected("empty_body",
"<body></body>", expected);
CheckVariable(MobilizeRewriteFilter::kPagesMobilized, 1);
CheckVariable(MobilizeRewriteFilter::kKeeperBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kHeaderBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kNavigationalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kContentBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kMarginalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kDeletedElements, 0);
}
TEST_F(MobilizeRewriteFunctionalTest, EmptyBodyWithProgress) {
FilterSetAddedProgress(false);
GoogleString expected = StrCat(
"<body>",
Spacer(),
"<div id=\"ps-progress-scrim\" class=\"psProgressScrim\">"
"<a href=\"javascript:psRemoveProgressBar();\" id=\"ps-progress-remove\""
" id=\"ps-progress-show-log\">Remove Progress Bar"
" (doesn't stop mobilization)</a><br>"
"<a href=\"javascript:psSetDebugMode();\">"
"Show Debug Log In Progress Bar</a>"
"<div class=\"psProgressBar\">"
"<span id=\"ps-progress-span\" class=\"psProgressSpan\"></span>"
"</div><pre id=\"ps-progress-log\" class=\"psProgressLog\"/></div>",
ScriptsAtEndOfBody(), "</body>");
ValidateExpected("empty_body_with_progress",
"<body></body>", expected);
CheckVariable(MobilizeRewriteFilter::kPagesMobilized, 1);
CheckVariable(MobilizeRewriteFilter::kKeeperBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kHeaderBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kNavigationalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kContentBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kMarginalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kDeletedElements, 0);
}
TEST_F(MobilizeRewriteFunctionalTest, MapTagsUnmodified) {
KeeperTagsTest("map_tags_unmodified",
"<map name='planetmap'><area shape='rect'"
" coords='0,0,82,126' alt='Sun'></map>");
}
TEST_F(MobilizeRewriteFunctionalTest, ScriptTagsUnmodified) {
KeeperTagsTest("script_tags_unmodified",
"<script>document.getElementById('demo')."
"innerHTML = 'Hello JavaScript!';</script>");
}
TEST_F(MobilizeRewriteFunctionalTest, StyleTagsUnmodified) {
KeeperTagsTest("style_tags_unmodified",
"<style>* { foo: bar; }</style>");
}
TEST_F(MobilizeRewriteFunctionalTest, UnknownMobileRole) {
// Its probably OK if the behavior resulting from having a weird
// data-mobile-role value is unexpected, as long as it doesn't crash.
BodyUnchanged(
"unknown_mobile_role",
"<div data-mobile-role='garbage'><a>123</a></div>");
CheckVariable(MobilizeRewriteFilter::kKeeperBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kHeaderBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kNavigationalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kContentBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kMarginalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kDeletedElements, 0);
}
TEST_F(MobilizeRewriteFunctionalTest, MultipleHeads) {
// Check we only add the style and viewport tag once.
const char kRestOfHeads[] = "</head><head></head>";
GoogleString original = StrCat("<head>", kRestOfHeads);
GoogleString expected =
StrCat("<head>", HeadAndViewport(LayoutMode()), Styles(LayoutMode()),
kRestOfHeads, ScriptsAtEndOfBody());
ValidateExpected("multiple_heads", original, expected);
CheckVariable(MobilizeRewriteFilter::kPagesMobilized, 1);
CheckVariable(MobilizeRewriteFilter::kKeeperBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kHeaderBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kNavigationalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kContentBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kMarginalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kDeletedElements, 0);
}
TEST_F(MobilizeRewriteFunctionalTest, MultipleBodys) {
// Each body should be handled as its own unit.
TwoBodysTest("multiple_bodys", "", "");
CheckVariable(MobilizeRewriteFilter::kKeeperBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kHeaderBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kNavigationalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kContentBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kMarginalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kDeletedElements, 0);
}
TEST_F(MobilizeRewriteFunctionalTest, MultipleBodysWithContent) {
TwoBodysTest(
"multiple_bodys_with_content",
"123<div data-mobile-role='marginal'>567</div>",
"<div data-mobile-role='content'>890</div>"
"<div data-mobile-role='header'>abc</div>");
CheckVariable(MobilizeRewriteFilter::kKeeperBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kHeaderBlocks, 1);
CheckVariable(MobilizeRewriteFilter::kNavigationalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kContentBlocks, 1);
CheckVariable(MobilizeRewriteFilter::kMarginalBlocks, 1);
CheckVariable(MobilizeRewriteFilter::kDeletedElements, 0);
}
TEST_F(MobilizeRewriteFunctionalTest, HeaderWithinBody) {
BodyUnchanged(
"header_within_body",
"<div data-mobile-role='content'>123<div data-mobile-role='header'>"
"456</div>789</div>");
CheckVariable(MobilizeRewriteFilter::kKeeperBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kHeaderBlocks, 1);
CheckVariable(MobilizeRewriteFilter::kNavigationalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kContentBlocks, 1);
CheckVariable(MobilizeRewriteFilter::kMarginalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kDeletedElements, 0);
}
TEST_F(MobilizeRewriteFunctionalTest, HeaderWithinHeader) {
// Note: this should occur primarily as a result of a nested HTML5 tag, as the
// labeler should not label children with the parent's label.
BodyUnchanged(
"header_within_header",
"<div data-mobile-role='header'>123<div data-mobile-role='header'>"
"456</div>789</div>");
CheckVariable(MobilizeRewriteFilter::kKeeperBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kHeaderBlocks, 2);
CheckVariable(MobilizeRewriteFilter::kNavigationalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kContentBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kMarginalBlocks, 0);
CheckVariable(MobilizeRewriteFilter::kDeletedElements, 0);
}
class MobilizeRewriteThemeTest : public MobilizeRewriteFunctionalTest {
protected:
virtual bool LayoutMode() const { return false; }
};
TEST_F(MobilizeRewriteThemeTest, ConfigureTheme) {
options()->ClearSignatureForTesting();
ASSERT_EQ(RewriteOptions::kOptionOk,
options()->SetOptionFromName(RewriteOptions::kMobTheme,
"#ff0000 #0000ff"));
GoogleString original = StrCat("<head></head>", Body());
GoogleString expected = StrCat(
"<head>", HeadAndViewport(false /* layout_mode */), Styles(LayoutMode()),
"</head>", ExpectedBody("[255,0,0]", "[0,0,255]"));
ValidateExpected("ConfigureTheme", original, expected);
ASSERT_EQ(RewriteOptions::kOptionOk,
options()->SetOptionFromName(RewriteOptions::kMobTheme,
"#ff0000 #0000ff http://logo.com"));
expected = StrCat("<head>", HeadAndViewport(false /* layout_mode*/),
Styles(LayoutMode()), "</head>",
ExpectedBody("[255,0,0]", "[0,0,255]"));
ValidateExpected("ConfigureTheme2", original, expected);
}
TEST_F(MobilizeRewriteThemeTest, PreComputeTheme) {
options()->ClearSignatureForTesting();
options()->EnableFilter(RewriteOptions::kMobilizePrecompute);
GoogleString original = StrCat("<head></head>", Body());
GoogleString expected =
StrCat("<head>", HeadAndViewport(false /* layout_mode */),
Styles(LayoutMode()), "</head>", ExpectedBody());
ValidateExpected("Precompute", original, expected);
}
// Check we are called correctly from the driver.
class MobilizeRewriteEndToEndTest : public MobilizeRewriteFilterTest {
protected:
MobilizeRewriteEndToEndTest() {}
virtual void SetUp() {
RewriteTestBase::SetUp();
SetHtmlMimetype(); // Don't wrap scripts in <![CDATA[ ]]>
options()->ClearSignatureForTesting();
options()->set_mob_phone_number(kPhoneNumber);
options()->set_mob_conversion_id(kConversionId);
options()->set_mob_phone_conversion_label(kPhoneConversionLabel);
options()->set_mob_beacon_url(kMobBeaconUrl);
options()->set_mob_layout(false);
options()->set_mob_nav(true);
}
virtual bool AddBody() const { return false; }
virtual bool AddHtmlTags() const { return false; }
virtual bool LayoutMode() const { return options()->mob_layout(); }
void Layout(bool layout) {
options()->set_mob_layout(layout);
AddFilter(RewriteOptions::kMobilize);
}
void ValidateWithUA(StringPiece test_name, StringPiece user_agent,
StringPiece input, StringPiece expected) {
SetCurrentUserAgent(user_agent);
// We need to add the input to our fetcher so the
// menu extractor can see it.
GoogleString url = StrCat(kTestDomain, test_name, ".html");
SetResponseWithDefaultHeaders(url, kContentTypeHtml, input, 1000);
ValidateExpected(test_name, input, expected);
}
GoogleString NoScriptRedirect(StringPiece test_name) const {
GoogleString url = StrCat(kTestDomain, test_name,
".html?PageSpeed=noscript");
return StringPrintf(kNoScriptRedirectFormatter, url.c_str(), url.c_str());
}
StdioFileSystem filesystem_;
private:
DISALLOW_COPY_AND_ASSIGN(MobilizeRewriteEndToEndTest);
};
TEST_F(MobilizeRewriteEndToEndTest, FullPageLayout) {
// These tests will break when the CSS is changed. Update the expected output
// accordingly.
Layout(true);
GoogleString original_buffer;
GoogleString original_filename =
StrCat(GTestSrcDir(), kTestDataDir, kOriginal);
ASSERT_TRUE(filesystem_.ReadFile(original_filename.c_str(), &original_buffer,
message_handler()));
GoogleString rewritten_buffer;
GoogleString rewritten_filename =
StrCat(GTestSrcDir(), kTestDataDir, kRewritten);
ASSERT_TRUE(filesystem_.ReadFile(rewritten_filename.c_str(),
&rewritten_buffer, message_handler()));
GlobalReplaceSubstring("@@VIEWPORT@@", "", &rewritten_buffer);
GlobalReplaceSubstring(
"@@SPACER@@",
StrCat(NoScriptRedirect("EndToEndMobileLayout"), Spacer(), Scrim()),
&rewritten_buffer);
GlobalReplaceSubstring("@@HEAD_SCRIPT_LOAD@@",
HeadAndViewport(true /* layout_mode */),
&rewritten_buffer);
GlobalReplaceSubstring("@@HEAD_STYLES@@", Styles(true), &rewritten_buffer);
GlobalReplaceSubstring("@@TRAILING_SCRIPT_LOADS@@", ScriptsAtEndOfBody(),
&rewritten_buffer);
ValidateWithUA("EndToEndMobileLayout",
UserAgentMatcherTestBase::kAndroidChrome21UserAgent,
original_buffer, rewritten_buffer);
}
TEST_F(MobilizeRewriteEndToEndTest, NonMobileLayout) {
// Don't mobilize on a non-mobile browser.
Layout(true);
GoogleString original_buffer;
GoogleString original_filename =
StrCat(GTestSrcDir(), kTestDataDir, kOriginal);
ASSERT_TRUE(filesystem_.ReadFile(original_filename.c_str(), &original_buffer,
message_handler()));
ValidateWithUA("EndToEndNonMobileLayout",
UserAgentMatcherTestBase::kChrome37UserAgent,
original_buffer, original_buffer);
}
TEST_F(MobilizeRewriteEndToEndTest, FullPage) {
Layout(false);
GoogleString original_buffer;
GoogleString original_filename =
StrCat(GTestSrcDir(), kTestDataDir, kOriginal);
ASSERT_TRUE(filesystem_.ReadFile(original_filename.c_str(), &original_buffer,
message_handler()));
GoogleString rewritten_buffer;
GoogleString rewritten_filename =
StrCat(GTestSrcDir(), kTestDataDir, kRewritten);
ASSERT_TRUE(filesystem_.ReadFile(rewritten_filename.c_str(),
&rewritten_buffer, message_handler()));
GlobalReplaceSubstring("@@VIEWPORT@@",
"<meta name=\"viewport\" content=\"width=100px;\"/>",
&rewritten_buffer);
GlobalReplaceSubstring("@@SPACER@@",
StrCat(NoScriptRedirect("EndToEndMobile"), Spacer()),
&rewritten_buffer);
GlobalReplaceSubstring("@@HEAD_SCRIPT_LOAD@@",
HeadAndViewport(false /* layout_mode */),
&rewritten_buffer);
GlobalReplaceSubstring("@@HEAD_STYLES@@", Styles(false), &rewritten_buffer);
GlobalReplaceSubstring("@@TRAILING_SCRIPT_LOADS@@", ScriptsAtEndOfBody(),
&rewritten_buffer);
ValidateWithUA("EndToEndMobile",
UserAgentMatcherTestBase::kAndroidChrome21UserAgent,
original_buffer, rewritten_buffer);
ValidateWithUA("EndToEndMobile",
UserAgentMatcherTestBase::kGoogleAdsBotMobileUserAgent,
original_buffer, rewritten_buffer);
}
TEST_F(MobilizeRewriteEndToEndTest, NonMobile) {
// Don't mobilize on a non-mobile browser.
Layout(false);
GoogleString original_buffer;
GoogleString original_filename =
StrCat(GTestSrcDir(), kTestDataDir, kOriginal);
ASSERT_TRUE(filesystem_.ReadFile(original_filename.c_str(), &original_buffer,
message_handler()));
ValidateWithUA("NonMobile",
UserAgentMatcherTestBase::kChrome37UserAgent,
original_buffer, original_buffer);
ValidateWithUA("NonMobile",
UserAgentMatcherTestBase::kGoogleAdsBotUserAgent,
original_buffer, original_buffer);
}
class MobilizeRewriteFilterNoLayoutTest : public MobilizeRewriteFunctionalTest {
protected:
virtual bool LayoutMode() const { return false; }
};
TEST_F(MobilizeRewriteFilterNoLayoutTest, AddStyleAndViewport) {
HeadTest("add_style_and_viewport", "", "", 0, 0);
}
TEST_F(MobilizeRewriteFilterNoLayoutTest, BeaconCat) {
options()->ClearSignatureForTesting();
options()->set_mob_beacon_category("'experiment2'");
server_context()->ComputeSignature(options());
Parse("beacon_cat", "<head>");
EXPECT_NE(GoogleString::npos, output_buffer_.find(
";window.psMobBeaconCategory=\'\\'experiment2\\'\';"))
<< output_buffer_;
}
} // namespace
} // namespace net_instaweb