diff --git a/src/valdi_modules/src/cpp/valdi_http/HTTPRequestManagerModuleFactory.cpp b/src/valdi_modules/src/cpp/valdi_http/HTTPRequestManagerModuleFactory.cpp index b127bbe3..35a330f9 100644 --- a/src/valdi_modules/src/cpp/valdi_http/HTTPRequestManagerModuleFactory.cpp +++ b/src/valdi_modules/src/cpp/valdi_http/HTTPRequestManagerModuleFactory.cpp @@ -96,15 +96,11 @@ Value HTTPRequestManagerModuleFactory::loadModule() { std::move(url), std::move(method), std::move(headers), std::move(convertedBody), priority), requestCompletion); - Value out; - out.setMapValue("cancel", - Value(makeShared( - [cancellable](const ValueFunctionCallContext& /*callContext*/) -> Value { - cancellable->cancel(); - return Value::undefined(); - }))); - - return out; + return Value(makeShared( + [cancellable](const ValueFunctionCallContext& /*callContext*/) -> Value { + cancellable->cancel(); + return Value::undefined(); + })); }))); return out; diff --git a/valdi/BUILD.bazel b/valdi/BUILD.bazel index 39fc87a1..92f2daf7 100644 --- a/valdi/BUILD.bazel +++ b/valdi/BUILD.bazel @@ -823,6 +823,7 @@ valdi_test( deps = [ ":test_utils", ":valdi_runtime_with_vm", + "//src/valdi_modules/src/cpp/valdi_http:valdi_http_cpp", "//src/valdi_modules/src/valdi/persistence:persistence_native", "//src/valdi_modules/src/valdi/valdi_core:valdi_core_native", "//src/valdi_modules/src/valdi/valdi_protobuf:valdi_protobuf_native", diff --git a/valdi/test/integration/HTTPClientCancel_tests.cpp b/valdi/test/integration/HTTPClientCancel_tests.cpp new file mode 100644 index 00000000..3c430fca --- /dev/null +++ b/valdi/test/integration/HTTPClientCancel_tests.cpp @@ -0,0 +1,58 @@ +#include "RequestManagerMock.hpp" +#include "valdi/runtime/Runtime.hpp" +#include "valdi_core/cpp/Utils/ByteBuffer.hpp" + +#include "JSBridgeTestFixture.hpp" +#include "RuntimeTestsUtils.hpp" +#include "gtest/gtest.h" + +using namespace Valdi; + +namespace ValdiTest { + +class HTTPClientCancelFixture : public JSBridgeTestFixture { +protected: + void SetUp() override { + wrapper = RuntimeWrapper(getJsBridge(), TSNMode::Disabled); + } + + void TearDown() override { + wrapper.teardown(); + } + + RuntimeWrapper wrapper; +}; + +// The native performRequest must return a bare cancel function (matching NativeHTTPClient.d.ts), so +// HTTPClient.ts's `() => cancelFn?.()` can call it. Before the fix the C++ factory returns a +// { cancel: fn } object, so `typeof` is "object" and calling it throws "cancel is not a function". +TEST_P(HTTPClientCancelFixture, performRequestCancelHandleIsCallable) { + auto requestManager = Valdi::makeShared(*wrapper.logger); + wrapper.runtimeManager->setRequestManager(requestManager); + // Give the request a clean completion so the mock's async queue doesn't surface a + // "No mocked response" error if it runs before the synchronous cancel() below. + requestManager->addMockedResponse(STRING_LITERAL("http://localhost/"), STRING_LITERAL("GET"), BytesView()); + + std::string js = + "var m = global.require('valdi_http/src/NativeHTTPClient');" + "var cancel = m.performRequest(" + " { url: 'http://localhost/', method: 'GET', headers: {} }, function () {});" + "var kind = typeof cancel;" + "cancel();" // nulls the mocked request's completion before the mock queue can fire it + "return kind;"; + + auto result = wrapper.runtime->getJavaScriptRuntime()->evaluateScript( + makeShared(js)->toBytesView(), STRING_LITERAL("http_cancel_test.js")); + + ASSERT_TRUE(result) << result.description(); + ASSERT_EQ("function", result.value().toString()); +} + +INSTANTIATE_TEST_SUITE_P(HTTPClientCancelTests, + HTTPClientCancelFixture, + ::testing::Values(JavaScriptEngineTestCase::Hermes, + JavaScriptEngineTestCase::QuickJS, + JavaScriptEngineTestCase::JSCore), + PrintJavaScriptEngineType()); + +} // namespace ValdiTest