// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <ChromeWebView/ChromeWebView.h>
#import <Foundation/Foundation.h>

#import "base/functional/bind.h"
#import "base/test/ios/wait_util.h"
#import "ios/web_view/public/cwv_global_state.h"
#import "ios/web_view/test/web_view_inttest_base.h"
#import "ios/web_view/test/web_view_test_util.h"
#import "net/base/apple/url_conversions.h"
#import "net/test/embedded_test_server/embedded_test_server.h"
#import "net/test/embedded_test_server/http_request.h"
#import "net/test/embedded_test_server/http_response.h"
#import "testing/gtest_mac.h"
#import "url/gurl.h"

using base::test::ios::kWaitForPageLoadTimeout;
using base::test::ios::WaitUntilConditionOrTimeout;

namespace ios_web_view {

// Tests public methods in CWVWebView.
//
// Note that some methods are covered by other tests in this directory.
class WebViewTest : public ios_web_view::WebViewInttestBase {
 public:
  WebViewTest() {
    test_server_->RegisterRequestHandler(base::BindRepeating(
        &WebViewTest::CaptureRequestHandler, base::Unretained(this)));
  }

  void SetUp() override {
    ios_web_view::WebViewInttestBase::SetUp();
    CWVGlobalState.sharedInstance.customUserAgent = nil;
  }

  void TearDown() override {
    ios_web_view::WebViewInttestBase::TearDown();
    CWVGlobalState.sharedInstance.customUserAgent = nil;
  }

  std::unique_ptr<net::test_server::HttpResponse> CaptureRequestHandler(
      const net::test_server::HttpRequest& request) {
    if (request.relative_url == "/CaptureRequest") {
      last_request_ = std::make_unique<net::test_server::HttpRequest>(request);
      auto http_response =
          std::make_unique<net::test_server::BasicHttpResponse>();
      http_response->set_content(
          "<html><body>CaptureRequestHTML</body></html>");
      return std::move(http_response);
    }
    return nullptr;
  }

  std::unique_ptr<net::test_server::HttpRequest> last_request_;
};

// Tests CWVWebView uses the configured custom user agent.
TEST_F(WebViewTest, CustomUserAgent) {
  ASSERT_TRUE(test_server_->Start());

  CWVGlobalState.sharedInstance.customUserAgent = @"FooCustomUserAgent";
  ASSERT_NSEQ(@"FooCustomUserAgent",
              CWVGlobalState.sharedInstance.customUserAgent);

  // Cannot use existing |web_view_| here because the change above may only
  // affect web views created after the change.
  CWVWebView* web_view = test::CreateWebView();
  GURL url = test_server_->GetURL("/CaptureRequest");
  ASSERT_TRUE(test::LoadUrl(web_view, net::NSURLWithGURL(url)));

  // Investigates the HTTP headers captured by CaptureRequestHandler(), and
  // tests that they include User-Agent HTTP header with the specified product
  // name. /echoheader?User-Agent provided by EmbeddedTestServer cannot be used
  // here because it returns content with type text/plain, but we cannot extract
  // the content using test::WaitForWebViewContainingTextOrTimeout() because
  // JavaScript cannot be executed on text/plain content.
  ASSERT_NE(nullptr, last_request_.get());
  auto user_agent_it = last_request_->headers.find("User-Agent");
  ASSERT_NE(last_request_->headers.end(), user_agent_it);
  EXPECT_EQ("FooCustomUserAgent", user_agent_it->second);
}

// Tests CWVWebView uses the configured user agent product.
TEST_F(WebViewTest, UserAgentProduct) {
  ASSERT_TRUE(test_server_->Start());

  CWVGlobalState.sharedInstance.userAgentProduct = @"MyUserAgentProduct";
  ASSERT_NSEQ(@"MyUserAgentProduct",
              CWVGlobalState.sharedInstance.userAgentProduct);

  // Cannot use existing |web_view_| here because the change above may only
  // affect web views created after the change.
  CWVWebView* web_view = test::CreateWebView();
  GURL url = test_server_->GetURL("/CaptureRequest");
  ASSERT_TRUE(test::LoadUrl(web_view, net::NSURLWithGURL(url)));

  // Investigates the HTTP headers captured by CaptureRequestHandler(), and
  // tests that they include User-Agent HTTP header with the specified product
  // name. /echoheader?User-Agent provided by EmbeddedTestServer cannot be used
  // here because it returns content with type text/plain, but we cannot extract
  // the content using test::WaitForWebViewContainingTextOrTimeout() because
  // JavaScript cannot be executed on text/plain content.
  ASSERT_NE(nullptr, last_request_.get());
  auto user_agent_it = last_request_->headers.find("User-Agent");
  ASSERT_NE(last_request_->headers.end(), user_agent_it);
  EXPECT_NE(std::string::npos,
            user_agent_it->second.find("MyUserAgentProduct"));
}

// Tests -[CWVWebView loadRequest:].
TEST_F(WebViewTest, LoadRequest) {
  ASSERT_TRUE(test_server_->Start());
  GURL url = GetUrlForPageWithTitleAndBody("Title", "Body");
  ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(url)));
  EXPECT_TRUE(test::WaitForWebViewContainingTextOrTimeout(web_view_, @"Body"));
}

// Tests -[CWVWebView reload].
TEST_F(WebViewTest, Reload) {
  ASSERT_TRUE(test_server_->Start());
  GURL url = test_server_->GetURL("/CaptureRequest");
  ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(url)));

  last_request_.reset(nullptr);
  [web_view_ reload];
  // Tests that a request has been sent again.
  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{
    return last_request_.get() != nullptr;
  }));
  EXPECT_TRUE(test::WaitForWebViewContainingTextOrTimeout(
      web_view_, @"CaptureRequestHTML"));
}

// Tests -[CWVWebView evaluateJavaScript:completionHandler:].
TEST_F(WebViewTest, EvaluateJavaScript) {
  ASSERT_TRUE(test_server_->Start());
  GURL url = GetUrlForPageWithTitleAndBody("Title", "Body");
  ASSERT_TRUE(test::LoadUrl(web_view_, net::NSURLWithGURL(url)));

  NSError* error;
  EXPECT_NSEQ(@"Body", test::EvaluateJavaScript(
                           web_view_, @"document.body.textContent", &error));
  EXPECT_FALSE(error);

  // Calls a function which doesn't exist.
  test::EvaluateJavaScript(web_view_, @"hoge()", &error);
  EXPECT_TRUE(error);
}

}  // namespace ios_web_view
