// Copyright 2024 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/test/ios/wait_util.h"
#import "ios/web/public/test/javascript_test.h"
#import "ios/web/public/test/js_test_util.h"
#import "net/test/embedded_test_server/embedded_test_server.h"
#import "testing/gtest_mac.h"
namespace {
const char kClearWatchPageUrl[] = "/clearWatch";
const char kGetCurrentPositionPageUrl[] = "/getCurrentPosition";
const char kWatchPositionPageUrl[] = "/watchPosition";
const char kClearWatchPageHtml[] = "<html><body><script>"
"navigator.geolocation.clearWatch("
");"
"</script></body></html>";
const char kGetCurrentPositionPageHtml[] =
"<html><body><script>"
"navigator.geolocation.getCurrentPosition((position) => {});"
"</script></body></html>";
const char kWatchPositionPageHtml[] =
"<html><body><script>"
"function doNothing(arg) {}"
"watchId = navigator.geolocation.watchPosition(doNothing, doNothing, {});"
"</script></body></html>";
// Provides responses for initial page and destination URLs.
std::unique_ptr<net::test_server::HttpResponse> StandardResponse(
const net::test_server::HttpRequest& request) {
std::unique_ptr<net::test_server::BasicHttpResponse> http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
if (request.relative_url == kClearWatchPageUrl) {
http_response->set_content(kClearWatchPageHtml);
} else if (request.relative_url == kGetCurrentPositionPageUrl) {
http_response->set_content(kGetCurrentPositionPageHtml);
} else if (request.relative_url == kWatchPositionPageUrl) {
http_response->set_content(kWatchPositionPageHtml);
} else {
return nullptr;
}
return std::move(http_response);
}
} // namespace
static NSString* kMessageHandlerResponseApiKey = @"api";
static NSString* kMessageHandlerResponseApiValueClearWatch = @"clearWatch";
static NSString* kMessageHandlerResponseApiValueGetPosition =
@"getCurrentPosition";
static NSString* kMessageHandlerResponseApiValueWatch = @"watchPosition";
@interface GeolocationScriptMessageHandler : NSObject <WKScriptMessageHandler>
@property(nonatomic, strong) WKScriptMessage* lastReceivedMessage;
@end
@implementation GeolocationScriptMessageHandler
- (void)configureForWebView:(WKWebView*)webView {
[webView.configuration.userContentController
addScriptMessageHandler:self
name:@"GeolocationAPIAccessedHandler"];
}
- (void)userContentController:(WKUserContentController*)userContentController
didReceiveScriptMessage:(WKScriptMessage*)message {
self.lastReceivedMessage = message;
}
@end
// Test fixture for geolocation_overrides.ts.
class GeolocationOverridesJavaScriptTest : public web::JavascriptTest {
protected:
GeolocationOverridesJavaScriptTest()
: server_(net::EmbeddedTestServer::TYPE_HTTP),
message_handler_([[GeolocationScriptMessageHandler alloc] init]) {}
~GeolocationOverridesJavaScriptTest() override {}
void SetUp() override {
JavascriptTest::SetUp();
AddUserScript(@"geolocation_overrides");
server_.RegisterRequestHandler(base::BindRepeating(&StandardResponse));
ASSERT_TRUE(server_.Start());
[message_handler_ configureForWebView:web_view()];
}
const net::EmbeddedTestServer& server() { return server_; }
GeolocationScriptMessageHandler* message_handler() {
return message_handler_;
}
private:
net::EmbeddedTestServer server_;
GeolocationScriptMessageHandler* message_handler_;
};
// Tests that the correct message is received when the clearWatch API is called.
TEST_F(GeolocationOverridesJavaScriptTest,
GeolocationClearWatchMessageReceived) {
GURL URL = server().GetURL(kClearWatchPageUrl);
ASSERT_TRUE(LoadUrl(URL));
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForPageLoadTimeout, ^{
return message_handler().lastReceivedMessage != nil;
}));
NSDictionary* body = message_handler().lastReceivedMessage.body;
NSArray* allKeys = body.allKeys;
EXPECT_EQ(1ul, allKeys.count);
EXPECT_TRUE([allKeys containsObject:kMessageHandlerResponseApiKey]);
EXPECT_NSEQ(kMessageHandlerResponseApiValueClearWatch,
body[kMessageHandlerResponseApiKey]);
}
// Tests that the correct message is received when the getCurrentPosition API is
// called.
TEST_F(GeolocationOverridesJavaScriptTest,
GeolocationGetCurrentPositionMessageReceived) {
GURL URL = server().GetURL(kGetCurrentPositionPageUrl);
ASSERT_TRUE(LoadUrl(URL));
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForPageLoadTimeout, ^{
return message_handler().lastReceivedMessage != nil;
}));
NSDictionary* body = message_handler().lastReceivedMessage.body;
NSArray* allKeys = body.allKeys;
EXPECT_EQ(1ul, allKeys.count);
EXPECT_TRUE([allKeys containsObject:kMessageHandlerResponseApiKey]);
EXPECT_NSEQ(kMessageHandlerResponseApiValueGetPosition,
body[kMessageHandlerResponseApiKey]);
}
// Tests that the correct message is received when the watchPosition API is
// called.
TEST_F(GeolocationOverridesJavaScriptTest,
GeolocationWatchPositionMessageReceived) {
GURL URL = server().GetURL(kWatchPositionPageUrl);
ASSERT_TRUE(LoadUrl(URL));
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForPageLoadTimeout, ^{
return message_handler().lastReceivedMessage != nil;
}));
NSDictionary* body = message_handler().lastReceivedMessage.body;
NSArray* allKeys = body.allKeys;
EXPECT_EQ(1ul, allKeys.count);
EXPECT_TRUE([allKeys containsObject:kMessageHandlerResponseApiKey]);
EXPECT_NSEQ(kMessageHandlerResponseApiValueWatch,
body[kMessageHandlerResponseApiKey]);
}