Browse Source

test(server): Refactor common testing pattern into util function

Michael Bromley 7 years ago
parent
commit
798f24860f

+ 20 - 19
server/e2e/administrator.e2e-spec.ts

@@ -16,6 +16,7 @@ import {
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestClient } from './test-client';
 import { TestServer } from './test-server';
+import { assertThrowsWithMessage } from './test-utils';
 
 describe('Administrator resolver', () => {
     const client = new TestClient();
@@ -102,24 +103,24 @@ describe('Administrator resolver', () => {
         expect(result.updateAdministrator.lastName).toBe('new last');
     });
 
-    it('updateAdministrator throws with invalid roleId', async () => {
-        try {
-            const result = await client.query<UpdateAdministrator.Mutation, UpdateAdministrator.Variables>(
-                UPDATE_ADMINISTRATOR,
-                {
-                    input: {
-                        id: createdAdmin.id,
-                        emailAddress: 'new-email',
-                        firstName: 'new first',
-                        lastName: 'new last',
-                        password: 'new password',
-                        roleIds: ['999'],
+    it(
+        'updateAdministrator throws with invalid roleId',
+        assertThrowsWithMessage(
+            () =>
+                client.query<UpdateAdministrator.Mutation, UpdateAdministrator.Variables>(
+                    UPDATE_ADMINISTRATOR,
+                    {
+                        input: {
+                            id: createdAdmin.id,
+                            emailAddress: 'new-email',
+                            firstName: 'new first',
+                            lastName: 'new last',
+                            password: 'new password',
+                            roleIds: ['999'],
+                        },
                     },
-                },
-            );
-            fail(`Should throw`);
-        } catch (err) {
-            expect(err.message).toEqual(expect.stringContaining(`No Role with the id '999' could be found`));
-        }
-    });
+                ),
+            `No Role with the id '999' could be found`,
+        ),
+    );
 });

+ 41 - 46
server/e2e/auth.e2e-spec.ts

@@ -28,6 +28,7 @@ import { defaultEmailTypes } from '../src/email/default-email-types';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestClient } from './test-client';
 import { TestServer } from './test-server';
+import { assertThrowsWithMessage } from './test-utils';
 
 let sendEmailFn: jest.Mock;
 const emailOptions = {
@@ -226,17 +227,17 @@ describe('Authorization & permissions', () => {
             }
         });
 
