blob: dbc425cb61b39523b10741142808797849e7999d [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/payments/PaymentRequest.h"
#include "bindings/core/v8/JSONValuesForV8.h"
#include "bindings/core/v8/V8BindingForTesting.h"
#include "core/dom/Document.h"
#include "core/dom/ExceptionCode.h"
#include "modules/payments/PaymentAddress.h"
#include "modules/payments/PaymentTestHelper.h"
#include "platform/heap/HeapAllocator.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
TEST(PaymentRequestTest, SecureContextRequired)
{
V8TestingScope scope;
scope.document().setSecurityOrigin(SecurityOrigin::create(KURL(KURL(), "http://www.example.com/")));
PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_TRUE(scope.getExceptionState().hadException());
EXPECT_EQ(SecurityError, scope.getExceptionState().code());
}
TEST(PaymentRequestTest, NoExceptionWithValidData)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
}
TEST(PaymentRequestTest, SupportedMethodListRequired)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentRequest::create(scope.getScriptState(), HeapVector<PaymentMethodData>(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_TRUE(scope.getExceptionState().hadException());
EXPECT_EQ(V8TypeError, scope.getExceptionState().code());
}
TEST(PaymentRequestTest, TotalRequired)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), PaymentDetails(), scope.getExceptionState());
EXPECT_TRUE(scope.getExceptionState().hadException());
EXPECT_EQ(V8TypeError, scope.getExceptionState().code());
}
TEST(PaymentRequestTest, NullShippingOptionWhenNoOptionsAvailable)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentDetails details;
details.setTotal(buildPaymentItemForTest());
PaymentOptions options;
options.setRequestShipping(true);
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), details, options, scope.getExceptionState());
EXPECT_TRUE(request->shippingOption().isNull());
}
TEST(PaymentRequestTest, NullShippingOptionWhenMultipleOptionsAvailable)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentDetails details;
details.setTotal(buildPaymentItemForTest());
details.setShippingOptions(HeapVector<PaymentShippingOption>(2, buildShippingOptionForTest()));
PaymentOptions options;
options.setRequestShipping(true);
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), details, options, scope.getExceptionState());
EXPECT_TRUE(request->shippingOption().isNull());
}
TEST(PaymentRequestTest, DontSelectSingleAvailableShippingOptionByDefault)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentDetails details;
details.setTotal(buildPaymentItemForTest());
details.setShippingOptions(HeapVector<PaymentShippingOption>(1, buildShippingOptionForTest(PaymentTestDataId, PaymentTestOverwriteValue, "standard")));
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), details, scope.getExceptionState());
EXPECT_TRUE(request->shippingOption().isNull());
}
TEST(PaymentRequestTest, DontSelectSingleAvailableShippingOptionWhenShippingNotRequested)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentDetails details;
details.setTotal(buildPaymentItemForTest());
details.setShippingOptions(HeapVector<PaymentShippingOption>(1, buildShippingOptionForTest()));
PaymentOptions options;
options.setRequestShipping(false);
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), details, options, scope.getExceptionState());
EXPECT_TRUE(request->shippingOption().isNull());
}
TEST(PaymentRequestTest, DontSelectSingleUnselectedShippingOptionWhenShippingRequested)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentDetails details;
details.setTotal(buildPaymentItemForTest());
details.setShippingOptions(HeapVector<PaymentShippingOption>(1, buildShippingOptionForTest()));
PaymentOptions options;
options.setRequestShipping(true);
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), details, options, scope.getExceptionState());
EXPECT_TRUE(request->shippingOption().isNull());
}
TEST(PaymentRequestTest, SelectSingleSelectedShippingOptionWhenShippingRequested)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentDetails details;
details.setTotal(buildPaymentItemForTest());
HeapVector<PaymentShippingOption> shippingOptions(1, buildShippingOptionForTest(PaymentTestDataId, PaymentTestOverwriteValue, "standard"));
shippingOptions[0].setSelected(true);
details.setShippingOptions(shippingOptions);
PaymentOptions options;
options.setRequestShipping(true);
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), details, options, scope.getExceptionState());
EXPECT_EQ("standard", request->shippingOption());
}
TEST(PaymentRequestTest, SelectOnlySelectedShippingOptionWhenShippingRequested)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentDetails details;
details.setTotal(buildPaymentItemForTest());
HeapVector<PaymentShippingOption> shippingOptions(2);
shippingOptions[0] = buildShippingOptionForTest(PaymentTestDataId, PaymentTestOverwriteValue, "standard");
shippingOptions[0].setSelected(true);
shippingOptions[1] = buildShippingOptionForTest(PaymentTestDataId, PaymentTestOverwriteValue, "express");
details.setShippingOptions(shippingOptions);
PaymentOptions options;
options.setRequestShipping(true);
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), details, options, scope.getExceptionState());
EXPECT_EQ("standard", request->shippingOption());
}
TEST(PaymentRequestTest, SelectLastSelectedShippingOptionWhenShippingRequested)
{
V8TestingScope scope;
makePaymentRequestOriginSecure(scope.document());
PaymentDetails details;
details.setTotal(buildPaymentItemForTest());
HeapVector<PaymentShippingOption> shippingOptions(2);
shippingOptions[0] = buildShippingOptionForTest(PaymentTestDataId, PaymentTestOverwriteValue, "standard");
shippingOptions[0].setSelected(true);
shippingOptions[1] = buildShippingOptionForTest(PaymentTestDataId, PaymentTestOverwriteValue, "express");
shippingOptions[1].setSelected(true);
details.setShippingOptions(shippingOptions);
PaymentOptions options;
options.setRequestShipping(true);
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), details, options, scope.getExceptionState());
EXPECT_EQ("express", request->shippingOption());
}
TEST(PaymentRequestTest, RejectShowPromiseOnInvalidShippingAddress)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState()).then(funcs.expectNoCall(), funcs.expectCall());
static_cast<mojom::blink::PaymentRequestClient*>(request)->OnShippingAddressChange(mojom::blink::PaymentAddress::New());
}
TEST(PaymentRequestTest, OnShippingOptionChange)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState()).then(funcs.expectNoCall(), funcs.expectNoCall());
static_cast<mojom::blink::PaymentRequestClient*>(request)->OnShippingOptionChange("standardShipping");
}
TEST(PaymentRequestTest, CannotCallShowTwice)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState());
request->show(scope.getScriptState()).then(funcs.expectNoCall(), funcs.expectCall());
}
TEST(PaymentRequestTest, CannotCallCompleteTwice)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState());
static_cast<mojom::blink::PaymentRequestClient*>(request)->OnPaymentResponse(buildPaymentResponseForTest());
request->complete(scope.getScriptState(), Fail);
request->complete(scope.getScriptState(), Success).then(funcs.expectNoCall(), funcs.expectCall());
}
TEST(PaymentRequestTest, RejectShowPromiseOnError)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState()).then(funcs.expectNoCall(), funcs.expectCall());
static_cast<mojom::blink::PaymentRequestClient*>(request)->OnError();
}
TEST(PaymentRequestTest, RejectCompletePromiseOnError)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState());
static_cast<mojom::blink::PaymentRequestClient*>(request)->OnPaymentResponse(buildPaymentResponseForTest());
request->complete(scope.getScriptState(), Success).then(funcs.expectNoCall(), funcs.expectCall());
static_cast<mojom::blink::PaymentRequestClient*>(request)->OnError();
}
TEST(PaymentRequestTest, ResolvePromiseOnComplete)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState());
static_cast<mojom::blink::PaymentRequestClient*>(request)->OnPaymentResponse(buildPaymentResponseForTest());
request->complete(scope.getScriptState(), Success).then(funcs.expectCall(), funcs.expectNoCall());
static_cast<mojom::blink::PaymentRequestClient*>(request)->OnComplete();
}
TEST(PaymentRequestTest, RejectShowPromiseOnUpdateDetailsFailure)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState()).then(funcs.expectNoCall(), funcs.expectCall());
request->onUpdatePaymentDetailsFailure(ScriptValue::from(scope.getScriptState(), "oops"));
}
TEST(PaymentRequestTest, RejectCompletePromiseOnUpdateDetailsFailure)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState()).then(funcs.expectCall(), funcs.expectNoCall());
static_cast<mojom::blink::PaymentRequestClient*>(request)->OnPaymentResponse(buildPaymentResponseForTest());
request->complete(scope.getScriptState(), Success).then(funcs.expectNoCall(), funcs.expectCall());
request->onUpdatePaymentDetailsFailure(ScriptValue::from(scope.getScriptState(), "oops"));
}
TEST(PaymentRequestTest, IgnoreUpdatePaymentDetailsAfterShowPromiseResolved)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState()).then(funcs.expectCall(), funcs.expectNoCall());
static_cast<mojom::blink::PaymentRequestClient*>(request)->OnPaymentResponse(buildPaymentResponseForTest());
request->onUpdatePaymentDetails(ScriptValue::from(scope.getScriptState(), "foo"));
}
TEST(PaymentRequestTest, RejectShowPromiseOnNonPaymentDetailsUpdate)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState()).then(funcs.expectNoCall(), funcs.expectCall());
request->onUpdatePaymentDetails(ScriptValue::from(scope.getScriptState(), "NotPaymentDetails"));
}
TEST(PaymentRequestTest, RejectShowPromiseOnInvalidPaymentDetailsUpdate)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState()).then(funcs.expectNoCall(), funcs.expectCall());
request->onUpdatePaymentDetails(ScriptValue::from(scope.getScriptState(), fromJSONString(scope.getScriptState(), "{}", scope.getExceptionState())));
EXPECT_FALSE(scope.getExceptionState().hadException());
}
TEST(PaymentRequestTest, ClearShippingOptionOnPaymentDetailsUpdateWithoutShippingOptions)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentDetails details;
details.setTotal(buildPaymentItemForTest());
PaymentOptions options;
options.setRequestShipping(true);
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), details, options, scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
EXPECT_TRUE(request->shippingOption().isNull());
request->show(scope.getScriptState()).then(funcs.expectNoCall(), funcs.expectNoCall());
String detailWithShippingOptions = "{\"total\": {\"label\": \"Total\", \"amount\": {\"currency\": \"USD\", \"value\": \"5.00\"}},"
"\"shippingOptions\": [{\"id\": \"standardShippingOption\", \"label\": \"Standard shipping\", \"amount\": {\"currency\": \"USD\", \"value\": \"5.00\"}, \"selected\": true}]}";
request->onUpdatePaymentDetails(ScriptValue::from(scope.getScriptState(), fromJSONString(scope.getScriptState(), detailWithShippingOptions, scope.getExceptionState())));
EXPECT_FALSE(scope.getExceptionState().hadException());
EXPECT_EQ("standardShippingOption", request->shippingOption());
String detailWithoutShippingOptions = "{\"total\": {\"label\": \"Total\", \"amount\": {\"currency\": \"USD\", \"value\": \"5.00\"}}}";
request->onUpdatePaymentDetails(ScriptValue::from(scope.getScriptState(), fromJSONString(scope.getScriptState(), detailWithoutShippingOptions, scope.getExceptionState())));
EXPECT_FALSE(scope.getExceptionState().hadException());
EXPECT_TRUE(request->shippingOption().isNull());
}
TEST(PaymentRequestTest, ClearShippingOptionOnPaymentDetailsUpdateWithMultipleUnselectedShippingOptions)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentOptions options;
options.setRequestShipping(true);
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), options, scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState()).then(funcs.expectNoCall(), funcs.expectNoCall());
String detail = "{\"total\": {\"label\": \"Total\", \"amount\": {\"currency\": \"USD\", \"value\": \"5.00\"}},"
"\"shippingOptions\": [{\"id\": \"slow\", \"label\": \"Slow\", \"amount\": {\"currency\": \"USD\", \"value\": \"5.00\"}},"
"{\"id\": \"fast\", \"label\": \"Fast\", \"amount\": {\"currency\": \"USD\", \"value\": \"50.00\"}}]}";
request->onUpdatePaymentDetails(ScriptValue::from(scope.getScriptState(), fromJSONString(scope.getScriptState(), detail, scope.getExceptionState())));
EXPECT_FALSE(scope.getExceptionState().hadException());
EXPECT_TRUE(request->shippingOption().isNull());
}
TEST(PaymentRequestTest, UseTheSelectedShippingOptionFromPaymentDetailsUpdate)
{
V8TestingScope scope;
PaymentRequestMockFunctionScope funcs(scope.getScriptState());
makePaymentRequestOriginSecure(scope.document());
PaymentOptions options;
options.setRequestShipping(true);
PaymentRequest* request = PaymentRequest::create(scope.getScriptState(), buildPaymentMethodDataForTest(), buildPaymentDetailsForTest(), options, scope.getExceptionState());
EXPECT_FALSE(scope.getExceptionState().hadException());
request->show(scope.getScriptState()).then(funcs.expectNoCall(), funcs.expectNoCall());
String detail = "{\"total\": {\"label\": \"Total\", \"amount\": {\"currency\": \"USD\", \"value\": \"5.00\"}},"
"\"shippingOptions\": [{\"id\": \"slow\", \"label\": \"Slow\", \"amount\": {\"currency\": \"USD\", \"value\": \"5.00\"}},"
"{\"id\": \"fast\", \"label\": \"Fast\", \"amount\": {\"currency\": \"USD\", \"value\": \"50.00\"}, \"selected\": true}]}";
request->onUpdatePaymentDetails(ScriptValue::from(scope.getScriptState(), fromJSONString(scope.getScriptState(), detail, scope.getExceptionState())));
EXPECT_FALSE(scope.getExceptionState().hadException());
EXPECT_EQ("fast", request->shippingOption());
}
} // namespace
} // namespace blink