Skip to content

Commit

Permalink
E2E Test for Partial Refunds (#2519)
Browse files Browse the repository at this point in the history
Added E2E Test for partial refunds
  • Loading branch information
rodelgc authored Aug 13, 2021
1 parent 0381f02 commit 702d2bd
Show file tree
Hide file tree
Showing 2 changed files with 235 additions and 3 deletions.
197 changes: 197 additions & 0 deletions tests/e2e/specs/merchant/merchant-orders-partial-refund.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/**
* External dependencies
*/
import config from 'config';

const { merchant, shopper, uiUnblocked } = require( '@woocommerce/e2e-utils' );

/**
* Internal dependencies
*/
import { merchantWCP } from '../../utils';
import { fillCardDetails, setupProductCheckout } from '../../utils/payments';

const card = config.get( 'cards.basic' );
const product1 = config.get( 'products.simple.name' );
const product2 = 'Belt';
const product3 = 'Hoodie';

/**
* Elements:
* - test title
* - object containing the items to be ordered, and the quantities and amounts to be refunded
*/
const dataTable = [
[
'a single line item',
{
lineItems: [
[ product1, 1 ],
[ product2, 1 ],
],
refundInputs: [ { refundQty: 0, refundAmount: 5 } ],
},
],
[
'several line items',
{
lineItems: [
[ product1, 1 ],
[ product2, 2 ],
[ product3, 1 ],
],
refundInputs: [
{ refundQty: 1, refundAmount: 18 },
{ refundQty: 1, refundAmount: 55 },
],
},
],
];

describe.each( dataTable )(
'Order > Partial refund',
( testName, { lineItems, refundInputs } ) => {
let orderId;
let orderTotal;

beforeAll( async () => {
// Set up the test order
await setupProductCheckout(
config.get( 'addresses.customer.billing' ),
lineItems
);
await fillCardDetails( page, card );
await shopper.placeOrder();
await expect( page ).toMatch( 'Order received' );

// Remember the order ID and order total. We will need them later.
orderId = await page.$eval(
'.woocommerce-order-overview__order.order > strong',
( el ) => el.innerText
);
orderTotal = await page.$eval(
'.woocommerce-order-overview__total strong',
( el ) => Number( el.innerText.replace( '$', '' ) )
);

// Login as merchant
await merchant.login();
} );

afterAll( async () => {
await merchant.logout();
} );

it( `should refund ${ testName }`, async () => {
const refundReason = `Refunding ${ testName }`;
const refundTotal = refundInputs
.map( ( { refundAmount } ) => refundAmount )
.reduce( ( acc, cur ) => acc + cur );
const refundTotalString = refundTotal.toFixed( 2 );
const netPayment = ( orderTotal - refundTotal ).toFixed( 2 );

await merchant.goToOrder( orderId );

// We need to remove any listeners on the `dialog` event otherwise we can't catch the dialog below
await page.removeAllListeners( 'dialog' );

// Click the Refund button
await expect( page ).toClick( 'button.refund-items' );

// Fill up the quantity and/or amount to be refunded per line item
const rows = await page.$$( '#order_line_items tr.item' );
for ( let i = 0; i < refundInputs.length; i++ ) {
const { refundQty, refundAmount } = refundInputs[ i ];
const row = rows[ i ];

if ( refundQty ) {
await expect( row ).toFill(
'.refund_order_item_qty',
`${ refundQty }`
);
} else {
await expect( row ).toFill(
'.refund_line_total',
`${ refundAmount }`
);
}
}

// Fill up the rest of the form and complete the refund flow
await expect( page ).toFill( '#refund_reason', refundReason );
await expect( page ).toMatchElement( '.do-api-refund', {
text: `Refund $${ refundTotalString } via WooCommerce Payments`,
} );
const refundDialog = await expect( page ).toDisplayDialog(
async () => {
await expect( page ).toClick( 'button.do-api-refund' );
}
);
await refundDialog.accept();
await uiUnblocked();
await page.waitForNavigation( { waitUntil: 'networkidle0' } );

// Verify each line item shows the refunded quantity and/or amount
const updatedRows = await page.$$( '#order_line_items tr.item' );
for ( let i = 0; i < refundInputs.length; i++ ) {
const { refundQty, refundAmount } = refundInputs[ i ];
const row = updatedRows[ i ];

if ( refundQty ) {
await expect( row ).toMatchElement( '.quantity .refunded', {
text: `-${ refundQty }`,
} );
}

await expect( row ).toMatchElement( '.line_cost .refunded', {
text: `-$${ refundAmount.toFixed( 2 ) }`,
} );
}

// Verify the refund shows in the list with the amount
await expect( page ).toMatchElement( '.refund > .line_cost', {
text: `-$${ refundTotalString }`,
} );

// Verify system note was added
await expect( page ).toMatchElement( '.system-note', {
text: `refund of $${ refundTotalString }`,
} );
await expect( page ).toMatchElement( '.system-note', {
text: `Reason: ${ refundReason }`,
} );

// Verify "Refunded" and "Net Payment" values in Order Totals section
await expect( page ).toMatchElement(
'.wc-order-totals .total.refunded-total',
{
text: `-$${ refundTotalString }`,
}
);
await expect( page ).toMatchElement( '.wc-order-totals .total', {
text: `$${ netPayment }`,
} );

// Pull out and follow the link to avoid working in multiple tabs
const paymentDetailsLink = await page.$eval(
'p.order_number > a',
( anchor ) => anchor.getAttribute( 'href' )
);

await merchantWCP.openPaymentDetails( paymentDetailsLink );

// Verify the transaction timeline reflects the refund events
await Promise.all( [
expect( page ).toMatchElement( 'li.woocommerce-timeline-item', {
text: `A payment of $${ refundTotalString } was successfully refunded.`,
} ),
expect( page ).toMatchElement( 'li.woocommerce-timeline-item', {
text: `$${ refundTotalString } will be deducted from a future deposit.`,
} ),
expect( page ).toMatchElement( 'li.woocommerce-timeline-item', {
text: 'Payment status changed to Partial Refund.',
} ),
] );
} );
}
);
41 changes: 38 additions & 3 deletions tests/e2e/utils/payments.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,45 @@ export async function confirmCardAuthentication(
await button.click();
}

// Set up checkout with simple product
export async function setupProductCheckout( billingDetails ) {
/**
* Set up checkout with any number of products.
*
* @param {any} billingDetails Values to be entered into the 'Billing details' form in the Checkout page
* @param {any} lineItems A 2D array of line items where each line item is an array
* that contains the product title as the first element, and the quantity as the second.
* For example, if you want to checkout the products x2 "Hoodie" and x3 "Belt" then you can set this `lineItems` parameter like this:
*
* `[ [ "Hoodie", 2 ], [ "Belt", 3 ] ]`.
*
* Default value is 1 piece of `config.get( 'products.simple.name' )`.
*/
export async function setupProductCheckout(
billingDetails,
lineItems = [ [ config.get( 'products.simple.name' ), 1 ] ]
) {
const cartItemsCounter = '.cart-contents .count';

await shopper.goToShop();
await shopper.addToCartFromShopPage( config.get( 'products.simple.name' ) );

// Get the current number of items in the cart
let cartSize = await page.$eval( cartItemsCounter, ( e ) =>
Number( e.innerText.replace( /\D/g, '' ) )
);

// Add items to the cart
for ( const line of lineItems ) {
let [ productTitle, qty ] = line;

while ( qty-- ) {
await shopper.addToCartFromShopPage( productTitle );

// Make sure that the number of items in the cart is incremented first before adding another item.
await expect( page ).toMatchElement( cartItemsCounter, {
text: new RegExp( `${ ++cartSize } items?` ),
} );
}
}

await setupCheckout( billingDetails );
}

Expand Down

0 comments on commit 702d2bd

Please sign in to comment.