-        it('verification fails with wrong token', async () => {
-            try {
-                await client.query(VERIFY_EMAIL, {
-                    password,
-                    token: 'bad-token',
-                });
-                fail('should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(expect.stringContaining(`Verification token not recognized`));
-            }
-        });
+        it(
+            'verification fails with wrong token',
+            assertThrowsWithMessage(
+                () =>
+                    client.query(VERIFY_EMAIL, {
+                        password,
+                        token: 'bad-token',
+                    }),
+                `Verification token not recognized`,
+            ),
+        );
 
         it('verification succeeds with correct token', async () => {
             const result = await client.query(VERIFY_EMAIL, {
@@ -259,17 +260,17 @@ describe('Authorization & permissions', () => {
             expect(sendEmailFn).not.toHaveBeenCalled();
         });
 
-        it('verification fails if attempted a second time', async () => {
-            try {
-                await client.query(VERIFY_EMAIL, {
-                    password,
-                    token: verificationToken,
-                });
-                fail('should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(expect.stringContaining(`Verification token not recognized`));
-            }
-        });
+        it(
+            'verification fails if attempted a second time',
+            assertThrowsWithMessage(
+                () =>
+                    client.query(VERIFY_EMAIL, {
+                        password,
+                        token: verificationToken,
+                    }),
+                `Verification token not recognized`,
+            ),
+        );
     });
 
     async function assertRequestAllowed<V>(operation: DocumentNode, variables?: V) {
@@ -374,37 +375,31 @@ describe('Expiring registration token', () => {
         await server.destroy();
     });
 
-    it('attempting to verify after token has expired throws', async () => {
-        const verificationTokenPromise = getVerificationTokenPromise();
-        const input: RegisterCustomerInput = {
-            firstName: 'Barry',
-            lastName: 'Wallace',
-            emailAddress: 'barry.wallace@test.com',
-        };
-        const result1 = await client.query(REGISTER_ACCOUNT, { input });
+    it(
+        'attempting to verify after token has expired throws',
+        assertThrowsWithMessage(async () => {
+            const verificationTokenPromise = getVerificationTokenPromise();
+            const input: RegisterCustomerInput = {
+                firstName: 'Barry',
+                lastName: 'Wallace',
+                emailAddress: 'barry.wallace@test.com',
+            };
+            const result1 = await client.query(REGISTER_ACCOUNT, { input });
 
-        const verificationToken = await verificationTokenPromise;
+            const verificationToken = await verificationTokenPromise;
 
-        expect(result1.registerCustomerAccount).toBe(true);
-        expect(sendEmailFn).toHaveBeenCalledTimes(1);
-        expect(verificationToken).toBeDefined();
+            expect(result1.registerCustomerAccount).toBe(true);
+            expect(sendEmailFn).toHaveBeenCalledTimes(1);
+            expect(verificationToken).toBeDefined();
 
-        await new Promise(resolve => setTimeout(resolve, 3));
+            await new Promise(resolve => setTimeout(resolve, 3));
 
-        try {
-            await client.query(VERIFY_EMAIL, {
+            return client.query(VERIFY_EMAIL, {
                 password: 'test',
                 token: verificationToken,
             });
-            fail('should have thrown');
-        } catch (err) {
-            expect(err.message).toEqual(
-                expect.stringContaining(
-                    `Verification token has expired. Use refreshCustomerVerification to send a new token.`,
-                ),
-            );
-        }
-    });
+        }, `Verification token has expired. Use refreshCustomerVerification to send a new token.`),
+    );
 });
 
 function getVerificationTokenPromise(): Promise<string> {

+ 44 - 49
server/e2e/customer.e2e-spec.ts

@@ -19,6 +19,7 @@ import { omit } from '../../shared/omit';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestClient } from './test-client';
 import { TestServer } from './test-server';
+import { assertThrowsWithMessage } from './test-utils';
 
 // tslint:disable:no-non-null-assertion
 
@@ -56,22 +57,20 @@ describe('Customer resolver', () => {
     describe('addresses', () => {
         let firstCustomerAddressIds: string[] = [];
 
-        it('createCustomerAddress throws on invalid countryCode', async () => {
-            try {
-                await client.query(CREATE_ADDRESS, {
-                    id: firstCustomer.id,
-                    input: {
-                        streetLine1: 'streetLine1',
-                        countryCode: 'INVALID',
-                    },
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`The countryCode "INVALID" was not recognized`),
-                );
-            }
-        });
+        it(
+            'createCustomerAddress throws on invalid countryCode',
+            assertThrowsWithMessage(
+                () =>
+                    client.query(CREATE_ADDRESS, {
+                        id: firstCustomer.id,
+                        input: {
+                            streetLine1: 'streetLine1',
+                            countryCode: 'INVALID',
+                        },
+                    }),
+                `The countryCode "INVALID" was not recognized`,
+            ),
+        );
 
         it('createCustomerAddress creates a new address', async () => {
             const result = await client.query(CREATE_ADDRESS, {
@@ -258,41 +257,37 @@ describe('Customer resolver', () => {
             expect(result.customers.items.map(c => c.id).includes(thirdCustomer.id)).toBe(false);
         });
 
-        it('updateCustomer throws for deleted customer', async () => {
-            try {
-                await client.query<UpdateCustomer.Mutation, UpdateCustomer.Variables>(UPDATE_CUSTOMER, {
-                    input: {
-                        id: thirdCustomer.id,
-                        firstName: 'updated',
-                    },
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No Customer with the id '3' could be found`),
-                );
-            }
-        });
-
-        it('createCustomerAddress throws for deleted customer', async () => {
-            try {
-                await client.query<CreateCustomerAddress.Mutation, CreateCustomerAddress.Variables>(
-                    CREATE_CUSTOMER_ADDRESS,
-                    {
-                        customerId: thirdCustomer.id,
+        it(
+            'updateCustomer throws for deleted customer',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<UpdateCustomer.Mutation, UpdateCustomer.Variables>(UPDATE_CUSTOMER, {
                         input: {
-                            streetLine1: 'test',
-                            countryCode: 'GB',
+                            id: thirdCustomer.id,
+                            firstName: 'updated',
                         },
-                    },
-                );
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No Customer with the id '3' could be found`),
-                );
-            }
-        });
+                    }),
+                `No Customer with the id '3' could be found`,
+            ),
+        );
+
+        it(
+            'createCustomerAddress throws for deleted customer',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<CreateCustomerAddress.Mutation, CreateCustomerAddress.Variables>(
+                        CREATE_CUSTOMER_ADDRESS,
+                        {
+                            customerId: thirdCustomer.id,
+                            input: {
+                                streetLine1: 'test',
+                                countryCode: 'GB',
+                            },
+                        },
+                    ),
+                `No Customer with the id '3' could be found`,
+            ),
+        );
     });
 });
 

+ 140 - 191
server/e2e/order.e2e-spec.ts

@@ -7,6 +7,7 @@ import { PaymentMethodHandler } from '../src/config/payment-method/payment-metho
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestClient } from './test-client';
 import { TestServer } from './test-server';
+import { assertThrowsWithMessage } from './test-utils';
 
 describe('Orders', () => {
     const client = new TestClient();
@@ -65,33 +66,29 @@ describe('Orders', () => {
             firstOrderItemId = result.addItemToOrder.lines[0].id;
         });
 
-        it('addItemToOrder errors with an invalid productVariantId', async () => {
-            try {
-                await client.query(ADD_ITEM_TO_ORDER, {
-                    productVariantId: 'T_999',
-                    quantity: 1,
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No ProductVariant with the id '999' could be found`),
-                );
-            }
-        });
+        it(
+            'addItemToOrder errors with an invalid productVariantId',
+            assertThrowsWithMessage(
+                () =>
+                    client.query(ADD_ITEM_TO_ORDER, {
+                        productVariantId: 'T_999',
+                        quantity: 1,
+                    }),
+                `No ProductVariant with the id '999' could be found`,
+            ),
+        );
 
-        it('addItemToOrder errors with a negative quantity', async () => {
-            try {
-                await client.query(ADD_ITEM_TO_ORDER, {
-                    productVariantId: 'T_999',
-                    quantity: -3,
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`-3 is not a valid quantity for an OrderItem`),
-                );
-            }
-        });
+        it(
+            'addItemToOrder errors with a negative quantity',
+            assertThrowsWithMessage(
+                () =>
+                    client.query(ADD_ITEM_TO_ORDER, {
+                        productVariantId: 'T_999',
+                        quantity: -3,
+                    }),
+                `-3 is not a valid quantity for an OrderItem`,
+            ),
+        );
 
         it('addItemToOrder with an existing productVariantId adds quantity to the existing OrderLine', async () => {
             const result = await client.query(ADD_ITEM_TO_ORDER, {
@@ -113,33 +110,29 @@ describe('Orders', () => {
             expect(result.adjustItemQuantity.lines[0].quantity).toBe(50);
         });
 
-        it('adjustItemQuantity errors with a negative quantity', async () => {
-            try {
-                await client.query(ADJUST_ITEM_QUENTITY, {
-                    orderItemId: firstOrderItemId,
-                    quantity: -3,
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`-3 is not a valid quantity for an OrderItem`),
-                );
-            }
-        });
+        it(
+            'adjustItemQuantity errors with a negative quantity',
+            assertThrowsWithMessage(
+                () =>
+                    client.query(ADJUST_ITEM_QUENTITY, {
+                        orderItemId: firstOrderItemId,
+                        quantity: -3,
+                    }),
+                `-3 is not a valid quantity for an OrderItem`,
+            ),
+        );
 
-        it('adjustItemQuantity errors with an invalid orderItemId', async () => {
-            try {
-                await client.query(ADJUST_ITEM_QUENTITY, {
-                    orderItemId: 'T_999',
-                    quantity: 5,
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`This order does not contain an OrderLine with the id 999`),
-                );
-            }
-        });
+        it(
+            'adjustItemQuantity errors with an invalid orderItemId',
+            assertThrowsWithMessage(
+                () =>
+                    client.query(ADJUST_ITEM_QUENTITY, {
+                        orderItemId: 'T_999',
+                        quantity: 5,
+                    }),
+                `This order does not contain an OrderLine with the id 999`,
+            ),
+        );
 
         it('removeItemFromOrder removes the correct item', async () => {
             const result1 = await client.query(ADD_ITEM_TO_ORDER, {
@@ -156,18 +149,16 @@ describe('Orders', () => {
             expect(result2.removeItemFromOrder.lines.map(i => i.productVariant.id)).toEqual(['T_3']);
         });
 
-        it('removeItemFromOrder errors with an invalid orderItemId', async () => {
-            try {
-                await client.query(REMOVE_ITEM_FROM_ORDER, {
-                    orderItemId: 'T_999',
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`This order does not contain an OrderLine with the id 999`),
-                );
-            }
-        });
+        it(
+            'removeItemFromOrder errors with an invalid orderItemId',
+            assertThrowsWithMessage(
+                () =>
+                    client.query(REMOVE_ITEM_FROM_ORDER, {
+                        orderItemId: 'T_999',
+                    }),
+                `This order does not contain an OrderLine with the id 999`,
+            ),
+        );
 
         it('nextOrderStates returns next valid states', async () => {
             const result = await client.query(gql`
@@ -179,29 +170,21 @@ describe('Orders', () => {
             expect(result.nextOrderStates).toEqual(['ArrangingPayment']);
         });
 
-        it('transitionOrderToState throws for an invalid state', async () => {
-            try {
-                await client.query(TRANSITION_TO_STATE, { state: 'Completed' });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`Cannot transition Order from "AddingItems" to "Completed"`),
-                );
-            }
-        });
+        it(
+            'transitionOrderToState throws for an invalid state',
+            assertThrowsWithMessage(
+                () => client.query(TRANSITION_TO_STATE, { state: 'Completed' }),
+                `Cannot transition Order from "AddingItems" to "Completed"`,
+            ),
+        );
 
-        it('attempting to transition to ArrangingPayment throws when Order has no Customer', async () => {
-            try {
-                await client.query(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(
-                        `Cannot transition Order to the "ArrangingShipping" state without Customer details`,
-                    ),
-                );
-            }
-        });
+        it(
+            'attempting to transition to ArrangingPayment throws when Order has no Customer',
+            assertThrowsWithMessage(
+                () => client.query(TRANSITION_TO_STATE, { state: 'ArrangingPayment' }),
+                `Cannot transition Order to the "ArrangingShipping" state without Customer details`,
+            ),
+        );
 
         it('setCustomerForOrder creates a new Customer and associates it with the Order', async () => {
             const result = await client.query(SET_CUSTOMER, {
@@ -342,23 +325,19 @@ describe('Orders', () => {
         describe('shipping', () => {
             let shippingMethods: any;
 
-            it('setOrderShippingAddress throws with invalid countryCode', async () => {
-                const address: CreateAddressInput = {
-                    streetLine1: '12 the street',
-                    countryCode: 'INVALID',
-                };
+            it(
+                'setOrderShippingAddress throws with invalid countryCode',
+                assertThrowsWithMessage(() => {
+                    const address: CreateAddressInput = {
+                        streetLine1: '12 the street',
+                        countryCode: 'INVALID',
+                    };
 
-                try {
-                    await client.query(SET_SHIPPING_ADDRESS, {
+                    return client.query(SET_SHIPPING_ADDRESS, {
                         input: address,
                     });
-                    fail('Should have thrown');
-                } catch (err) {
-                    expect(err.message).toEqual(
-                        expect.stringContaining(`The countryCode "INVALID" was not recognized`),
-                    );
-                }
-            });
+                }, `The countryCode "INVALID" was not recognized`),
+            );
 
             it('setOrderShippingAddress sets shipping address', async () => {
                 const address: CreateAddressInput = {
@@ -438,23 +417,19 @@ describe('Orders', () => {
         });
 
         describe('payment', () => {
-            it('attempting add a Payment throws error when in AddingItems state', async () => {
-                try {
-                    await client.query(ADD_PAYMENT, {
-                        input: {
-                            method: testPaymentMethod.code,
-                            metadata: {},
-                        },
-                    });
-                    fail('Should have thrown');
-                } catch (err) {
-                    expect(err.message).toEqual(
-                        expect.stringContaining(
-                            `A Payment may only be added when Order is in "ArrangingPayment" state`,
-                        ),
-                    );
-                }
-            });
+            it(
+                'attempting add a Payment throws error when in AddingItems state',
+                assertThrowsWithMessage(
+                    () =>
+                        client.query(ADD_PAYMENT, {
+                            input: {
+                                method: testPaymentMethod.code,
+                                metadata: {},
+                            },
+                        }),
+                    `A Payment may only be added when Order is in "ArrangingPayment" state`,
+                ),
+            );
 
             it('transitions to the ArrangingPayment state', async () => {
                 const result = await client.query(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
@@ -464,70 +439,51 @@ describe('Orders', () => {
                 });
             });
 
-            it('attempting to add an item throws error when in ArrangingPayment state', async () => {
-                try {
-                    const result = await client.query(ADD_ITEM_TO_ORDER, {
-                        productVariantId: 'T_4',
-                        quantity: 1,
-                    });
-                    fail('Should have thrown');
-                } catch (err) {
-                    expect(err.message).toEqual(
-                        expect.stringContaining(
-                            `Order contents may only be modified when in the "AddingItems" state`,
-                        ),
-                    );
-                }
-            });
-
-            it('attempting to modify item quantity throws error when in ArrangingPayment state', async () => {
-                try {
-                    const result = await client.query(ADJUST_ITEM_QUENTITY, {
-                        orderItemId: activeOrder.lines[0].id,
-                        quantity: 12,
-                    });
-                    fail('Should have thrown');
-                } catch (err) {
-                    expect(err.message).toEqual(
-                        expect.stringContaining(
-                            `Order contents may only be modified when in the "AddingItems" state`,
-                        ),
-                    );
-                }
-            });
+            it(
+                'attempting to add an item throws error when in ArrangingPayment state',
+                assertThrowsWithMessage(
+                    () =>
+                        client.query(ADD_ITEM_TO_ORDER, {
+                            productVariantId: 'T_4',
+                            quantity: 1,
+                        }),
+                    `Order contents may only be modified when in the "AddingItems" state`,
+                ),
+            );
 
-            it('attempting to remove an item throws error when in ArrangingPayment state', async () => {
-                try {
-                    const result = await client.query(REMOVE_ITEM_FROM_ORDER, {
-                        orderItemId: activeOrder.lines[0].id,
-                    });
-                    fail('Should have thrown');
-                } catch (err) {
-                    expect(err.message).toEqual(
-                        expect.stringContaining(
-                            `Order contents may only be modified when in the "AddingItems" state`,
-                        ),
-                    );
-                }
-            });
+            it(
+                'attempting to modify item quantity throws error when in ArrangingPayment state',
+                assertThrowsWithMessage(
+                    () =>
+                        client.query(ADJUST_ITEM_QUENTITY, {
+                            orderItemId: activeOrder.lines[0].id,
+                            quantity: 12,
+                        }),
+                    `Order contents may only be modified when in the "AddingItems" state`,
+                ),
+            );
 
-            it('attempting to setOrderShippingMethod throws error when in ArrangingPayment state', async () => {
-                const shippingMethodsResult = await client.query(GET_ELIGIBLE_SHIPPING_METHODS);
-                const shippingMethods = shippingMethodsResult.eligibleShippingMethods;
+            it(
+                'attempting to remove an item throws error when in ArrangingPayment state',
+                assertThrowsWithMessage(
+                    () =>
+                        client.query(REMOVE_ITEM_FROM_ORDER, {
+                            orderItemId: activeOrder.lines[0].id,
+                        }),
+                    `Order contents may only be modified when in the "AddingItems" state`,
+                ),
+            );
 
-                try {
-                    await client.query(SET_SHIPPING_METHOD, {
+            it(
+                'attempting to setOrderShippingMethod throws error when in ArrangingPayment state',
+                assertThrowsWithMessage(async () => {
+                    const shippingMethodsResult = await client.query(GET_ELIGIBLE_SHIPPING_METHODS);
+                    const shippingMethods = shippingMethodsResult.eligibleShippingMethods;
+                    return client.query(SET_SHIPPING_METHOD, {
                         id: shippingMethods[0].id,
                     });
-                    fail('Should have thrown');
-                } catch (err) {
-                    expect(err.message).toEqual(
-                        expect.stringContaining(
-                            `Order contents may only be modified when in the "AddingItems" state`,
-                        ),
-                    );
-                }
-            });
+                }, `Order contents may only be modified when in the "AddingItems" state`),
+            );
 
             it('adds a declined payment', async () => {
                 const result = await client.query(ADD_PAYMENT, {
@@ -591,23 +547,16 @@ describe('Orders', () => {
                     expect(result.orderByCode.id).toBe(activeOrder.id);
                 });
 
-                it(`throws error for another user's Order`, async () => {
-                    authenticatedUserEmailAddress = customers[1].emailAddress;
-                    await client.asUserWithCredentials(authenticatedUserEmailAddress, password);
-
-                    try {
-                        await client.query(GET_ORDER_BY_CODE, {
+                it(
+                    `throws error for another user's Order`,
+                    assertThrowsWithMessage(async () => {
+                        authenticatedUserEmailAddress = customers[1].emailAddress;
+                        await client.asUserWithCredentials(authenticatedUserEmailAddress, password);
+                        return client.query(GET_ORDER_BY_CODE, {
                             code: activeOrder.code,
                         });
-                        fail('Should have thrown');
-                    } catch (err) {
-                        expect(err.message).toEqual(
-                            expect.stringContaining(
-                                `You are not currently authorized to perform this action`,
-                            ),
-                        );
-                    }
-                });
+                    }, `You are not currently authorized to perform this action`),
+                );
             });
         });
     });

+ 33 - 36
server/e2e/product-category.e2e-spec.ts

@@ -22,6 +22,7 @@ import { ROOT_CATEGORY_NAME } from '../../shared/shared-constants';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestClient } from './test-client';
 import { TestServer } from './test-server';
+import { assertThrowsWithMessage } from './test-utils';
 
 describe('ProductCategory resolver', () => {
     const client = new TestClient();
@@ -223,45 +224,41 @@ describe('ProductCategory resolver', () => {
             expect(afterResult.map(i => i.id)).toEqual([laptopsCategory.id, appleCategory.id]);
         });
 
-        it('throws if attempting to move into self', async () => {
-            try {
-                await client.query<MoveProductCategory.Mutation, MoveProductCategory.Variables>(
-                    MOVE_PRODUCT_CATEGORY,
-                    {
-                        input: {
-                            categoryId: appleCategory.id,
-                            parentId: appleCategory.id,
-                            index: 0,
+        it(
+            'throws if attempting to move into self',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<MoveProductCategory.Mutation, MoveProductCategory.Variables>(
+                        MOVE_PRODUCT_CATEGORY,
+                        {
+                            input: {
+                                categoryId: appleCategory.id,
+                                parentId: appleCategory.id,
+                                index: 0,
+                            },
                         },
-                    },
-                );
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`Cannot move a ProductCategory into itself`),
-                );
-            }
-        });
+                    ),
+                `Cannot move a ProductCategory into itself`,
+            ),
+        );
 
-        it('throws if attempting to move into a decendant of self', async () => {
-            try {
-                await client.query<MoveProductCategory.Mutation, MoveProductCategory.Variables>(
-                    MOVE_PRODUCT_CATEGORY,
-                    {
-                        input: {
-                            categoryId: appleCategory.id,
-                            parentId: appleCategory.id,
-                            index: 0,
+        it(
+            'throws if attempting to move into a decendant of self',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<MoveProductCategory.Mutation, MoveProductCategory.Variables>(
+                        MOVE_PRODUCT_CATEGORY,
+                        {
+                            input: {
+                                categoryId: appleCategory.id,
+                                parentId: appleCategory.id,
+                                index: 0,
+                            },
                         },
-                    },
-                );
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`Cannot move a ProductCategory into itself`),
-                );
-            }
-        });
+                    ),
+                `Cannot move a ProductCategory into itself`,
+            ),
+        );
 
         async function getChildrenOf(parentId: string): Promise<Array<{ name: string; id: string }>> {
             const result = await client.query(GET_CATEGORIES);

+ 172 - 193
server/e2e/product.e2e-spec.ts

@@ -30,6 +30,7 @@ import { omit } from '../../shared/omit';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestClient } from './test-client';
 import { TestServer } from './test-server';
+import { assertThrowsWithMessage } from './test-utils';
 
 // tslint:disable:no-non-null-assertion
 
@@ -325,34 +326,32 @@ describe('Product resolver', () => {
             expect(result.updateProduct.facetValues.length).toEqual(1);
         });
 
-        it('updateProduct errors with an invalid productId', async () => {
-            try {
-                await client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
-                    input: {
-                        id: '999',
-                        translations: [
-                            {
-                                languageCode: LanguageCode.en,
-                                name: 'en Mashed Potato',
-                                slug: 'en-mashed-potato',
-                                description: 'A blob of mashed potato',
-                            },
-                            {
-                                languageCode: LanguageCode.de,
-                                name: 'de Mashed Potato',
-                                slug: 'de-mashed-potato',
-                                description: 'Eine blob von gemashed Erdapfel',
-                            },
-                        ],
-                    },
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No Product with the id '999' could be found`),
-                );
-            }
-        });
+        it(
+            'updateProduct errors with an invalid productId',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
+                        input: {
+                            id: '999',
+                            translations: [
+                                {
+                                    languageCode: LanguageCode.en,
+                                    name: 'en Mashed Potato',
+                                    slug: 'en-mashed-potato',
+                                    description: 'A blob of mashed potato',
+                                },
+                                {
+                                    languageCode: LanguageCode.de,
+                                    name: 'de Mashed Potato',
+                                    slug: 'de-mashed-potato',
+                                    description: 'Eine blob von gemashed Erdapfel',
+                                },
+                            ],
+                        },
+                    }),
+                `No Product with the id '999' could be found`,
+            ),
+        );
 
         it('addOptionGroupToProduct adds an option group', async () => {
             const result = await client.query<
@@ -366,39 +365,35 @@ describe('Product resolver', () => {
             expect(result.addOptionGroupToProduct.optionGroups[0].id).toBe('T_1');
         });
 
-        it('addOptionGroupToProduct errors with an invalid productId', async () => {
-            try {
-                await client.query<AddOptionGroupToProduct.Mutation, AddOptionGroupToProduct.Variables>(
-                    ADD_OPTION_GROUP_TO_PRODUCT,
-                    {
-                        optionGroupId: 'T_1',
-                        productId: '999',
-                    },
-                );
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No Product with the id '999' could be found`),
-                );
-            }
-        });
-
-        it('addOptionGroupToProduct errors with an invalid optionGroupId', async () => {
-            try {
-                await client.query<AddOptionGroupToProduct.Mutation, AddOptionGroupToProduct.Variables>(
-                    ADD_OPTION_GROUP_TO_PRODUCT,
-                    {
-                        optionGroupId: '999',
-                        productId: newProduct.id,
-                    },
-                );
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No ProductOptionGroup with the id '999' could be found`),
-                );
-            }
-        });
+        it(
+            'addOptionGroupToProduct errors with an invalid productId',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<AddOptionGroupToProduct.Mutation, AddOptionGroupToProduct.Variables>(
+                        ADD_OPTION_GROUP_TO_PRODUCT,
+                        {
+                            optionGroupId: 'T_1',
+                            productId: '999',
+                        },
+                    ),
+                `No Product with the id '999' could be found`,
+            ),
+        );
+
+        it(
+            'addOptionGroupToProduct errors with an invalid optionGroupId',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<AddOptionGroupToProduct.Mutation, AddOptionGroupToProduct.Variables>(
+                        ADD_OPTION_GROUP_TO_PRODUCT,
+                        {
+                            optionGroupId: '999',
+                            productId: newProduct.id,
+                        },
+                    ),
+                `No ProductOptionGroup with the id '999' could be found`,
+            ),
+        );
 
         it('removeOptionGroupFromProduct removes an option group', async () => {
             const result = await client.query<
@@ -411,58 +406,52 @@ describe('Product resolver', () => {
             expect(result.removeOptionGroupFromProduct.optionGroups.length).toBe(0);
         });
 
-        it('removeOptionGroupFromProduct errors with an invalid productId', async () => {
-            try {
-                await client.query<
-                    RemoveOptionGroupFromProduct.Mutation,
-                    RemoveOptionGroupFromProduct.Variables
-                >(REMOVE_OPTION_GROUP_FROM_PRODUCT, {
-                    optionGroupId: '1',
-                    productId: '999',
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No Product with the id '999' could be found`),
-                );
-            }
-        });
+        it(
+            'removeOptionGroupFromProduct errors with an invalid productId',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<
+                        RemoveOptionGroupFromProduct.Mutation,
+                        RemoveOptionGroupFromProduct.Variables
+                    >(REMOVE_OPTION_GROUP_FROM_PRODUCT, {
+                        optionGroupId: '1',
+                        productId: '999',
+                    }),
+                `No Product with the id '999' could be found`,
+            ),
+        );
 
         describe('variants', () => {
             let variants: ProductWithVariants.Variants[];
 
-            it('generateVariantsForProduct throws with an invalid productId', async () => {
-                try {
-                    await client.query<GenerateProductVariants.Mutation, GenerateProductVariants.Variables>(
-                        GENERATE_PRODUCT_VARIANTS,
-                        {
-                            productId: '999',
-                        },
-                    );
-                    fail('Should have thrown');
-                } catch (err) {
-                    expect(err.message).toEqual(
-                        expect.stringContaining(`No Product with the id '999' could be found`),
-                    );
-                }
-            });
+            it(
+                'generateVariantsForProduct throws with an invalid productId',
+                assertThrowsWithMessage(
+                    () =>
+                        client.query<GenerateProductVariants.Mutation, GenerateProductVariants.Variables>(
+                            GENERATE_PRODUCT_VARIANTS,
+                            {
+                                productId: '999',
+                            },
+                        ),
+                    `No Product with the id '999' could be found`,
+                ),
+            );
 
-            it('generateVariantsForProduct throws with an invalid defaultTaxCategoryId', async () => {
-                try {
-                    await client.query<GenerateProductVariants.Mutation, GenerateProductVariants.Variables>(
-                        GENERATE_PRODUCT_VARIANTS,
-                        {
-                            productId: newProduct.id,
-                            defaultTaxCategoryId: '999',
-                        },
-                    );
-                    fail('Should have thrown');
-                } catch (err) {
-                    expect(err.message).toEqual(
-                        expect.stringContaining(`No TaxCategory with the id '999' could be found`),
-                    );
-                }
-            });
+            it(
+                'generateVariantsForProduct throws with an invalid defaultTaxCategoryId',
+                assertThrowsWithMessage(
+                    () =>
+                        client.query<GenerateProductVariants.Mutation, GenerateProductVariants.Variables>(
+                            GENERATE_PRODUCT_VARIANTS,
+                            {
+                                productId: newProduct.id,
+                                defaultTaxCategoryId: '999',
+                            },
+                        ),
+                    `No TaxCategory with the id '999' could be found`,
+                ),
+            );
 
             it('generateVariantsForProduct generates variants', async () => {
                 const result = await client.query<
@@ -571,28 +560,26 @@ describe('Product resolver', () => {
                 expect(updatedVariant.facetValues[0].id).toBe('T_1');
             });
 
-            it('updateProductVariants throws with an invalid variant id', async () => {
-                try {
-                    await client.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
-                        UPDATE_PRODUCT_VARIANTS,
-                        {
-                            input: [
-                                {
-                                    id: 'T_999',
-                                    translations: variants[0].translations,
-                                    sku: 'ABC',
-                                    price: 432,
-                                },
-                            ],
-                        },
-                    );
-                    fail('Should have thrown');
-                } catch (err) {
-                    expect(err.message).toEqual(
-                        expect.stringContaining(`No ProductVariant with the id '999' could be found`),
-                    );
-                }
-            });
+            it(
+                'updateProductVariants throws with an invalid variant id',
+                assertThrowsWithMessage(
+                    () =>
+                        client.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
+                            UPDATE_PRODUCT_VARIANTS,
+                            {
+                                input: [
+                                    {
+                                        id: 'T_999',
+                                        translations: variants[0].translations,
+                                        sku: 'ABC',
+                                        price: 432,
+                                    },
+                                ],
+                            },
+                        ),
+                    `No ProductVariant with the id '999' could be found`,
+                ),
+            );
         });
     });
 
@@ -630,71 +617,63 @@ describe('Product resolver', () => {
             expect(result.products.items.map(c => c.id).includes(productToDelete.id)).toBe(false);
         });
 
-        it('updateProduct throws for deleted product', async () => {
-            try {
-                await client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
-                    input: {
-                        id: productToDelete.id,
-                        facetValueIds: ['T_1'],
-                    },
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No Product with the id '1' could be found`),
-                );
-            }
-        });
-
-        it('addOptionGroupToProduct throws for deleted product', async () => {
-            try {
-                await client.query<AddOptionGroupToProduct.Mutation, AddOptionGroupToProduct.Variables>(
-                    ADD_OPTION_GROUP_TO_PRODUCT,
-                    {
+        it(
+            'updateProduct throws for deleted product',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
+                        input: {
+                            id: productToDelete.id,
+                            facetValueIds: ['T_1'],
+                        },
+                    }),
+                `No Product with the id '1' could be found`,
+            ),
+        );
+
+        it(
+            'addOptionGroupToProduct throws for deleted product',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<AddOptionGroupToProduct.Mutation, AddOptionGroupToProduct.Variables>(
+                        ADD_OPTION_GROUP_TO_PRODUCT,
+                        {
+                            optionGroupId: 'T_1',
+                            productId: productToDelete.id,
+                        },
+                    ),
+                `No Product with the id '1' could be found`,
+            ),
+        );
+
+        it(
+            'removeOptionGroupToProduct throws for deleted product',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<
+                        RemoveOptionGroupFromProduct.Mutation,
+                        RemoveOptionGroupFromProduct.Variables
+                    >(REMOVE_OPTION_GROUP_FROM_PRODUCT, {
                         optionGroupId: 'T_1',
                         productId: productToDelete.id,
-                    },
-                );
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No Product with the id '1' could be found`),
-                );
-            }
-        });
-
-        it('removeOptionGroupToProduct throws for deleted product', async () => {
-            try {
-                await client.query<
-                    RemoveOptionGroupFromProduct.Mutation,
-                    RemoveOptionGroupFromProduct.Variables
-                >(REMOVE_OPTION_GROUP_FROM_PRODUCT, {
-                    optionGroupId: 'T_1',
-                    productId: productToDelete.id,
-                });
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No Product with the id '1' could be found`),
-                );
-            }
-        });
-
-        it('generateVariantsForProduct throws for deleted product', async () => {
-            try {
-                await client.query<GenerateProductVariants.Mutation, GenerateProductVariants.Variables>(
-                    GENERATE_PRODUCT_VARIANTS,
-                    {
-                        productId: productToDelete.id,
-                    },
-                );
-                fail('Should have thrown');
-            } catch (err) {
-                expect(err.message).toEqual(
-                    expect.stringContaining(`No Product with the id '1' could be found`),
-                );
-            }
-        });
+                    }),
+                `No Product with the id '1' could be found`,
+            ),
+        );
+
+        it(
+            'generateVariantsForProduct throws for deleted product',
+            assertThrowsWithMessage(
+                () =>
+                    client.query<GenerateProductVariants.Mutation, GenerateProductVariants.Variables>(
+                        GENERATE_PRODUCT_VARIANTS,
+                        {
+                            productId: productToDelete.id,
+                        },
+                    ),
+                `No Product with the id '1' could be found`,
+            ),
+        );
     });
 });
 

+ 24 - 31
server/e2e/role.e2e-spec.ts

@@ -12,6 +12,7 @@ import {
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TestClient } from './test-client';
 import { TestServer } from './test-server';
+import { assertThrowsWithMessage } from './test-utils';
 
 describe('Role resolver', () => {
     const client = new TestClient();
@@ -87,14 +88,15 @@ describe('Role resolver', () => {
         ]);
     });
 
-    it('updateRole is not allowed for SuperAdmin role', async () => {
-        const superAdminRole = defaultRoles.find(r => r.code === SUPER_ADMIN_ROLE_CODE);
-        if (!superAdminRole) {
-            fail(`Could not find SuperAdmin role`);
-            return;
-        }
-        try {
-            const result = await client.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
+    it(
+        'updateRole is not allowed for SuperAdmin role',
+        assertThrowsWithMessage(async () => {
+            const superAdminRole = defaultRoles.find(r => r.code === SUPER_ADMIN_ROLE_CODE);
+            if (!superAdminRole) {
+                fail(`Could not find SuperAdmin role`);
+                return;
+            }
+            return client.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
                 input: {
                     id: superAdminRole.id,
                     code: 'superadmin-modified',
@@ -102,22 +104,18 @@ describe('Role resolver', () => {
                     permissions: [Permission.Authenticated],
                 },
             });
-            fail(`Should throw`);
-        } catch (err) {
-            expect(err.message).toEqual(
-                expect.stringContaining(`The role '${SUPER_ADMIN_ROLE_CODE}' cannot be modified`),
-            );
-        }
-    });
-
-    it('updateRole is not allowed for Customer role', async () => {
-        const customerRole = defaultRoles.find(r => r.code === CUSTOMER_ROLE_CODE);
-        if (!customerRole) {
-            fail(`Could not find Customer role`);
-            return;
-        }
-        try {
-            const result = await client.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
+        }, `The role '${SUPER_ADMIN_ROLE_CODE}' cannot be modified`),
+    );
+
+    it(
+        'updateRole is not allowed for Customer role',
+        assertThrowsWithMessage(async () => {
+            const customerRole = defaultRoles.find(r => r.code === CUSTOMER_ROLE_CODE);
+            if (!customerRole) {
+                fail(`Could not find Customer role`);
+                return;
+            }
+            return client.query<UpdateRole.Mutation, UpdateRole.Variables>(UPDATE_ROLE, {
                 input: {
                     id: customerRole.id,
                     code: 'customer-modified',
@@ -125,11 +123,6 @@ describe('Role resolver', () => {
                     permissions: [Permission.Authenticated, Permission.DeleteAdministrator],
                 },
             });
-            fail(`Should throw`);
-        } catch (err) {
-            expect(err.message).toEqual(
-                expect.stringContaining(`The role '${CUSTOMER_ROLE_CODE}' cannot be modified`),
-            );
-        }
-    });
+        }, `The role '${CUSTOMER_ROLE_CODE}' cannot be modified`),
+    );
 });

+ 14 - 0
server/e2e/test-utils.ts

@@ -7,3 +7,17 @@ export function setTestEnvironment() {
 export function isTestEnvironment() {
     return !!process.env[E2E_TESTING_ENV_VARIABLE];
 }
+
+/**
+ * Helper method for creating tests which assert a given error message when the operation is attempted.
+ */
+export function assertThrowsWithMessage(operation: () => Promise<any>, message: string) {
+    return async () => {
+        try {
+            await operation();
+            fail('Should have thrown');
+        } catch (err) {
+            expect(err.message).toEqual(expect.stringContaining(message));
+        }
+    };
+}