Files

1483 lines
38 KiB
PHP
Raw Permalink Normal View History

2019-05-03 17:36:08 -04:00
<?php
2020-10-22 15:59:11 -04:00
class CartMutationsTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQLTestCase {
// tests
public function testAddToCartMutationWithProduct() {
$product_id = $this->factory->product->createSimple();
2019-05-03 17:36:08 -04:00
$query = '
2020-10-22 15:59:11 -04:00
mutation( $input: AddToCartInput! ) {
2019-05-03 17:36:08 -04:00
addToCart( input: $input ) {
clientMutationId
cartItem {
key
product {
2020-11-24 00:28:20 -05:00
node {
2019-10-17 21:39:38 -04:00
id
}
2019-05-03 17:36:08 -04:00
}
variation {
2020-11-24 00:28:20 -05:00
node {
id
}
2019-05-03 17:36:08 -04:00
}
quantity
subtotal
subtotalTax
total
tax
}
}
}
2020-10-22 15:59:11 -04:00
';
2019-05-03 17:36:08 -04:00
$variables = [
'input' => [
'clientMutationId' => 'someId',
'productId' => $product_id,
'quantity' => 2,
],
];
$response = $this->graphql( compact( 'query', 'variables' ) );
2019-05-03 17:36:08 -04:00
// Confirm valid response
$this->assertResponseIsValid( $response );
// Get/validate cart item key.
2020-10-22 15:59:11 -04:00
$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 );
2020-06-12 16:50:21 -04:00
2020-10-22 15:59:11 -04:00
$this->assertQuerySuccessful(
$response,
[
2021-07-05 13:51:52 -04:00
$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 ) ),
2021-07-05 13:51:52 -04:00
$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'] ) ),
]
2019-05-03 17:36:08 -04:00
);
}
2019-05-03 17:36:08 -04:00
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,
2020-10-02 12:37:30 -04:00
'variationId' => $ids['variations'][0],
'variation' => [
[
2020-10-02 12:37:30 -04:00
'attributeName' => 'color',
'attributeValue' => 'red',
],
],
],
];
$response = $this->graphql( compact( 'query', 'variables' ) );
2019-05-03 17:36:08 -04:00
// Confirm valid response
$this->assertResponseIsValid( $response );
2019-05-03 17:36:08 -04:00
// 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 );
2019-05-03 17:36:08 -04:00
$this->assertQuerySuccessful(
$response,
[
2021-07-05 13:51:52 -04:00
$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] ) ),
2021-07-05 13:51:52 -04:00
$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'] ) ),
]
);
}
2019-05-03 18:58:36 -04:00
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,
[
2021-07-05 13:51:52 -04:00
$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."
);
}
2019-05-03 18:58:36 -04:00
// 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 );
2020-10-27 23:55:16 -04:00
$cart_item = $cart->get_cart_item( $cart_item_key );
$query = '
mutation( $input: EmptyCartInput! ) {
emptyCart( input: $input ) {
clientMutationId
2020-10-13 15:01:44 -04:00
deletedCart {
contents {
nodes {
key
product {
2020-11-24 00:28:20 -05:00
node {
2019-10-17 21:39:38 -04:00
id
}
}
variation {
2020-11-24 00:28:20 -05:00
node {
id
}
}
quantity
subtotal
subtotalTax
total
tax
}
}
}
}
}
';
$variables = [
'input' => [ 'clientMutationId' => 'someId' ],
];
$response = $this->graphql( compact( 'query', 'variables' ) );
$this->assertQuerySuccessful(
$response,
[
2021-07-05 13:51:52 -04:00
$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'] ),
],
],
2020-10-27 23:55:16 -04:00
'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() );
}
2019-05-06 13:59:11 -04:00
public function testApplyCouponMutation() {
// Create products.
$product_id = $this->factory->product->createSimple(
[ 'regular_price' => 100 ]
);
2019-05-06 13:59:11 -04:00
// 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',
]
)
);
2019-05-06 13:59:11 -04:00
// Add items to carts.
$cart = \WC()->cart;
$cart_item_key = $cart->add_to_cart( $product_id, 1 );
2019-05-06 13:59:11 -04:00
$old_total = \WC()->cart->get_cart_contents_total();
2019-05-06 13:59:11 -04:00
$query = '
mutation( $input: ApplyCouponInput! ) {
2019-05-06 13:59:11 -04:00
applyCoupon( input: $input ) {
clientMutationId
cart {
appliedCoupons {
2021-02-18 02:11:14 -05:00
code
description
2019-05-06 13:59:11 -04:00
}
contents {
nodes {
key
product {
2020-11-24 00:28:20 -05:00
node {
2019-10-17 21:39:38 -04:00
id
}
2019-05-06 13:59:11 -04:00
}
quantity
subtotal
subtotalTax
total
tax
}
}
}
}
}
';
$variables = [
'input' => [
'clientMutationId' => 'someId',
'code' => $coupon_code,
],
];
$response = $this->graphql( compact( 'query', 'variables' ) );
2019-05-06 13:59:11 -04:00
// Get updated cart item.
$cart_item = $cart->get_cart_item( $cart_item_key );
2019-05-06 13:59:11 -04:00
$this->assertQuerySuccessful(
$response,
[
2021-07-05 13:51:52 -04:00
$this->expectedField( 'applyCoupon.clientMutationId', 'someId' ),
$this->expectedNode(
2020-10-27 23:55:16 -04:00
'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'] ),
]
),
]
);
2019-05-06 13:59:11 -04:00
$new_total = \WC()->cart->get_cart_contents_total();
2019-05-06 13:59:11 -04:00
// Use --debug to view.
codecept_debug(
[
'old' => $old_total,
'new' => $new_total,
]
);
2019-05-06 13:59:11 -04:00
$this->assertTrue( $old_total > $new_total );
}
public function testApplyCouponMutationWithInvalidCoupons() {
$cart = WC()->cart;
2020-01-12 01:59:11 -05:00
// Create products.
$product_id = $this->factory->product->createSimple();
2020-01-12 01:59:11 -05:00
// 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 ] ]
)
);
2020-01-12 01:59:11 -05:00
// Add items to carts.
$cart_item_key = $cart->add_to_cart( $product_id, 1 );
$cart->apply_coupon( $applied_coupon_code );
2020-01-12 01:59:11 -05:00
$old_total = \WC()->cart->get_cart_contents_total();
2020-01-12 01:59:11 -05:00
$query = '
mutation( $input: ApplyCouponInput! ) {
2020-01-12 01:59:11 -05:00
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' ) );
2020-01-12 01:59:11 -05:00
$this->assertNotEmpty( $response['errors'] );
$this->assertEmpty( $response['data']['applyCoupon'] );
2020-01-12 01:59:11 -05:00
/**
* 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' ) );
2020-01-12 01:59:11 -05:00
$this->assertNotEmpty( $response['errors'] );
$this->assertEmpty( $response['data']['applyCoupon'] );
2020-01-12 01:59:11 -05:00
/**
* 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' ) );
2020-01-12 01:59:11 -05:00
$this->assertNotEmpty( $response['errors'] );
$this->assertEmpty( $response['data']['applyCoupon'] );
2020-01-12 01:59:11 -05:00
$this->assertEquals( $old_total, \WC()->cart->get_cart_contents_total() );
}
2020-01-12 01:59:11 -05:00
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 = '
2019-05-11 00:40:48 -04:00
mutation removeCoupons( $input: RemoveCouponsInput! ) {
removeCoupons( input: $input ) {
clientMutationId
cart {
appliedCoupons {
2021-02-18 02:11:14 -05:00
code
}
contents {
nodes {
key
product {
2020-11-24 00:28:20 -05:00
node {
2019-10-17 21:39:38 -04:00
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,
[
2021-07-05 13:51:52 -04:00
$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();
2020-10-13 15:01:44 -04:00
$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' ] );
2020-10-13 15:01:44 -04:00
$attribute->set_position( 3 );
$attribute->set_visible( true );
$attribute->set_variation( true );
$attributes = array_values( $product->get_attributes() );
2020-10-13 15:01:44 -04:00
$attributes[] = $attribute;
$product->set_attributes( $attributes );
$product->save();
$query = '
mutation( $input: AddToCartInput! ) {
addToCart( input: $input ) {
clientMutationId
cartItem {
key
product {
2020-10-27 23:55:16 -04:00
node {
id
}
}
variation {
2020-10-27 23:55:16 -04:00
node {
id
}
}
quantity
subtotal
subtotalTax
total
tax
}
}
}
';
2020-10-02 12:37:30 -04:00
\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' ) );
2020-10-02 12:37:30 -04:00
$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' ) );
2020-10-02 12:37:30 -04:00
$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.
2020-10-27 23:55:16 -04:00
$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();
2020-10-27 23:55:16 -04:00
$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
}
2021-02-24 12:37:28 -05:00
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',
],
[
2020-10-27 23:55:16 -04:00
'attributeName' => 'color',
'attributeValue' => 'green',
],
],
],
],
],
];
$response = $this->graphql( compact( 'query', 'variables' ) );
$this->assertQuerySuccessful(
$response,
[
2021-07-05 13:51:52 -04:00
$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,
]
2021-02-24 12:37:28 -05:00
),
$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 ),
]
),
]
);
}
2021-02-18 02:11:14 -05:00
2021-02-24 12:37:28 -05:00
public function testFillCartMutationAndErrors() {
2021-02-18 02:11:14 -05:00
// Create products.
$product_one = $this->factory->product->createSimple( [ 'regular_price' => 100 ] );
$product_two = $this->factory->product->createSimple( [ 'regular_price' => 40 ] );
2021-02-18 02:11:14 -05:00
// Create coupons.
$coupon_code_one = wc_get_coupon_code_by_id(
2020-10-27 23:55:16 -04:00
$this->factory->coupon->create(
[
2020-10-27 23:55:16 -04:00
'amount' => 0.5,
'product_ids' => [ $product_one ],
]
2020-10-27 23:55:16 -04:00
)
);
2021-02-18 02:11:14 -05:00
$coupon_code_two = wc_get_coupon_code_by_id(
$this->factory->coupon->create(
[
'amount' => 0.2,
'product_ids' => [ $product_two ],
]
)
);
2021-02-18 02:11:14 -05:00
2021-02-24 12:37:28 -05:00
$invalid_product = 1000;
$invalid_coupon = 'failed';
$invalid_shipping_method = 'fakityfake-shipping';
2021-02-18 02:11:14 -05:00
2021-06-03 21:37:09 -04:00
$this->factory->shipping_zone->createLegacyFlatRate();
2021-02-18 02:11:14 -05:00
$query = '
2021-02-18 02:11:14 -05:00
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
}
}
2021-02-18 02:11:14 -05:00
}
}
';
$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 ],
],
];
2021-02-18 02:11:14 -05:00
$response = $this->graphql( compact( 'query', 'variables' ) );
$expected = [
2021-07-05 13:51:52 -04:00
$this->expectedObject(
'fillCart',
[
2021-07-05 13:51:52 -04:00
$this->expectedField( 'clientMutationId', 'someId' ),
$this->expectedObject(
'cart',
[
2021-07-05 13:51:52 -04:00
$this->expectedField( 'chosenShippingMethods.0', 'legacy_flat_rate' ),
$this->expectedObject(
'contents',
[
2021-07-05 13:51:52 -04:00
$this->expectedNode(
'nodes',
[
2021-07-05 13:51:52 -04:00
$this->expectedField( 'product.node.databaseId', $product_one ),
$this->expectedField( 'quantity', 3 ),
$this->expectedField( 'variation', static::IS_NULL ),
]
2021-07-05 13:51:52 -04:00
),
$this->expectedNode(
'nodes',
[
2021-07-05 13:51:52 -04:00
$this->expectedField( 'product.node.databaseId', $product_two ),
$this->expectedField( 'quantity', 2 ),
$this->expectedField( 'variation', static::IS_NULL ),
]
2021-07-05 13:51:52 -04:00
),
]
2021-07-05 13:51:52 -04:00
),
$this->expectedNode(
'appliedCoupons',
[
2021-07-05 13:51:52 -04:00
$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 ) )
),
]
2021-07-05 13:51:52 -04:00
),
$this->expectedNode(
'appliedCoupons',
[
2021-07-05 13:51:52 -04:00
$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 ) )
),
]
2021-07-05 13:51:52 -04:00
),
]
2021-07-05 13:51:52 -04:00
),
$this->expectedNode(
'cartErrors',
[
2021-07-05 13:51:52 -04:00
$this->expectedField( 'type', 'INVALID_CART_ITEM' ),
$this->expectedField( 'reasons', [ 'No product found matching the ID provided' ] ),
2021-07-05 13:51:52 -04:00
$this->expectedField( 'productId', $invalid_product ),
$this->expectedField( 'quantity', 4 ),
]
2021-07-05 13:51:52 -04:00
),
$this->expectedNode(
'cartErrors',
[
2021-07-05 13:51:52 -04:00
$this->expectedField( 'type', 'INVALID_COUPON' ),
$this->expectedField( 'reasons', [ "Coupon &quot;{$invalid_coupon}&quot; cannot be applied because it does not exist." ] ),
2021-07-05 13:51:52 -04:00
$this->expectedField( 'code', $invalid_coupon ),
]
2021-07-05 13:51:52 -04:00
),
$this->expectedNode(
'cartErrors',
[
2021-07-05 13:51:52 -04:00
$this->expectedField( 'type', 'INVALID_SHIPPING_METHOD' ),
$this->expectedField( 'chosenMethod', $invalid_shipping_method ),
$this->expectedField( 'package', 1 ),
]
2021-07-05 13:51:52 -04:00
),
]
2021-07-05 13:51:52 -04:00
),
];
2021-07-05 13:51:52 -04:00
$this->assertQuerySuccessful( $response, $expected );
2021-02-18 02:11:14 -05:00
}
/**
* 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.'
);
}
2020-06-12 16:50:21 -04:00
}