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

#import "base/functional/bind.h"
#import "base/ios/ios_util.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "ios/testing/embedded_test_server_handlers.h"
#import "ios/web/common/features.h"
#import "net/test/embedded_test_server/default_handlers.h"
#import "net/test/embedded_test_server/http_request.h"
#import "net/test/embedded_test_server/http_response.h"
#import "net/test/embedded_test_server/request_handler_util.h"

namespace {
const char kPage1[] = "/page1.html";
const char kPageLinkID[] = "IDForLinkInPlatformPage";
const char kLinkPage[] = "/link.html";
const char kLinkLoaded[] = "Link Page Loaded";
const char kLinkPageLinkID[] = "linkPageIDForLink";
const char kWindowOpenJSPage[] = "/window_open.html";
const char kIFramePage[] = "/iframe.html";

// Handler for the test server. Depending of the URL of the page, it can returns
// a page displaying the platform, a page with a link to the platform page, an
// iframe, or a page opening a new page with JavaScript window.open.
std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
    const net::test_server::HttpRequest& request) {
  if (request.GetURL().GetPath() == kPage1) {
    auto result = std::make_unique<net::test_server::BasicHttpResponse>();
    result->set_content_type("text/html");
    std::string href = std::string(kLinkPage).substr(1);
    result->set_content(
        "<html><body><div><a id=\"" + std::string(kPageLinkID) + "\" href=\"" +
        href +
        "\">page with link</a></div><p id=\"platformResult\"/><script "
        "type=\"text/javascript\">"
        "document.getElementById(\"platformResult\").innerHTML = "
        "navigator.platform;</script></body></html>");
    return std::move(result);
  }
  if (request.GetURL().GetPath() == kLinkPage) {
    auto result = std::make_unique<net::test_server::BasicHttpResponse>();
    result->set_content_type("text/html");
    std::string href = std::string(kPage1).substr(1);
    result->set_content("<html><body>" + std::string(kLinkLoaded) + "<a id=\"" +
                        kLinkPageLinkID + "\" href=\"" + href +
                        "\">page with platform</a></body></html>");
    return std::move(result);
  }
  if (request.GetURL().GetPath() == kWindowOpenJSPage) {
    auto result = std::make_unique<net::test_server::BasicHttpResponse>();
    result->set_content_type("text/html");
    std::string href = std::string(kPage1).substr(1);
    result->set_content(
        "<html><body><button id=\"button\" "
        "onclick=\"openWithJS()\">button</button><script "
        "type=\"text/javascript\">function openWithJS() {window.open(\"" +
        href + "\");}</script></body></html>");
    return std::move(result);
  }
  if (request.GetURL().GetPath() == kIFramePage) {
    auto result = std::make_unique<net::test_server::BasicHttpResponse>();
    result->set_content_type("text/html");
    std::string href = std::string(kLinkPage).substr(1);
    result->set_content("<html><body><iframe src=\"" + href +
                        "\"/></body></html>");
    return std::move(result);
  }

  return nullptr;
}

// Returns the platform name of the current device.
std::string platform() {
  return [ChromeEarlGrey isMobileModeByDefault]
             ? base::SysNSStringToUTF8([[UIDevice currentDevice] model])
             : "MacIntel";
}

}  // namespace

// Test cases to make sure that the platform advertised by the navigator is the
// correct one.
@interface ContentModeTestCase : ChromeTestCase
@end

@implementation ContentModeTestCase

- (void)setUp {
  [super setUp];
  self.testServer->RegisterRequestHandler(base::BindRepeating(&HandleRequest));
  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
}

// Tests the platform when the page is directly loaded.
- (void)testPageLoad {
  [ChromeEarlGrey loadURL:self.testServer->GetURL(kPage1)];
  [ChromeEarlGrey waitForWebStateContainingText:platform()];
}

// Tests the platform after a back navigation on a loaded page.
- (void)testBackForward {
  [ChromeEarlGrey loadURL:self.testServer->GetURL(kPage1)];
  [ChromeEarlGrey waitForWebStateContainingText:platform()];

  [ChromeEarlGrey loadURL:self.testServer->GetURL(kLinkPage)];
  [ChromeEarlGrey waitForWebStateContainingText:kLinkLoaded];

  [ChromeEarlGrey goBack];
  [ChromeEarlGrey waitForWebStateContainingText:platform()];
}

// Tests the platform when the page is loaded after a rendered navigation.
- (void)testRendererLoad {
  // Load the first page and do a renderer-initialized navigation to the second
  // page.
  [ChromeEarlGrey loadURL:self.testServer->GetURL(kLinkPage)];
  [ChromeEarlGrey
      tapWebStateElementWithID:base::SysUTF8ToNSString(kLinkPageLinkID)];

  [ChromeEarlGrey waitForWebStateContainingText:platform()];
}

// Tests the platform when the page is opened after a window.open JS event.
- (void)testJSWindowOpenPage {
  [ChromeEarlGrey loadURL:self.testServer->GetURL(kWindowOpenJSPage)];
  [ChromeEarlGrey tapWebStateElementWithID:@"button"];

  [ChromeEarlGrey waitForWebStateContainingText:platform()];
}

// Tests the platform when the page is inside an iframe.
- (void)testIFrameNavigation {
  [ChromeEarlGrey loadURL:self.testServer->GetURL(kIFramePage)];
  [ChromeEarlGrey tapWebStateElementInIFrameWithID:kLinkPageLinkID];

  [ChromeEarlGrey waitForWebStateFrameContainingText:platform()];
  [ChromeEarlGrey tapWebStateElementInIFrameWithID:kPageLinkID];
  [ChromeEarlGrey waitForWebStateFrameContainingText:kLinkLoaded];

  [ChromeEarlGrey goBack];
  [ChromeEarlGrey waitForWebStateFrameContainingText:platform()];
}

@end
