| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/devtools/devtools_ui_bindings.h" |
| |
| #include "base/feature_list.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/test/bind.h" |
| #include "chrome/browser/devtools/devtools_dispatch_http_request_params.h" |
| #include "chrome/browser/devtools/devtools_http_service_handler.h" |
| #include "chrome/browser/devtools/devtools_http_service_registry.h" |
| #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" |
| #include "chrome/browser/sync/sync_service_factory.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/signin/public/base/signin_switches.h" |
| #include "components/signin/public/identity_manager/identity_test_environment.h" |
| #include "components/sync/test/test_sync_service.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_web_contents_factory.h" |
| #include "content/public/test/url_loader_interceptor.h" |
| #include "google_apis/gaia/google_service_auth_error.h" |
| #include "net/base/net_errors.h" |
| #include "net/http/http_status_code.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::_; |
| |
| class DevToolsUIBindingsTest : public testing::Test {}; |
| |
| TEST_F(DevToolsUIBindingsTest, SanitizeFrontendURL) { |
| std::vector<std::pair<std::string, std::string>> tests = { |
| {"random-string", "devtools://devtools/"}, |
| {"http://valid.url/but/wrong", "devtools://devtools/but/wrong"}, |
| {"devtools://wrong-domain/", "devtools://devtools/"}, |
| {"devtools://devtools/bundled/devtools.html", |
| "devtools://devtools/bundled/devtools.html"}, |
| {"devtools://devtools:1234/bundled/devtools.html#hash", |
| "devtools://devtools/bundled/devtools.html#hash"}, |
| {"devtools://devtools/some/random/path", |
| "devtools://devtools/some/random/path"}, |
| {"devtools://devtools/bundled/devtools.html?debugFrontend=true", |
| "devtools://devtools/bundled/devtools.html?debugFrontend=true"}, |
| {"devtools://devtools/bundled/devtools.html" |
| "?some-flag=flag&v8only=true&debugFrontend=a" |
| "&another-flag=another-flag&can_dock=false&isSharedWorker=notreally" |
| "&remoteFrontend=sure", |
| "devtools://devtools/bundled/devtools.html" |
| "?v8only=true&debugFrontend=true" |
| "&can_dock=true&isSharedWorker=true&remoteFrontend=true"}, |
| {"devtools://devtools/?ws=any-value-is-fine", |
| "devtools://devtools/?ws=any-value-is-fine"}, |
| {"devtools://devtools/" |
| "?service-backend=ws://localhost:9222/services", |
| "devtools://devtools/" |
| "?service-backend=ws://localhost:9222/services"}, |
| {"devtools://devtools/?remoteBase=" |
| "http://example.com:1234/remote-base#hash", |
| "devtools://devtools/?remoteBase=" |
| "https://chrome-devtools-frontend.appspot.com/" |
| "serve_file//#hash"}, |
| {"devtools://devtools/?ws=1%26evil%3dtrue", |
| "devtools://devtools/?ws=1%26evil%3dtrue"}, |
| {"devtools://devtools/?ws=encoded-ok'", |
| "devtools://devtools/?ws=encoded-ok%27"}, |
| {"devtools://devtools/?remoteBase=" |
| "https://chrome-devtools-frontend.appspot.com/some/path/" |
| "@123719741873/more/path.html", |
| "devtools://devtools/?remoteBase=" |
| "https://chrome-devtools-frontend.appspot.com/serve_file/path/"}, |
| {"devtools://devtools/?remoteBase=" |
| "https://chrome-devtools-frontend.appspot.com/serve_file/" |
| "@123719741873/inspector.html%3FdebugFrontend%3Dfalse", |
| "devtools://devtools/?remoteBase=" |
| "https://chrome-devtools-frontend.appspot.com/serve_file/" |
| "@123719741873/"}, |
| {"devtools://devtools/bundled/inspector.html?" |
| "&remoteBase=https://chrome-devtools-frontend.appspot.com/serve_file/" |
| "@b4907cc5d602ff470740b2eb6344b517edecb7b9/&can_dock=true", |
| "devtools://devtools/bundled/inspector.html?" |
| "remoteBase=https://chrome-devtools-frontend.appspot.com/serve_file/" |
| "@b4907cc5d602ff470740b2eb6344b517edecb7b9/&can_dock=true"}, |
| {"devtools://devtools/?remoteFrontendUrl=" |
| "https://chrome-devtools-frontend.appspot.com/serve_rev/" |
| "@12345/inspector.html%3FdebugFrontend%3Dfalse", |
| "devtools://devtools/?remoteFrontendUrl=" |
| "https%3A%2F%2Fchrome-devtools-frontend.appspot.com%2Fserve_rev" |
| "%2F%4012345%2Finspector.html%3FdebugFrontend%3Dtrue"}, |
| {"devtools://devtools/?remoteFrontendUrl=" |
| "https://chrome-devtools-frontend.appspot.com/serve_rev/" |
| "@12345/inspector.html%22></iframe>something", |
| "devtools://devtools/?remoteFrontendUrl=" |
| "https%3A%2F%2Fchrome-devtools-frontend.appspot.com%2Fserve_rev" |
| "%2F%4012345%2Finspector.html"}, |
| {"devtools://devtools/?remoteFrontendUrl=" |
| "http://domain:1234/path/rev/a/filename.html%3Fparam%3Dvalue#hash", |
| "devtools://devtools/?remoteFrontendUrl=" |
| "https%3A%2F%2Fchrome-devtools-frontend.appspot.com%2Fserve_rev" |
| "%2Frev%2Finspector.html#hash"}, |
| {"devtools://devtools/?remoteFrontendUrl=" |
| "https://chrome-devtools-frontend.appspot.com/serve_rev/" |
| "@12345/devtools.html%3Fws%3Danyvalue" |
| "&unencoded=value&debugFrontend=true", |
| "devtools://devtools/?remoteFrontendUrl=" |
| "https%3A%2F%2Fchrome-devtools-frontend.appspot.com%2Fserve_rev" |
| "%2F%4012345%2Fdevtools.html%3Fws%3Danyvalue" |
| "&debugFrontend=true"}, |
| {"devtools://devtools/?remoteFrontendUrl=" |
| "https://chrome-devtools-frontend.appspot.com/serve_rev/" |
| "@12345/inspector.html%23%27", |
| "devtools://devtools/?remoteFrontendUrl=" |
| "https%3A%2F%2Fchrome-devtools-frontend.appspot.com%2Fserve_rev" |
| "%2F%4012345%2Finspector.html"}, |
| {"devtools://devtools/" |
| "?enabledExperiments=explosionsWhileTyping;newA11yTool", |
| "devtools://devtools/" |
| "?enabledExperiments=explosionsWhileTyping;newA11yTool"}, |
| {"devtools://devtools/?enabledExperiments=invalidExperiment$", |
| "devtools://devtools/"}, |
| }; |
| |
| for (const auto& pair : tests) { |
| GURL url = GURL(pair.first); |
| url = DevToolsUIBindings::SanitizeFrontendURL(url); |
| EXPECT_EQ(pair.second, url.spec()); |
| } |
| } |
| |
| class DevToolsUIBindingsSyncInfoTest : public testing::Test { |
| public: |
| void SetUp() override { |
| SyncServiceFactory::GetInstance()->SetTestingFactory( |
| &profile_, base::BindRepeating([](content::BrowserContext*) { |
| return static_cast<std::unique_ptr<KeyedService>>( |
| std::make_unique<syncer::TestSyncService>()); |
| })); |
| sync_service_ = static_cast<syncer::TestSyncService*>( |
| SyncServiceFactory::GetForProfile(&profile_)); |
| } |
| |
| protected: |
| content::BrowserTaskEnvironment browser_task_environment_; |
| signin::IdentityTestEnvironment identity_test_env_; |
| |
| TestingProfile profile_; |
| raw_ptr<syncer::TestSyncService> sync_service_; |
| }; |
| |
| TEST_F(DevToolsUIBindingsSyncInfoTest, SyncDisabled) { |
| sync_service_->SetSignedOut(); |
| |
| base::Value::Dict info = |
| DevToolsUIBindings::GetSyncInformationForProfile(&profile_); |
| |
| EXPECT_EQ( |
| base::FeatureList::IsEnabled(switches::kEnablePreferencesAccountStorage), |
| info.FindBool("isSyncActive").value()); |
| } |
| |
| TEST_F(DevToolsUIBindingsSyncInfoTest, PreferencesNotSynced) { |
| sync_service_->GetUserSettings()->SetSelectedTypes( |
| /*sync_everything=*/false, |
| /*types=*/{syncer::UserSelectableType::kBookmarks}); |
| |
| base::Value::Dict info = |
| DevToolsUIBindings::GetSyncInformationForProfile(&profile_); |
| |
| EXPECT_THAT(info.FindBool("isSyncActive"), testing::Optional(true)); |
| EXPECT_THAT(info.FindBool("arePreferencesSynced"), testing::Optional(false)); |
| } |
| |
| TEST_F(DevToolsUIBindingsSyncInfoTest, ImageAlwaysProvided) { |
| AccountInfo account_info = identity_test_env_.MakePrimaryAccountAvailable( |
| "sync@devtools.dev", signin::ConsentLevel::kSync); |
| sync_service_->SetSignedIn(signin::ConsentLevel::kSync, account_info); |
| |
| EXPECT_TRUE(account_info.account_image.IsEmpty()); |
| |
| base::Value::Dict info = |
| DevToolsUIBindings::GetSyncInformationForProfile(&profile_); |
| |
| EXPECT_EQ(*info.FindString("accountEmail"), "sync@devtools.dev"); |
| EXPECT_NE(info.FindString("accountImage"), nullptr); |
| } |
| |
| class MockServiceHandler : public DevToolsHttpServiceHandler { |
| public: |
| MockServiceHandler() = default; |
| ~MockServiceHandler() override = default; |
| |
| GURL BaseURL() const override { return GURL("http://localhost:8000"); } |
| signin::ScopeSet OAuthScopes() const override { return {}; } |
| net::NetworkTrafficAnnotationTag NetworkTrafficAnnotationTag() |
| const override { |
| return TRAFFIC_ANNOTATION_FOR_TESTS; |
| } |
| |
| MOCK_METHOD(void, |
| CanMakeRequest, |
| (Profile * profile, base::OnceCallback<void(bool)> callback), |
| (override)); |
| }; |
| |
| class DevToolsUIBindingsDispatchHttpRequestTest : public testing::Test { |
| public: |
| DevToolsUIBindingsDispatchHttpRequestTest() |
| : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {} |
| |
| void SetUp() override { |
| profile_ = IdentityTestEnvironmentProfileAdaptor:: |
| CreateProfileForIdentityTestEnvironment(); |
| identity_test_env_adaptor_ = |
| std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile_.get()); |
| |
| // Initialize the interceptor with a callback to our custom handler. |
| interceptor_ = |
| std::make_unique<content::URLLoaderInterceptor>(base::BindRepeating( |
| &DevToolsUIBindingsDispatchHttpRequestTest::InterceptRequest, |
| base::Unretained(this))); |
| |
| web_contents_ = web_contents_factory_.CreateWebContents(profile_.get()); |
| bindings_ = std::make_unique<DevToolsUIBindings>(web_contents_); |
| |
| auto registry = std::make_unique<DevToolsHttpServiceRegistry>(); |
| auto mock_handler = base::WrapUnique(new MockServiceHandler()); |
| mock_handler_ptr_ = mock_handler.get(); |
| registry->AddForTesting(DevToolsHttpServiceRegistry::Service( |
| "mockService", {{"/getFoo", "GET"}, {"/postBar", "POST"}}, |
| std::move(mock_handler))); |
| bindings_->SetHttpServiceRegistryForTesting(std::move(registry)); |
| } |
| |
| protected: |
| // A struct to hold response data for our interception map. |
| struct TestResponse { |
| std::string headers; |
| std::string body; |
| net::Error net_error; |
| }; |
| |
| void DispatchHttpRequest(DevToolsUIBindings::DispatchCallback callback, |
| const DevToolsDispatchHttpRequestParams& params) { |
| bindings_->DispatchHttpRequest(std::move(callback), params); |
| } |
| |
| void ExpectCanMakeRequest(bool can_make_request) { |
| EXPECT_CALL(*mock_handler_ptr_, CanMakeRequest(_, _)) |
| .WillOnce([=](Profile*, base::OnceCallback<void(bool)> callback) { |
| std::move(callback).Run(can_make_request); |
| }); |
| } |
| |
| // Helper to configure a response for a specific URL. |
| void SetResponse(const GURL& url, |
| const std::string& headers, |
| const std::string& body, |
| net::Error net_error = net::OK) { |
| response_map_[url] = {headers, body, net_error}; |
| } |
| |
| IdentityTestEnvironmentProfileAdaptor* identity_test_env_adaptor() { |
| return identity_test_env_adaptor_.get(); |
| } |
| |
| content::URLLoaderInterceptor* interceptor() { return interceptor_.get(); } |
| |
| const std::optional<network::ResourceRequest>& last_request() const { |
| return last_request_; |
| } |
| |
| private: |
| bool InterceptRequest(content::URLLoaderInterceptor::RequestParams* params) { |
| last_request_ = params->url_request; |
| const GURL& url = params->url_request.url; |
| auto it = response_map_.find(url); |
| |
| // If a specific response is not configured, use the default response. |
| if (it == response_map_.end()) { |
| content::URLLoaderInterceptor::WriteResponse("HTTP/1.1 200 OK\n", "body", |
| params->client.get()); |
| return true; |
| } |
| |
| const TestResponse& response = it->second; |
| if (response.net_error != net::OK) { |
| // Handle network errors. |
| params->client->OnComplete( |
| network::URLLoaderCompletionStatus(response.net_error)); |
| return true; |
| } |
| |
| // Handle HTTP success or error responses. |
| content::URLLoaderInterceptor::WriteResponse( |
| response.headers, response.body, params->client.get()); |
| return true; |
| } |
| |
| content::BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<IdentityTestEnvironmentProfileAdaptor> |
| identity_test_env_adaptor_; |
| std::unique_ptr<content::URLLoaderInterceptor> interceptor_; |
| std::optional<network::ResourceRequest> last_request_; |
| |
| std::unique_ptr<TestingProfile> profile_; |
| content::TestWebContentsFactory web_contents_factory_; |
| |
| raw_ptr<content::WebContents> web_contents_; |
| std::unique_ptr<DevToolsUIBindings> bindings_; |
| raw_ptr<MockServiceHandler> mock_handler_ptr_; |
| |
| std::map<GURL, TestResponse> response_map_; |
| }; |
| |
| TEST_F(DevToolsUIBindingsDispatchHttpRequestTest, |
| DispatchHttpRequestUnknownService) { |
| base::Value::Dict result; |
| DevToolsDispatchHttpRequestParams params; |
| params.service = "unknownService"; |
| params.path = "/path"; |
| params.method = "GET"; |
| DispatchHttpRequest(base::BindLambdaForTesting([&](const base::Value* value) { |
| result = value->GetDict().Clone(); |
| }), |
| params); |
| |
| EXPECT_EQ(*result.FindString("error"), "Service not found"); |
| } |
| |
| TEST_F(DevToolsUIBindingsDispatchHttpRequestTest, |
| DispatchHttpRequestDisallowedPath) { |
| base::Value::Dict result; |
| base::RunLoop run_loop; |
| DevToolsDispatchHttpRequestParams params; |
| params.service = "mockService"; |
| params.path = "/disallowedPath"; |
| params.method = "GET"; |
| DispatchHttpRequest(base::BindLambdaForTesting([&](const base::Value* value) { |
| result = value->GetDict().Clone(); |
| run_loop.Quit(); |
| }), |
| params); |
| run_loop.Run(); |
| EXPECT_EQ(*result.FindString("error"), "Disallowed path or method"); |
| } |
| |
| TEST_F(DevToolsUIBindingsDispatchHttpRequestTest, |
| DispatchHttpRequestValidationFailure) { |
| ExpectCanMakeRequest(false); |
| |
| base::RunLoop run_loop; |
| base::Value::Dict result; |
| DevToolsDispatchHttpRequestParams params; |
| params.service = "mockService"; |
| params.path = "/getFoo"; |
| params.method = "GET"; |
| DispatchHttpRequest(base::BindLambdaForTesting([&](const base::Value* value) { |
| result = value->GetDict().Clone(); |
| run_loop.Quit(); |
| }), |
| params); |
| run_loop.Run(); |
| |
| EXPECT_EQ(*result.FindString("error"), "Request validation failed"); |
| } |
| |
| TEST_F(DevToolsUIBindingsDispatchHttpRequestTest, |
| DispatchHttpRequestTokenFetchFailure) { |
| ExpectCanMakeRequest(true); |
| identity_test_env_adaptor()->identity_test_env()->MakePrimaryAccountAvailable( |
| "test@google.com", signin::ConsentLevel::kSignin); |
| base::RunLoop run_loop; |
| base::Value::Dict result; |
| DevToolsDispatchHttpRequestParams params; |
| params.service = "mockService"; |
| params.path = "/getFoo"; |
| params.method = "GET"; |
| DispatchHttpRequest(base::BindLambdaForTesting([&](const base::Value* value) { |
| result = value->GetDict().Clone(); |
| run_loop.Quit(); |
| }), |
| params); |
| |
| identity_test_env_adaptor() |
| ->identity_test_env() |
| ->WaitForAccessTokenRequestIfNecessaryAndRespondWithError( |
| GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); |
| run_loop.Run(); |
| |
| EXPECT_EQ(*result.FindString("error"), "Token fetch error"); |
| EXPECT_EQ(*result.FindString("detail"), |
| "Service unavailable; try again later."); |
| } |
| |
| TEST_F(DevToolsUIBindingsDispatchHttpRequestTest, |
| DispatchHttpRequestSuccessful) { |
| ExpectCanMakeRequest(true); |
| identity_test_env_adaptor()->identity_test_env()->MakePrimaryAccountAvailable( |
| "test@google.com", signin::ConsentLevel::kSignin); |
| |
| base::RunLoop run_loop; |
| base::Value::Dict result; |
| DevToolsDispatchHttpRequestParams params; |
| params.service = "mockService"; |
| params.path = "/getFoo"; |
| params.method = "GET"; |
| DispatchHttpRequest(base::BindLambdaForTesting([&](const base::Value* value) { |
| result = value->GetDict().Clone(); |
| run_loop.Quit(); |
| }), |
| params); |
| |
| identity_test_env_adaptor() |
| ->identity_test_env() |
| ->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( |
| "test_token", base::Time::Max()); |
| run_loop.Run(); |
| |
| net::HttpRequestHeaders headers = last_request()->headers; |
| EXPECT_EQ(last_request()->url, GURL("http://localhost:8000/getFoo")); |
| EXPECT_EQ(headers.GetHeader("Authorization"), "Bearer test_token"); |
| EXPECT_EQ(*result.FindString("response"), "body"); |
| EXPECT_EQ(*result.FindInt("statusCode"), net::HTTP_OK); |
| } |
| |
| TEST_F(DevToolsUIBindingsDispatchHttpRequestTest, |
| DispatchHttpRequestNetworkError) { |
| SetResponse(GURL("http://localhost:8000/postBar"), "", "", net::ERR_FAILED); |
| ExpectCanMakeRequest(true); |
| identity_test_env_adaptor()->identity_test_env()->MakePrimaryAccountAvailable( |
| "test@google.com", signin::ConsentLevel::kSignin); |
| |
| base::RunLoop run_loop; |
| base::Value::Dict result; |
| DevToolsDispatchHttpRequestParams params; |
| params.service = "mockService"; |
| params.path = "/postBar"; |
| params.method = "POST"; |
| params.body = "{\"foo\": \"bar\"}"; |
| DispatchHttpRequest(base::BindLambdaForTesting([&](const base::Value* value) { |
| result = value->GetDict().Clone(); |
| run_loop.Quit(); |
| }), |
| params); |
| |
| identity_test_env_adaptor() |
| ->identity_test_env() |
| ->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( |
| "test_token", base::Time::Max()); |
| run_loop.Run(); |
| |
| EXPECT_EQ(*result.FindString("error"), "Request failed"); |
| EXPECT_EQ(*result.FindInt("netError"), net::ERR_FAILED); |
| EXPECT_EQ(*result.FindInt("statusCode"), -1); |
| } |
| |
| TEST_F(DevToolsUIBindingsDispatchHttpRequestTest, |
| DispatchHttpRequestHttpError) { |
| SetResponse(GURL("http://localhost:8000/postBar"), |
| "HTTP/1.1 400 Internal Server Error\n\n", "Bad request detail"); |
| |
| ExpectCanMakeRequest(true); |
| identity_test_env_adaptor()->identity_test_env()->MakePrimaryAccountAvailable( |
| "test@google.com", signin::ConsentLevel::kSignin); |
| |
| base::RunLoop run_loop; |
| base::Value::Dict result; |
| DevToolsDispatchHttpRequestParams params; |
| params.service = "mockService"; |
| params.path = "/postBar"; |
| params.method = "POST"; |
| params.body = "{\"foo\": \"bar\"}"; |
| DispatchHttpRequest(base::BindLambdaForTesting([&](const base::Value* value) { |
| result = value->GetDict().Clone(); |
| run_loop.Quit(); |
| }), |
| params); |
| |
| identity_test_env_adaptor() |
| ->identity_test_env() |
| ->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( |
| "test_token", base::Time::Max()); |
| run_loop.Run(); |
| |
| EXPECT_EQ(*result.FindString("error"), "Request failed"); |
| EXPECT_EQ(*result.FindString("detail"), "Bad request detail"); |
| EXPECT_EQ(*result.FindInt("statusCode"), net::HTTP_BAD_REQUEST); |
| EXPECT_EQ(*result.FindInt("netError"), net::OK); |
| } |
| |
| TEST_F(DevToolsUIBindingsDispatchHttpRequestTest, DispatchHttpRequestWithBody) { |
| ExpectCanMakeRequest(true); |
| identity_test_env_adaptor()->identity_test_env()->MakePrimaryAccountAvailable( |
| "test@google.com", signin::ConsentLevel::kSignin); |
| |
| base::RunLoop run_loop; |
| base::Value::Dict result; |
| DevToolsDispatchHttpRequestParams params; |
| params.service = "mockService"; |
| params.path = "/postBar"; |
| params.method = "POST"; |
| params.body = "{\"foo\": \"bar\"}"; |
| DispatchHttpRequest(base::BindLambdaForTesting([&](const base::Value* value) { |
| result = value->GetDict().Clone(); |
| run_loop.Quit(); |
| }), |
| params); |
| |
| identity_test_env_adaptor() |
| ->identity_test_env() |
| ->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( |
| "test_token", base::Time::Max()); |
| run_loop.Run(); |
| |
| ASSERT_TRUE(last_request().has_value()); |
| ASSERT_TRUE(last_request()->request_body); |
| ASSERT_EQ(last_request()->request_body->elements()->size(), 1u); |
| const network::DataElement& element = |
| last_request()->request_body->elements()->at(0); |
| ASSERT_EQ(element.type(), network::DataElement::Tag::kBytes); |
| EXPECT_EQ(element.As<network::DataElementBytes>().AsStringPiece(), |
| "{\"foo\": \"bar\"}"); |
| } |
| |
| TEST_F(DevToolsUIBindingsDispatchHttpRequestTest, |
| DispatchHttpRequestWithoutBody) { |
| ExpectCanMakeRequest(true); |
| identity_test_env_adaptor()->identity_test_env()->MakePrimaryAccountAvailable( |
| "test@google.com", signin::ConsentLevel::kSignin); |
| |
| base::RunLoop run_loop; |
| base::Value::Dict result; |
| DevToolsDispatchHttpRequestParams params; |
| params.service = "mockService"; |
| params.path = "/getFoo"; |
| params.method = "GET"; |
| DispatchHttpRequest(base::BindLambdaForTesting([&](const base::Value* value) { |
| result = value->GetDict().Clone(); |
| run_loop.Quit(); |
| }), |
| params); |
| |
| identity_test_env_adaptor() |
| ->identity_test_env() |
| ->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( |
| "test_token", base::Time::Max()); |
| run_loop.Run(); |
| |
| ASSERT_TRUE(last_request().has_value()); |
| EXPECT_FALSE(last_request()->request_body); |
| } |
| |
| TEST_F(DevToolsUIBindingsDispatchHttpRequestTest, |
| DispatchHttpRequestWithQueryParamsSuccessful) { |
| ExpectCanMakeRequest(true); |
| identity_test_env_adaptor()->identity_test_env()->MakePrimaryAccountAvailable( |
| "test@google.com", signin::ConsentLevel::kSignin); |
| |
| base::RunLoop run_loop; |
| base::Value::Dict result; |
| DevToolsDispatchHttpRequestParams params; |
| params.service = "mockService"; |
| params.path = "/getFoo"; |
| params.method = "GET"; |
| params.query_params["q"].push_back("test/toescape"); |
| params.query_params["q"].push_back("test2"); |
| |
| DispatchHttpRequest(base::BindLambdaForTesting([&](const base::Value* value) { |
| result = value->GetDict().Clone(); |
| run_loop.Quit(); |
| }), |
| params); |
| |
| identity_test_env_adaptor() |
| ->identity_test_env() |
| ->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken( |
| "test_token", base::Time::Max()); |
| run_loop.Run(); |
| |
| EXPECT_EQ(last_request()->url, |
| GURL("http://localhost:8000/getFoo?q=test%2Ftoescape&q=test2")); |
| EXPECT_EQ(*result.FindString("response"), "body"); |
| EXPECT_EQ(*result.FindInt("statusCode"), net::HTTP_OK); |
| } |