소스 검색

chore(core): Add types to all e2e test queries

Relates to #92
Michael Bromley 6 년 전
부모
커밋
02e5dd1a23

+ 7 - 2
README.md

@@ -61,8 +61,13 @@ Vendure uses [TypeORM](http://typeorm.io), so it compatible will any database wh
 [graphql-code-generator](https://github.com/dotansimha/graphql-code-generator) is used to automatically create TypeScript interfaces
 for all GraphQL server operations and admin ui queries. These generated interfaces are used in both the admin ui and the server.
 
-Run `yarn codegen` to generate TypeScript interfaces based on these queries. The generated
-types are located at [`packages/common/src/generated-types.ts`](./packages/common/src/generated-types.ts) & [`packages/common/src/generated-shop-types.ts`](./packages/common/src/generated-shop-types.ts).
+Running `yarn codegen` will generate the following files:
+
+* [`packages/common/src/generated-types.ts`](./packages/common/src/generated-types.ts): Types, Inputs & resolver args relating to the Admin API
+* [`packages/common/src/generated-shop-types.ts`](./packages/common/src/generated-shop-types.ts): Types, Inputs & resolver args relating to the Shop API
+* [`admin-ui/src/app/common/generated-types.ts`](./admin-ui/src/app/common/generated-types.ts): Types & operations relating to the admin-ui queries & mutations.
+* [`packages/core/e2e/graphql/generated-e2e-admin-types.ts`](./packages/core/e2e/graphql/generated-e2e-admin-types.ts): Types used in e2e tests of the Admin API
+* [`packages/core/e2e/graphql/generated-e2e-shop-types.ts`](./packages/core/e2e/graphql/generated-e2e-shop-types.ts): Types used in e2e tests of the Shop API
 
 ### Testing
 

+ 1 - 1
admin-ui/src/app/common/generated-types.ts

@@ -1,5 +1,5 @@
 // tslint:disable
-// Generated in 2019-05-07T17:59:46+02:00
+// Generated in 2019-05-07T21:25:30+02:00
 
 export type Maybe<T> = T | null;
 /** All built-in and custom scalars, mapped to their actual values */

+ 1 - 1
packages/common/src/generated-shop-types.ts

@@ -1,5 +1,5 @@
 // tslint:disable
-// Generated in 2019-05-07T17:59:45+02:00
+// Generated in 2019-05-07T21:25:29+02:00
 
 export type Maybe<T> = T | null;
 /** All built-in and custom scalars, mapped to their actual values */

+ 1 - 1
packages/common/src/generated-types.ts

@@ -1,5 +1,5 @@
 // tslint:disable
-// Generated in 2019-05-07T17:59:45+02:00
+// Generated in 2019-05-07T21:25:27+02:00
 
 export type Maybe<T> = T | null;
 /** All built-in and custom scalars, mapped to their actual values */

+ 66 - 43
packages/core/e2e/collection.e2e-spec.ts

@@ -4,10 +4,7 @@ import gql from 'graphql-tag';
 import path from 'path';
 
 import { StringOperator } from '../src/common/configurable-operation';
-import {
-    facetValueCollectionFilter,
-    variantNameCollectionFilter,
-} from '../src/config/collection/default-collection-filters';
+import { facetValueCollectionFilter, variantNameCollectionFilter } from '../src/config/collection/default-collection-filters';
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { COLLECTION_FRAGMENT, FACET_VALUE_FRAGMENT } from './graphql/fragments';
@@ -16,25 +13,24 @@ import {
     ConfigArgType,
     CreateCollection,
     CreateCollectionInput,
-    FacetValue,
+    CreateCollectionSelectVariants,
+    FacetValueFragment,
     GetAssetList,
     GetCollection,
+    GetCollectionBreadcrumbs,
     GetCollectionProducts,
+    GetCollections,
+    GetCollectionsForProducts,
+    GetFacetValues,
+    GetProductsWithVariantIds,
     LanguageCode,
     MoveCollection,
-    ProductWithVariants,
     SortOrder,
     UpdateCollection,
     UpdateProduct,
     UpdateProductVariants,
 } from './graphql/generated-e2e-admin-types';
-import {
-    CREATE_COLLECTION,
-    GET_ASSET_LIST,
-    UPDATE_COLLECTION,
-    UPDATE_PRODUCT,
-    UPDATE_PRODUCT_VARIANTS,
-} from './graphql/shared-definitions';
+import { CREATE_COLLECTION, GET_ASSET_LIST, UPDATE_COLLECTION, UPDATE_PRODUCT, UPDATE_PRODUCT_VARIANTS } from './graphql/shared-definitions';
 import { TestAdminClient } from './test-client';
 import { TestServer } from './test-server';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
@@ -43,7 +39,7 @@ describe('Collection resolver', () => {
     const client = new TestAdminClient();
     const server = new TestServer();
     let assets: GetAssetList.Items[];
-    let facetValues: FacetValue.Fragment[];
+    let facetValues: FacetValueFragment[];
     let electronicsCollection: Collection.Fragment;
     let computersCollection: Collection.Fragment;
     let pearCollection: Collection.Fragment;
@@ -62,10 +58,10 @@ describe('Collection resolver', () => {
             },
         });
         assets = assetsResult.assets.items;
-        const facetValuesResult = await client.query(GET_FACET_VALUES);
+        const facetValuesResult = await client.query<GetFacetValues.Query>(GET_FACET_VALUES);
         facetValues = facetValuesResult.facets.items.reduce(
-            (values: any, facet: any) => [...values, ...facet.values],
-            [],
+            (values, facet) => [...values, ...facet.values],
+            [] as FacetValueFragment[],
         );
     }, TEST_SETUP_TIMEOUT_MS);
 
@@ -170,9 +166,12 @@ describe('Collection resolver', () => {
     });
 
     it('breadcrumbs', async () => {
-        const result = await client.query(GET_COLLECTION_BREADCRUMBS, {
-            id: pearCollection.id,
-        });
+        const result = await client.query<GetCollectionBreadcrumbs.Query, GetCollectionBreadcrumbs.Variables>(
+            GET_COLLECTION_BREADCRUMBS,
+            {
+                id: pearCollection.id,
+            },
+        );
         if (!result.collection) {
             fail(`did not return the collection`);
             return;
@@ -186,9 +185,12 @@ describe('Collection resolver', () => {
     });
 
     it('breadcrumbs for root collection', async () => {
-        const result = await client.query(GET_COLLECTION_BREADCRUMBS, {
-            id: 'T_1',
-        });
+        const result = await client.query<GetCollectionBreadcrumbs.Query, GetCollectionBreadcrumbs.Variables>(
+            GET_COLLECTION_BREADCRUMBS,
+            {
+                id: 'T_1',
+            },
+        );
         if (!result.collection) {
             fail(`did not return the collection`);
             return;
@@ -228,12 +230,15 @@ describe('Collection resolver', () => {
             expect(result.moveCollection.parent!.id).toBe(electronicsCollection.id);
 
             const positions = await getChildrenOf(electronicsCollection.id);
-            expect(positions.map((i: any) => i.id)).toEqual([pearCollection.id, computersCollection.id]);
+            expect(positions.map(i => i.id)).toEqual([pearCollection.id, computersCollection.id]);
         });
 
         it('re-evaluates Collection contents on move', async () => {
-            const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
-            expect(result.collection.productVariants.items.map((i: any) => i.name)).toEqual([
+            const result = await client.query<GetCollectionProducts.Query, GetCollectionProducts.Variables>(
+                GET_COLLECTION_PRODUCT_VARIANTS,
+                { id: pearCollection.id },
+            );
+            expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                 'Laptop 13 inch 8GB',
                 'Laptop 15 inch 8GB',
                 'Laptop 13 inch 16GB',
@@ -252,7 +257,7 @@ describe('Collection resolver', () => {
             });
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
-            expect(afterResult.map((i: any) => i.id)).toEqual([computersCollection.id, pearCollection.id]);
+            expect(afterResult.map(i => i.id)).toEqual([computersCollection.id, pearCollection.id]);
         });
 
         it('alters the position in the current parent 2', async () => {
@@ -265,7 +270,7 @@ describe('Collection resolver', () => {
             });
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
-            expect(afterResult.map((i: any) => i.id)).toEqual([pearCollection.id, computersCollection.id]);
+            expect(afterResult.map(i => i.id)).toEqual([pearCollection.id, computersCollection.id]);
         });
 
         it('corrects an out-of-bounds negative index value', async () => {
@@ -278,7 +283,7 @@ describe('Collection resolver', () => {
             });
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
-            expect(afterResult.map((i: any) => i.id)).toEqual([pearCollection.id, computersCollection.id]);
+            expect(afterResult.map(i => i.id)).toEqual([pearCollection.id, computersCollection.id]);
         });
 
         it('corrects an out-of-bounds positive index value', async () => {
@@ -291,7 +296,7 @@ describe('Collection resolver', () => {
             });
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
-            expect(afterResult.map((i: any) => i.id)).toEqual([computersCollection.id, pearCollection.id]);
+            expect(afterResult.map(i => i.id)).toEqual([computersCollection.id, pearCollection.id]);
         });
 
         it(
@@ -325,14 +330,17 @@ describe('Collection resolver', () => {
         );
 
         async function getChildrenOf(parentId: string): Promise<Array<{ name: string; id: string }>> {
-            const result = await client.query(GET_COLLECTIONS);
-            return result.collections.items.filter((i: any) => i.parent.id === parentId);
+            const result = await client.query<GetCollections.Query>(GET_COLLECTIONS);
+            return result.collections.items.filter(i => i.parent!.id === parentId);
         }
     });
 
     describe('filters', () => {
         it('Collection with no filters has no productVariants', async () => {
-            const result = await client.query(CREATE_COLLECTION_SELECT_VARIANTS, {
+            const result = await client.query<
+                CreateCollectionSelectVariants.Mutation,
+                CreateCollectionSelectVariants.Variables
+            >(CREATE_COLLECTION_SELECT_VARIANTS, {
                 input: {
                     translations: [{ languageCode: LanguageCode.en, name: 'Empty', description: '' }],
                     filters: [],
@@ -343,10 +351,13 @@ describe('Collection resolver', () => {
 
         describe('facetValue filter', () => {
             it('electronics', async () => {
-                const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, {
+                const result = await client.query<
+                    GetCollectionProducts.Query,
+                    GetCollectionProducts.Variables
+                >(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: electronicsCollection.id,
                 });
-                expect(result.collection.productVariants.items.map((i: any) => i.name)).toEqual([
+                expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                     'Laptop 13 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 13 inch 16GB',
@@ -372,10 +383,13 @@ describe('Collection resolver', () => {
             });
 
             it('computers', async () => {
-                const result = await client.query(GET_COLLECTION_PRODUCT_VARIANTS, {
+                const result = await client.query<
+                    GetCollectionProducts.Query,
+                    GetCollectionProducts.Variables
+                >(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: computersCollection.id,
                 });
-                expect(result.collection.productVariants.items.map((i: any) => i.name)).toEqual([
+                expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                     'Laptop 13 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 13 inch 16GB',
@@ -397,7 +411,10 @@ describe('Collection resolver', () => {
             });
 
             it('photo and pear', async () => {
-                const result = await client.query(CREATE_COLLECTION_SELECT_VARIANTS, {
+                const result = await client.query<
+                    CreateCollectionSelectVariants.Mutation,
+                    CreateCollectionSelectVariants.Variables
+                >(CREATE_COLLECTION_SELECT_VARIANTS, {
                     input: {
                         translations: [
                             { languageCode: LanguageCode.en, name: 'Photo Pear', description: '' },
@@ -418,7 +435,7 @@ describe('Collection resolver', () => {
                         ],
                     } as CreateCollectionInput,
                 });
-                expect(result.createCollection.productVariants.items.map((i: any) => i.name)).toEqual([
+                expect(result.createCollection.productVariants.items.map(i => i.name)).toEqual([
                     'Instant Camera',
                 ]);
             });
@@ -462,7 +479,10 @@ describe('Collection resolver', () => {
             it('contains operator', async () => {
                 const collection = await createVariantNameFilteredCollection('contains', 'camera');
 
-                const result = await client.query<GetCollectionProducts.Query, GetCollectionProducts.Variables>(GET_COLLECTION_PRODUCT_VARIANTS, {
+                const result = await client.query<
+                    GetCollectionProducts.Query,
+                    GetCollectionProducts.Variables
+                >(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: collection.id,
                 });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
@@ -532,10 +552,10 @@ describe('Collection resolver', () => {
         });
 
         describe('re-evaluation of contents on changes', () => {
-            let products: ProductWithVariants.Fragment[];
+            let products: GetProductsWithVariantIds.Items[];
 
             beforeAll(async () => {
-                const result = await client.query(gql`
+                const result = await client.query<GetProductsWithVariantIds.Query>(gql`
                     query GetProductsWithVariantIds {
                         products {
                             items {
@@ -640,7 +660,10 @@ describe('Collection resolver', () => {
 
     describe('Product collections property', () => {
         it('returns all collections to which the Product belongs', async () => {
-            const result = await client.query(GET_COLLECTIONS_FOR_PRODUCTS, { term: 'camera' });
+            const result = await client.query<
+                GetCollectionsForProducts.Query,
+                GetCollectionsForProducts.Variables
+            >(GET_COLLECTIONS_FOR_PRODUCTS, { term: 'camera' });
             expect(result.products.items[0].collections).toEqual([
                 { id: 'T_3', name: 'Electronics' },
                 { id: 'T_5', name: 'Pear' },

+ 11 - 3
packages/core/e2e/country.e2e-spec.ts

@@ -3,7 +3,15 @@ import path from 'path';
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { COUNTRY_FRAGMENT } from './graphql/fragments';
-import { DeletionResult, GetCountryList, LanguageCode, GetCountry, UpdateCountry, CreateCountry } from './graphql/generated-e2e-admin-types';
+import {
+    CreateCountry,
+    DeleteCountry,
+    DeletionResult,
+    GetCountry,
+    GetCountryList,
+    LanguageCode,
+    UpdateCountry,
+} from './graphql/generated-e2e-admin-types';
 import { GET_COUNTRY_LIST, UPDATE_COUNTRY } from './graphql/shared-definitions';
 import { TestAdminClient } from './test-client';
 import { TestServer } from './test-server';
@@ -71,7 +79,7 @@ describe('Facet resolver', () => {
 
     describe('deletion', () => {
         it('deletes Country not used in any address', async () => {
-            const result1 = await client.query(DELETE_COUNTRY, { id: AT.id });
+            const result1 = await client.query<DeleteCountry.Mutation, DeleteCountry.Variables>(DELETE_COUNTRY, { id: AT.id });
 
             expect(result1.deleteCountry).toEqual({
                 result: DeletionResult.DELETED,
@@ -83,7 +91,7 @@ describe('Facet resolver', () => {
         });
 
         it('does not delete Country that is used in one or more addresses', async () => {
-            const result1 = await client.query(DELETE_COUNTRY, { id: GB.id });
+            const result1 = await client.query<DeleteCountry.Mutation, DeleteCountry.Variables>(DELETE_COUNTRY, { id: GB.id });
 
             expect(result1.deleteCountry).toEqual({
                 result: DeletionResult.NOT_DELETED,

+ 101 - 66
packages/core/e2e/customer.e2e-spec.ts

@@ -3,13 +3,24 @@ import gql from 'graphql-tag';
 import path from 'path';
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
-import { CreateAddress, DeletionResult, GetCustomer, GetCustomerList, UpdateCustomer } from './graphql/generated-e2e-admin-types';
+import { CUSTOMER_FRAGMENT } from './graphql/fragments';
+import {
+    CreateAddress,
+    DeleteCustomer,
+    DeleteCustomerAddress,
+    DeletionResult,
+    GetCustomer,
+    GetCustomerList,
+    GetCustomerOrders,
+    UpdateAddress,
+    UpdateCustomer,
+} from './graphql/generated-e2e-admin-types';
+import { AddItemToOrder } from './graphql/generated-e2e-shop-types';
 import { GET_CUSTOMER, GET_CUSTOMER_LIST } from './graphql/shared-definitions';
 import { ADD_ITEM_TO_ORDER } from './graphql/shop-definitions';
 import { TestAdminClient, TestShopClient } from './test-client';
 import { TestServer } from './test-server';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
-import { CUSTOMER_FRAGMENT } from './graphql/fragments';
 
 // tslint:disable:no-non-null-assertion
 
@@ -53,7 +64,7 @@ describe('Customer resolver', () => {
             'createCustomerAddress throws on invalid countryCode',
             assertThrowsWithMessage(
                 () =>
-                    adminClient.query(CREATE_ADDRESS, {
+                    adminClient.query<CreateAddress.Mutation, CreateAddress.Variables>(CREATE_ADDRESS, {
                         id: firstCustomer.id,
                         input: {
                             streetLine1: 'streetLine1',
@@ -65,22 +76,25 @@ describe('Customer resolver', () => {
         );
 
         it('createCustomerAddress creates a new address', async () => {
-            const result = await adminClient.query(CREATE_ADDRESS, {
-                id: firstCustomer.id,
-                input: {
-                    fullName: 'fullName',
-                    company: 'company',
-                    streetLine1: 'streetLine1',
-                    streetLine2: 'streetLine2',
-                    city: 'city',
-                    province: 'province',
-                    postalCode: 'postalCode',
-                    countryCode: 'GB',
-                    phoneNumber: 'phoneNumber',
-                    defaultShippingAddress: false,
-                    defaultBillingAddress: false,
+            const result = await adminClient.query<CreateAddress.Mutation, CreateAddress.Variables>(
+                CREATE_ADDRESS,
+                {
+                    id: firstCustomer.id,
+                    input: {
+                        fullName: 'fullName',
+                        company: 'company',
+                        streetLine1: 'streetLine1',
+                        streetLine2: 'streetLine2',
+                        city: 'city',
+                        province: 'province',
+                        postalCode: 'postalCode',
+                        countryCode: 'GB',
+                        phoneNumber: 'phoneNumber',
+                        defaultShippingAddress: false,
+                        defaultBillingAddress: false,
+                    },
                 },
-            });
+            );
             expect(omit(result.createCustomerAddress, ['id'])).toEqual({
                 fullName: 'fullName',
                 company: 'company',
@@ -109,27 +123,33 @@ describe('Customer resolver', () => {
         });
 
         it('updateCustomerAddress updates the country', async () => {
-            const result = await adminClient.query(UPDATE_ADDRESS, {
-                input: {
-                    id: firstCustomerAddressIds[0],
-                    countryCode: 'AT',
+            const result = await adminClient.query<UpdateAddress.Mutation, UpdateAddress.Variables>(
+                UPDATE_ADDRESS,
+                {
+                    input: {
+                        id: firstCustomerAddressIds[0],
+                        countryCode: 'AT',
+                    },
                 },
-            });
+            );
             expect(result.updateCustomerAddress.country).toEqual({
-                    code: 'AT',
-                    name: 'Austria',
+                code: 'AT',
+                name: 'Austria',
             });
         });
 
         it('updateCustomerAddress allows only a single default address', async () => {
             // set the first customer's second address to be default
-            const result1 = await adminClient.query(UPDATE_ADDRESS, {
-                input: {
-                    id: firstCustomerAddressIds[1],
-                    defaultShippingAddress: true,
-                    defaultBillingAddress: true,
+            const result1 = await adminClient.query<UpdateAddress.Mutation, UpdateAddress.Variables>(
+                UPDATE_ADDRESS,
+                {
+                    input: {
+                        id: firstCustomerAddressIds[1],
+                        defaultShippingAddress: true,
+                        defaultBillingAddress: true,
+                    },
                 },
-            });
+            );
             expect(result1.updateCustomerAddress.defaultShippingAddress).toBe(true);
             expect(result1.updateCustomerAddress.defaultBillingAddress).toBe(true);
 
@@ -141,13 +161,16 @@ describe('Customer resolver', () => {
             expect(result2.customer!.addresses![0].defaultBillingAddress).toBe(false);
 
             // set the first customer's first address to be default
-            const result3 = await adminClient.query(UPDATE_ADDRESS, {
-                input: {
-                    id: firstCustomerAddressIds[0],
-                    defaultShippingAddress: true,
-                    defaultBillingAddress: true,
+            const result3 = await adminClient.query<UpdateAddress.Mutation, UpdateAddress.Variables>(
+                UPDATE_ADDRESS,
+                {
+                    input: {
+                        id: firstCustomerAddressIds[0],
+                        defaultShippingAddress: true,
+                        defaultBillingAddress: true,
+                    },
                 },
-            });
+            );
             expect(result3.updateCustomerAddress.defaultShippingAddress).toBe(true);
             expect(result3.updateCustomerAddress.defaultBillingAddress).toBe(true);
 
@@ -165,13 +188,16 @@ describe('Customer resolver', () => {
             const secondCustomerAddressId = result5.customer!.addresses![0].id;
 
             // set the second customer's address to be default
-            const result6 = await adminClient.query(UPDATE_ADDRESS, {
-                input: {
-                    id: secondCustomerAddressId,
-                    defaultShippingAddress: true,
-                    defaultBillingAddress: true,
+            const result6 = await adminClient.query<UpdateAddress.Mutation, UpdateAddress.Variables>(
+                UPDATE_ADDRESS,
+                {
+                    input: {
+                        id: secondCustomerAddressId,
+                        defaultShippingAddress: true,
+                        defaultBillingAddress: true,
+                    },
                 },
-            });
+            );
             expect(result6.updateCustomerAddress.defaultShippingAddress).toBe(true);
             expect(result6.updateCustomerAddress.defaultBillingAddress).toBe(true);
 
@@ -186,15 +212,18 @@ describe('Customer resolver', () => {
         });
 
         it('createCustomerAddress with true defaults unsets existing defaults', async () => {
-            const result1 = await adminClient.query(CREATE_ADDRESS, {
-                id: firstCustomer.id,
-                input: {
-                    streetLine1: 'new default streetline',
-                    countryCode: 'GB',
-                    defaultShippingAddress: true,
-                    defaultBillingAddress: true,
+            const result1 = await adminClient.query<CreateAddress.Mutation, CreateAddress.Variables>(
+                CREATE_ADDRESS,
+                {
+                    id: firstCustomer.id,
+                    input: {
+                        streetLine1: 'new default streetline',
+                        countryCode: 'GB',
+                        defaultShippingAddress: true,
+                        defaultBillingAddress: true,
+                    },
                 },
-            });
+            );
             expect(omit(result1.createCustomerAddress, ['id'])).toEqual({
                 fullName: '',
                 company: '',
@@ -226,7 +255,10 @@ describe('Customer resolver', () => {
         });
 
         it('deleteCustomerAddress on default address resets defaults', async () => {
-            const result = await adminClient.query(
+            const result = await adminClient.query<
+                DeleteCustomerAddress.Mutation,
+                DeleteCustomerAddress.Variables
+            >(
                 gql`
                     mutation DeleteCustomerAddress($id: ID!) {
                         deleteCustomerAddress(id: $id)
@@ -253,21 +285,27 @@ describe('Customer resolver', () => {
             // log in as first customer
             await shopClient.asUserWithCredentials(firstCustomer.emailAddress, 'test');
             // add an item to the order to create an order
-            const result1 = await shopClient.query(ADD_ITEM_TO_ORDER, {
+            const { addItemToOrder } = await shopClient.query<
+                AddItemToOrder.Mutation,
+                AddItemToOrder.Variables
+            >(ADD_ITEM_TO_ORDER, {
                 productVariantId: 'T_1',
                 quantity: 1,
             });
 
-            const result2 = await adminClient.query(GET_CUSTOMER_ORDERS, { id: firstCustomer.id });
+            const { customer } = await adminClient.query<
+                GetCustomerOrders.Query,
+                GetCustomerOrders.Variables
+            >(GET_CUSTOMER_ORDERS, { id: firstCustomer.id });
 
-            expect(result2.customer.orders.totalItems).toBe(1);
-            expect(result2.customer.orders.items[0].id).toBe(result1.addItemToOrder.id);
+            expect(customer!.orders.totalItems).toBe(1);
+            expect(customer!.orders.items[0].id).toBe(addItemToOrder!.id);
         });
     });
 
     describe('deletion', () => {
         it('deletes a customer', async () => {
-            const result = await adminClient.query(DELETE_CUSTOMER, { id: thirdCustomer.id });
+            const result = await adminClient.query<DeleteCustomer.Mutation, DeleteCustomer.Variables>(DELETE_CUSTOMER, { id: thirdCustomer.id });
 
             expect(result.deleteCustomer).toEqual({ result: DeletionResult.DELETED });
         });
@@ -306,16 +344,13 @@ describe('Customer resolver', () => {
             'createCustomerAddress throws for deleted customer',
             assertThrowsWithMessage(
                 () =>
-                    adminClient.query<CreateAddress.Mutation, CreateAddress.Variables>(
-                        CREATE_ADDRESS,
-                        {
-                            id: thirdCustomer.id,
-                            input: {
-                                streetLine1: 'test',
-                                countryCode: 'GB',
-                            },
+                    adminClient.query<CreateAddress.Mutation, CreateAddress.Variables>(CREATE_ADDRESS, {
+                        id: thirdCustomer.id,
+                        input: {
+                            streetLine1: 'test',
+                            countryCode: 'GB',
                         },
-                    ),
+                    }),
                 `No Customer with the id '3' could be found`,
             ),
         );

+ 9 - 7
packages/core/e2e/default-search-plugin.e2e-spec.ts

@@ -12,6 +12,8 @@ import {
     CreateCollection,
     CreateFacet,
     LanguageCode,
+    SearchFacetValues,
+    SearchGetPrices,
     SearchInput,
     UpdateCollection,
     UpdateProduct,
@@ -118,7 +120,7 @@ describe('Default search plugin', () => {
     }
 
     async function testSinglePrices(client: SimpleGraphQLClient) {
-        const result = await client.query(SEARCH_GET_PRICES, {
+        const result = await client.query<SearchGetPrices.Query, SearchGetPrices.Variables>(SEARCH_GET_PRICES, {
             input: {
                 groupByProduct: false,
                 take: 3,
@@ -141,7 +143,7 @@ describe('Default search plugin', () => {
     }
 
     async function testPriceRanges(client: SimpleGraphQLClient) {
-        const result = await client.query(SEARCH_GET_PRICES, {
+        const result = await client.query<SearchGetPrices.Query, SearchGetPrices.Variables>(SEARCH_GET_PRICES, {
             input: {
                 groupByProduct: true,
                 take: 3,
@@ -179,7 +181,7 @@ describe('Default search plugin', () => {
         it('price ranges', () => testPriceRanges(shopClient));
 
         it('returns correct facetValues when not grouped by product', async () => {
-            const result = await shopClient.query(SEARCH_GET_FACET_VALUES, {
+            const result = await shopClient.query<SearchFacetValues.Query, SearchFacetValues.Variables>(SEARCH_GET_FACET_VALUES, {
                 input: {
                     groupByProduct: false,
                 },
@@ -195,7 +197,7 @@ describe('Default search plugin', () => {
         });
 
         it('returns correct facetValues when grouped by product', async () => {
-            const result = await shopClient.query(SEARCH_GET_FACET_VALUES, {
+            const result = await shopClient.query<SearchFacetValues.Query, SearchFacetValues.Variables>(SEARCH_GET_FACET_VALUES, {
                 input: {
                     groupByProduct: true,
                 },
@@ -231,7 +233,7 @@ describe('Default search plugin', () => {
                 },
             });
 
-            const result = await shopClient.query(SEARCH_GET_FACET_VALUES, {
+            const result = await shopClient.query<SearchFacetValues.Query, SearchFacetValues.Variables>(SEARCH_GET_FACET_VALUES, {
                 input: {
                     groupByProduct: true,
                 },
@@ -277,7 +279,7 @@ describe('Default search plugin', () => {
         });
 
         it('encodes collectionIds', async () => {
-            const result = await shopClient.query(SEARCH_PRODUCTS_SHOP, {
+            const result = await shopClient.query<SearchProductsShop.Query, SearchProductsShop.Variables>(SEARCH_PRODUCTS_SHOP, {
                 input: {
                     groupByProduct: false,
                     term: 'cactus',
@@ -426,7 +428,7 @@ describe('Default search plugin', () => {
                     value: 50,
                 },
             });
-            const result = await adminClient.query(SEARCH_GET_PRICES, {
+            const result = await adminClient.query<SearchGetPrices.Query, SearchGetPrices.Variables>(SEARCH_GET_PRICES, {
                 input: {
                     groupByProduct: true,
                     term: 'laptop',

+ 31 - 18
packages/core/e2e/facet.e2e-spec.ts

@@ -6,11 +6,13 @@ import { FACET_VALUE_FRAGMENT, FACET_WITH_VALUES_FRAGMENT } from './graphql/frag
 import {
     CreateFacet,
     CreateFacetValues,
+    DeleteFacet,
+    DeleteFacetValues,
     DeletionResult,
     FacetWithValues,
     GetFacetList,
     GetFacetWithValues,
-    GetProductList,
+    GetProductListWithVariants,
     GetProductWithVariants,
     LanguageCode,
     UpdateFacet,
@@ -141,11 +143,13 @@ describe('Facet resolver', () => {
     });
 
     describe('deletion', () => {
-        let products: Array<GetProductList.Items & { variants: Array<{ id: string; name: string }> }>;
+        let products: GetProductListWithVariants.Items[];
 
         beforeAll(async () => {
             // add the FacetValues to products and variants
-            const result1 = await client.query(GET_PRODUCTS_LIST_WITH_VARIANTS);
+            const result1 = await client.query<GetProductListWithVariants.Query>(
+                GET_PRODUCTS_LIST_WITH_VARIANTS,
+            );
             products = result1.products.items;
 
             await client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
@@ -177,10 +181,13 @@ describe('Facet resolver', () => {
 
         it('deleteFacetValues deletes unused facetValue', async () => {
             const facetValueToDelete = speakerTypeFacet.values[2];
-            const result1 = await client.query(DELETE_FACET_VALUES, {
-                ids: [facetValueToDelete.id],
-                force: false,
-            });
+            const result1 = await client.query<DeleteFacetValues.Mutation, DeleteFacetValues.Variables>(
+                DELETE_FACET_VALUES,
+                {
+                    ids: [facetValueToDelete.id],
+                    force: false,
+                },
+            );
             const result2 = await client.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
                 GET_FACET_WITH_VALUES,
                 {
@@ -200,10 +207,13 @@ describe('Facet resolver', () => {
 
         it('deleteFacetValues for FacetValue in use returns NOT_DELETED', async () => {
             const facetValueToDelete = speakerTypeFacet.values[0];
-            const result1 = await client.query(DELETE_FACET_VALUES, {
-                ids: [facetValueToDelete.id],
-                force: false,
-            });
+            const result1 = await client.query<DeleteFacetValues.Mutation, DeleteFacetValues.Variables>(
+                DELETE_FACET_VALUES,
+                {
+                    ids: [facetValueToDelete.id],
+                    force: false,
+                },
+            );
             const result2 = await client.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
                 GET_FACET_WITH_VALUES,
                 {
@@ -223,10 +233,13 @@ describe('Facet resolver', () => {
 
         it('deleteFacetValues for FacetValue in use can be force deleted', async () => {
             const facetValueToDelete = speakerTypeFacet.values[0];
-            const result1 = await client.query(DELETE_FACET_VALUES, {
-                ids: [facetValueToDelete.id],
-                force: true,
-            });
+            const result1 = await client.query<DeleteFacetValues.Mutation, DeleteFacetValues.Variables>(
+                DELETE_FACET_VALUES,
+                {
+                    ids: [facetValueToDelete.id],
+                    force: true,
+                },
+            );
 
             expect(result1.deleteFacetValues).toEqual([
                 {
@@ -255,7 +268,7 @@ describe('Facet resolver', () => {
         });
 
         it('deleteFacet that is in use returns NOT_DELETED', async () => {
-            const result1 = await client.query(DELETE_FACET, { id: speakerTypeFacet.id, force: false });
+            const result1 = await client.query<DeleteFacet.Mutation, DeleteFacet.Variables>(DELETE_FACET, { id: speakerTypeFacet.id, force: false });
             const result2 = await client.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
                 GET_FACET_WITH_VALUES,
                 {
@@ -272,7 +285,7 @@ describe('Facet resolver', () => {
         });
 
         it('deleteFacet that is in use can be force deleted', async () => {
-            const result1 = await client.query(DELETE_FACET, { id: speakerTypeFacet.id, force: true });
+            const result1 = await client.query<DeleteFacet.Mutation, DeleteFacet.Variables>(DELETE_FACET, { id: speakerTypeFacet.id, force: true });
 
             expect(result1.deleteFacet).toEqual({
                 result: DeletionResult.DELETED,
@@ -310,7 +323,7 @@ export const GET_FACET_WITH_VALUES = gql`
 `;
 
 const DELETE_FACET_VALUES = gql`
-    mutation DeleteFacetValue($ids: [ID!]!, $force: Boolean) {
+    mutation DeleteFacetValues($ids: [ID!]!, $force: Boolean) {
         deleteFacetValues(ids: $ids, force: $force) {
             result
             message

+ 16 - 16
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -1,5 +1,5 @@
 // tslint:disable
-// Generated in 2019-05-07T17:59:48+02:00
+// Generated in 2019-05-07T21:25:31+02:00
 
 export type Maybe<T> = T | null;
 /** All built-in and custom scalars, mapped to their actual values */
@@ -2885,11 +2885,6 @@ export type GetCustomerCountQuery = { __typename?: 'Query' } & {
     customers: { __typename?: 'CustomerList' } & Pick<CustomerList, 'totalItems'>;
 };
 
-export type CurrentUserFragment = { __typename?: 'CurrentUser' } & Pick<
-    CurrentUser,
-    'id' | 'identifier' | 'channelTokens'
->;
-
 export type GetProductsWithVariantIdsQueryVariables = {};
 
 export type GetProductsWithVariantIdsQuery = { __typename?: 'Query' } & {
@@ -3175,12 +3170,12 @@ export type GetFacetWithValuesQuery = { __typename?: 'Query' } & {
     facet: Maybe<{ __typename?: 'Facet' } & FacetWithValuesFragment>;
 };
 
-export type DeleteFacetValueMutationVariables = {
+export type DeleteFacetValuesMutationVariables = {
     ids: Array<Scalars['ID']>;
     force?: Maybe<Scalars['Boolean']>;
 };
 
-export type DeleteFacetValueMutation = { __typename?: 'Mutation' } & {
+export type DeleteFacetValuesMutation = { __typename?: 'Mutation' } & {
     deleteFacetValues: Array<
         { __typename?: 'DeletionResponse' } & Pick<DeletionResponse, 'result' | 'message'>
     >;
@@ -3476,6 +3471,11 @@ export type TaxRateFragment = { __typename?: 'TaxRate' } & Pick<
         customerGroup: Maybe<{ __typename?: 'CustomerGroup' } & Pick<CustomerGroup, 'id' | 'name'>>;
     };
 
+export type CurrentUserFragment = { __typename?: 'CurrentUser' } & Pick<
+    CurrentUser,
+    'id' | 'identifier' | 'channelTokens'
+>;
+
 export type CreateAdministratorMutationVariables = {
     input: CreateAdministratorInput;
 };
@@ -4096,10 +4096,6 @@ export namespace GetCustomerCount {
     export type Customers = GetCustomerCountQuery['customers'];
 }
 
-export namespace CurrentUser {
-    export type Fragment = CurrentUserFragment;
-}
-
 export namespace GetProductsWithVariantIds {
     export type Variables = GetProductsWithVariantIdsQueryVariables;
     export type Query = GetProductsWithVariantIdsQuery;
@@ -4295,10 +4291,10 @@ export namespace GetFacetWithValues {
     export type Facet = FacetWithValuesFragment;
 }
 
-export namespace DeleteFacetValue {
-    export type Variables = DeleteFacetValueMutationVariables;
-    export type Mutation = DeleteFacetValueMutation;
-    export type DeleteFacetValues = NonNullable<DeleteFacetValueMutation['deleteFacetValues'][0]>;
+export namespace DeleteFacetValues {
+    export type Variables = DeleteFacetValuesMutationVariables;
+    export type Mutation = DeleteFacetValuesMutation;
+    export type DeleteFacetValues = NonNullable<DeleteFacetValuesMutation['deleteFacetValues'][0]>;
 }
 
 export namespace DeleteFacet {
@@ -4456,6 +4452,10 @@ export namespace TaxRate {
     export type CustomerGroup = NonNullable<TaxRateFragment['customerGroup']>;
 }
 
+export namespace CurrentUser {
+    export type Fragment = CurrentUserFragment;
+}
+
 export namespace CreateAdministrator {
     export type Variables = CreateAdministratorMutationVariables;
     export type Mutation = CreateAdministratorMutation;

+ 1 - 1
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -1,5 +1,5 @@
 // tslint:disable
-// Generated in 2019-05-07T17:59:46+02:00
+// Generated in 2019-05-07T21:25:29+02:00
 
 export type Maybe<T> = T | null;
 /** All built-in and custom scalars, mapped to their actual values */

+ 3 - 2
packages/core/e2e/order.e2e-spec.ts

@@ -5,6 +5,7 @@ import path from 'path';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { ORDER_FRAGMENT, ORDER_WITH_LINES_FRAGMENT } from './graphql/fragments';
 import { GetCustomerList, GetOrder, GetOrderList } from './graphql/generated-e2e-admin-types';
+import { AddItemToOrder } from './graphql/generated-e2e-shop-types';
 import { GET_CUSTOMER_LIST } from './graphql/shared-definitions';
 import { ADD_ITEM_TO_ORDER } from './graphql/shop-definitions';
 import { TestAdminClient, TestShopClient } from './test-client';
@@ -35,12 +36,12 @@ describe('Orders resolver', () => {
         );
         customers = result.customers.items;
         await shopClient.asUserWithCredentials(customers[0].emailAddress, password);
-        await shopClient.query(ADD_ITEM_TO_ORDER, {
+        await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
             productVariantId: 'T_1',
             quantity: 1,
         });
         await shopClient.asUserWithCredentials(customers[1].emailAddress, password);
-        await shopClient.query(ADD_ITEM_TO_ORDER, {
+        await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
             productVariantId: 'T_2',
             quantity: 1,
         });

+ 15 - 5
packages/core/e2e/product.e2e-spec.ts

@@ -4,12 +4,22 @@ import path from 'path';
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { PRODUCT_WITH_VARIANTS_FRAGMENT } from './graphql/fragments';
-import { DeletionResult, GetProductWithVariants, LanguageCode, SortOrder, GetProductList, ProductWithVariants, CreateProduct, GetAssetList,
-    UpdateProduct,
+import {
     AddOptionGroupToProduct,
-    RemoveOptionGroupFromProduct,
+    CreateProduct,
+    DeleteProduct,
+    DeletionResult,
     GenerateProductVariants,
-    UpdateProductVariants} from './graphql/generated-e2e-admin-types';
+    GetAssetList,
+    GetProductList,
+    GetProductWithVariants,
+    LanguageCode,
+    ProductWithVariants,
+    RemoveOptionGroupFromProduct,
+    SortOrder,
+    UpdateProduct,
+    UpdateProductVariants,
+} from './graphql/generated-e2e-admin-types';
 import {
     CREATE_PRODUCT,
     GET_ASSET_LIST,
@@ -585,7 +595,7 @@ describe('Product resolver', () => {
 
         it('deletes a product', async () => {
             productToDelete = allProducts[0];
-            const result = await client.query(DELETE_PRODUCT, { id: productToDelete.id });
+            const result = await client.query<DeleteProduct.Mutation, DeleteProduct.Variables>(DELETE_PRODUCT, { id: productToDelete.id });
 
             expect(result.deleteProduct).toEqual({ result: DeletionResult.DELETED });
         });

+ 2 - 1
packages/core/e2e/promotion.e2e-spec.ts

@@ -10,6 +10,7 @@ import { CONFIGURABLE_FRAGMENT, PROMOTION_FRAGMENT } from './graphql/fragments';
 import {
     ConfigArgType,
     CreatePromotion,
+    DeletePromotion,
     DeletionResult,
     GetAdjustmentOperations,
     GetPromotion,
@@ -148,7 +149,7 @@ describe('Promotion resolver', () => {
 
         it('deletes a promotion', async () => {
             promotionToDelete = allPromotions[0];
-            const result = await client.query(DELETE_PROMOTION, { id: promotionToDelete.id });
+            const result = await client.query<DeletePromotion.Mutation, DeletePromotion.Variables>(DELETE_PROMOTION, { id: promotionToDelete.id });
 
             expect(result.deletePromotion).toEqual({ result: DeletionResult.DELETED });
         });

+ 120 - 55
packages/core/e2e/shop-auth.e2e-spec.ts

@@ -14,6 +14,16 @@ import { PasswordResetEvent } from '../src/event-bus/events/password-reset-event
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { CreateAdministrator, CreateRole, GetCustomer, Permission } from './graphql/generated-e2e-admin-types';
+import {
+    GetActiveCustomer,
+    RefreshToken,
+    Register,
+    RequestPasswordReset,
+    RequestUpdateEmailAddress,
+    ResetPassword,
+    UpdateEmailAddress,
+    Verify,
+} from './graphql/generated-e2e-shop-types';
 import { CREATE_ADMINISTRATOR, CREATE_ROLE, GET_CUSTOMER } from './graphql/shared-definitions';
 import {
     GET_ACTIVE_CUSTOMER,
@@ -72,7 +82,10 @@ describe('Shop auth & accounts', () => {
                     emailAddress: 'sofia.green@test.com',
                     password: 'test',
                 };
-                const result = await shopClient.query(REGISTER_ACCOUNT, { input });
+                const result = await shopClient.query<Register.Mutation, Register.Variables>(
+                    REGISTER_ACCOUNT,
+                    { input },
+                );
             }, 'Do not provide a password when `authOptions.requireVerification` is set to "true"'),
         );
 
@@ -83,7 +96,9 @@ describe('Shop auth & accounts', () => {
                 lastName: 'Tester',
                 emailAddress,
             };
-            const result = await shopClient.query(REGISTER_ACCOUNT, { input });
+            const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
+                input,
+            });
 
             verificationToken = await verificationTokenPromise;
 
@@ -103,7 +118,9 @@ describe('Shop auth & accounts', () => {
                 lastName: 'Tester',
                 emailAddress,
             };
-            const result = await shopClient.query(REGISTER_ACCOUNT, { input });
+            const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
+                input,
+            });
 
             const newVerificationToken = await sendEmail;
 
@@ -120,7 +137,10 @@ describe('Shop auth & accounts', () => {
                     resolve(event.user.verificationToken!);
                 });
             });
-            const result = await shopClient.query(REFRESH_TOKEN, { emailAddress });
+            const result = await shopClient.query<RefreshToken.Mutation, RefreshToken.Variables>(
+                REFRESH_TOKEN,
+                { emailAddress },
+            );
 
             const newVerificationToken = await sendEmail;
 
@@ -132,9 +152,12 @@ describe('Shop auth & accounts', () => {
         });
 
         it('refreshCustomerVerification does nothing with an unrecognized emailAddress', async () => {
-            const result = await shopClient.query(REFRESH_TOKEN, {
-                emailAddress: 'never-been-registered@test.com',
-            });
+            const result = await shopClient.query<RefreshToken.Mutation, RefreshToken.Variables>(
+                REFRESH_TOKEN,
+                {
+                    emailAddress: 'never-been-registered@test.com',
+                },
+            );
             await waitForSendEmailFn();
             expect(result.refreshCustomerVerification).toBe(true);
             expect(sendEmailFn).not.toHaveBeenCalled();
@@ -153,7 +176,7 @@ describe('Shop auth & accounts', () => {
             'verification fails with wrong token',
             assertThrowsWithMessage(
                 () =>
-                    shopClient.query(VERIFY_EMAIL, {
+                    shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
                         password,
                         token: 'bad-token',
                     }),
@@ -162,7 +185,7 @@ describe('Shop auth & accounts', () => {
         );
 
         it('verification succeeds with correct token', async () => {
-            const result = await shopClient.query(VERIFY_EMAIL, {
+            const result = await shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
                 password,
                 token: verificationToken,
             });
@@ -176,7 +199,9 @@ describe('Shop auth & accounts', () => {
                 lastName: 'Hacker',
                 emailAddress,
             };
-            const result = await shopClient.query(REGISTER_ACCOUNT, { input });
+            const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
+                input,
+            });
             await waitForSendEmailFn();
             expect(result.registerCustomerAccount).toBe(true);
             expect(sendEmailFn).not.toHaveBeenCalled();
@@ -186,7 +211,7 @@ describe('Shop auth & accounts', () => {
             'verification fails if attempted a second time',
             assertThrowsWithMessage(
                 () =>
-                    shopClient.query(VERIFY_EMAIL, {
+                    shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
                         password,
                         token: verificationToken,
                     }),
@@ -211,7 +236,10 @@ describe('Shop auth & accounts', () => {
         });
 
         it('requestPasswordReset silently fails with invalid identifier', async () => {
-            const result = await shopClient.query(REQUEST_PASSWORD_RESET, {
+            const result = await shopClient.query<
+                RequestPasswordReset.Mutation,
+                RequestPasswordReset.Variables
+            >(REQUEST_PASSWORD_RESET, {
                 identifier: 'invalid-identifier',
             });
 
@@ -223,7 +251,10 @@ describe('Shop auth & accounts', () => {
 
         it('requestPasswordReset sends reset token', async () => {
             const passwordResetTokenPromise = getPasswordResetTokenPromise();
-            const result = await shopClient.query(REQUEST_PASSWORD_RESET, {
+            const result = await shopClient.query<
+                RequestPasswordReset.Mutation,
+                RequestPasswordReset.Variables
+            >(REQUEST_PASSWORD_RESET, {
                 identifier: customer.emailAddress,
             });
 
@@ -238,7 +269,7 @@ describe('Shop auth & accounts', () => {
             'resetPassword fails with wrong token',
             assertThrowsWithMessage(
                 () =>
-                    shopClient.query(RESET_PASSWORD, {
+                    shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(RESET_PASSWORD, {
                         password: 'newPassword',
                         token: 'bad-token',
                     }),
@@ -247,10 +278,13 @@ describe('Shop auth & accounts', () => {
         );
 
         it('resetPassword works with valid token', async () => {
-            const result = await shopClient.query(RESET_PASSWORD, {
-                token: passwordResetToken,
-                password: 'newPassword',
-            });
+            const result = await shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(
+                RESET_PASSWORD,
+                {
+                    token: passwordResetToken,
+                    password: 'newPassword',
+                },
+            );
 
             const loginResult = await shopClient.asUserWithCredentials(customer.emailAddress, 'newPassword');
             expect(loginResult.user.identifier).toBe(customer.emailAddress);
@@ -264,7 +298,9 @@ describe('Shop auth & accounts', () => {
         const PASSWORD = 'newPassword';
 
         beforeAll(async () => {
-            const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, { id: 'T_1' });
+            const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
+                id: 'T_1',
+            });
             customer = result.customer!;
         });
 
@@ -275,7 +311,10 @@ describe('Shop auth & accounts', () => {
         it('throws if not logged in', async () => {
             try {
                 await shopClient.asAnonymousUser();
-                await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
+                await shopClient.query<
+                    RequestUpdateEmailAddress.Mutation,
+                    RequestUpdateEmailAddress.Variables
+                >(REQUEST_UPDATE_EMAIL_ADDRESS, {
                     password: PASSWORD,
                     newEmailAddress: NEW_EMAIL_ADDRESS,
                 });
@@ -288,7 +327,10 @@ describe('Shop auth & accounts', () => {
         it('throws if password is incorrect', async () => {
             try {
                 await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
-                await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
+                await shopClient.query<
+                    RequestUpdateEmailAddress.Mutation,
+                    RequestUpdateEmailAddress.Variables
+                >(REQUEST_UPDATE_EMAIL_ADDRESS, {
                     password: 'bad password',
                     newEmailAddress: NEW_EMAIL_ADDRESS,
                 });
@@ -298,29 +340,37 @@ describe('Shop auth & accounts', () => {
             }
         });
 
-        it('throws if email address already in use', assertThrowsWithMessage(
-            async () => {
+        it(
+            'throws if email address already in use',
+            assertThrowsWithMessage(async () => {
                 await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
-                const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, { id: 'T_2' });
+                const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
+                    GET_CUSTOMER,
+                    { id: 'T_2' },
+                );
                 const otherCustomer = result.customer!;
 
-                await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
+                await shopClient.query<
+                    RequestUpdateEmailAddress.Mutation,
+                    RequestUpdateEmailAddress.Variables
+                >(REQUEST_UPDATE_EMAIL_ADDRESS, {
                     password: PASSWORD,
                     newEmailAddress: otherCustomer.emailAddress,
                 });
-            },
-            'This email address is not available',
-            ),
+            }, 'This email address is not available'),
         );
 
         it('triggers event with token', async () => {
             await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
             const emailUpdateTokenPromise = getEmailUpdateTokenPromise();
 
-            await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
-                password: PASSWORD,
-                newEmailAddress: NEW_EMAIL_ADDRESS,
-            });
+            await shopClient.query<RequestUpdateEmailAddress.Mutation, RequestUpdateEmailAddress.Variables>(
+                REQUEST_UPDATE_EMAIL_ADDRESS,
+                {
+                    password: PASSWORD,
+                    newEmailAddress: NEW_EMAIL_ADDRESS,
+                },
+            );
 
             const { identifierChangeToken, pendingIdentifier } = await emailUpdateTokenPromise;
             emailUpdateToken = identifierChangeToken!;
@@ -338,16 +388,15 @@ describe('Shop auth & accounts', () => {
             }
         });
 
-        it('throws with bad token', assertThrowsWithMessage(
-            async () => {
-                await shopClient.query(UPDATE_EMAIL_ADDRESS, { token: 'bad token' });
-            },
-            'Identifier change token not recognized',
-            ),
+        it(
+            'throws with bad token',
+            assertThrowsWithMessage(async () => {
+                await shopClient.query<UpdateEmailAddress.Mutation, UpdateEmailAddress.Variables>(UPDATE_EMAIL_ADDRESS, { token: 'bad token' });
+            }, 'Identifier change token not recognized'),
         );
 
         it('verify the new email address', async () => {
-            const result = await shopClient.query(UPDATE_EMAIL_ADDRESS, { token: emailUpdateToken });
+            const result = await shopClient.query<UpdateEmailAddress.Mutation, UpdateEmailAddress.Variables>(UPDATE_EMAIL_ADDRESS, { token: emailUpdateToken });
             expect(result.updateCustomerEmailAddress).toBe(true);
 
             expect(sendEmailFn).toHaveBeenCalled();
@@ -356,9 +405,9 @@ describe('Shop auth & accounts', () => {
 
         it('can login with new email address after verification', async () => {
             await shopClient.asUserWithCredentials(NEW_EMAIL_ADDRESS, PASSWORD);
-            const { activeCustomer } = await shopClient.query(GET_ACTIVE_CUSTOMER);
-            expect(activeCustomer.id).toBe(customer.id);
-            expect(activeCustomer.emailAddress).toBe(NEW_EMAIL_ADDRESS);
+            const { activeCustomer } = await shopClient.query<GetActiveCustomer.Query>(GET_ACTIVE_CUSTOMER);
+            expect(activeCustomer!.id).toBe(customer.id);
+            expect(activeCustomer!.emailAddress).toBe(NEW_EMAIL_ADDRESS);
         });
 
         it('cannot login with old email address after verification', async () => {
@@ -369,7 +418,6 @@ describe('Shop auth & accounts', () => {
                 expect(getErrorCode(err)).toBe('UNAUTHORIZED');
             }
         });
-
     });
 
     async function assertRequestAllowed<V>(operation: DocumentNode, variables?: V) {
@@ -421,7 +469,7 @@ describe('Shop auth & accounts', () => {
         const adminResult = await shopClient.query<
             CreateAdministrator.Mutation,
             CreateAdministrator.Variables
-            >(CREATE_ADMINISTRATOR, {
+        >(CREATE_ADMINISTRATOR, {
             input: {
                 emailAddress: identifier,
                 firstName: code,
@@ -485,7 +533,9 @@ describe('Expiring tokens', () => {
                 lastName: 'Wallace',
                 emailAddress: 'barry.wallace@test.com',
             };
-            const result = await shopClient.query(REGISTER_ACCOUNT, { input });
+            const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
+                input,
+            });
 
             const verificationToken = await verificationTokenPromise;
 
@@ -511,7 +561,10 @@ describe('Expiring tokens', () => {
             );
 
             const passwordResetTokenPromise = getPasswordResetTokenPromise();
-            const result = await shopClient.query(REQUEST_PASSWORD_RESET, {
+            const result = await shopClient.query<
+                RequestPasswordReset.Mutation,
+                RequestPasswordReset.Variables
+            >(REQUEST_PASSWORD_RESET, {
                 identifier: customer!.emailAddress,
             });
 
@@ -523,7 +576,7 @@ describe('Expiring tokens', () => {
 
             await new Promise(resolve => setTimeout(resolve, 3));
 
-            return shopClient.query(RESET_PASSWORD, {
+            return shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(RESET_PASSWORD, {
                 password: 'test',
                 token: passwordResetToken,
             });
@@ -568,7 +621,9 @@ describe('Registration without email verification', () => {
                 lastName: 'Beardsley',
                 emailAddress: userEmailAddress,
             };
-            const result = await shopClient.query(REGISTER_ACCOUNT, { input });
+            const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
+                input,
+            });
         }, 'A password must be provided when `authOptions.requireVerification` is set to "false"'),
     );
 
@@ -579,7 +634,9 @@ describe('Registration without email verification', () => {
             emailAddress: userEmailAddress,
             password: 'test',
         };
-        const result = await shopClient.query(REGISTER_ACCOUNT, { input });
+        const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
+            input,
+        });
 
         expect(result.registerCustomerAccount).toBe(true);
         expect(sendEmailFn).not.toHaveBeenCalled();
@@ -590,7 +647,7 @@ describe('Registration without email verification', () => {
 
         const result = await shopClient.query(
             gql`
-                query GetMe{
+                query GetMe {
                     me {
                         identifier
                     }
@@ -626,7 +683,9 @@ describe('Updating email address without email verification', () => {
     }, TEST_SETUP_TIMEOUT_MS);
 
     beforeAll(async () => {
-        const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, { id: 'T_1' });
+        const result = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
+            id: 'T_1',
+        });
         customer = result.customer!;
     });
 
@@ -640,7 +699,10 @@ describe('Updating email address without email verification', () => {
 
     it('updates email address', async () => {
         await shopClient.asUserWithCredentials(customer.emailAddress, 'test');
-        const { requestUpdateCustomerEmailAddress } = await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
+        const { requestUpdateCustomerEmailAddress } = await shopClient.query<
+            RequestUpdateEmailAddress.Mutation,
+            RequestUpdateEmailAddress.Variables
+        >(REQUEST_UPDATE_EMAIL_ADDRESS, {
             password: 'test',
             newEmailAddress: NEW_EMAIL_ADDRESS,
         });
@@ -649,8 +711,8 @@ describe('Updating email address without email verification', () => {
         expect(sendEmailFn).toHaveBeenCalledTimes(1);
         expect(sendEmailFn.mock.calls[0][0] instanceof IdentifierChangeEvent).toBe(true);
 
-        const { activeCustomer } = await shopClient.query(GET_ACTIVE_CUSTOMER);
-        expect(activeCustomer.emailAddress).toBe(NEW_EMAIL_ADDRESS);
+        const { activeCustomer } = await shopClient.query<GetActiveCustomer.Query>(GET_ACTIVE_CUSTOMER);
+        expect(activeCustomer!.emailAddress).toBe(NEW_EMAIL_ADDRESS);
     });
 });
 
@@ -692,7 +754,10 @@ function getPasswordResetTokenPromise(): Promise<string> {
     });
 }
 
-function getEmailUpdateTokenPromise(): Promise<{ identifierChangeToken: string | null; pendingIdentifier: string | null; }> {
+function getEmailUpdateTokenPromise(): Promise<{
+    identifierChangeToken: string | null;
+    pendingIdentifier: string | null;
+}> {
     return new Promise(resolve => {
         sendEmailFn.mockImplementation((event: IdentifierChangeRequestEvent) => {
             resolve(pick(event.user, ['identifierChangeToken', 'pendingIdentifier']));

+ 49 - 21
packages/core/e2e/shop-catalog.e2e-spec.ts

@@ -11,8 +11,16 @@ import {
     CreateFacet,
     DisableProduct,
     FacetWithValues,
+    GetCollectionList,
+    GetCollectionVariants,
     GetFacetList,
+    GetProduct1,
+    GetProduct2Variants,
+    GetProductCollection,
+    GetProductFacetValues,
+    GetProductsTake3,
     GetProductWithVariants,
+    GetVariantFacetValues,
     LanguageCode,
     UpdateCollection,
     UpdateProduct,
@@ -77,7 +85,7 @@ describe('Shop catalog', () => {
         });
 
         it('products list omits disabled products', async () => {
-            const result = await shopClient.query(gql`
+            const result = await shopClient.query<GetProductsTake3.Query>(gql`
                 query GetProductsTake3 {
                     products(options: { take: 3 }) {
                         items {
@@ -87,11 +95,11 @@ describe('Shop catalog', () => {
                 }
             `);
 
-            expect(result.products.items.map((item: any) => item.id)).toEqual(['T_2', 'T_3', 'T_4']);
+            expect(result.products.items.map(item => item.id)).toEqual(['T_2', 'T_3', 'T_4']);
         });
 
         it('product returns null for disabled product', async () => {
-            const result = await shopClient.query(gql`
+            const result = await shopClient.query<GetProduct1.Query>(gql`
                 query GetProduct1 {
                     product(id: "T_1") {
                         id
@@ -103,7 +111,7 @@ describe('Shop catalog', () => {
         });
 
         it('omits disabled variants from product response', async () => {
-            const result = await shopClient.query(gql`
+            const result = await shopClient.query<GetProduct2Variants.Query>(gql`
                 query GetProduct2Variants {
                     product(id: "T_2") {
                         id
@@ -115,7 +123,7 @@ describe('Shop catalog', () => {
                 }
             `);
 
-            expect(result.product.variants).toEqual([{ id: 'T_6', name: 'Curvy Monitor 27 inch' }]);
+            expect(result.product!.variants).toEqual([{ id: 'T_6', name: 'Curvy Monitor 27 inch' }]);
         });
     });
 
@@ -162,19 +170,25 @@ describe('Shop catalog', () => {
         });
 
         it('omits private Product.facetValues', async () => {
-            const result = await shopClient.query(GET_PRODUCT_FACET_VALUES, {
+            const result = await shopClient.query<
+                GetProductFacetValues.Query,
+                GetProductFacetValues.Variables
+            >(GET_PRODUCT_FACET_VALUES, {
                 id: 'T_2',
             });
 
-            expect(result.product!.facetValues.map((fv: any) => fv.name)).toEqual([]);
+            expect(result.product!.facetValues.map(fv => fv.name)).toEqual([]);
         });
 
         it('omits private ProductVariant.facetValues', async () => {
-            const result = await shopClient.query(GET_PRODUCT_VARIANT_FACET_VALUES, {
+            const result = await shopClient.query<
+                GetVariantFacetValues.Query,
+                GetVariantFacetValues.Variables
+            >(GET_PRODUCT_VARIANT_FACET_VALUES, {
                 id: 'T_2',
             });
 
-            expect(result.product!.variants[0].facetValues.map((fv: any) => fv.name)).toEqual([]);
+            expect(result.product!.variants[0].facetValues.map(fv => fv.name)).toEqual([]);
         });
     });
 
@@ -208,8 +222,11 @@ describe('Shop catalog', () => {
         });
 
         it('returns collection with variants', async () => {
-            const result = await shopClient.query(GET_COLLECTION_VARIANTS, { id: collection.id });
-            expect(result.collection.productVariants.items).toEqual([
+            const result = await shopClient.query<
+                GetCollectionVariants.Query,
+                GetCollectionVariants.Variables
+            >(GET_COLLECTION_VARIANTS, { id: collection.id });
+            expect(result.collection!.productVariants.items).toEqual([
                 { id: 'T_22', name: 'Road Bike' },
                 { id: 'T_23', name: 'Skipping Rope' },
                 { id: 'T_24', name: 'Boxing Gloves' },
@@ -224,10 +241,15 @@ describe('Shop catalog', () => {
         });
 
         it('omits variants from disabled products', async () => {
-            await adminClient.query(DISABLE_PRODUCT, { id: 'T_17' });
+            await adminClient.query<DisableProduct.Mutation, DisableProduct.Variables>(DISABLE_PRODUCT, {
+                id: 'T_17',
+            });
 
-            const result = await shopClient.query(GET_COLLECTION_VARIANTS, { id: collection.id });
-            expect(result.collection.productVariants.items).toEqual([
+            const result = await shopClient.query<
+                GetCollectionVariants.Query,
+                GetCollectionVariants.Variables
+            >(GET_COLLECTION_VARIANTS, { id: collection.id });
+            expect(result.collection!.productVariants.items).toEqual([
                 { id: 'T_22', name: 'Road Bike' },
                 { id: 'T_23', name: 'Skipping Rope' },
                 { id: 'T_24', name: 'Boxing Gloves' },
@@ -245,8 +267,11 @@ describe('Shop catalog', () => {
                 },
             );
 
-            const result = await shopClient.query(GET_COLLECTION_VARIANTS, { id: collection.id });
-            expect(result.collection.productVariants.items).toEqual([
+            const result = await shopClient.query<
+                GetCollectionVariants.Query,
+                GetCollectionVariants.Variables
+            >(GET_COLLECTION_VARIANTS, { id: collection.id });
+            expect(result.collection!.productVariants.items).toEqual([
                 { id: 'T_23', name: 'Skipping Rope' },
                 { id: 'T_24', name: 'Boxing Gloves' },
                 { id: 'T_25', name: 'Tent' },
@@ -256,7 +281,7 @@ describe('Shop catalog', () => {
         });
 
         it('collection list', async () => {
-            const result = await shopClient.query(GET_COLLECTION_LIST);
+            const result = await shopClient.query<GetCollectionList.Query>(GET_COLLECTION_LIST);
 
             expect(result.collections.items).toEqual([
                 { id: 'T_2', name: 'Plants' },
@@ -274,19 +299,22 @@ describe('Shop catalog', () => {
                     },
                 },
             );
-            const result = await shopClient.query(GET_COLLECTION_LIST);
+            const result = await shopClient.query<GetCollectionList.Query>(GET_COLLECTION_LIST);
 
             expect(result.collections.items).toEqual([{ id: 'T_2', name: 'Plants' }]);
         });
 
         it('returns null for private collection', async () => {
-            const result = await shopClient.query(GET_COLLECTION_VARIANTS, { id: collection.id });
+            const result = await shopClient.query<
+                GetCollectionVariants.Query,
+                GetCollectionVariants.Variables
+            >(GET_COLLECTION_VARIANTS, { id: collection.id });
 
             expect(result.collection).toBeNull();
         });
 
         it('product.collections list omits private collections', async () => {
-            const result = await shopClient.query(gql`
+            const result = await shopClient.query<GetProductCollection.Query>(gql`
                 query GetProductCollection {
                     product(id: "T_12") {
                         collections {
@@ -297,7 +325,7 @@ describe('Shop catalog', () => {
                 }
             `);
 
-            expect(result.product.collections).toEqual([]);
+            expect(result.product!.collections).toEqual([]);
         });
     });
 });

+ 63 - 16
packages/core/e2e/shop-customer.e2e-spec.ts

@@ -3,10 +3,25 @@ import gql from 'graphql-tag';
 import path from 'path';
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
-import { AttemptLogin, GetCustomer } from './graphql/generated-e2e-admin-types';
-import { CreateAddressInput, UpdateAddressInput, UpdateCustomerInput } from './graphql/generated-e2e-shop-types';
+import { AttemptLogin, GetCustomer, GetCustomerIds } from './graphql/generated-e2e-admin-types';
+import {
+    CreateAddressInput,
+    CreateAddressShop,
+    DeleteAddressShop,
+    UpdateAddressInput,
+    UpdateAddressShop,
+    UpdateCustomer,
+    UpdateCustomerInput,
+    UpdatePassword,
+} from './graphql/generated-e2e-shop-types';
 import { ATTEMPT_LOGIN, GET_CUSTOMER } from './graphql/shared-definitions';
-import { CREATE_ADDRESS, DELETE_ADDRESS, UPDATE_ADDRESS, UPDATE_CUSTOMER, UPDATE_PASSWORD } from './graphql/shop-definitions';
+import {
+    CREATE_ADDRESS,
+    DELETE_ADDRESS,
+    UPDATE_ADDRESS,
+    UPDATE_CUSTOMER,
+    UPDATE_PASSWORD,
+} from './graphql/shop-definitions';
 import { TestAdminClient, TestShopClient } from './test-client';
 import { TestServer } from './test-server';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
@@ -26,7 +41,7 @@ describe('Shop customers', () => {
         await adminClient.init();
 
         // Fetch the first Customer and store it as the `customer` variable.
-        const { customers } = await adminClient.query(gql`
+        const { customers } = await adminClient.query<GetCustomerIds.Query>(gql`
             query GetCustomerIds {
                 customers {
                     items {
@@ -51,7 +66,9 @@ describe('Shop customers', () => {
             const input: UpdateCustomerInput = {
                 firstName: 'xyz',
             };
-            await shopClient.query(UPDATE_CUSTOMER, { input });
+            await shopClient.query<UpdateCustomer.Mutation, UpdateCustomer.Variables>(UPDATE_CUSTOMER, {
+                input,
+            });
         }, 'You are not currently authorized to perform this action'),
     );
 
@@ -62,7 +79,9 @@ describe('Shop customers', () => {
                 streetLine1: '1 Test Street',
                 countryCode: 'GB',
             };
-            await shopClient.query(CREATE_ADDRESS, { input });
+            await shopClient.query<CreateAddressShop.Mutation, CreateAddressShop.Variables>(CREATE_ADDRESS, {
+                input,
+            });
         }, 'You are not currently authorized to perform this action'),
     );
 
@@ -73,14 +92,18 @@ describe('Shop customers', () => {
                 id: 'T_1',
                 streetLine1: 'zxc',
             };
-            await shopClient.query(UPDATE_ADDRESS, { input });
+            await shopClient.query<UpdateAddressShop.Mutation, UpdateAddressShop.Variables>(UPDATE_ADDRESS, {
+                input,
+            });
         }, 'You are not currently authorized to perform this action'),
     );
 
     it(
         'deleteCustomerAddress throws if not logged in',
         assertThrowsWithMessage(async () => {
-            await shopClient.query(DELETE_ADDRESS, { id: 'T_1' });
+            await shopClient.query<DeleteAddressShop.Mutation, DeleteAddressShop.Variables>(DELETE_ADDRESS, {
+                id: 'T_1',
+            });
         }, 'You are not currently authorized to perform this action'),
     );
 
@@ -99,7 +122,10 @@ describe('Shop customers', () => {
             const input: UpdateCustomerInput = {
                 firstName: 'xyz',
             };
-            const result = await shopClient.query(UPDATE_CUSTOMER, { input });
+            const result = await shopClient.query<UpdateCustomer.Mutation, UpdateCustomer.Variables>(
+                UPDATE_CUSTOMER,
+                { input },
+            );
 
             expect(result.updateCustomer.firstName).toBe('xyz');
         });
@@ -109,7 +135,10 @@ describe('Shop customers', () => {
                 streetLine1: '1 Test Street',
                 countryCode: 'GB',
             };
-            const { createCustomerAddress } = await shopClient.query(CREATE_ADDRESS, { input });
+            const { createCustomerAddress } = await shopClient.query<
+                CreateAddressShop.Mutation,
+                CreateAddressShop.Variables
+            >(CREATE_ADDRESS, { input });
 
             expect(createCustomerAddress).toEqual({
                 id: 'T_3',
@@ -127,7 +156,10 @@ describe('Shop customers', () => {
                 streetLine1: '5 Test Street',
                 countryCode: 'AT',
             };
-            const result = await shopClient.query(UPDATE_ADDRESS, { input });
+            const result = await shopClient.query<UpdateAddressShop.Mutation, UpdateAddressShop.Variables>(
+                UPDATE_ADDRESS,
+                { input },
+            );
 
             expect(result.updateCustomerAddress.streetLine1).toEqual('5 Test Street');
             expect(result.updateCustomerAddress.country.code).toEqual('AT');
@@ -140,12 +172,18 @@ describe('Shop customers', () => {
                     id: 'T_2',
                     streetLine1: '1 Test Street',
                 };
-                await shopClient.query(UPDATE_ADDRESS, { input });
+                await shopClient.query<UpdateAddressShop.Mutation, UpdateAddressShop.Variables>(
+                    UPDATE_ADDRESS,
+                    { input },
+                );
             }, 'You are not currently authorized to perform this action'),
         );
 
         it('deleteCustomerAddress works', async () => {
-            const result = await shopClient.query(DELETE_ADDRESS, { id: 'T_3' });
+            const result = await shopClient.query<DeleteAddressShop.Mutation, DeleteAddressShop.Variables>(
+                DELETE_ADDRESS,
+                { id: 'T_3' },
+            );
 
             expect(result.deleteCustomerAddress).toBe(true);
         });
@@ -153,19 +191,28 @@ describe('Shop customers', () => {
         it(
             'deleteCustomerAddress fails for address not owned by Customer',
             assertThrowsWithMessage(async () => {
-                await shopClient.query(DELETE_ADDRESS, { id: 'T_2' });
+                await shopClient.query<DeleteAddressShop.Mutation, DeleteAddressShop.Variables>(
+                    DELETE_ADDRESS,
+                    { id: 'T_2' },
+                );
             }, 'You are not currently authorized to perform this action'),
         );
 
         it(
             'updatePassword fails with incorrect current password',
             assertThrowsWithMessage(async () => {
-                await shopClient.query(UPDATE_PASSWORD, { old: 'wrong', new: 'test2' });
+                await shopClient.query<UpdatePassword.Mutation, UpdatePassword.Variables>(UPDATE_PASSWORD, {
+                    old: 'wrong',
+                    new: 'test2',
+                });
             }, 'The credentials did not match. Please check and try again'),
         );
 
         it('updatePassword works', async () => {
-            const response = await shopClient.query(UPDATE_PASSWORD, { old: 'test', new: 'test2' });
+            const response = await shopClient.query<UpdatePassword.Mutation, UpdatePassword.Variables>(
+                UPDATE_PASSWORD,
+                { old: 'test', new: 'test2' },
+            );
 
             expect(response.updateCustomerPassword).toBe(true);
 

+ 280 - 151
packages/core/e2e/shop-order.e2e-spec.ts

@@ -1,11 +1,26 @@
 /* tslint:disable:no-non-null-assertion */
-import gql from 'graphql-tag';
 import path from 'path';
 
 import { PaymentMethodHandler } from '../src/config/payment-method/payment-method-handler';
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { CreateAddressInput, GetCountryList, GetCustomer, GetCustomerList, UpdateCountry } from './graphql/generated-e2e-admin-types';
+import {
+    AddItemToOrder,
+    AddPaymentToOrder,
+    AdjustItemQuantity,
+    GetActiveOrder,
+    GetAvailableCountries,
+    GetCustomerAddresses,
+    GetNextOrderStates,
+    GetOrderByCode,
+    GetShippingMethods,
+    RemoveItemFromOrder,
+    SetCustomerForOrder,
+    SetShippingAddress,
+    SetShippingMethod,
+    TransitionToState,
+} from './graphql/generated-e2e-shop-types';
 import { GET_COUNTRY_LIST, GET_CUSTOMER, GET_CUSTOMER_LIST, UPDATE_COUNTRY } from './graphql/shared-definitions';
 import {
     ADD_ITEM_TO_ORDER,
@@ -66,9 +81,9 @@ describe('Shop orders', () => {
             },
         });
 
-        const result = await shopClient.query(GET_AVAILABLE_COUNTRIES);
+        const result = await shopClient.query<GetAvailableCountries.Query>(GET_AVAILABLE_COUNTRIES);
         expect(result.availableCountries.length).toBe(countries.items.length - 1);
-        expect(result.availableCountries.find((c: GetCountryList.Items) => c.id === AT.id)).toBeUndefined();
+        expect(result.availableCountries.find(c => c.id === AT.id)).toBeUndefined();
     });
 
     describe('ordering as anonymous user', () => {
@@ -81,7 +96,7 @@ describe('Shop orders', () => {
         });
 
         it('activeOrder returns null before any items have been added', async () => {
-            const result = await shopClient.query(GET_ACTIVE_ORDER);
+            const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
             expect(result.activeOrder).toBeNull();
         });
 
@@ -90,24 +105,27 @@ describe('Shop orders', () => {
         });
 
         it('addItemToOrder creates a new Order with an item', async () => {
-            const result = await shopClient.query(ADD_ITEM_TO_ORDER, {
+            const { addItemToOrder } = await shopClient.query<
+                AddItemToOrder.Mutation,
+                AddItemToOrder.Variables
+            >(ADD_ITEM_TO_ORDER, {
                 productVariantId: 'T_1',
                 quantity: 1,
             });
 
-            expect(result.addItemToOrder.lines.length).toBe(1);
-            expect(result.addItemToOrder.lines[0].quantity).toBe(1);
-            expect(result.addItemToOrder.lines[0].productVariant.id).toBe('T_1');
-            expect(result.addItemToOrder.lines[0].id).toBe('T_1');
-            firstOrderItemId = result.addItemToOrder.lines[0].id;
-            orderCode = result.addItemToOrder.code;
+            expect(addItemToOrder!.lines.length).toBe(1);
+            expect(addItemToOrder!.lines[0].quantity).toBe(1);
+            expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_1');
+            expect(addItemToOrder!.lines[0].id).toBe('T_1');
+            firstOrderItemId = addItemToOrder!.lines[0].id;
+            orderCode = addItemToOrder!.code;
         });
 
         it(
             'addItemToOrder errors with an invalid productVariantId',
             assertThrowsWithMessage(
                 () =>
-                    shopClient.query(ADD_ITEM_TO_ORDER, {
+                    shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
                         productVariantId: 'T_999',
                         quantity: 1,
                     }),
@@ -119,7 +137,7 @@ describe('Shop orders', () => {
             'addItemToOrder errors with a negative quantity',
             assertThrowsWithMessage(
                 () =>
-                    shopClient.query(ADD_ITEM_TO_ORDER, {
+                    shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
                         productVariantId: 'T_999',
                         quantity: -3,
                     }),
@@ -128,19 +146,22 @@ describe('Shop orders', () => {
         );
 
         it('addItemToOrder with an existing productVariantId adds quantity to the existing OrderLine', async () => {
-            const result = await shopClient.query(ADD_ITEM_TO_ORDER, {
+            const { addItemToOrder } = await shopClient.query<
+                AddItemToOrder.Mutation,
+                AddItemToOrder.Variables
+            >(ADD_ITEM_TO_ORDER, {
                 productVariantId: 'T_1',
                 quantity: 2,
             });
 
-            expect(result.addItemToOrder.lines.length).toBe(1);
-            expect(result.addItemToOrder.lines[0].quantity).toBe(3);
+            expect(addItemToOrder!.lines.length).toBe(1);
+            expect(addItemToOrder!.lines[0].quantity).toBe(3);
         });
 
         it(
             'addItemToOrder errors when going beyond orderItemsLimit',
             assertThrowsWithMessage(async () => {
-                await shopClient.query(ADD_ITEM_TO_ORDER, {
+                await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
                     productVariantId: 'T_1',
                     quantity: 100,
                 });
@@ -148,22 +169,28 @@ describe('Shop orders', () => {
         );
 
         it('adjustItemQuantity adjusts the quantity', async () => {
-            const result = await shopClient.query(ADJUST_ITEM_QUENTITY, {
+            const { adjustItemQuantity } = await shopClient.query<
+                AdjustItemQuantity.Mutation,
+                AdjustItemQuantity.Variables
+            >(ADJUST_ITEM_QUENTITY, {
                 orderItemId: firstOrderItemId,
                 quantity: 50,
             });
 
-            expect(result.adjustItemQuantity.lines.length).toBe(1);
-            expect(result.adjustItemQuantity.lines[0].quantity).toBe(50);
+            expect(adjustItemQuantity!.lines.length).toBe(1);
+            expect(adjustItemQuantity!.lines[0].quantity).toBe(50);
         });
 
         it(
             'adjustItemQuantity errors when going beyond orderItemsLimit',
             assertThrowsWithMessage(async () => {
-                await shopClient.query(ADJUST_ITEM_QUENTITY, {
-                    orderItemId: firstOrderItemId,
-                    quantity: 100,
-                });
+                await shopClient.query<AdjustItemQuantity.Mutation, AdjustItemQuantity.Variables>(
+                    ADJUST_ITEM_QUENTITY,
+                    {
+                        orderItemId: firstOrderItemId,
+                        quantity: 100,
+                    },
+                );
             }, 'Cannot add items. An order may consist of a maximum of 99 items'),
         );
 
@@ -171,10 +198,13 @@ describe('Shop orders', () => {
             'adjustItemQuantity errors with a negative quantity',
             assertThrowsWithMessage(
                 () =>
-                    shopClient.query(ADJUST_ITEM_QUENTITY, {
-                        orderItemId: firstOrderItemId,
-                        quantity: -3,
-                    }),
+                    shopClient.query<AdjustItemQuantity.Mutation, AdjustItemQuantity.Variables>(
+                        ADJUST_ITEM_QUENTITY,
+                        {
+                            orderItemId: firstOrderItemId,
+                            quantity: -3,
+                        },
+                    ),
                 `-3 is not a valid quantity for an OrderItem`,
             ),
         );
@@ -183,42 +213,54 @@ describe('Shop orders', () => {
             'adjustItemQuantity errors with an invalid orderItemId',
             assertThrowsWithMessage(
                 () =>
-                    shopClient.query(ADJUST_ITEM_QUENTITY, {
-                        orderItemId: 'T_999',
-                        quantity: 5,
-                    }),
+                    shopClient.query<AdjustItemQuantity.Mutation, AdjustItemQuantity.Variables>(
+                        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 shopClient.query(ADD_ITEM_TO_ORDER, {
+            const { addItemToOrder } = await shopClient.query<
+                AddItemToOrder.Mutation,
+                AddItemToOrder.Variables
+            >(ADD_ITEM_TO_ORDER, {
                 productVariantId: 'T_3',
                 quantity: 3,
             });
-            expect(result1.addItemToOrder.lines.length).toBe(2);
-            expect(result1.addItemToOrder.lines.map((i: any) => i.productVariant.id)).toEqual(['T_1', 'T_3']);
+            expect(addItemToOrder!.lines.length).toBe(2);
+            expect(addItemToOrder!.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
 
-            const result2 = await shopClient.query(REMOVE_ITEM_FROM_ORDER, {
+            const { removeItemFromOrder } = await shopClient.query<
+                RemoveItemFromOrder.Mutation,
+                RemoveItemFromOrder.Variables
+            >(REMOVE_ITEM_FROM_ORDER, {
                 orderItemId: firstOrderItemId,
             });
-            expect(result2.removeItemFromOrder.lines.length).toBe(1);
-            expect(result2.removeItemFromOrder.lines.map((i: any) => i.productVariant.id)).toEqual(['T_3']);
+            expect(removeItemFromOrder!.lines.length).toBe(1);
+            expect(removeItemFromOrder!.lines.map(i => i.productVariant.id)).toEqual(['T_3']);
         });
 
         it(
             'removeItemFromOrder errors with an invalid orderItemId',
             assertThrowsWithMessage(
                 () =>
-                    shopClient.query(REMOVE_ITEM_FROM_ORDER, {
-                        orderItemId: 'T_999',
-                    }),
+                    shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
+                        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 shopClient.query(GET_NEXT_STATES);
+            const result = await shopClient.query<GetNextOrderStates.Query>(GET_NEXT_STATES);
 
             expect(result.nextOrderStates).toEqual(['ArrangingPayment']);
         });
@@ -226,7 +268,11 @@ describe('Shop orders', () => {
         it(
             'transitionOrderToState throws for an invalid state',
             assertThrowsWithMessage(
-                () => shopClient.query(TRANSITION_TO_STATE, { state: 'Completed' }),
+                () =>
+                    shopClient.query<TransitionToState.Mutation, TransitionToState.Variables>(
+                        TRANSITION_TO_STATE,
+                        { state: 'Completed' },
+                    ),
                 `Cannot transition Order from "AddingItems" to "Completed"`,
             ),
         );
@@ -234,13 +280,20 @@ describe('Shop orders', () => {
         it(
             'attempting to transition to ArrangingPayment throws when Order has no Customer',
             assertThrowsWithMessage(
-                () => shopClient.query(TRANSITION_TO_STATE, { state: 'ArrangingPayment' }),
+                () =>
+                    shopClient.query<TransitionToState.Mutation, TransitionToState.Variables>(
+                        TRANSITION_TO_STATE,
+                        { state: 'ArrangingPayment' },
+                    ),
                 `Cannot transition Order to the "ArrangingPayment" state without Customer details`,
             ),
         );
 
         it('setCustomerForOrder creates a new Customer and associates it with the Order', async () => {
-            const result = await shopClient.query(SET_CUSTOMER, {
+            const { setCustomerForOrder } = await shopClient.query<
+                SetCustomerForOrder.Mutation,
+                SetCustomerForOrder.Variables
+            >(SET_CUSTOMER, {
                 input: {
                     emailAddress: 'test@test.com',
                     firstName: 'Test',
@@ -248,7 +301,7 @@ describe('Shop orders', () => {
                 },
             });
 
-            const customer = result.setCustomerForOrder.customer;
+            const customer = setCustomerForOrder!.customer!;
             expect(customer.firstName).toBe('Test');
             expect(customer.lastName).toBe('Person');
             expect(customer.emailAddress).toBe('test@test.com');
@@ -256,7 +309,10 @@ describe('Shop orders', () => {
         });
 
         it('setCustomerForOrder updates the existing customer if Customer already set', async () => {
-            const result = await shopClient.query(SET_CUSTOMER, {
+            const { setCustomerForOrder } = await shopClient.query<
+                SetCustomerForOrder.Mutation,
+                SetCustomerForOrder.Variables
+            >(SET_CUSTOMER, {
                 input: {
                     emailAddress: 'test@test.com',
                     firstName: 'Changed',
@@ -264,7 +320,7 @@ describe('Shop orders', () => {
                 },
             });
 
-            const customer = result.setCustomerForOrder.customer;
+            const customer = setCustomerForOrder!.customer!;
             expect(customer.firstName).toBe('Changed');
             expect(customer.lastName).toBe('Person');
             expect(customer.emailAddress).toBe('test@test.com');
@@ -283,11 +339,14 @@ describe('Shop orders', () => {
                 countryCode: 'US',
                 phoneNumber: '4444444',
             };
-            const result = await shopClient.query(SET_SHIPPING_ADDRESS, {
+            const { setOrderShippingAddress } = await shopClient.query<
+                SetShippingAddress.Mutation,
+                SetShippingAddress.Variables
+            >(SET_SHIPPING_ADDRESS, {
                 input: address,
             });
 
-            expect(result.setOrderShippingAddress.shippingAddress).toEqual({
+            expect(setOrderShippingAddress!.shippingAddress).toEqual({
                 fullName: 'name',
                 company: 'company',
                 streetLine1: '12 the street',
@@ -301,35 +360,41 @@ describe('Shop orders', () => {
         });
 
         it('customer default Addresses are not updated before payment', async () => {
-            const result = await shopClient.query(GET_ACTIVE_ORDER_ADDRESSES);
+            const result = await shopClient.query<GetCustomerAddresses.Query>(GET_ACTIVE_ORDER_ADDRESSES);
 
-            expect(result.activeOrder.customer.addresses).toEqual([]);
+            expect(result.activeOrder!.customer!.addresses).toEqual([]);
         });
 
         it('can transition to ArrangingPayment once Customer has been set', async () => {
-            const result = await shopClient.query(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
+            const result = await shopClient.query<TransitionToState.Mutation, TransitionToState.Variables>(
+                TRANSITION_TO_STATE,
+                { state: 'ArrangingPayment' },
+            );
 
             expect(result.transitionOrderToState).toEqual({ id: 'T_1', state: 'ArrangingPayment' });
         });
 
         it('adds a successful payment and transitions Order state', async () => {
-            const result = await shopClient.query(ADD_PAYMENT, {
-                input: {
-                    method: testPaymentMethod.code,
-                    metadata: {},
+            const { addPaymentToOrder } = await shopClient.query<AddPaymentToOrder.Mutation, AddPaymentToOrder.Variables>(
+                ADD_PAYMENT,
+                {
+                    input: {
+                        method: testPaymentMethod.code,
+                        metadata: {},
+                    },
                 },
-            });
+            );
 
-            const payment = result.addPaymentToOrder.payments[0];
-            expect(result.addPaymentToOrder.state).toBe('PaymentSettled');
-            expect(result.addPaymentToOrder.active).toBe(false);
-            expect(result.addPaymentToOrder.payments.length).toBe(1);
+            const payment = addPaymentToOrder!.payments![0];
+            expect(addPaymentToOrder!.state).toBe('PaymentSettled');
+            expect(addPaymentToOrder!.active).toBe(false);
+            expect(addPaymentToOrder!.payments!.length).toBe(1);
             expect(payment.method).toBe(testPaymentMethod.code);
             expect(payment.state).toBe('Settled');
         });
 
         it('activeOrder is null after payment', async () => {
-            const result = await shopClient.query(GET_ACTIVE_ORDER);
+            const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
 
             expect(result.activeOrder).toBeNull();
         });
@@ -350,7 +415,7 @@ describe('Shop orders', () => {
 
     describe('ordering as authenticated user', () => {
         let firstOrderItemId: string;
-        let activeOrder: any;
+        let activeOrder: AddItemToOrder.AddItemToOrder;
         let authenticatedUserEmailAddress: string;
         let customers: GetCustomerList.Items[];
         const password = 'test';
@@ -371,82 +436,97 @@ describe('Shop orders', () => {
         });
 
         it('activeOrder returns null before any items have been added', async () => {
-            const result = await shopClient.query(GET_ACTIVE_ORDER);
+            const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
             expect(result.activeOrder).toBeNull();
         });
 
         it('addItemToOrder creates a new Order with an item', async () => {
-            const result = await shopClient.query(ADD_ITEM_TO_ORDER, {
+            const { addItemToOrder } = await shopClient.query<
+                AddItemToOrder.Mutation,
+                AddItemToOrder.Variables
+            >(ADD_ITEM_TO_ORDER, {
                 productVariantId: 'T_1',
                 quantity: 1,
             });
 
-            expect(result.addItemToOrder.lines.length).toBe(1);
-            expect(result.addItemToOrder.lines[0].quantity).toBe(1);
-            expect(result.addItemToOrder.lines[0].productVariant.id).toBe('T_1');
-            activeOrder = result.addItemToOrder;
-            firstOrderItemId = result.addItemToOrder.lines[0].id;
+            expect(addItemToOrder!.lines.length).toBe(1);
+            expect(addItemToOrder!.lines[0].quantity).toBe(1);
+            expect(addItemToOrder!.lines[0].productVariant.id).toBe('T_1');
+            activeOrder = addItemToOrder!;
+            firstOrderItemId = addItemToOrder!.lines[0].id;
         });
 
         it('activeOrder returns order after item has been added', async () => {
-            const result = await shopClient.query(GET_ACTIVE_ORDER);
-            expect(result.activeOrder.id).toBe(activeOrder.id);
-            expect(result.activeOrder.state).toBe('AddingItems');
+            const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
+            expect(result.activeOrder!.id).toBe(activeOrder.id);
+            expect(result.activeOrder!.state).toBe('AddingItems');
         });
 
         it('addItemToOrder with an existing productVariantId adds quantity to the existing OrderLine', async () => {
-            const result = await shopClient.query(ADD_ITEM_TO_ORDER, {
+            const { addItemToOrder } = await shopClient.query<
+                AddItemToOrder.Mutation,
+                AddItemToOrder.Variables
+            >(ADD_ITEM_TO_ORDER, {
                 productVariantId: 'T_1',
                 quantity: 2,
             });
 
-            expect(result.addItemToOrder.lines.length).toBe(1);
-            expect(result.addItemToOrder.lines[0].quantity).toBe(3);
+            expect(addItemToOrder!.lines.length).toBe(1);
+            expect(addItemToOrder!.lines[0].quantity).toBe(3);
         });
 
         it('adjustItemQuantity adjusts the quantity', async () => {
-            const result = await shopClient.query(ADJUST_ITEM_QUENTITY, {
+            const { adjustItemQuantity } = await shopClient.query<
+                AdjustItemQuantity.Mutation,
+                AdjustItemQuantity.Variables
+            >(ADJUST_ITEM_QUENTITY, {
                 orderItemId: firstOrderItemId,
                 quantity: 50,
             });
 
-            expect(result.adjustItemQuantity.lines.length).toBe(1);
-            expect(result.adjustItemQuantity.lines[0].quantity).toBe(50);
+            expect(adjustItemQuantity!.lines.length).toBe(1);
+            expect(adjustItemQuantity!.lines[0].quantity).toBe(50);
         });
 
         it('removeItemFromOrder removes the correct item', async () => {
-            const result1 = await shopClient.query(ADD_ITEM_TO_ORDER, {
+            const { addItemToOrder } = await shopClient.query<
+                AddItemToOrder.Mutation,
+                AddItemToOrder.Variables
+            >(ADD_ITEM_TO_ORDER, {
                 productVariantId: 'T_3',
                 quantity: 3,
             });
-            expect(result1.addItemToOrder.lines.length).toBe(2);
-            expect(result1.addItemToOrder.lines.map((i: any) => i.productVariant.id)).toEqual(['T_1', 'T_3']);
+            expect(addItemToOrder!.lines.length).toBe(2);
+            expect(addItemToOrder!.lines.map(i => i.productVariant.id)).toEqual(['T_1', 'T_3']);
 
-            const result2 = await shopClient.query(REMOVE_ITEM_FROM_ORDER, {
+            const { removeItemFromOrder } = await shopClient.query<
+                RemoveItemFromOrder.Mutation,
+                RemoveItemFromOrder.Variables
+            >(REMOVE_ITEM_FROM_ORDER, {
                 orderItemId: firstOrderItemId,
             });
-            expect(result2.removeItemFromOrder.lines.length).toBe(1);
-            expect(result2.removeItemFromOrder.lines.map((i: any) => i.productVariant.id)).toEqual(['T_3']);
+            expect(removeItemFromOrder!.lines.length).toBe(1);
+            expect(removeItemFromOrder!.lines.map(i => i.productVariant.id)).toEqual(['T_3']);
         });
 
         it('nextOrderStates returns next valid states', async () => {
-            const result = await shopClient.query(GET_NEXT_STATES);
+            const result = await shopClient.query<GetNextOrderStates.Query>(GET_NEXT_STATES);
 
             expect(result.nextOrderStates).toEqual(['ArrangingPayment']);
         });
 
         it('logging out and back in again resumes the last active order', async () => {
             await shopClient.asAnonymousUser();
-            const result1 = await shopClient.query(GET_ACTIVE_ORDER);
+            const result1 = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
             expect(result1.activeOrder).toBeNull();
 
             await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
-            const result2 = await shopClient.query(GET_ACTIVE_ORDER);
-            expect(result2.activeOrder.id).toBe(activeOrder.id);
+            const result2 = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
+            expect(result2.activeOrder!.id).toBe(activeOrder.id);
         });
 
         describe('shipping', () => {
-            let shippingMethods: any;
+            let shippingMethods: GetShippingMethods.EligibleShippingMethods[];
 
             it(
                 'setOrderShippingAddress throws with invalid countryCode',
@@ -456,9 +536,12 @@ describe('Shop orders', () => {
                         countryCode: 'INVALID',
                     };
 
-                    return shopClient.query(SET_SHIPPING_ADDRESS, {
-                        input: address,
-                    });
+                    return shopClient.query<SetShippingAddress.Mutation, SetShippingAddress.Variables>(
+                        SET_SHIPPING_ADDRESS,
+                        {
+                            input: address,
+                        },
+                    );
                 }, `The countryCode "INVALID" was not recognized`),
             );
 
@@ -474,11 +557,14 @@ describe('Shop orders', () => {
                     countryCode: 'US',
                     phoneNumber: '4444444',
                 };
-                const result = await shopClient.query(SET_SHIPPING_ADDRESS, {
+                const { setOrderShippingAddress } = await shopClient.query<
+                    SetShippingAddress.Mutation,
+                    SetShippingAddress.Variables
+                >(SET_SHIPPING_ADDRESS, {
                     input: address,
                 });
 
-                expect(result.setOrderShippingAddress.shippingAddress).toEqual({
+                expect(setOrderShippingAddress!.shippingAddress).toEqual({
                     fullName: 'name',
                     company: 'company',
                     streetLine1: '12 the street',
@@ -492,7 +578,9 @@ describe('Shop orders', () => {
             });
 
             it('eligibleShippingMethods lists shipping methods', async () => {
-                const result = await shopClient.query(GET_ELIGIBLE_SHIPPING_METHODS);
+                const result = await shopClient.query<GetShippingMethods.Query>(
+                    GET_ELIGIBLE_SHIPPING_METHODS,
+                );
 
                 shippingMethods = result.eligibleShippingMethods;
 
@@ -503,37 +591,43 @@ describe('Shop orders', () => {
             });
 
             it('shipping is initially unset', async () => {
-                const result = await shopClient.query(GET_ACTIVE_ORDER);
+                const result = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
 
-                expect(result.activeOrder.shipping).toEqual(0);
-                expect(result.activeOrder.shippingMethod).toEqual(null);
+                expect(result.activeOrder!.shipping).toEqual(0);
+                expect(result.activeOrder!.shippingMethod).toEqual(null);
             });
 
             it('setOrderShippingMethod sets the shipping method', async () => {
-                const result = await shopClient.query(SET_SHIPPING_METHOD, {
+                const result = await shopClient.query<
+                    SetShippingMethod.Mutation,
+                    SetShippingMethod.Variables
+                >(SET_SHIPPING_METHOD, {
                     id: shippingMethods[1].id,
                 });
 
-                const activeOrderResult = await shopClient.query(GET_ACTIVE_ORDER);
+                const activeOrderResult = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
 
-                const order = activeOrderResult.activeOrder;
+                const order = activeOrderResult.activeOrder!;
 
                 expect(order.shipping).toBe(shippingMethods[1].price);
-                expect(order.shippingMethod.id).toBe(shippingMethods[1].id);
-                expect(order.shippingMethod.description).toBe(shippingMethods[1].description);
+                expect(order.shippingMethod!.id).toBe(shippingMethods[1].id);
+                expect(order.shippingMethod!.description).toBe(shippingMethods[1].description);
             });
 
             it('shipping method is preserved after adjustItemQuantity', async () => {
-                const activeOrderResult = await shopClient.query(GET_ACTIVE_ORDER);
-                activeOrder = activeOrderResult.activeOrder;
-                const result = await shopClient.query(ADJUST_ITEM_QUENTITY, {
+                const activeOrderResult = await shopClient.query<GetActiveOrder.Query>(GET_ACTIVE_ORDER);
+                activeOrder = activeOrderResult.activeOrder!;
+                const { adjustItemQuantity } = await shopClient.query<
+                    AdjustItemQuantity.Mutation,
+                    AdjustItemQuantity.Variables
+                >(ADJUST_ITEM_QUENTITY, {
                     orderItemId: activeOrder.lines[0].id,
                     quantity: 10,
                 });
 
-                expect(result.adjustItemQuantity.shipping).toBe(shippingMethods[1].price);
-                expect(result.adjustItemQuantity.shippingMethod.id).toBe(shippingMethods[1].id);
-                expect(result.adjustItemQuantity.shippingMethod.description).toBe(
+                expect(adjustItemQuantity!.shipping).toBe(shippingMethods[1].price);
+                expect(adjustItemQuantity!.shippingMethod!.id).toBe(shippingMethods[1].id);
+                expect(adjustItemQuantity!.shippingMethod!.description).toBe(
                     shippingMethods[1].description,
                 );
             });
@@ -544,18 +638,24 @@ describe('Shop orders', () => {
                 'attempting add a Payment throws error when in AddingItems state',
                 assertThrowsWithMessage(
                     () =>
-                        shopClient.query(ADD_PAYMENT, {
-                            input: {
-                                method: testPaymentMethod.code,
-                                metadata: {},
+                        shopClient.query<AddPaymentToOrder.Mutation, AddPaymentToOrder.Variables>(
+                            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 shopClient.query(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
+                const result = await shopClient.query<
+                    TransitionToState.Mutation,
+                    TransitionToState.Variables
+                >(TRANSITION_TO_STATE, { state: 'ArrangingPayment' });
                 expect(result.transitionOrderToState).toEqual({
                     id: activeOrder.id,
                     state: 'ArrangingPayment',
@@ -566,10 +666,13 @@ describe('Shop orders', () => {
                 'attempting to add an item throws error when in ArrangingPayment state',
                 assertThrowsWithMessage(
                     () =>
-                        shopClient.query(ADD_ITEM_TO_ORDER, {
-                            productVariantId: 'T_4',
-                            quantity: 1,
-                        }),
+                        shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(
+                            ADD_ITEM_TO_ORDER,
+                            {
+                                productVariantId: 'T_4',
+                                quantity: 1,
+                            },
+                        ),
                     `Order contents may only be modified when in the "AddingItems" state`,
                 ),
             );
@@ -578,10 +681,13 @@ describe('Shop orders', () => {
                 'attempting to modify item quantity throws error when in ArrangingPayment state',
                 assertThrowsWithMessage(
                     () =>
-                        shopClient.query(ADJUST_ITEM_QUENTITY, {
-                            orderItemId: activeOrder.lines[0].id,
-                            quantity: 12,
-                        }),
+                        shopClient.query<AdjustItemQuantity.Mutation, AdjustItemQuantity.Variables>(
+                            ADJUST_ITEM_QUENTITY,
+                            {
+                                orderItemId: activeOrder.lines[0].id,
+                                quantity: 12,
+                            },
+                        ),
                     `Order contents may only be modified when in the "AddingItems" state`,
                 ),
             );
@@ -590,9 +696,12 @@ describe('Shop orders', () => {
                 'attempting to remove an item throws error when in ArrangingPayment state',
                 assertThrowsWithMessage(
                     () =>
-                        shopClient.query(REMOVE_ITEM_FROM_ORDER, {
-                            orderItemId: activeOrder.lines[0].id,
-                        }),
+                        shopClient.query<RemoveItemFromOrder.Mutation, RemoveItemFromOrder.Variables>(
+                            REMOVE_ITEM_FROM_ORDER,
+                            {
+                                orderItemId: activeOrder.lines[0].id,
+                            },
+                        ),
                     `Order contents may only be modified when in the "AddingItems" state`,
                 ),
             );
@@ -600,16 +709,24 @@ describe('Shop orders', () => {
             it(
                 'attempting to setOrderShippingMethod throws error when in ArrangingPayment state',
                 assertThrowsWithMessage(async () => {
-                    const shippingMethodsResult = await shopClient.query(GET_ELIGIBLE_SHIPPING_METHODS);
+                    const shippingMethodsResult = await shopClient.query<GetShippingMethods.Query>(
+                        GET_ELIGIBLE_SHIPPING_METHODS,
+                    );
                     const shippingMethods = shippingMethodsResult.eligibleShippingMethods;
-                    return shopClient.query(SET_SHIPPING_METHOD, {
-                        id: shippingMethods[0].id,
-                    });
+                    return shopClient.query<SetShippingMethod.Mutation, SetShippingMethod.Variables>(
+                        SET_SHIPPING_METHOD,
+                        {
+                            id: shippingMethods[0].id,
+                        },
+                    );
                 }, `Order contents may only be modified when in the "AddingItems" state`),
             );
 
             it('adds a declined payment', async () => {
-                const result = await shopClient.query(ADD_PAYMENT, {
+                const { addPaymentToOrder } = await shopClient.query<
+                    AddPaymentToOrder.Mutation,
+                    AddPaymentToOrder.Variables
+                >(ADD_PAYMENT, {
                     input: {
                         method: testFailingPaymentMethod.code,
                         metadata: {
@@ -618,8 +735,8 @@ describe('Shop orders', () => {
                     },
                 });
 
-                const payment = result.addPaymentToOrder.payments[0];
-                expect(result.addPaymentToOrder.payments.length).toBe(1);
+                const payment = addPaymentToOrder!.payments![0];
+                expect(addPaymentToOrder!.payments!.length).toBe(1);
                 expect(payment.method).toBe(testFailingPaymentMethod.code);
                 expect(payment.state).toBe('Declined');
                 expect(payment.transactionId).toBe(null);
@@ -629,7 +746,10 @@ describe('Shop orders', () => {
             });
 
             it('adds a successful payment and transitions Order state', async () => {
-                const result = await shopClient.query(ADD_PAYMENT, {
+                const { addPaymentToOrder } = await shopClient.query<
+                    AddPaymentToOrder.Mutation,
+                    AddPaymentToOrder.Variables
+                >(ADD_PAYMENT, {
                     input: {
                         method: testPaymentMethod.code,
                         metadata: {
@@ -638,10 +758,10 @@ describe('Shop orders', () => {
                     },
                 });
 
-                const payment = result.addPaymentToOrder.payments[0];
-                expect(result.addPaymentToOrder.state).toBe('PaymentSettled');
-                expect(result.addPaymentToOrder.active).toBe(false);
-                expect(result.addPaymentToOrder.payments.length).toBe(1);
+                const payment = addPaymentToOrder!.payments![0];
+                expect(addPaymentToOrder!.state).toBe('PaymentSettled');
+                expect(addPaymentToOrder!.active).toBe(false);
+                expect(addPaymentToOrder!.payments!.length).toBe(1);
                 expect(payment.method).toBe(testPaymentMethod.code);
                 expect(payment.state).toBe('Settled');
                 expect(payment.transactionId).toBe('12345');
@@ -654,20 +774,26 @@ describe('Shop orders', () => {
         describe('orderByCode', () => {
             describe('immediately after Order is placed', () => {
                 it('works when authenticated', async () => {
-                    const result = await shopClient.query(GET_ORDER_BY_CODE, {
-                        code: activeOrder.code,
-                    });
+                    const result = await shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
+                        GET_ORDER_BY_CODE,
+                        {
+                            code: activeOrder.code,
+                        },
+                    );
 
-                    expect(result.orderByCode.id).toBe(activeOrder.id);
+                    expect(result.orderByCode!.id).toBe(activeOrder.id);
                 });
 
                 it('works when anonymous', async () => {
                     await shopClient.asAnonymousUser();
-                    const result = await shopClient.query(GET_ORDER_BY_CODE, {
-                        code: activeOrder.code,
-                    });
+                    const result = await shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
+                        GET_ORDER_BY_CODE,
+                        {
+                            code: activeOrder.code,
+                        },
+                    );
 
-                    expect(result.orderByCode.id).toBe(activeOrder.id);
+                    expect(result.orderByCode!.id).toBe(activeOrder.id);
                 });
 
                 it(
@@ -675,9 +801,12 @@ describe('Shop orders', () => {
                     assertThrowsWithMessage(async () => {
                         authenticatedUserEmailAddress = customers[1].emailAddress;
                         await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
-                        return shopClient.query(GET_ORDER_BY_CODE, {
-                            code: activeOrder.code,
-                        });
+                        return shopClient.query<GetOrderByCode.Query, GetOrderByCode.Variables>(
+                            GET_ORDER_BY_CODE,
+                            {
+                                code: activeOrder.code,
+                            },
+                        );
                     }, `You are not currently authorized to perform this action`),
                 );
             });

+ 96 - 68
packages/core/e2e/stock-control.e2e-spec.ts

@@ -1,3 +1,4 @@
+/* tslint:disable:no-non-null-assertion */
 import gql from 'graphql-tag';
 import path from 'path';
 
@@ -7,17 +8,14 @@ import { OrderState } from '../src/service/helpers/order-state-machine/order-sta
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import {
     CreateAddressInput,
-    ProductVariant,
+    GetStockMovement,
     StockMovementType,
     UpdateProductVariantInput,
+    UpdateStock,
+    VariantWithStockFragment,
 } from './graphql/generated-e2e-admin-types';
-import { AddItemToOrder, PaymentInput } from './graphql/generated-e2e-shop-types';
-import {
-    ADD_ITEM_TO_ORDER,
-    ADD_PAYMENT,
-    SET_SHIPPING_ADDRESS,
-    TRANSITION_TO_STATE,
-} from './graphql/shop-definitions';
+import { AddItemToOrder, AddPaymentToOrder, PaymentInput, SetShippingAddress, TransitionToState } from './graphql/generated-e2e-shop-types';
+import { ADD_ITEM_TO_ORDER, ADD_PAYMENT, SET_SHIPPING_ADDRESS, TRANSITION_TO_STATE } from './graphql/shop-definitions';
 import { TestAdminClient, TestShopClient } from './test-client';
 import { TestServer } from './test-server';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
@@ -50,12 +48,15 @@ describe('Stock control', () => {
     });
 
     describe('stock adjustments', () => {
-        let variants: ProductVariant[];
+        let variants: VariantWithStockFragment[];
 
         it('stockMovements are initially empty', async () => {
-            const result = await adminClient.query(GET_STOCK_MOVEMENT, { id: 'T_1' });
+            const { product } = await adminClient.query<GetStockMovement.Query, GetStockMovement.Variables>(
+                GET_STOCK_MOVEMENT,
+                { id: 'T_1' },
+            );
 
-            variants = result.product.variants;
+            variants = product!.variants;
             for (const variant of variants) {
                 expect(variant.stockMovements.items).toEqual([]);
                 expect(variant.stockMovements.totalItems).toEqual(0);
@@ -63,76 +64,91 @@ describe('Stock control', () => {
         });
 
         it('updating ProductVariant with same stockOnHand does not create a StockMovement', async () => {
-            const result = await adminClient.query(UPDATE_STOCK_ON_HAND, {
-                input: [
-                    {
-                        id: variants[0].id,
-                        stockOnHand: variants[0].stockOnHand,
-                    },
-                ] as UpdateProductVariantInput[],
-            });
+            const { updateProductVariants } = await adminClient.query<UpdateStock.Mutation, UpdateStock.Variables>(
+                UPDATE_STOCK_ON_HAND,
+                {
+                    input: [
+                        {
+                            id: variants[0].id,
+                            stockOnHand: variants[0].stockOnHand,
+                        },
+                    ] as UpdateProductVariantInput[],
+                },
+            );
 
-            expect(result.updateProductVariants[0].stockMovements.items).toEqual([]);
-            expect(result.updateProductVariants[0].stockMovements.totalItems).toEqual(0);
+            expect(updateProductVariants[0]!.stockMovements.items).toEqual([]);
+            expect(updateProductVariants[0]!.stockMovements.totalItems).toEqual(0);
         });
 
         it('increasing stockOnHand creates a StockMovement with correct quantity', async () => {
-            const result = await adminClient.query(UPDATE_STOCK_ON_HAND, {
-                input: [
-                    {
-                        id: variants[0].id,
-                        stockOnHand: variants[0].stockOnHand + 5,
-                    },
-                ] as UpdateProductVariantInput[],
-            });
+            const { updateProductVariants } = await adminClient.query<UpdateStock.Mutation, UpdateStock.Variables>(
+                UPDATE_STOCK_ON_HAND,
+                {
+                    input: [
+                        {
+                            id: variants[0].id,
+                            stockOnHand: variants[0].stockOnHand + 5,
+                        },
+                    ] as UpdateProductVariantInput[],
+                },
+            );
 
-            expect(result.updateProductVariants[0].stockOnHand).toBe(5);
-            expect(result.updateProductVariants[0].stockMovements.totalItems).toEqual(1);
-            expect(result.updateProductVariants[0].stockMovements.items[0].type).toBe(
+            expect(updateProductVariants[0]!.stockOnHand).toBe(5);
+            expect(updateProductVariants[0]!.stockMovements.totalItems).toEqual(1);
+            expect(updateProductVariants[0]!.stockMovements.items[0].type).toBe(
                 StockMovementType.ADJUSTMENT,
             );
-            expect(result.updateProductVariants[0].stockMovements.items[0].quantity).toBe(5);
+            expect(updateProductVariants[0]!.stockMovements.items[0].quantity).toBe(5);
         });
 
         it('decreasing stockOnHand creates a StockMovement with correct quantity', async () => {
-            const result = await adminClient.query(UPDATE_STOCK_ON_HAND, {
-                input: [
-                    {
-                        id: variants[0].id,
-                        stockOnHand: variants[0].stockOnHand + 5 - 2,
-                    },
-                ] as UpdateProductVariantInput[],
-            });
+            const { updateProductVariants } = await adminClient.query<UpdateStock.Mutation, UpdateStock.Variables>(
+                UPDATE_STOCK_ON_HAND,
+                {
+                    input: [
+                        {
+                            id: variants[0].id,
+                            stockOnHand: variants[0].stockOnHand + 5 - 2,
+                        },
+                    ] as UpdateProductVariantInput[],
+                },
+            );
 
-            expect(result.updateProductVariants[0].stockOnHand).toBe(3);
-            expect(result.updateProductVariants[0].stockMovements.totalItems).toEqual(2);
-            expect(result.updateProductVariants[0].stockMovements.items[1].type).toBe(
+            expect(updateProductVariants[0]!.stockOnHand).toBe(3);
+            expect(updateProductVariants[0]!.stockMovements.totalItems).toEqual(2);
+            expect(updateProductVariants[0]!.stockMovements.items[1].type).toBe(
                 StockMovementType.ADJUSTMENT,
             );
-            expect(result.updateProductVariants[0].stockMovements.items[1].quantity).toBe(-2);
+            expect(updateProductVariants[0]!.stockMovements.items[1].quantity).toBe(-2);
         });
 
         it(
             'attempting to set a negative stockOnHand throws',
             assertThrowsWithMessage(async () => {
-                const result = await adminClient.query(UPDATE_STOCK_ON_HAND, {
-                    input: [
-                        {
-                            id: variants[0].id,
-                            stockOnHand: -1,
-                        },
-                    ] as UpdateProductVariantInput[],
-                });
+                const result = await adminClient.query<UpdateStock.Mutation, UpdateStock.Variables>(
+                    UPDATE_STOCK_ON_HAND,
+                    {
+                        input: [
+                            {
+                                id: variants[0].id,
+                                stockOnHand: -1,
+                            },
+                        ] as UpdateProductVariantInput[],
+                    },
+                );
             }, 'stockOnHand cannot be a negative value'),
         );
     });
 
     describe('sales', () => {
         beforeAll(async () => {
-            const { product } = await adminClient.query(GET_STOCK_MOVEMENT, { id: 'T_2' });
-            const [variant1, variant2]: ProductVariant[] = product.variants;
+            const { product } = await adminClient.query<GetStockMovement.Query, GetStockMovement.Variables>(
+                GET_STOCK_MOVEMENT,
+                { id: 'T_2' },
+            );
+            const [variant1, variant2] = product!.variants;
 
-            await adminClient.query(UPDATE_STOCK_ON_HAND, {
+            await adminClient.query<UpdateStock.Mutation, UpdateStock.Variables>(UPDATE_STOCK_ON_HAND, {
                 input: [
                     {
                         id: variant1.id,
@@ -157,14 +173,20 @@ describe('Stock control', () => {
                 productVariantId: variant2.id,
                 quantity: 3,
             });
-            await shopClient.query(SET_SHIPPING_ADDRESS, {
-                input: {
-                    streetLine1: '1 Test Street',
-                    countryCode: 'GB',
-                } as CreateAddressInput,
-            });
-            await shopClient.query(TRANSITION_TO_STATE, { state: 'ArrangingPayment' as OrderState });
-            await shopClient.query(ADD_PAYMENT, {
+            await shopClient.query<SetShippingAddress.Mutation, SetShippingAddress.Variables>(
+                SET_SHIPPING_ADDRESS,
+                {
+                    input: {
+                        streetLine1: '1 Test Street',
+                        countryCode: 'GB',
+                    } as CreateAddressInput,
+                },
+            );
+            await shopClient.query<TransitionToState.Mutation, TransitionToState.Variables>(
+                TRANSITION_TO_STATE,
+                { state: 'ArrangingPayment' as OrderState },
+            );
+            await shopClient.query<AddPaymentToOrder.Mutation, AddPaymentToOrder.Variables>(ADD_PAYMENT, {
                 input: {
                     method: testPaymentMethod.code,
                     metadata: {},
@@ -173,8 +195,11 @@ describe('Stock control', () => {
         });
 
         it('creates a Sale when order completed', async () => {
-            const result = await adminClient.query(GET_STOCK_MOVEMENT, { id: 'T_2' });
-            const [variant1, variant2]: ProductVariant[] = result.product.variants;
+            const { product } = await adminClient.query<GetStockMovement.Query, GetStockMovement.Variables>(
+                GET_STOCK_MOVEMENT,
+                { id: 'T_2' },
+            );
+            const [variant1, variant2] = product!.variants;
 
             expect(variant1.stockMovements.totalItems).toBe(2);
             expect(variant1.stockMovements.items[1].type).toBe(StockMovementType.SALE);
@@ -186,8 +211,11 @@ describe('Stock control', () => {
         });
 
         it('stockOnHand is updated according to trackInventory setting', async () => {
-            const result = await adminClient.query(GET_STOCK_MOVEMENT, { id: 'T_2' });
-            const [variant1, variant2]: ProductVariant[] = result.product.variants;
+            const { product } = await adminClient.query<GetStockMovement.Query, GetStockMovement.Variables>(
+                GET_STOCK_MOVEMENT,
+                { id: 'T_2' },
+            );
+            const [variant1, variant2] = product!.variants;
 
             expect(variant1.stockOnHand).toBe(5); // untracked inventory
             expect(variant2.stockOnHand).toBe(2); // tracked inventory

+ 9 - 10
packages/core/e2e/zone.e2e-spec.ts

@@ -5,10 +5,10 @@ import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { ZONE_FRAGMENT } from './graphql/fragments';
 import {
     AddMembersToZone,
-    CreateZone,
+    CreateZone, DeleteZone,
     DeletionResult,
     GetCountryList,
-    GetZone,
+    GetZone, GetZones,
     RemoveMembersFromZone,
     UpdateZone,
 } from './graphql/generated-e2e-admin-types';
@@ -42,8 +42,7 @@ describe('Facet resolver', () => {
     });
 
     it('zones', async () => {
-        const result = await client.query(GET_ZONE_LIST);
-
+        const result = await client.query<GetZones.Query>(GET_ZONE_LIST);
         expect(result.zones.length).toBe(5);
         zones = result.zones;
         oceania = zones[0];
@@ -110,19 +109,19 @@ describe('Facet resolver', () => {
 
     describe('deletion', () => {
         it('deletes Zone not used in any TaxRate', async () => {
-            const result1 = await client.query(DELETE_ZONE, { id: pangaea.id });
+            const result1 = await client.query<DeleteZone.Mutation, DeleteZone.Variables>(DELETE_ZONE, { id: pangaea.id });
 
             expect(result1.deleteZone).toEqual({
                 result: DeletionResult.DELETED,
                 message: '',
             });
 
-            const result2 = await client.query(GET_ZONE_LIST, {});
-            expect(result2.zones.find((c: any) => c.id === pangaea.id)).toBeUndefined();
+            const result2 = await client.query<GetZones.Query>(GET_ZONE_LIST);
+            expect(result2.zones.find(c => c.id === pangaea.id)).toBeUndefined();
         });
 
         it('does not delete Zone that is used in one or more TaxRates', async () => {
-            const result1 = await client.query(DELETE_ZONE, { id: oceania.id });
+            const result1 = await client.query<DeleteZone.Mutation, DeleteZone.Variables>(DELETE_ZONE, { id: oceania.id });
 
             expect(result1.deleteZone).toEqual({
                 result: DeletionResult.NOT_DELETED,
@@ -131,8 +130,8 @@ describe('Facet resolver', () => {
                     'TaxRates: Standard Tax Oceania, Reduced Tax Oceania, Zero Tax Oceania',
             });
 
-            const result2 = await client.query(GET_ZONE_LIST, {});
-            expect(result2.zones.find((c: any) => c.id === oceania.id)).not.toBeUndefined();
+            const result2 = await client.query<GetZones.Query>(GET_ZONE_LIST);
+            expect(result2.zones.find(c => c.id === oceania.id)).not.toBeUndefined();
         });
     });
 });