mirror of
https://github.com/wp-graphql/wp-graphql-woocommerce.git
synced 2026-08-14 12:53:44 +02:00
* feat: WC Settings API, compatibility refactor, HPOS fix, CI improvements WC Settings GraphQL API: - Add WCSetting interface with common fields and type-safe resolveType - Add concrete types: WCStringSetting, WCArraySetting, WCRelativeDateSetting, WCImageWidthSetting with typed value/default fields - Add WCSettingGroup type with nested settings field - Add wcSettingGroups and wcSettings root query fields (admin only) - Add updateWCSetting and updateWCSettings mutations - Dynamic WCSettingTypeEnum collected from registered WC settings - Filters: graphql_woocommerce_setting_type_map, graphql_woocommerce_setting_types - Register WC admin settings for GraphQL requests via Compatibility class Compatibility refactor: - Consolidate ACF, JWT Auth, and QL Search filters into Compatibility class - Remove class-acf-schema-filters.php, class-jwt-auth-schema-filters.php, functions.php HPOS compatibility fix: - Replace hardcoded directory name check with dirname(__DIR__) === WP_PLUGIN_DIR - Works with any plugin folder name while still skipping nested vendor installs CI improvements: - Remove STRIPE_API_PUBLISHABLE_KEY restriction from coverage job - Add HPOS to coverage matrix entries - Sort type registry and includes alphabetically Other fixes: - Fix Settings_Mutation::validate_setting_checkbox_field to be static - Update ShippingZone settings field type to WCStringSetting Closes #864, closes #969 * refactor: Rename and split core classes, add order cursor pagination tests Class renames: - WooCommerce_Filters → WooCommerce (class-woocommerce.php), setup() → init() - Core_Schema_Filters → Post_Types (class-post-types.php) Class split: - Extract taxonomy registration from Core_Schema_Filters into Taxonomies (class-taxonomies.php) Access functions: - Add wc_graphql_resolve_product_type() for interface resolveType callbacks - Add wc_graphql_is_session_handler_disabled() - Add wc_graphql_enabled_authorizing_url_fields() - Add wc_graphql_get_authorizing_url_nonce_param_name() - Replace direct class references with global functions in type-registry, compatibility, protected-router, and all product interface files Stripe gateway compatibility: - Move woographql_stripe_gateway_args from WooCommerce to Compatibility class - Rename to woocommerce_gateway_stripe_args Tests: - Add OrderCursorPaginationTest (6 tests) covering COT cursor-based pagination: forward/backward, date ordering ASC/DESC, cursor integrity * fix: Use proper expectedField/expectedNode assertions in cursor pagination tests Replace empty assertQuerySuccessful([]) calls and manual lodashGet assertions with expectedField, expectedNode, and not()->expectedNode() for proper GraphQL response validation.
1483 lines
38 KiB
PHP
1483 lines
38 KiB
PHP
<?php
|
|
|
|
class CartMutationsTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQLTestCase {
|
|
// tests
|
|
public function testAddToCartMutationWithProduct() {
|
|
$product_id = $this->factory->product->createSimple();
|
|
|
|
$query = '
|
|
mutation( $input: AddToCartInput! ) {
|
|
addToCart( input: $input ) {
|
|
clientMutationId
|
|
cartItem {
|
|
key
|
|
product {
|
|
node {
|
|
id
|
|
}
|
|
}
|
|
variation {
|
|
node {
|
|
id
|
|
}
|
|
}
|
|
quantity
|
|
subtotal
|
|
subtotalTax
|
|
total
|
|
tax
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'productId' => $product_id,
|
|
'quantity' => 2,
|
|
],
|
|
];
|
|
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
// Confirm valid response
|
|
$this->assertResponseIsValid( $response );
|
|
|
|
// Get/validate cart item key.
|
|
$cart_item_key = $this->lodashGet( $response, 'data.addToCart.cartItem.key' );
|
|
$this->assertNotEmpty( $cart_item_key );
|
|
|
|
// Get cart item data.
|
|
$cart = \WC()->cart;
|
|
$cart_item = $cart->get_cart_item( $cart_item_key );
|
|
$this->assertNotEmpty( $cart_item );
|
|
|
|
$this->assertQuerySuccessful(
|
|
$response,
|
|
[
|
|
$this->expectedField( 'addToCart.clientMutationId', 'someId' ),
|
|
$this->expectedField( 'addToCart.cartItem.key', $cart_item_key ),
|
|
$this->expectedField( 'addToCart.cartItem.product.node.id', $this->toRelayId( 'post', $product_id ) ),
|
|
$this->expectedField( 'addToCart.cartItem.quantity', 2 ),
|
|
$this->expectedField( 'addToCart.cartItem.subtotal', wc_graphql_price( $cart_item['line_subtotal'] ) ),
|
|
$this->expectedField( 'addToCart.cartItem.subtotalTax', wc_graphql_price( $cart_item['line_subtotal_tax'] ) ),
|
|
$this->expectedField( 'addToCart.cartItem.total', wc_graphql_price( $cart_item['line_total'] ) ),
|
|
$this->expectedField( 'addToCart.cartItem.tax', wc_graphql_price( $cart_item['line_tax'] ) ),
|
|
]
|
|
);
|
|
}
|
|
|
|
public function testAddToCartMutationWithProductVariation() {
|
|
$ids = $this->factory->product_variation->createSome();
|
|
|
|
$query = '
|
|
mutation( $input: AddToCartInput! ) {
|
|
addToCart( input: $input ) {
|
|
clientMutationId
|
|
cartItem {
|
|
key
|
|
product {
|
|
node {
|
|
id
|
|
}
|
|
}
|
|
variation {
|
|
node {
|
|
id
|
|
}
|
|
}
|
|
quantity
|
|
subtotal
|
|
subtotalTax
|
|
total
|
|
tax
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'productId' => $ids['product'],
|
|
'quantity' => 3,
|
|
'variationId' => $ids['variations'][0],
|
|
'variation' => [
|
|
[
|
|
'attributeName' => 'color',
|
|
'attributeValue' => 'red',
|
|
],
|
|
],
|
|
],
|
|
];
|
|
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
// Confirm valid response
|
|
$this->assertResponseIsValid( $response );
|
|
|
|
// Get/validate cart item key.
|
|
$cart_item_key = $this->lodashGet( $response, 'data.addToCart.cartItem.key' );
|
|
$this->assertNotEmpty( $cart_item_key );
|
|
|
|
// Get cart item data.
|
|
$cart = \WC()->cart;
|
|
$cart_item = $cart->get_cart_item( $cart_item_key );
|
|
$this->assertNotEmpty( $cart_item );
|
|
|
|
$this->assertQuerySuccessful(
|
|
$response,
|
|
[
|
|
$this->expectedField( 'addToCart.clientMutationId', 'someId' ),
|
|
$this->expectedField( 'addToCart.cartItem.key', $cart_item_key ),
|
|
$this->expectedField( 'addToCart.cartItem.product.node.id', $this->toRelayId( 'post', $ids['product'] ) ),
|
|
$this->expectedField( 'addToCart.cartItem.variation.node.id', $this->toRelayId( 'post', $ids['variations'][0] ) ),
|
|
$this->expectedField( 'addToCart.cartItem.quantity', 3 ),
|
|
$this->expectedField( 'addToCart.cartItem.subtotal', wc_graphql_price( $cart_item['line_subtotal'] ) ),
|
|
$this->expectedField( 'addToCart.cartItem.subtotalTax', wc_graphql_price( $cart_item['line_subtotal_tax'] ) ),
|
|
$this->expectedField( 'addToCart.cartItem.total', wc_graphql_price( $cart_item['line_total'] ) ),
|
|
$this->expectedField( 'addToCart.cartItem.tax', wc_graphql_price( $cart_item['line_tax'] ) ),
|
|
]
|
|
);
|
|
}
|
|
|
|
public function testUpdateCartItemQuantitiesMutation() {
|
|
// Create/add some products to the cart.
|
|
$cart_item_data = [
|
|
[
|
|
'product_id' => $this->factory->product->createSimple(),
|
|
'quantity' => 2,
|
|
],
|
|
[
|
|
'product_id' => $this->factory->product->createSimple(),
|
|
'quantity' => 5,
|
|
],
|
|
[
|
|
'product_id' => $this->factory->product->createSimple(),
|
|
'quantity' => 1,
|
|
],
|
|
];
|
|
|
|
// Store cart item keys for use in mutation.
|
|
$keys = $this->factory->cart->add( ...$cart_item_data );
|
|
|
|
// Define mutation.
|
|
$query = '
|
|
mutation( $input: UpdateItemQuantitiesInput! ) {
|
|
updateItemQuantities( input: $input ) {
|
|
clientMutationId
|
|
updated {
|
|
key
|
|
quantity
|
|
}
|
|
removed {
|
|
key
|
|
quantity
|
|
}
|
|
items {
|
|
key
|
|
quantity
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
// Define variables
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'items' => [
|
|
[
|
|
'key' => $keys[0],
|
|
'quantity' => 4,
|
|
],
|
|
[
|
|
'key' => $keys[1],
|
|
'quantity' => 2,
|
|
],
|
|
[
|
|
'key' => $keys[2],
|
|
'quantity' => 0,
|
|
],
|
|
],
|
|
],
|
|
];
|
|
|
|
// Execute mutation.
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
$this->assertQuerySuccessful(
|
|
$response,
|
|
[
|
|
$this->expectedField( 'updateItemQuantities.clientMutationId', 'someId' ),
|
|
$this->expectedNode(
|
|
'updateItemQuantities.updated',
|
|
[
|
|
'key' => $keys[0],
|
|
'quantity' => 4,
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'updateItemQuantities.updated',
|
|
[
|
|
'key' => $keys[1],
|
|
'quantity' => 2,
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'updateItemQuantities.removed',
|
|
[
|
|
'key' => $keys[2],
|
|
'quantity' => 1,
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'updateItemQuantities.items',
|
|
[
|
|
'key' => $keys[0],
|
|
'quantity' => 4,
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'updateItemQuantities.items',
|
|
[
|
|
'key' => $keys[1],
|
|
'quantity' => 2,
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'updateItemQuantities.items',
|
|
[
|
|
'key' => $keys[2],
|
|
'quantity' => 1,
|
|
]
|
|
),
|
|
]
|
|
);
|
|
}
|
|
|
|
public function testRemoveItemsFromCartMutation() {
|
|
// Create/add some products to the cart.
|
|
$cart_item_data = [
|
|
[
|
|
'product_id' => $this->factory->product->createSimple(),
|
|
'quantity' => 2,
|
|
],
|
|
[
|
|
'product_id' => $this->factory->product->createSimple(),
|
|
'quantity' => 5,
|
|
],
|
|
[
|
|
'product_id' => $this->factory->product->createSimple(),
|
|
'quantity' => 1,
|
|
],
|
|
];
|
|
|
|
// Store cart item keys for use in mutation.
|
|
$keys = $this->factory->cart->add( ...$cart_item_data );
|
|
|
|
$query = '
|
|
mutation( $input: RemoveItemsFromCartInput! ) {
|
|
removeItemsFromCart( input: $input ) {
|
|
clientMutationId
|
|
cartItems {
|
|
key
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
// Define expected response data.
|
|
$expected = [ $this->expectedField( 'removeItemsFromCart.clientMutationId', 'someId' ) ];
|
|
foreach ( $keys as $key ) {
|
|
$expected[] = $this->expectedNode( 'removeItemsFromCart.cartItems', compact( 'key' ) );
|
|
}
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'keys' => $keys,
|
|
],
|
|
];
|
|
|
|
// Execute mutation w/ "keys" array.
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
|
|
// Confirm none of the items in cart.
|
|
foreach ( $keys as $key ) {
|
|
$this->assertEmpty(
|
|
\WC()->cart->get_cart_item( $key ),
|
|
"{$key} still in cart after \"removeItemsFromCart\" mutation."
|
|
);
|
|
}
|
|
|
|
// Add more items and execute mutation with "all" flag.
|
|
$keys = $this->factory->cart->add( ...$cart_item_data );
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'all' => true,
|
|
],
|
|
];
|
|
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
|
|
// Confirm none of the items in cart.
|
|
foreach ( $keys as $key ) {
|
|
$this->assertEmpty(
|
|
\WC()->cart->get_cart_item( $key ),
|
|
"{$key} still in cart after \"removeItemsFromCart\" mutation with \"all\" flag."
|
|
);
|
|
}
|
|
}
|
|
|
|
public function testRestoreCartItemsMutation() {
|
|
// Create/add some products to the cart.
|
|
$cart_item_data = [
|
|
[
|
|
'product_id' => $this->factory->product->createSimple(),
|
|
'quantity' => 2,
|
|
],
|
|
[
|
|
'product_id' => $this->factory->product->createSimple(),
|
|
'quantity' => 5,
|
|
],
|
|
[
|
|
'product_id' => $this->factory->product->createSimple(),
|
|
'quantity' => 1,
|
|
],
|
|
];
|
|
$keys = $this->factory->cart->add( ...$cart_item_data );
|
|
$this->factory->cart->remove( ...$keys );
|
|
|
|
$query = '
|
|
mutation( $input: RestoreCartItemsInput! ) {
|
|
restoreCartItems( input: $input ) {
|
|
clientMutationId
|
|
cartItems {
|
|
key
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'keys' => $keys,
|
|
],
|
|
];
|
|
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
$expected = [ $this->expectedField( 'restoreCartItems.clientMutationId', 'someId' ) ];
|
|
foreach ( $keys as $key ) {
|
|
$expected[] = $this->expectedNode( 'restoreCartItems.cartItems', compact( 'key' ) );
|
|
}
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
|
|
// Confirm items in cart.
|
|
foreach ( $keys as $key ) {
|
|
$this->assertNotEmpty(
|
|
\WC()->cart->get_cart_item( $key ),
|
|
"{$key} not found in cart after \"restoreCartItems\" mutation."
|
|
);
|
|
}
|
|
}
|
|
|
|
public function testEmptyCartMutation() {
|
|
// Create/add some products to the cart.
|
|
$product_id = $this->factory->product->createSimple();
|
|
$cart = \WC()->cart;
|
|
$cart_item_key = $cart->add_to_cart( $product_id, 1 );
|
|
$cart_item = $cart->get_cart_item( $cart_item_key );
|
|
|
|
$query = '
|
|
mutation( $input: EmptyCartInput! ) {
|
|
emptyCart( input: $input ) {
|
|
clientMutationId
|
|
deletedCart {
|
|
contents {
|
|
nodes {
|
|
key
|
|
product {
|
|
node {
|
|
id
|
|
}
|
|
}
|
|
variation {
|
|
node {
|
|
id
|
|
}
|
|
}
|
|
quantity
|
|
subtotal
|
|
subtotalTax
|
|
total
|
|
tax
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [ 'clientMutationId' => 'someId' ],
|
|
];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
$this->assertQuerySuccessful(
|
|
$response,
|
|
[
|
|
$this->expectedField( 'emptyCart.clientMutationId', 'someId' ),
|
|
$this->expectedNode(
|
|
'emptyCart.deletedCart.contents.nodes',
|
|
[
|
|
'key' => $cart_item['key'],
|
|
'product' => [
|
|
'node' => [
|
|
'id' => $this->toRelayId( 'post', $cart_item['product_id'] ),
|
|
],
|
|
],
|
|
'variation' => null,
|
|
'quantity' => $cart_item['quantity'],
|
|
'subtotal' => wc_graphql_price( $cart_item['line_subtotal'] ),
|
|
'subtotalTax' => wc_graphql_price( $cart_item['line_subtotal_tax'] ),
|
|
'total' => wc_graphql_price( $cart_item['line_total'] ),
|
|
'tax' => wc_graphql_price( $cart_item['line_tax'] ),
|
|
]
|
|
),
|
|
]
|
|
);
|
|
|
|
$this->assertTrue( \WC()->cart->is_empty() );
|
|
}
|
|
|
|
public function testApplyCouponMutation() {
|
|
// Create products.
|
|
$product_id = $this->factory->product->createSimple(
|
|
[ 'regular_price' => 100 ]
|
|
);
|
|
|
|
// Create coupon.
|
|
$coupon_code = wc_get_coupon_code_by_id(
|
|
$this->factory->coupon->create(
|
|
[
|
|
'amount' => 0.5,
|
|
'product_ids' => [ $product_id ],
|
|
'description' => 'lorem ipsum dolor',
|
|
]
|
|
)
|
|
);
|
|
|
|
// Add items to carts.
|
|
$cart = \WC()->cart;
|
|
$cart_item_key = $cart->add_to_cart( $product_id, 1 );
|
|
|
|
$old_total = \WC()->cart->get_cart_contents_total();
|
|
|
|
$query = '
|
|
mutation( $input: ApplyCouponInput! ) {
|
|
applyCoupon( input: $input ) {
|
|
clientMutationId
|
|
cart {
|
|
appliedCoupons {
|
|
code
|
|
description
|
|
}
|
|
contents {
|
|
nodes {
|
|
key
|
|
product {
|
|
node {
|
|
id
|
|
}
|
|
}
|
|
quantity
|
|
subtotal
|
|
subtotalTax
|
|
total
|
|
tax
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'code' => $coupon_code,
|
|
],
|
|
];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
// Get updated cart item.
|
|
$cart_item = $cart->get_cart_item( $cart_item_key );
|
|
|
|
$this->assertQuerySuccessful(
|
|
$response,
|
|
[
|
|
$this->expectedField( 'applyCoupon.clientMutationId', 'someId' ),
|
|
$this->expectedNode(
|
|
'applyCoupon.cart.appliedCoupons',
|
|
[
|
|
'code' => $coupon_code,
|
|
'description' => 'lorem ipsum dolor',
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'applyCoupon.cart.contents.nodes',
|
|
[
|
|
'key' => $cart_item['key'],
|
|
'product' => [
|
|
'node' => [
|
|
'id' => $this->toRelayId( 'post', $cart_item['product_id'] ),
|
|
],
|
|
],
|
|
'quantity' => $cart_item['quantity'],
|
|
'subtotal' => wc_graphql_price( $cart_item['line_subtotal'] ),
|
|
'subtotalTax' => wc_graphql_price( $cart_item['line_subtotal_tax'] ),
|
|
'total' => wc_graphql_price( $cart_item['line_total'] ),
|
|
'tax' => wc_graphql_price( $cart_item['line_tax'] ),
|
|
]
|
|
),
|
|
]
|
|
);
|
|
|
|
$new_total = \WC()->cart->get_cart_contents_total();
|
|
|
|
// Use --debug to view.
|
|
codecept_debug(
|
|
[
|
|
'old' => $old_total,
|
|
'new' => $new_total,
|
|
]
|
|
);
|
|
|
|
$this->assertTrue( $old_total > $new_total );
|
|
}
|
|
|
|
public function testApplyCouponMutationWithInvalidCoupons() {
|
|
$cart = WC()->cart;
|
|
|
|
// Create products.
|
|
$product_id = $this->factory->product->createSimple();
|
|
|
|
// Create invalid coupon codes.
|
|
$coupon_id = $this->factory->coupon->create(
|
|
[ 'product_ids' => [ $product_id ] ]
|
|
);
|
|
$expired_coupon_code = wc_get_coupon_code_by_id(
|
|
$this->factory->coupon->create(
|
|
[
|
|
'product_ids' => [ $product_id ],
|
|
'date_expires' => time() - 20,
|
|
]
|
|
)
|
|
);
|
|
$applied_coupon_code = wc_get_coupon_code_by_id(
|
|
$this->factory->coupon->create(
|
|
[ 'product_ids' => [ $product_id ] ]
|
|
)
|
|
);
|
|
|
|
// Add items to carts.
|
|
$cart_item_key = $cart->add_to_cart( $product_id, 1 );
|
|
$cart->apply_coupon( $applied_coupon_code );
|
|
|
|
$old_total = \WC()->cart->get_cart_contents_total();
|
|
|
|
$query = '
|
|
mutation( $input: ApplyCouponInput! ) {
|
|
applyCoupon( input: $input ) {
|
|
clientMutationId
|
|
}
|
|
}
|
|
';
|
|
|
|
/**
|
|
* Assertion One
|
|
*
|
|
* Can't pass coupon ID as coupon 'code'. Mutation should fail.
|
|
*/
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'code' => '$coupon_id',
|
|
],
|
|
];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
$this->assertNotEmpty( $response['errors'] );
|
|
$this->assertEmpty( $response['data']['applyCoupon'] );
|
|
|
|
/**
|
|
* Assertion Two
|
|
*
|
|
* Can't pass expired coupon code. Mutation should fail.
|
|
*/
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'code' => $expired_coupon_code,
|
|
],
|
|
];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
$this->assertNotEmpty( $response['errors'] );
|
|
$this->assertEmpty( $response['data']['applyCoupon'] );
|
|
|
|
/**
|
|
* Assertion Three
|
|
*
|
|
* Can't pass coupon already applied to the cart. Mutation should fail.
|
|
*/
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'code' => $applied_coupon_code,
|
|
],
|
|
];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
$this->assertNotEmpty( $response['errors'] );
|
|
$this->assertEmpty( $response['data']['applyCoupon'] );
|
|
|
|
$this->assertEquals( $old_total, \WC()->cart->get_cart_contents_total() );
|
|
}
|
|
|
|
public function testRemoveCouponMutation() {
|
|
// Create product and coupon.
|
|
$product_id = $this->factory->product->createSimple();
|
|
$coupon_code = wc_get_coupon_code_by_id(
|
|
$this->factory->coupon->create(
|
|
[ 'product_ids' => [ $product_id ] ]
|
|
)
|
|
);
|
|
|
|
// Add item and coupon to cart and get total..
|
|
$cart = \WC()->cart;
|
|
$cart_item_key = $cart->add_to_cart( $product_id, 3 );
|
|
$cart->apply_coupon( $coupon_code );
|
|
|
|
$query = '
|
|
mutation removeCoupons( $input: RemoveCouponsInput! ) {
|
|
removeCoupons( input: $input ) {
|
|
clientMutationId
|
|
cart {
|
|
appliedCoupons {
|
|
code
|
|
}
|
|
contents {
|
|
nodes {
|
|
key
|
|
product {
|
|
node {
|
|
id
|
|
}
|
|
}
|
|
quantity
|
|
subtotal
|
|
subtotalTax
|
|
total
|
|
tax
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'codes' => [ $coupon_code ],
|
|
],
|
|
];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
// Get updated cart item.
|
|
$cart_item = \WC()->cart->get_cart_item( $cart_item_key );
|
|
|
|
$this->assertQuerySuccessful(
|
|
$response,
|
|
[
|
|
$this->expectedField( 'removeCoupons.clientMutationId', 'someId' ),
|
|
$this->expectedField( 'removeCoupons.cart.appliedCoupons', static::IS_NULL ),
|
|
$this->expectedNode(
|
|
'removeCoupons.cart.contents.nodes',
|
|
[
|
|
'key' => $cart_item['key'],
|
|
'product' => [
|
|
'node' => [
|
|
'id' => $this->toRelayId( 'post', $cart_item['product_id'] ),
|
|
],
|
|
],
|
|
'quantity' => $cart_item['quantity'],
|
|
'subtotal' => wc_graphql_price( $cart_item['line_subtotal'] ),
|
|
'subtotalTax' => wc_graphql_price( $cart_item['line_subtotal_tax'] ),
|
|
'total' => wc_graphql_price( $cart_item['line_total'] ),
|
|
'tax' => wc_graphql_price( $cart_item['line_tax'] ),
|
|
]
|
|
),
|
|
]
|
|
);
|
|
}
|
|
public function testAddToCartMutationErrors() {
|
|
// Create products.
|
|
$product_id = $this->factory->product->createSimple(
|
|
[
|
|
'manage_stock' => true,
|
|
'stock_quantity' => 1,
|
|
]
|
|
);
|
|
$variation_ids = $this->factory->product_variation->createSome();
|
|
|
|
$product = \wc_get_product( $variation_ids['product'] );
|
|
$attribute = new WC_Product_Attribute();
|
|
$attribute->set_id( 0 );
|
|
$attribute->set_name( 'test' );
|
|
$attribute->set_options( [ 'yes', 'no' ] );
|
|
$attribute->set_position( 3 );
|
|
$attribute->set_visible( true );
|
|
$attribute->set_variation( true );
|
|
$attributes = array_values( $product->get_attributes() );
|
|
$attributes[] = $attribute;
|
|
$product->set_attributes( $attributes );
|
|
$product->save();
|
|
|
|
$query = '
|
|
mutation( $input: AddToCartInput! ) {
|
|
addToCart( input: $input ) {
|
|
clientMutationId
|
|
cartItem {
|
|
key
|
|
product {
|
|
node {
|
|
id
|
|
}
|
|
}
|
|
variation {
|
|
node {
|
|
id
|
|
}
|
|
}
|
|
quantity
|
|
subtotal
|
|
subtotalTax
|
|
total
|
|
tax
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
\WC()->session->set( 'wc_notices', null );
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'productId' => $variation_ids['product'],
|
|
'quantity' => 5,
|
|
'variationId' => $variation_ids['variations'][0],
|
|
],
|
|
];
|
|
$missing_attributes = $this->graphql( compact( 'query', 'variables' ) );
|
|
$this->assertArrayHasKey( 'errors', $missing_attributes );
|
|
|
|
\WC()->session->set( 'wc_notices', null );
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'productId' => $product_id,
|
|
'quantity' => 5,
|
|
],
|
|
];
|
|
$not_enough_stock = $this->graphql( compact( 'query', 'variables' ) );
|
|
$this->assertArrayHasKey( 'errors', $not_enough_stock );
|
|
}
|
|
|
|
public function testAddToCartMutationItemEdgeData() {
|
|
// Create variable product for later use.
|
|
$variation_ids = $this->factory->product_variation->createSome();
|
|
$product = \wc_get_product( $variation_ids['product'] );
|
|
$attribute = new WC_Product_Attribute();
|
|
$attribute->set_id( 0 );
|
|
$attribute->set_name( 'test' );
|
|
$attribute->set_options( [ 'yes', 'no' ] );
|
|
$attribute->set_position( 3 );
|
|
$attribute->set_visible( true );
|
|
$attribute->set_variation( true );
|
|
$attributes = array_values( $product->get_attributes() );
|
|
$attributes[] = $attribute;
|
|
$product->set_attributes( $attributes );
|
|
$product->save();
|
|
|
|
$query = '
|
|
mutation( $input: AddToCartInput! ) {
|
|
addToCart(input: $input) {
|
|
cartItem {
|
|
product {
|
|
simpleVariations {
|
|
name
|
|
value
|
|
}
|
|
node {
|
|
databaseId
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'productId' => $variation_ids['product'],
|
|
'quantity' => 3,
|
|
'variationId' => $variation_ids['variations'][1],
|
|
'variation' => [
|
|
[
|
|
'attributeName' => 'test',
|
|
'attributeValue' => 'yes',
|
|
],
|
|
[
|
|
'attributeName' => 'color',
|
|
'attributeValue' => 'green',
|
|
],
|
|
],
|
|
],
|
|
];
|
|
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$expected = [
|
|
$this->expectedObject(
|
|
'addToCart.cartItem.product',
|
|
[
|
|
$this->expectedField( 'node.databaseId', $variation_ids['product'] ),
|
|
$this->expectedObject(
|
|
'simpleVariations.#',
|
|
[
|
|
$this->expectedField( 'name', 'attribute_test' ),
|
|
$this->expectedField( 'value', 'yes' ),
|
|
]
|
|
),
|
|
$this->expectedObject(
|
|
'simpleVariations.#',
|
|
[
|
|
$this->expectedField( 'name', 'attribute_pa_color' ),
|
|
$this->expectedField( 'value', 'green' ),
|
|
]
|
|
),
|
|
]
|
|
),
|
|
];
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
}
|
|
|
|
public function testAddCartItemsMutationAndErrors() {
|
|
// Create variable product for later use.
|
|
$variation_ids = $this->factory->product_variation->createSome();
|
|
$product = \wc_get_product( $variation_ids['product'] );
|
|
$attribute = new WC_Product_Attribute();
|
|
$attribute->set_id( 0 );
|
|
$attribute->set_name( 'test' );
|
|
$attribute->set_options( [ 'yes', 'no' ] );
|
|
$attribute->set_position( 3 );
|
|
$attribute->set_visible( true );
|
|
$attribute->set_variation( true );
|
|
$attributes = array_values( $product->get_attributes() );
|
|
$attributes[] = $attribute;
|
|
$product->set_attributes( $attributes );
|
|
$product->save();
|
|
|
|
$product_one = $this->factory->product->createSimple();
|
|
$invalid_product = 1000;
|
|
|
|
$query = '
|
|
mutation ($input: AddCartItemsInput!) {
|
|
addCartItems(input: $input) {
|
|
clientMutationId
|
|
added {
|
|
product {
|
|
node { databaseId }
|
|
}
|
|
variation {
|
|
node { databaseId }
|
|
}
|
|
quantity
|
|
}
|
|
cartErrors {
|
|
type
|
|
reasons
|
|
productId
|
|
quantity
|
|
variationId
|
|
variation {
|
|
attributeName
|
|
attributeValue
|
|
}
|
|
extraData
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'items' => [
|
|
[
|
|
'productId' => $product_one,
|
|
'quantity' => 2,
|
|
],
|
|
[
|
|
'productId' => $variation_ids['product'],
|
|
'quantity' => 5,
|
|
'variationId' => $variation_ids['variations'][0],
|
|
],
|
|
[
|
|
'productId' => $invalid_product,
|
|
'quantity' => 4,
|
|
],
|
|
[
|
|
'productId' => $variation_ids['product'],
|
|
'quantity' => 3,
|
|
'variationId' => $variation_ids['variations'][1],
|
|
'variation' => [
|
|
[
|
|
'attributeName' => 'test',
|
|
'attributeValue' => 'yes',
|
|
],
|
|
[
|
|
'attributeName' => 'color',
|
|
'attributeValue' => 'green',
|
|
],
|
|
],
|
|
],
|
|
],
|
|
],
|
|
];
|
|
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
|
|
$this->assertQuerySuccessful(
|
|
$response,
|
|
[
|
|
$this->expectedField( 'addCartItems.clientMutationId', 'someId' ),
|
|
$this->expectedNode(
|
|
'addCartItems.added',
|
|
[
|
|
'product' => [
|
|
'node' => [ 'databaseId' => $product_one ],
|
|
],
|
|
'variation' => null,
|
|
'quantity' => 2,
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'addCartItems.added',
|
|
[
|
|
'product' => [
|
|
'node' => [ 'databaseId' => $variation_ids['product'] ],
|
|
],
|
|
'variation' => [
|
|
'node' => [ 'databaseId' => $variation_ids['variations'][1] ],
|
|
],
|
|
'quantity' => 3,
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'addCartItems.cartErrors',
|
|
[
|
|
$this->expectedField( 'type', 'INVALID_CART_ITEM' ),
|
|
$this->expectedField( 'reasons.0', static::NOT_NULL ),
|
|
$this->expectedField( 'productId', $variation_ids['product'] ),
|
|
$this->expectedField( 'quantity', 5 ),
|
|
$this->expectedField( 'variationId', $variation_ids['variations'][0] ),
|
|
$this->expectedField( 'variation', static::IS_NULL ),
|
|
$this->expectedField( 'extraData', static::IS_NULL ),
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'addCartItems.cartErrors',
|
|
[
|
|
$this->expectedField( 'type', 'INVALID_CART_ITEM' ),
|
|
$this->expectedField( 'reasons.0', 'No product found matching the ID provided' ),
|
|
$this->expectedField( 'productId', $invalid_product ),
|
|
$this->expectedField( 'quantity', 4 ),
|
|
$this->expectedField( 'variationId', static::IS_NULL ),
|
|
$this->expectedField( 'variation', static::IS_NULL ),
|
|
$this->expectedField( 'extraData', static::IS_NULL ),
|
|
]
|
|
),
|
|
]
|
|
);
|
|
}
|
|
|
|
public function testFillCartMutationAndErrors() {
|
|
// Create products.
|
|
$product_one = $this->factory->product->createSimple( [ 'regular_price' => 100 ] );
|
|
$product_two = $this->factory->product->createSimple( [ 'regular_price' => 40 ] );
|
|
|
|
// Create coupons.
|
|
$coupon_code_one = wc_get_coupon_code_by_id(
|
|
$this->factory->coupon->create(
|
|
[
|
|
'amount' => 0.5,
|
|
'product_ids' => [ $product_one ],
|
|
]
|
|
)
|
|
);
|
|
$coupon_code_two = wc_get_coupon_code_by_id(
|
|
$this->factory->coupon->create(
|
|
[
|
|
'amount' => 0.2,
|
|
'product_ids' => [ $product_two ],
|
|
]
|
|
)
|
|
);
|
|
|
|
$invalid_product = 1000;
|
|
$invalid_coupon = 'failed';
|
|
$invalid_shipping_method = 'fakityfake-shipping';
|
|
|
|
$this->factory->shipping_zone->createLegacyFlatRate();
|
|
|
|
$query = '
|
|
mutation ($input: FillCartInput!) {
|
|
fillCart( input: $input ) {
|
|
clientMutationId
|
|
cart {
|
|
chosenShippingMethods
|
|
contents {
|
|
nodes {
|
|
product {
|
|
node { databaseId }
|
|
}
|
|
quantity
|
|
variation {
|
|
node { databaseId }
|
|
}
|
|
}
|
|
}
|
|
appliedCoupons {
|
|
code
|
|
discountAmount
|
|
discountTax
|
|
}
|
|
}
|
|
cartErrors {
|
|
type
|
|
... on CartItemError {
|
|
reasons
|
|
productId
|
|
quantity
|
|
}
|
|
... on CouponError {
|
|
reasons
|
|
code
|
|
}
|
|
... on ShippingMethodError {
|
|
chosenMethod
|
|
package
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'clientMutationId' => 'someId',
|
|
'items' => [
|
|
[
|
|
'productId' => $product_one,
|
|
'quantity' => 3,
|
|
],
|
|
[
|
|
'productId' => $product_two,
|
|
'quantity' => 2,
|
|
],
|
|
[
|
|
'productId' => $invalid_product,
|
|
'quantity' => 4,
|
|
],
|
|
],
|
|
'coupons' => [ $coupon_code_one, $coupon_code_two, $invalid_coupon ],
|
|
'shippingMethods' => [ 'legacy_flat_rate', $invalid_shipping_method ],
|
|
],
|
|
];
|
|
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$expected = [
|
|
$this->expectedObject(
|
|
'fillCart',
|
|
[
|
|
$this->expectedField( 'clientMutationId', 'someId' ),
|
|
$this->expectedObject(
|
|
'cart',
|
|
[
|
|
$this->expectedField( 'chosenShippingMethods.0', 'legacy_flat_rate' ),
|
|
$this->expectedObject(
|
|
'contents',
|
|
[
|
|
$this->expectedNode(
|
|
'nodes',
|
|
[
|
|
$this->expectedField( 'product.node.databaseId', $product_one ),
|
|
$this->expectedField( 'quantity', 3 ),
|
|
$this->expectedField( 'variation', static::IS_NULL ),
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'nodes',
|
|
[
|
|
$this->expectedField( 'product.node.databaseId', $product_two ),
|
|
$this->expectedField( 'quantity', 2 ),
|
|
$this->expectedField( 'variation', static::IS_NULL ),
|
|
]
|
|
),
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'appliedCoupons',
|
|
[
|
|
$this->expectedField( 'code', $coupon_code_one ),
|
|
$this->expectedField(
|
|
'discountAmount',
|
|
\wc_graphql_price( \WC()->cart->get_coupon_discount_amount( $coupon_code_one, true ) )
|
|
),
|
|
$this->expectedField(
|
|
'discountTax',
|
|
\wc_graphql_price( \WC()->cart->get_coupon_discount_tax_amount( $coupon_code_one ) )
|
|
),
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'appliedCoupons',
|
|
[
|
|
$this->expectedField( 'code', $coupon_code_two ),
|
|
$this->expectedField(
|
|
'discountAmount',
|
|
\wc_graphql_price( \WC()->cart->get_coupon_discount_amount( $coupon_code_two, true ) )
|
|
),
|
|
$this->expectedField(
|
|
'discountTax',
|
|
\wc_graphql_price( \WC()->cart->get_coupon_discount_tax_amount( $coupon_code_two ) )
|
|
),
|
|
]
|
|
),
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'cartErrors',
|
|
[
|
|
$this->expectedField( 'type', 'INVALID_CART_ITEM' ),
|
|
$this->expectedField( 'reasons', [ 'No product found matching the ID provided' ] ),
|
|
$this->expectedField( 'productId', $invalid_product ),
|
|
$this->expectedField( 'quantity', 4 ),
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'cartErrors',
|
|
[
|
|
$this->expectedField( 'type', 'INVALID_COUPON' ),
|
|
$this->expectedField( 'reasons', [ "Coupon "{$invalid_coupon}" cannot be applied because it does not exist." ] ),
|
|
$this->expectedField( 'code', $invalid_coupon ),
|
|
]
|
|
),
|
|
$this->expectedNode(
|
|
'cartErrors',
|
|
[
|
|
$this->expectedField( 'type', 'INVALID_SHIPPING_METHOD' ),
|
|
$this->expectedField( 'chosenMethod', $invalid_shipping_method ),
|
|
$this->expectedField( 'package', 1 ),
|
|
]
|
|
),
|
|
]
|
|
),
|
|
];
|
|
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
}
|
|
|
|
/**
|
|
* Test that fillCart reports coupon errors when coupon minimum spend is not met.
|
|
*
|
|
* @see https://github.com/wp-graphql/wp-graphql-woocommerce/issues/458
|
|
*/
|
|
public function testFillCartReportsCouponMinimumSpendError() {
|
|
$product_id = $this->factory->product->createSimple(
|
|
[
|
|
'regular_price' => 10,
|
|
'price' => 10,
|
|
]
|
|
);
|
|
|
|
$coupon_id = $this->factory->coupon->create(
|
|
[
|
|
'code' => 'minspend500',
|
|
'discount_type' => 'percent',
|
|
'amount' => 10,
|
|
'minimum_amount' => 500,
|
|
]
|
|
);
|
|
|
|
$query = '
|
|
mutation fillCart($input: FillCartInput!) {
|
|
fillCart(input: $input) {
|
|
added {
|
|
key
|
|
product {
|
|
node {
|
|
databaseId
|
|
}
|
|
}
|
|
}
|
|
applied {
|
|
code
|
|
}
|
|
cartErrors {
|
|
type
|
|
reasons
|
|
... on CouponError {
|
|
code
|
|
reasons
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'items' => [
|
|
[
|
|
'productId' => $product_id,
|
|
'quantity' => 1,
|
|
],
|
|
],
|
|
'coupons' => [ 'minspend500' ],
|
|
],
|
|
];
|
|
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$expected = [
|
|
$this->expectedField( 'fillCart.added.0.product.node.databaseId', $product_id ),
|
|
];
|
|
$this->assertQuerySuccessful( $response, $expected );
|
|
|
|
$applied = $this->lodashGet( $response, 'data.fillCart.applied' );
|
|
$this->assertNull( $applied, 'Coupon should not be applied when minimum spend is not met.' );
|
|
|
|
$cart_errors = $this->lodashGet( $response, 'data.fillCart.cartErrors', [] );
|
|
$this->assertNotEmpty( $cart_errors, 'cartErrors should contain the coupon error.' );
|
|
$this->assertEquals( 'INVALID_COUPON', $cart_errors[0]['type'] );
|
|
$this->assertEquals( 'minspend500', $cart_errors[0]['code'] );
|
|
$this->assertNotEmpty( $cart_errors[0]['reasons'], 'Coupon error should have a reason.' );
|
|
}
|
|
|
|
/**
|
|
* Test that fillCart reports errors for completely invalid coupon codes.
|
|
*/
|
|
public function testFillCartReportsInvalidCouponCodeError() {
|
|
$product_id = $this->factory->product->createSimple(
|
|
[
|
|
'regular_price' => 10,
|
|
'price' => 10,
|
|
]
|
|
);
|
|
|
|
$query = '
|
|
mutation fillCart($input: FillCartInput!) {
|
|
fillCart(input: $input) {
|
|
added {
|
|
key
|
|
}
|
|
applied {
|
|
code
|
|
}
|
|
cartErrors {
|
|
type
|
|
reasons
|
|
... on CouponError {
|
|
code
|
|
reasons
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [
|
|
'input' => [
|
|
'items' => [
|
|
[
|
|
'productId' => $product_id,
|
|
'quantity' => 1,
|
|
],
|
|
],
|
|
'coupons' => [ 'nonexistent_coupon' ],
|
|
],
|
|
];
|
|
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$this->assertQuerySuccessful( $response, [] );
|
|
|
|
$applied = $this->lodashGet( $response, 'data.fillCart.applied' );
|
|
$this->assertNull( $applied, 'Invalid coupon should not be applied.' );
|
|
|
|
$cart_errors = $this->lodashGet( $response, 'data.fillCart.cartErrors', [] );
|
|
$this->assertNotEmpty( $cart_errors, 'cartErrors should contain the invalid coupon error.' );
|
|
$this->assertEquals( 'INVALID_COUPON', $cart_errors[0]['type'] );
|
|
}
|
|
|
|
/**
|
|
* Test that applyCoupon returns the same error message regardless of coupon code casing.
|
|
*
|
|
* @see https://github.com/wp-graphql/wp-graphql-woocommerce/issues/600
|
|
*/
|
|
public function testApplyCouponErrorMessageConsistentAcrossCasing() {
|
|
$product_id = $this->factory->product->createSimple(
|
|
[
|
|
'regular_price' => 10,
|
|
'price' => 10,
|
|
]
|
|
);
|
|
|
|
\WC()->cart->add_to_cart( $product_id );
|
|
|
|
$query = '
|
|
mutation applyCoupon($input: ApplyCouponInput!) {
|
|
applyCoupon(input: $input) {
|
|
cart {
|
|
appliedCoupons {
|
|
code
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$expected_error = [
|
|
$this->expectedErrorMessage( 'does not exist', self::MESSAGE_CONTAINS ),
|
|
];
|
|
|
|
// Test with lowercase.
|
|
$variables = [ 'input' => [ 'code' => 'fakecoupon' ] ];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$this->assertQueryError( $response, $expected_error );
|
|
|
|
// Test with uppercase.
|
|
$variables = [ 'input' => [ 'code' => 'FAKECOUPON' ] ];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$this->assertQueryError( $response, $expected_error );
|
|
|
|
// Test with mixed case.
|
|
$variables = [ 'input' => [ 'code' => 'FakeCoupon' ] ];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$this->assertQueryError( $response, $expected_error );
|
|
}
|
|
|
|
/**
|
|
* Test that removeCoupons recalculates cart totals after coupon removal.
|
|
*
|
|
* @see https://github.com/wp-graphql/wp-graphql-woocommerce/issues/260
|
|
*/
|
|
public function testRemoveCouponsRecalculatesTotals() {
|
|
$product_id = $this->factory->product->createSimple(
|
|
[
|
|
'regular_price' => 100,
|
|
'price' => 100,
|
|
]
|
|
);
|
|
|
|
$coupon_id = $this->factory->coupon->create(
|
|
[
|
|
'code' => 'half-off',
|
|
'discount_type' => 'percent',
|
|
'amount' => 50,
|
|
]
|
|
);
|
|
|
|
// Add product to cart.
|
|
\WC()->cart->add_to_cart( $product_id );
|
|
|
|
// Apply coupon.
|
|
$apply_query = '
|
|
mutation applyCoupon($input: ApplyCouponInput!) {
|
|
applyCoupon(input: $input) {
|
|
cart {
|
|
total
|
|
discountTotal
|
|
appliedCoupons {
|
|
code
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$query = $apply_query;
|
|
$variables = [ 'input' => [ 'code' => 'half-off' ] ];
|
|
$response = $this->graphql( compact( 'query', 'variables' ) );
|
|
$this->assertQuerySuccessful( $response, [] );
|
|
|
|
$total_with_coupon = $this->lodashGet( $response, 'data.applyCoupon.cart.total' );
|
|
$discount_with_coupon = $this->lodashGet( $response, 'data.applyCoupon.cart.discountTotal' );
|
|
|
|
$this->assertNotEmpty( $discount_with_coupon, 'Discount should be applied.' );
|
|
|
|
// Remove coupon.
|
|
$remove_query = '
|
|
mutation removeCoupons($input: RemoveCouponsInput!) {
|
|
removeCoupons(input: $input) {
|
|
cart {
|
|
total
|
|
discountTotal
|
|
discountTax
|
|
appliedCoupons {
|
|
code
|
|
}
|
|
}
|
|
}
|
|
}
|
|
';
|
|
|
|
$variables = [ 'input' => [ 'codes' => [ 'half-off' ] ] ];
|
|
$response = $this->graphql(
|
|
[
|
|
'query' => $remove_query,
|
|
'variables' => $variables,
|
|
]
|
|
);
|
|
$this->assertQuerySuccessful( $response, [] );
|
|
|
|
// Coupon should be removed.
|
|
$applied = $this->lodashGet( $response, 'data.removeCoupons.cart.appliedCoupons' );
|
|
$this->assertEmpty( $applied, 'No coupons should be applied after removal.' );
|
|
|
|
// Totals should be recalculated — discount should be zero.
|
|
$discount_after = $this->lodashGet( $response, 'data.removeCoupons.cart.discountTotal' );
|
|
$total_after = $this->lodashGet( $response, 'data.removeCoupons.cart.total' );
|
|
|
|
$this->assertNotEquals(
|
|
$total_with_coupon,
|
|
$total_after,
|
|
'Cart total should change after removing coupon.'
|
|
);
|
|
}
|
|
}
|