Browse Source

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

Relates to #92
Michael Bromley 6 years ago
parent
commit
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
 [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.
 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
 ### Testing
 
 

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

@@ -1,5 +1,5 @@
 // tslint:disable
 // 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;
 export type Maybe<T> = T | null;
 /** All built-in and custom scalars, mapped to their actual values */
 /** 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
 // 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;
 export type Maybe<T> = T | null;
 /** All built-in and custom scalars, mapped to their actual values */
 /** 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
 // 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;
 export type Maybe<T> = T | null;
 /** All built-in and custom scalars, mapped to their actual values */
 /** 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 path from 'path';
 
 
 import { StringOperator } from '../src/common/configurable-operation';
 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 { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { COLLECTION_FRAGMENT, FACET_VALUE_FRAGMENT } from './graphql/fragments';
 import { COLLECTION_FRAGMENT, FACET_VALUE_FRAGMENT } from './graphql/fragments';
@@ -16,25 +13,24 @@ import {
     ConfigArgType,
     ConfigArgType,
     CreateCollection,
     CreateCollection,
     CreateCollectionInput,
     CreateCollectionInput,
-    FacetValue,
+    CreateCollectionSelectVariants,
+    FacetValueFragment,
     GetAssetList,
     GetAssetList,
     GetCollection,
     GetCollection,
+    GetCollectionBreadcrumbs,
     GetCollectionProducts,
     GetCollectionProducts,
+    GetCollections,
+    GetCollectionsForProducts,
+    GetFacetValues,
+    GetProductsWithVariantIds,
     LanguageCode,
     LanguageCode,
     MoveCollection,
     MoveCollection,
-    ProductWithVariants,
     SortOrder,
     SortOrder,
     UpdateCollection,
     UpdateCollection,
     UpdateProduct,
     UpdateProduct,
     UpdateProductVariants,
     UpdateProductVariants,
 } from './graphql/generated-e2e-admin-types';
 } 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 { TestAdminClient } from './test-client';
 import { TestServer } from './test-server';
 import { TestServer } from './test-server';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
@@ -43,7 +39,7 @@ describe('Collection resolver', () => {
     const client = new TestAdminClient();
     const client = new TestAdminClient();
     const server = new TestServer();
     const server = new TestServer();
     let assets: GetAssetList.Items[];
     let assets: GetAssetList.Items[];
-    let facetValues: FacetValue.Fragment[];
+    let facetValues: FacetValueFragment[];
     let electronicsCollection: Collection.Fragment;
     let electronicsCollection: Collection.Fragment;
     let computersCollection: Collection.Fragment;
     let computersCollection: Collection.Fragment;
     let pearCollection: Collection.Fragment;
     let pearCollection: Collection.Fragment;
@@ -62,10 +58,10 @@ describe('Collection resolver', () => {
             },
             },
         });
         });
         assets = assetsResult.assets.items;
         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(
         facetValues = facetValuesResult.facets.items.reduce(
-            (values: any, facet: any) => [...values, ...facet.values],
-            [],
+            (values, facet) => [...values, ...facet.values],
+            [] as FacetValueFragment[],
         );
         );
     }, TEST_SETUP_TIMEOUT_MS);
     }, TEST_SETUP_TIMEOUT_MS);
 
 
@@ -170,9 +166,12 @@ describe('Collection resolver', () => {
     });
     });
 
 
     it('breadcrumbs', async () => {
     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) {
         if (!result.collection) {
             fail(`did not return the collection`);
             fail(`did not return the collection`);
             return;
             return;
@@ -186,9 +185,12 @@ describe('Collection resolver', () => {
     });
     });
 
 
     it('breadcrumbs for root collection', async () => {
     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) {
         if (!result.collection) {
             fail(`did not return the collection`);
             fail(`did not return the collection`);
             return;
             return;
@@ -228,12 +230,15 @@ describe('Collection resolver', () => {
             expect(result.moveCollection.parent!.id).toBe(electronicsCollection.id);
             expect(result.moveCollection.parent!.id).toBe(electronicsCollection.id);
 
 
             const positions = await getChildrenOf(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 () => {
         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 13 inch 8GB',
                 'Laptop 15 inch 8GB',
                 'Laptop 15 inch 8GB',
                 'Laptop 13 inch 16GB',
                 'Laptop 13 inch 16GB',
@@ -252,7 +257,7 @@ describe('Collection resolver', () => {
             });
             });
 
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
             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 () => {
         it('alters the position in the current parent 2', async () => {
@@ -265,7 +270,7 @@ describe('Collection resolver', () => {
             });
             });
 
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
             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 () => {
         it('corrects an out-of-bounds negative index value', async () => {
@@ -278,7 +283,7 @@ describe('Collection resolver', () => {
             });
             });
 
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
             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 () => {
         it('corrects an out-of-bounds positive index value', async () => {
@@ -291,7 +296,7 @@ describe('Collection resolver', () => {
             });
             });
 
 
             const afterResult = await getChildrenOf(electronicsCollection.id);
             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(
         it(
@@ -325,14 +330,17 @@ describe('Collection resolver', () => {
         );
         );
 
 
         async function getChildrenOf(parentId: string): Promise<Array<{ name: string; id: string }>> {
         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', () => {
     describe('filters', () => {
         it('Collection with no filters has no productVariants', async () => {
         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: {
                 input: {
                     translations: [{ languageCode: LanguageCode.en, name: 'Empty', description: '' }],
                     translations: [{ languageCode: LanguageCode.en, name: 'Empty', description: '' }],
                     filters: [],
                     filters: [],
@@ -343,10 +351,13 @@ describe('Collection resolver', () => {
 
 
         describe('facetValue filter', () => {
         describe('facetValue filter', () => {
             it('electronics', async () => {
             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,
                     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 13 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 13 inch 16GB',
                     'Laptop 13 inch 16GB',
@@ -372,10 +383,13 @@ describe('Collection resolver', () => {
             });
             });
 
 
             it('computers', async () => {
             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,
                     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 13 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 13 inch 16GB',
                     'Laptop 13 inch 16GB',
@@ -397,7 +411,10 @@ describe('Collection resolver', () => {
             });
             });
 
 
             it('photo and pear', async () => {
             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: {
                     input: {
                         translations: [
                         translations: [
                             { languageCode: LanguageCode.en, name: 'Photo Pear', description: '' },
                             { languageCode: LanguageCode.en, name: 'Photo Pear', description: '' },
@@ -418,7 +435,7 @@ describe('Collection resolver', () => {
                         ],
                         ],
                     } as CreateCollectionInput,
                     } 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',
                     'Instant Camera',
                 ]);
                 ]);
             });
             });
@@ -462,7 +479,10 @@ describe('Collection resolver', () => {
             it('contains operator', async () => {
             it('contains operator', async () => {
                 const collection = await createVariantNameFilteredCollection('contains', 'camera');
                 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,
                     id: collection.id,
                 });
                 });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
@@ -532,10 +552,10 @@ describe('Collection resolver', () => {
         });
         });
 
 
         describe('re-evaluation of contents on changes', () => {
         describe('re-evaluation of contents on changes', () => {
-            let products: ProductWithVariants.Fragment[];
+            let products: GetProductsWithVariantIds.Items[];
 
 
             beforeAll(async () => {
             beforeAll(async () => {
-                const result = await client.query(gql`
+                const result = await client.query<GetProductsWithVariantIds.Query>(gql`
                     query GetProductsWithVariantIds {
                     query GetProductsWithVariantIds {
                         products {
                         products {
                             items {
                             items {
@@ -640,7 +660,10 @@ describe('Collection resolver', () => {
 
 
     describe('Product collections property', () => {
     describe('Product collections property', () => {
         it('returns all collections to which the Product belongs', async () => {
         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([
             expect(result.products.items[0].collections).toEqual([
                 { id: 'T_3', name: 'Electronics' },
                 { id: 'T_3', name: 'Electronics' },
                 { id: 'T_5', name: 'Pear' },
                 { 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 { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { COUNTRY_FRAGMENT } from './graphql/fragments';
 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 { GET_COUNTRY_LIST, UPDATE_COUNTRY } from './graphql/shared-definitions';
 import { TestAdminClient } from './test-client';
 import { TestAdminClient } from './test-client';
 import { TestServer } from './test-server';
 import { TestServer } from './test-server';
@@ -71,7 +79,7 @@ describe('Facet resolver', () => {
 
 
     describe('deletion', () => {
     describe('deletion', () => {
         it('deletes Country not used in any address', async () => {
         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({
             expect(result1.deleteCountry).toEqual({
                 result: DeletionResult.DELETED,
                 result: DeletionResult.DELETED,
@@ -83,7 +91,7 @@ describe('Facet resolver', () => {
         });
         });
 
 
         it('does not delete Country that is used in one or more addresses', async () => {
         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({
             expect(result1.deleteCountry).toEqual({
                 result: DeletionResult.NOT_DELETED,
                 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 path from 'path';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 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 { GET_CUSTOMER, GET_CUSTOMER_LIST } from './graphql/shared-definitions';
 import { ADD_ITEM_TO_ORDER } from './graphql/shop-definitions';
 import { ADD_ITEM_TO_ORDER } from './graphql/shop-definitions';
 import { TestAdminClient, TestShopClient } from './test-client';
 import { TestAdminClient, TestShopClient } from './test-client';
 import { TestServer } from './test-server';
 import { TestServer } from './test-server';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
-import { CUSTOMER_FRAGMENT } from './graphql/fragments';
 
 
 // tslint:disable:no-non-null-assertion
 // tslint:disable:no-non-null-assertion
 
 
@@ -53,7 +64,7 @@ describe('Customer resolver', () => {
             'createCustomerAddress throws on invalid countryCode',
             'createCustomerAddress throws on invalid countryCode',
             assertThrowsWithMessage(
             assertThrowsWithMessage(
                 () =>
                 () =>
-                    adminClient.query(CREATE_ADDRESS, {
+                    adminClient.query<CreateAddress.Mutation, CreateAddress.Variables>(CREATE_ADDRESS, {
                         id: firstCustomer.id,
                         id: firstCustomer.id,
                         input: {
                         input: {
                             streetLine1: 'streetLine1',
                             streetLine1: 'streetLine1',
@@ -65,22 +76,25 @@ describe('Customer resolver', () => {
         );
         );
 
 
         it('createCustomerAddress creates a new address', async () => {
         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({
             expect(omit(result.createCustomerAddress, ['id'])).toEqual({
                 fullName: 'fullName',
                 fullName: 'fullName',
                 company: 'company',
                 company: 'company',
@@ -109,27 +123,33 @@ describe('Customer resolver', () => {
         });
         });
 
 
         it('updateCustomerAddress updates the country', async () => {
         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({
             expect(result.updateCustomerAddress.country).toEqual({
-                    code: 'AT',
-                    name: 'Austria',
+                code: 'AT',
+                name: 'Austria',
             });
             });
         });
         });
 
 
         it('updateCustomerAddress allows only a single default address', async () => {
         it('updateCustomerAddress allows only a single default address', async () => {
             // set the first customer's second address to be default
             // 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.defaultShippingAddress).toBe(true);
             expect(result1.updateCustomerAddress.defaultBillingAddress).toBe(true);
             expect(result1.updateCustomerAddress.defaultBillingAddress).toBe(true);
 
 
@@ -141,13 +161,16 @@ describe('Customer resolver', () => {
             expect(result2.customer!.addresses![0].defaultBillingAddress).toBe(false);
             expect(result2.customer!.addresses![0].defaultBillingAddress).toBe(false);
 
 
             // set the first customer's first address to be default
             // 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.defaultShippingAddress).toBe(true);
             expect(result3.updateCustomerAddress.defaultBillingAddress).toBe(true);
             expect(result3.updateCustomerAddress.defaultBillingAddress).toBe(true);
 
 
@@ -165,13 +188,16 @@ describe('Customer resolver', () => {
             const secondCustomerAddressId = result5.customer!.addresses![0].id;
             const secondCustomerAddressId = result5.customer!.addresses![0].id;
 
 
             // set the second customer's address to be default
             // 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.defaultShippingAddress).toBe(true);
             expect(result6.updateCustomerAddress.defaultBillingAddress).toBe(true);
             expect(result6.updateCustomerAddress.defaultBillingAddress).toBe(true);
 
 
@@ -186,15 +212,18 @@ describe('Customer resolver', () => {
         });
         });
 
 
         it('createCustomerAddress with true defaults unsets existing defaults', async () => {
         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({
             expect(omit(result1.createCustomerAddress, ['id'])).toEqual({
                 fullName: '',
                 fullName: '',
                 company: '',
                 company: '',
@@ -226,7 +255,10 @@ describe('Customer resolver', () => {
         });
         });
 
 
         it('deleteCustomerAddress on default address resets defaults', async () => {
         it('deleteCustomerAddress on default address resets defaults', async () => {
-            const result = await adminClient.query(
+            const result = await adminClient.query<
+                DeleteCustomerAddress.Mutation,
+                DeleteCustomerAddress.Variables
+            >(
                 gql`
                 gql`
                     mutation DeleteCustomerAddress($id: ID!) {
                     mutation DeleteCustomerAddress($id: ID!) {
                         deleteCustomerAddress(id: $id)
                         deleteCustomerAddress(id: $id)
@@ -253,21 +285,27 @@ describe('Customer resolver', () => {
             // log in as first customer
             // log in as first customer
             await shopClient.asUserWithCredentials(firstCustomer.emailAddress, 'test');
             await shopClient.asUserWithCredentials(firstCustomer.emailAddress, 'test');
             // add an item to the order to create an order
             // 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',
                 productVariantId: 'T_1',
                 quantity: 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', () => {
     describe('deletion', () => {
         it('deletes a customer', async () => {
         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 });
             expect(result.deleteCustomer).toEqual({ result: DeletionResult.DELETED });
         });
         });
@@ -306,16 +344,13 @@ describe('Customer resolver', () => {
             'createCustomerAddress throws for deleted customer',
             'createCustomerAddress throws for deleted customer',
             assertThrowsWithMessage(
             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`,
                 `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,
     CreateCollection,
     CreateFacet,
     CreateFacet,
     LanguageCode,
     LanguageCode,
+    SearchFacetValues,
+    SearchGetPrices,
     SearchInput,
     SearchInput,
     UpdateCollection,
     UpdateCollection,
     UpdateProduct,
     UpdateProduct,
@@ -118,7 +120,7 @@ describe('Default search plugin', () => {
     }
     }
 
 
     async function testSinglePrices(client: SimpleGraphQLClient) {
     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: {
             input: {
                 groupByProduct: false,
                 groupByProduct: false,
                 take: 3,
                 take: 3,
@@ -141,7 +143,7 @@ describe('Default search plugin', () => {
     }
     }
 
 
     async function testPriceRanges(client: SimpleGraphQLClient) {
     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: {
             input: {
                 groupByProduct: true,
                 groupByProduct: true,
                 take: 3,
                 take: 3,
@@ -179,7 +181,7 @@ describe('Default search plugin', () => {
         it('price ranges', () => testPriceRanges(shopClient));
         it('price ranges', () => testPriceRanges(shopClient));
 
 
         it('returns correct facetValues when not grouped by product', async () => {
         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: {
                 input: {
                     groupByProduct: false,
                     groupByProduct: false,
                 },
                 },
@@ -195,7 +197,7 @@ describe('Default search plugin', () => {
         });
         });
 
 
         it('returns correct facetValues when grouped by product', async () => {
         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: {
                 input: {
                     groupByProduct: true,
                     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: {
                 input: {
                     groupByProduct: true,
                     groupByProduct: true,
                 },
                 },
@@ -277,7 +279,7 @@ describe('Default search plugin', () => {
         });
         });
 
 
         it('encodes collectionIds', async () => {
         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: {
                 input: {
                     groupByProduct: false,
                     groupByProduct: false,
                     term: 'cactus',
                     term: 'cactus',
@@ -426,7 +428,7 @@ describe('Default search plugin', () => {
                     value: 50,
                     value: 50,
                 },
                 },
             });
             });
-            const result = await adminClient.query(SEARCH_GET_PRICES, {
+            const result = await adminClient.query<SearchGetPrices.Query, SearchGetPrices.Variables>(SEARCH_GET_PRICES, {
                 input: {
                 input: {
                     groupByProduct: true,
                     groupByProduct: true,
                     term: 'laptop',
                     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 {
 import {
     CreateFacet,
     CreateFacet,
     CreateFacetValues,
     CreateFacetValues,
+    DeleteFacet,
+    DeleteFacetValues,
     DeletionResult,
     DeletionResult,
     FacetWithValues,
     FacetWithValues,
     GetFacetList,
     GetFacetList,
     GetFacetWithValues,
     GetFacetWithValues,
-    GetProductList,
+    GetProductListWithVariants,
     GetProductWithVariants,
     GetProductWithVariants,
     LanguageCode,
     LanguageCode,
     UpdateFacet,
     UpdateFacet,
@@ -141,11 +143,13 @@ describe('Facet resolver', () => {
     });
     });
 
 
     describe('deletion', () => {
     describe('deletion', () => {
-        let products: Array<GetProductList.Items & { variants: Array<{ id: string; name: string }> }>;
+        let products: GetProductListWithVariants.Items[];
 
 
         beforeAll(async () => {
         beforeAll(async () => {
             // add the FacetValues to products and variants
             // 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;
             products = result1.products.items;
 
 
             await client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
             await client.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
@@ -177,10 +181,13 @@ describe('Facet resolver', () => {
 
 
         it('deleteFacetValues deletes unused facetValue', async () => {
         it('deleteFacetValues deletes unused facetValue', async () => {
             const facetValueToDelete = speakerTypeFacet.values[2];
             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>(
             const result2 = await client.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
                 GET_FACET_WITH_VALUES,
                 GET_FACET_WITH_VALUES,
                 {
                 {
@@ -200,10 +207,13 @@ describe('Facet resolver', () => {
 
 
         it('deleteFacetValues for FacetValue in use returns NOT_DELETED', async () => {
         it('deleteFacetValues for FacetValue in use returns NOT_DELETED', async () => {
             const facetValueToDelete = speakerTypeFacet.values[0];
             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>(
             const result2 = await client.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
                 GET_FACET_WITH_VALUES,
                 GET_FACET_WITH_VALUES,
                 {
                 {
@@ -223,10 +233,13 @@ describe('Facet resolver', () => {
 
 
         it('deleteFacetValues for FacetValue in use can be force deleted', async () => {
         it('deleteFacetValues for FacetValue in use can be force deleted', async () => {
             const facetValueToDelete = speakerTypeFacet.values[0];
             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([
             expect(result1.deleteFacetValues).toEqual([
                 {
                 {
@@ -255,7 +268,7 @@ describe('Facet resolver', () => {
         });
         });
 
 
         it('deleteFacet that is in use returns NOT_DELETED', async () => {
         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>(
             const result2 = await client.query<GetFacetWithValues.Query, GetFacetWithValues.Variables>(
                 GET_FACET_WITH_VALUES,
                 GET_FACET_WITH_VALUES,
                 {
                 {
@@ -272,7 +285,7 @@ describe('Facet resolver', () => {
         });
         });
 
 
         it('deleteFacet that is in use can be force deleted', async () => {
         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({
             expect(result1.deleteFacet).toEqual({
                 result: DeletionResult.DELETED,
                 result: DeletionResult.DELETED,
@@ -310,7 +323,7 @@ export const GET_FACET_WITH_VALUES = gql`
 `;
 `;
 
 
 const DELETE_FACET_VALUES = gql`
 const DELETE_FACET_VALUES = gql`
-    mutation DeleteFacetValue($ids: [ID!]!, $force: Boolean) {
+    mutation DeleteFacetValues($ids: [ID!]!, $force: Boolean) {
         deleteFacetValues(ids: $ids, force: $force) {
         deleteFacetValues(ids: $ids, force: $force) {
             result
             result
             message
             message

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

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

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

@@ -1,5 +1,5 @@
 // tslint:disable
 // 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;
 export type Maybe<T> = T | null;
 /** All built-in and custom scalars, mapped to their actual values */
 /** 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 { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { ORDER_FRAGMENT, ORDER_WITH_LINES_FRAGMENT } from './graphql/fragments';
 import { ORDER_FRAGMENT, ORDER_WITH_LINES_FRAGMENT } from './graphql/fragments';
 import { GetCustomerList, GetOrder, GetOrderList } from './graphql/generated-e2e-admin-types';
 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 { GET_CUSTOMER_LIST } from './graphql/shared-definitions';
 import { ADD_ITEM_TO_ORDER } from './graphql/shop-definitions';
 import { ADD_ITEM_TO_ORDER } from './graphql/shop-definitions';
 import { TestAdminClient, TestShopClient } from './test-client';
 import { TestAdminClient, TestShopClient } from './test-client';
@@ -35,12 +36,12 @@ describe('Orders resolver', () => {
         );
         );
         customers = result.customers.items;
         customers = result.customers.items;
         await shopClient.asUserWithCredentials(customers[0].emailAddress, password);
         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',
             productVariantId: 'T_1',
             quantity: 1,
             quantity: 1,
         });
         });
         await shopClient.asUserWithCredentials(customers[1].emailAddress, password);
         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',
             productVariantId: 'T_2',
             quantity: 1,
             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 { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { PRODUCT_WITH_VARIANTS_FRAGMENT } from './graphql/fragments';
 import { PRODUCT_WITH_VARIANTS_FRAGMENT } from './graphql/fragments';
-import { DeletionResult, GetProductWithVariants, LanguageCode, SortOrder, GetProductList, ProductWithVariants, CreateProduct, GetAssetList,
-    UpdateProduct,
+import {
     AddOptionGroupToProduct,
     AddOptionGroupToProduct,
-    RemoveOptionGroupFromProduct,
+    CreateProduct,
+    DeleteProduct,
+    DeletionResult,
     GenerateProductVariants,
     GenerateProductVariants,
-    UpdateProductVariants} from './graphql/generated-e2e-admin-types';
+    GetAssetList,
+    GetProductList,
+    GetProductWithVariants,
+    LanguageCode,
+    ProductWithVariants,
+    RemoveOptionGroupFromProduct,
+    SortOrder,
+    UpdateProduct,
+    UpdateProductVariants,
+} from './graphql/generated-e2e-admin-types';
 import {
 import {
     CREATE_PRODUCT,
     CREATE_PRODUCT,
     GET_ASSET_LIST,
     GET_ASSET_LIST,
@@ -585,7 +595,7 @@ describe('Product resolver', () => {
 
 
         it('deletes a product', async () => {
         it('deletes a product', async () => {
             productToDelete = allProducts[0];
             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 });
             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 {
 import {
     ConfigArgType,
     ConfigArgType,
     CreatePromotion,
     CreatePromotion,
+    DeletePromotion,
     DeletionResult,
     DeletionResult,
     GetAdjustmentOperations,
     GetAdjustmentOperations,
     GetPromotion,
     GetPromotion,
@@ -148,7 +149,7 @@ describe('Promotion resolver', () => {
 
 
         it('deletes a promotion', async () => {
         it('deletes a promotion', async () => {
             promotionToDelete = allPromotions[0];
             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 });
             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 { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { CreateAdministrator, CreateRole, GetCustomer, Permission } from './graphql/generated-e2e-admin-types';
 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 { CREATE_ADMINISTRATOR, CREATE_ROLE, GET_CUSTOMER } from './graphql/shared-definitions';
 import {
 import {
     GET_ACTIVE_CUSTOMER,
     GET_ACTIVE_CUSTOMER,
@@ -72,7 +82,10 @@ describe('Shop auth & accounts', () => {
                     emailAddress: 'sofia.green@test.com',
                     emailAddress: 'sofia.green@test.com',
                     password: 'test',
                     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"'),
             }, 'Do not provide a password when `authOptions.requireVerification` is set to "true"'),
         );
         );
 
 
@@ -83,7 +96,9 @@ describe('Shop auth & accounts', () => {
                 lastName: 'Tester',
                 lastName: 'Tester',
                 emailAddress,
                 emailAddress,
             };
             };
-            const result = await shopClient.query(REGISTER_ACCOUNT, { input });
+            const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
+                input,
+            });
 
 
             verificationToken = await verificationTokenPromise;
             verificationToken = await verificationTokenPromise;
 
 
@@ -103,7 +118,9 @@ describe('Shop auth & accounts', () => {
                 lastName: 'Tester',
                 lastName: 'Tester',
                 emailAddress,
                 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;
             const newVerificationToken = await sendEmail;
 
 
@@ -120,7 +137,10 @@ describe('Shop auth & accounts', () => {
                     resolve(event.user.verificationToken!);
                     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;
             const newVerificationToken = await sendEmail;
 
 
@@ -132,9 +152,12 @@ describe('Shop auth & accounts', () => {
         });
         });
 
 
         it('refreshCustomerVerification does nothing with an unrecognized emailAddress', async () => {
         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();
             await waitForSendEmailFn();
             expect(result.refreshCustomerVerification).toBe(true);
             expect(result.refreshCustomerVerification).toBe(true);
             expect(sendEmailFn).not.toHaveBeenCalled();
             expect(sendEmailFn).not.toHaveBeenCalled();
@@ -153,7 +176,7 @@ describe('Shop auth & accounts', () => {
             'verification fails with wrong token',
             'verification fails with wrong token',
             assertThrowsWithMessage(
             assertThrowsWithMessage(
                 () =>
                 () =>
-                    shopClient.query(VERIFY_EMAIL, {
+                    shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
                         password,
                         password,
                         token: 'bad-token',
                         token: 'bad-token',
                     }),
                     }),
@@ -162,7 +185,7 @@ describe('Shop auth & accounts', () => {
         );
         );
 
 
         it('verification succeeds with correct token', async () => {
         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,
                 password,
                 token: verificationToken,
                 token: verificationToken,
             });
             });
@@ -176,7 +199,9 @@ describe('Shop auth & accounts', () => {
                 lastName: 'Hacker',
                 lastName: 'Hacker',
                 emailAddress,
                 emailAddress,
             };
             };
-            const result = await shopClient.query(REGISTER_ACCOUNT, { input });
+            const result = await shopClient.query<Register.Mutation, Register.Variables>(REGISTER_ACCOUNT, {
+                input,
+            });
             await waitForSendEmailFn();
             await waitForSendEmailFn();
             expect(result.registerCustomerAccount).toBe(true);
             expect(result.registerCustomerAccount).toBe(true);
             expect(sendEmailFn).not.toHaveBeenCalled();
             expect(sendEmailFn).not.toHaveBeenCalled();
@@ -186,7 +211,7 @@ describe('Shop auth & accounts', () => {
             'verification fails if attempted a second time',
             'verification fails if attempted a second time',
             assertThrowsWithMessage(
             assertThrowsWithMessage(
                 () =>
                 () =>
-                    shopClient.query(VERIFY_EMAIL, {
+                    shopClient.query<Verify.Mutation, Verify.Variables>(VERIFY_EMAIL, {
                         password,
                         password,
                         token: verificationToken,
                         token: verificationToken,
                     }),
                     }),
@@ -211,7 +236,10 @@ describe('Shop auth & accounts', () => {
         });
         });
 
 
         it('requestPasswordReset silently fails with invalid identifier', async () => {
         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',
                 identifier: 'invalid-identifier',
             });
             });
 
 
@@ -223,7 +251,10 @@ describe('Shop auth & accounts', () => {
 
 
         it('requestPasswordReset sends reset token', async () => {
         it('requestPasswordReset sends reset token', async () => {
             const passwordResetTokenPromise = getPasswordResetTokenPromise();
             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,
                 identifier: customer.emailAddress,
             });
             });
 
 
@@ -238,7 +269,7 @@ describe('Shop auth & accounts', () => {
             'resetPassword fails with wrong token',
             'resetPassword fails with wrong token',
             assertThrowsWithMessage(
             assertThrowsWithMessage(
                 () =>
                 () =>
-                    shopClient.query(RESET_PASSWORD, {
+                    shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(RESET_PASSWORD, {
                         password: 'newPassword',
                         password: 'newPassword',
                         token: 'bad-token',
                         token: 'bad-token',
                     }),
                     }),
@@ -247,10 +278,13 @@ describe('Shop auth & accounts', () => {
         );
         );
 
 
         it('resetPassword works with valid token', async () => {
         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');
             const loginResult = await shopClient.asUserWithCredentials(customer.emailAddress, 'newPassword');
             expect(loginResult.user.identifier).toBe(customer.emailAddress);
             expect(loginResult.user.identifier).toBe(customer.emailAddress);
@@ -264,7 +298,9 @@ describe('Shop auth & accounts', () => {
         const PASSWORD = 'newPassword';
         const PASSWORD = 'newPassword';
 
 
         beforeAll(async () => {
         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!;
             customer = result.customer!;
         });
         });
 
 
@@ -275,7 +311,10 @@ describe('Shop auth & accounts', () => {
         it('throws if not logged in', async () => {
         it('throws if not logged in', async () => {
             try {
             try {
                 await shopClient.asAnonymousUser();
                 await shopClient.asAnonymousUser();
-                await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
+                await shopClient.query<
+                    RequestUpdateEmailAddress.Mutation,
+                    RequestUpdateEmailAddress.Variables
+                >(REQUEST_UPDATE_EMAIL_ADDRESS, {
                     password: PASSWORD,
                     password: PASSWORD,
                     newEmailAddress: NEW_EMAIL_ADDRESS,
                     newEmailAddress: NEW_EMAIL_ADDRESS,
                 });
                 });
@@ -288,7 +327,10 @@ describe('Shop auth & accounts', () => {
         it('throws if password is incorrect', async () => {
         it('throws if password is incorrect', async () => {
             try {
             try {
                 await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
                 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',
                     password: 'bad password',
                     newEmailAddress: NEW_EMAIL_ADDRESS,
                     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);
                 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!;
                 const otherCustomer = result.customer!;
 
 
-                await shopClient.query(REQUEST_UPDATE_EMAIL_ADDRESS, {
+                await shopClient.query<
+                    RequestUpdateEmailAddress.Mutation,
+                    RequestUpdateEmailAddress.Variables
+                >(REQUEST_UPDATE_EMAIL_ADDRESS, {
                     password: PASSWORD,
                     password: PASSWORD,
                     newEmailAddress: otherCustomer.emailAddress,
                     newEmailAddress: otherCustomer.emailAddress,
                 });
                 });
-            },
-            'This email address is not available',
-            ),
+            }, 'This email address is not available'),
         );
         );
 
 
         it('triggers event with token', async () => {
         it('triggers event with token', async () => {
             await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
             await shopClient.asUserWithCredentials(customer.emailAddress, PASSWORD);
             const emailUpdateTokenPromise = getEmailUpdateTokenPromise();
             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;
             const { identifierChangeToken, pendingIdentifier } = await emailUpdateTokenPromise;
             emailUpdateToken = identifierChangeToken!;
             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 () => {
         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(result.updateCustomerEmailAddress).toBe(true);
 
 
             expect(sendEmailFn).toHaveBeenCalled();
             expect(sendEmailFn).toHaveBeenCalled();
@@ -356,9 +405,9 @@ describe('Shop auth & accounts', () => {
 
 
         it('can login with new email address after verification', async () => {
         it('can login with new email address after verification', async () => {
             await shopClient.asUserWithCredentials(NEW_EMAIL_ADDRESS, PASSWORD);
             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 () => {
         it('cannot login with old email address after verification', async () => {
@@ -369,7 +418,6 @@ describe('Shop auth & accounts', () => {
                 expect(getErrorCode(err)).toBe('UNAUTHORIZED');
                 expect(getErrorCode(err)).toBe('UNAUTHORIZED');
             }
             }
         });
         });
-
     });
     });
 
 
     async function assertRequestAllowed<V>(operation: DocumentNode, variables?: V) {
     async function assertRequestAllowed<V>(operation: DocumentNode, variables?: V) {
@@ -421,7 +469,7 @@ describe('Shop auth & accounts', () => {
         const adminResult = await shopClient.query<
         const adminResult = await shopClient.query<
             CreateAdministrator.Mutation,
             CreateAdministrator.Mutation,
             CreateAdministrator.Variables
             CreateAdministrator.Variables
-            >(CREATE_ADMINISTRATOR, {
+        >(CREATE_ADMINISTRATOR, {
             input: {
             input: {
                 emailAddress: identifier,
                 emailAddress: identifier,
                 firstName: code,
                 firstName: code,
@@ -485,7 +533,9 @@ describe('Expiring tokens', () => {
                 lastName: 'Wallace',
                 lastName: 'Wallace',
                 emailAddress: 'barry.wallace@test.com',
                 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;
             const verificationToken = await verificationTokenPromise;
 
 
@@ -511,7 +561,10 @@ describe('Expiring tokens', () => {
             );
             );
 
 
             const passwordResetTokenPromise = getPasswordResetTokenPromise();
             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,
                 identifier: customer!.emailAddress,
             });
             });
 
 
@@ -523,7 +576,7 @@ describe('Expiring tokens', () => {
 
 
             await new Promise(resolve => setTimeout(resolve, 3));
             await new Promise(resolve => setTimeout(resolve, 3));
 
 
-            return shopClient.query(RESET_PASSWORD, {
+            return shopClient.query<ResetPassword.Mutation, ResetPassword.Variables>(RESET_PASSWORD, {
                 password: 'test',
                 password: 'test',
                 token: passwordResetToken,
                 token: passwordResetToken,
             });
             });
@@ -568,7 +621,9 @@ describe('Registration without email verification', () => {
                 lastName: 'Beardsley',
                 lastName: 'Beardsley',
                 emailAddress: userEmailAddress,
                 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"'),
         }, 'A password must be provided when `authOptions.requireVerification` is set to "false"'),
     );
     );
 
 
@@ -579,7 +634,9 @@ describe('Registration without email verification', () => {
             emailAddress: userEmailAddress,
             emailAddress: userEmailAddress,
             password: 'test',
             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(result.registerCustomerAccount).toBe(true);
         expect(sendEmailFn).not.toHaveBeenCalled();
         expect(sendEmailFn).not.toHaveBeenCalled();
@@ -590,7 +647,7 @@ describe('Registration without email verification', () => {
 
 
         const result = await shopClient.query(
         const result = await shopClient.query(
             gql`
             gql`
-                query GetMe{
+                query GetMe {
                     me {
                     me {
                         identifier
                         identifier
                     }
                     }
@@ -626,7 +683,9 @@ describe('Updating email address without email verification', () => {
     }, TEST_SETUP_TIMEOUT_MS);
     }, TEST_SETUP_TIMEOUT_MS);
 
 
     beforeAll(async () => {
     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!;
         customer = result.customer!;
     });
     });
 
 
@@ -640,7 +699,10 @@ describe('Updating email address without email verification', () => {
 
 
     it('updates email address', async () => {
     it('updates email address', async () => {
         await shopClient.asUserWithCredentials(customer.emailAddress, 'test');
         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',
             password: 'test',
             newEmailAddress: NEW_EMAIL_ADDRESS,
             newEmailAddress: NEW_EMAIL_ADDRESS,
         });
         });
@@ -649,8 +711,8 @@ describe('Updating email address without email verification', () => {
         expect(sendEmailFn).toHaveBeenCalledTimes(1);
         expect(sendEmailFn).toHaveBeenCalledTimes(1);
         expect(sendEmailFn.mock.calls[0][0] instanceof IdentifierChangeEvent).toBe(true);
         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 => {
     return new Promise(resolve => {
         sendEmailFn.mockImplementation((event: IdentifierChangeRequestEvent) => {
         sendEmailFn.mockImplementation((event: IdentifierChangeRequestEvent) => {
             resolve(pick(event.user, ['identifierChangeToken', 'pendingIdentifier']));
             resolve(pick(event.user, ['identifierChangeToken', 'pendingIdentifier']));

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

@@ -11,8 +11,16 @@ import {
     CreateFacet,
     CreateFacet,
     DisableProduct,
     DisableProduct,
     FacetWithValues,
     FacetWithValues,
+    GetCollectionList,
+    GetCollectionVariants,
     GetFacetList,
     GetFacetList,
+    GetProduct1,
+    GetProduct2Variants,
+    GetProductCollection,
+    GetProductFacetValues,
+    GetProductsTake3,
     GetProductWithVariants,
     GetProductWithVariants,
+    GetVariantFacetValues,
     LanguageCode,
     LanguageCode,
     UpdateCollection,
     UpdateCollection,
     UpdateProduct,
     UpdateProduct,
@@ -77,7 +85,7 @@ describe('Shop catalog', () => {
         });
         });
 
 
         it('products list omits disabled products', async () => {
         it('products list omits disabled products', async () => {
-            const result = await shopClient.query(gql`
+            const result = await shopClient.query<GetProductsTake3.Query>(gql`
                 query GetProductsTake3 {
                 query GetProductsTake3 {
                     products(options: { take: 3 }) {
                     products(options: { take: 3 }) {
                         items {
                         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 () => {
         it('product returns null for disabled product', async () => {
-            const result = await shopClient.query(gql`
+            const result = await shopClient.query<GetProduct1.Query>(gql`
                 query GetProduct1 {
                 query GetProduct1 {
                     product(id: "T_1") {
                     product(id: "T_1") {
                         id
                         id
@@ -103,7 +111,7 @@ describe('Shop catalog', () => {
         });
         });
 
 
         it('omits disabled variants from product response', async () => {
         it('omits disabled variants from product response', async () => {
-            const result = await shopClient.query(gql`
+            const result = await shopClient.query<GetProduct2Variants.Query>(gql`
                 query GetProduct2Variants {
                 query GetProduct2Variants {
                     product(id: "T_2") {
                     product(id: "T_2") {
                         id
                         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 () => {
         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',
                 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 () => {
         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',
                 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 () => {
         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_22', name: 'Road Bike' },
                 { id: 'T_23', name: 'Skipping Rope' },
                 { id: 'T_23', name: 'Skipping Rope' },
                 { id: 'T_24', name: 'Boxing Gloves' },
                 { id: 'T_24', name: 'Boxing Gloves' },
@@ -224,10 +241,15 @@ describe('Shop catalog', () => {
         });
         });
 
 
         it('omits variants from disabled products', async () => {
         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_22', name: 'Road Bike' },
                 { id: 'T_23', name: 'Skipping Rope' },
                 { id: 'T_23', name: 'Skipping Rope' },
                 { id: 'T_24', name: 'Boxing Gloves' },
                 { 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_23', name: 'Skipping Rope' },
                 { id: 'T_24', name: 'Boxing Gloves' },
                 { id: 'T_24', name: 'Boxing Gloves' },
                 { id: 'T_25', name: 'Tent' },
                 { id: 'T_25', name: 'Tent' },
@@ -256,7 +281,7 @@ describe('Shop catalog', () => {
         });
         });
 
 
         it('collection list', async () => {
         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([
             expect(result.collections.items).toEqual([
                 { id: 'T_2', name: 'Plants' },
                 { 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' }]);
             expect(result.collections.items).toEqual([{ id: 'T_2', name: 'Plants' }]);
         });
         });
 
 
         it('returns null for private collection', async () => {
         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();
             expect(result.collection).toBeNull();
         });
         });
 
 
         it('product.collections list omits private collections', async () => {
         it('product.collections list omits private collections', async () => {
-            const result = await shopClient.query(gql`
+            const result = await shopClient.query<GetProductCollection.Query>(gql`
                 query GetProductCollection {
                 query GetProductCollection {
                     product(id: "T_12") {
                     product(id: "T_12") {
                         collections {
                         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 path from 'path';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 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 { 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 { TestAdminClient, TestShopClient } from './test-client';
 import { TestServer } from './test-server';
 import { TestServer } from './test-server';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
@@ -26,7 +41,7 @@ describe('Shop customers', () => {
         await adminClient.init();
         await adminClient.init();
 
 
         // Fetch the first Customer and store it as the `customer` variable.
         // 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 {
             query GetCustomerIds {
                 customers {
                 customers {
                     items {
                     items {
@@ -51,7 +66,9 @@ describe('Shop customers', () => {
             const input: UpdateCustomerInput = {
             const input: UpdateCustomerInput = {
                 firstName: 'xyz',
                 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'),
         }, 'You are not currently authorized to perform this action'),
     );
     );
 
 
@@ -62,7 +79,9 @@ describe('Shop customers', () => {
                 streetLine1: '1 Test Street',
                 streetLine1: '1 Test Street',
                 countryCode: 'GB',
                 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'),
         }, 'You are not currently authorized to perform this action'),
     );
     );
 
 
@@ -73,14 +92,18 @@ describe('Shop customers', () => {
                 id: 'T_1',
                 id: 'T_1',
                 streetLine1: 'zxc',
                 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'),
         }, 'You are not currently authorized to perform this action'),
     );
     );
 
 
     it(
     it(
         'deleteCustomerAddress throws if not logged in',
         'deleteCustomerAddress throws if not logged in',
         assertThrowsWithMessage(async () => {
         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'),
         }, 'You are not currently authorized to perform this action'),
     );
     );
 
 
@@ -99,7 +122,10 @@ describe('Shop customers', () => {
             const input: UpdateCustomerInput = {
             const input: UpdateCustomerInput = {
                 firstName: 'xyz',
                 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');
             expect(result.updateCustomer.firstName).toBe('xyz');
         });
         });
@@ -109,7 +135,10 @@ describe('Shop customers', () => {
                 streetLine1: '1 Test Street',
                 streetLine1: '1 Test Street',
                 countryCode: 'GB',
                 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({
             expect(createCustomerAddress).toEqual({
                 id: 'T_3',
                 id: 'T_3',
@@ -127,7 +156,10 @@ describe('Shop customers', () => {
                 streetLine1: '5 Test Street',
                 streetLine1: '5 Test Street',
                 countryCode: 'AT',
                 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.streetLine1).toEqual('5 Test Street');
             expect(result.updateCustomerAddress.country.code).toEqual('AT');
             expect(result.updateCustomerAddress.country.code).toEqual('AT');
@@ -140,12 +172,18 @@ describe('Shop customers', () => {
                     id: 'T_2',
                     id: 'T_2',
                     streetLine1: '1 Test Street',
                     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'),
             }, 'You are not currently authorized to perform this action'),
         );
         );
 
 
         it('deleteCustomerAddress works', async () => {
         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);
             expect(result.deleteCustomerAddress).toBe(true);
         });
         });
@@ -153,19 +191,28 @@ describe('Shop customers', () => {
         it(
         it(
             'deleteCustomerAddress fails for address not owned by Customer',
             'deleteCustomerAddress fails for address not owned by Customer',
             assertThrowsWithMessage(async () => {
             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'),
             }, 'You are not currently authorized to perform this action'),
         );
         );
 
 
         it(
         it(
             'updatePassword fails with incorrect current password',
             'updatePassword fails with incorrect current password',
             assertThrowsWithMessage(async () => {
             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'),
             }, 'The credentials did not match. Please check and try again'),
         );
         );
 
 
         it('updatePassword works', async () => {
         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);
             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 */
 /* tslint:disable:no-non-null-assertion */
-import gql from 'graphql-tag';
 import path from 'path';
 import path from 'path';
 
 
 import { PaymentMethodHandler } from '../src/config/payment-method/payment-method-handler';
 import { PaymentMethodHandler } from '../src/config/payment-method/payment-method-handler';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { CreateAddressInput, GetCountryList, GetCustomer, GetCustomerList, UpdateCountry } from './graphql/generated-e2e-admin-types';
 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 { GET_COUNTRY_LIST, GET_CUSTOMER, GET_CUSTOMER_LIST, UPDATE_COUNTRY } from './graphql/shared-definitions';
 import {
 import {
     ADD_ITEM_TO_ORDER,
     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.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', () => {
     describe('ordering as anonymous user', () => {
@@ -81,7 +96,7 @@ describe('Shop orders', () => {
         });
         });
 
 
         it('activeOrder returns null before any items have been added', async () => {
         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();
             expect(result.activeOrder).toBeNull();
         });
         });
 
 
@@ -90,24 +105,27 @@ describe('Shop orders', () => {
         });
         });
 
 
         it('addItemToOrder creates a new Order with an item', async () => {
         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',
                 productVariantId: 'T_1',
                 quantity: 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(
         it(
             'addItemToOrder errors with an invalid productVariantId',
             'addItemToOrder errors with an invalid productVariantId',
             assertThrowsWithMessage(
             assertThrowsWithMessage(
                 () =>
                 () =>
-                    shopClient.query(ADD_ITEM_TO_ORDER, {
+                    shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
                         productVariantId: 'T_999',
                         productVariantId: 'T_999',
                         quantity: 1,
                         quantity: 1,
                     }),
                     }),
@@ -119,7 +137,7 @@ describe('Shop orders', () => {
             'addItemToOrder errors with a negative quantity',
             'addItemToOrder errors with a negative quantity',
             assertThrowsWithMessage(
             assertThrowsWithMessage(
                 () =>
                 () =>
-                    shopClient.query(ADD_ITEM_TO_ORDER, {
+                    shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
                         productVariantId: 'T_999',
                         productVariantId: 'T_999',
                         quantity: -3,
                         quantity: -3,
                     }),
                     }),
@@ -128,19 +146,22 @@ describe('Shop orders', () => {
         );
         );
 
 
         it('addItemToOrder with an existing productVariantId adds quantity to the existing OrderLine', async () => {
         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',
                 productVariantId: 'T_1',
                 quantity: 2,
                 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(
         it(
             'addItemToOrder errors when going beyond orderItemsLimit',
             'addItemToOrder errors when going beyond orderItemsLimit',
             assertThrowsWithMessage(async () => {
             assertThrowsWithMessage(async () => {
-                await shopClient.query(ADD_ITEM_TO_ORDER, {
+                await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
                     productVariantId: 'T_1',
                     productVariantId: 'T_1',
                     quantity: 100,
                     quantity: 100,
                 });
                 });
@@ -148,22 +169,28 @@ describe('Shop orders', () => {
         );
         );
 
 
         it('adjustItemQuantity adjusts the quantity', async () => {
         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,
                 orderItemId: firstOrderItemId,
                 quantity: 50,
                 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(
         it(
             'adjustItemQuantity errors when going beyond orderItemsLimit',
             'adjustItemQuantity errors when going beyond orderItemsLimit',
             assertThrowsWithMessage(async () => {
             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'),
             }, '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',
             'adjustItemQuantity errors with a negative quantity',
             assertThrowsWithMessage(
             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`,
                 `-3 is not a valid quantity for an OrderItem`,
             ),
             ),
         );
         );
@@ -183,42 +213,54 @@ describe('Shop orders', () => {
             'adjustItemQuantity errors with an invalid orderItemId',
             'adjustItemQuantity errors with an invalid orderItemId',
             assertThrowsWithMessage(
             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`,
                 `This order does not contain an OrderLine with the id 999`,
             ),
             ),
         );
         );
 
 
         it('removeItemFromOrder removes the correct item', async () => {
         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',
                 productVariantId: 'T_3',
                 quantity: 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,
                 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(
         it(
             'removeItemFromOrder errors with an invalid orderItemId',
             'removeItemFromOrder errors with an invalid orderItemId',
             assertThrowsWithMessage(
             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`,
                 `This order does not contain an OrderLine with the id 999`,
             ),
             ),
         );
         );
 
 
         it('nextOrderStates returns next valid states', async () => {
         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']);
             expect(result.nextOrderStates).toEqual(['ArrangingPayment']);
         });
         });
@@ -226,7 +268,11 @@ describe('Shop orders', () => {
         it(
         it(
             'transitionOrderToState throws for an invalid state',
             'transitionOrderToState throws for an invalid state',
             assertThrowsWithMessage(
             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"`,
                 `Cannot transition Order from "AddingItems" to "Completed"`,
             ),
             ),
         );
         );
@@ -234,13 +280,20 @@ describe('Shop orders', () => {
         it(
         it(
             'attempting to transition to ArrangingPayment throws when Order has no Customer',
             'attempting to transition to ArrangingPayment throws when Order has no Customer',
             assertThrowsWithMessage(
             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`,
                 `Cannot transition Order to the "ArrangingPayment" state without Customer details`,
             ),
             ),
         );
         );
 
 
         it('setCustomerForOrder creates a new Customer and associates it with the Order', async () => {
         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: {
                 input: {
                     emailAddress: 'test@test.com',
                     emailAddress: 'test@test.com',
                     firstName: 'Test',
                     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.firstName).toBe('Test');
             expect(customer.lastName).toBe('Person');
             expect(customer.lastName).toBe('Person');
             expect(customer.emailAddress).toBe('test@test.com');
             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 () => {
         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: {
                 input: {
                     emailAddress: 'test@test.com',
                     emailAddress: 'test@test.com',
                     firstName: 'Changed',
                     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.firstName).toBe('Changed');
             expect(customer.lastName).toBe('Person');
             expect(customer.lastName).toBe('Person');
             expect(customer.emailAddress).toBe('test@test.com');
             expect(customer.emailAddress).toBe('test@test.com');
@@ -283,11 +339,14 @@ describe('Shop orders', () => {
                 countryCode: 'US',
                 countryCode: 'US',
                 phoneNumber: '4444444',
                 phoneNumber: '4444444',
             };
             };
-            const result = await shopClient.query(SET_SHIPPING_ADDRESS, {
+            const { setOrderShippingAddress } = await shopClient.query<
+                SetShippingAddress.Mutation,
+                SetShippingAddress.Variables
+            >(SET_SHIPPING_ADDRESS, {
                 input: address,
                 input: address,
             });
             });
 
 
-            expect(result.setOrderShippingAddress.shippingAddress).toEqual({
+            expect(setOrderShippingAddress!.shippingAddress).toEqual({
                 fullName: 'name',
                 fullName: 'name',
                 company: 'company',
                 company: 'company',
                 streetLine1: '12 the street',
                 streetLine1: '12 the street',
@@ -301,35 +360,41 @@ describe('Shop orders', () => {
         });
         });
 
 
         it('customer default Addresses are not updated before payment', async () => {
         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 () => {
         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' });
             expect(result.transitionOrderToState).toEqual({ id: 'T_1', state: 'ArrangingPayment' });
         });
         });
 
 
         it('adds a successful payment and transitions Order state', async () => {
         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.method).toBe(testPaymentMethod.code);
             expect(payment.state).toBe('Settled');
             expect(payment.state).toBe('Settled');
         });
         });
 
 
         it('activeOrder is null after payment', async () => {
         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();
             expect(result.activeOrder).toBeNull();
         });
         });
@@ -350,7 +415,7 @@ describe('Shop orders', () => {
 
 
     describe('ordering as authenticated user', () => {
     describe('ordering as authenticated user', () => {
         let firstOrderItemId: string;
         let firstOrderItemId: string;
-        let activeOrder: any;
+        let activeOrder: AddItemToOrder.AddItemToOrder;
         let authenticatedUserEmailAddress: string;
         let authenticatedUserEmailAddress: string;
         let customers: GetCustomerList.Items[];
         let customers: GetCustomerList.Items[];
         const password = 'test';
         const password = 'test';
@@ -371,82 +436,97 @@ describe('Shop orders', () => {
         });
         });
 
 
         it('activeOrder returns null before any items have been added', async () => {
         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();
             expect(result.activeOrder).toBeNull();
         });
         });
 
 
         it('addItemToOrder creates a new Order with an item', async () => {
         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',
                 productVariantId: 'T_1',
                 quantity: 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 () => {
         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 () => {
         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',
                 productVariantId: 'T_1',
                 quantity: 2,
                 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 () => {
         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,
                 orderItemId: firstOrderItemId,
                 quantity: 50,
                 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 () => {
         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',
                 productVariantId: 'T_3',
                 quantity: 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,
                 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 () => {
         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']);
             expect(result.nextOrderStates).toEqual(['ArrangingPayment']);
         });
         });
 
 
         it('logging out and back in again resumes the last active order', async () => {
         it('logging out and back in again resumes the last active order', async () => {
             await shopClient.asAnonymousUser();
             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();
             expect(result1.activeOrder).toBeNull();
 
 
             await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
             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', () => {
         describe('shipping', () => {
-            let shippingMethods: any;
+            let shippingMethods: GetShippingMethods.EligibleShippingMethods[];
 
 
             it(
             it(
                 'setOrderShippingAddress throws with invalid countryCode',
                 'setOrderShippingAddress throws with invalid countryCode',
@@ -456,9 +536,12 @@ describe('Shop orders', () => {
                         countryCode: 'INVALID',
                         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`),
                 }, `The countryCode "INVALID" was not recognized`),
             );
             );
 
 
@@ -474,11 +557,14 @@ describe('Shop orders', () => {
                     countryCode: 'US',
                     countryCode: 'US',
                     phoneNumber: '4444444',
                     phoneNumber: '4444444',
                 };
                 };
-                const result = await shopClient.query(SET_SHIPPING_ADDRESS, {
+                const { setOrderShippingAddress } = await shopClient.query<
+                    SetShippingAddress.Mutation,
+                    SetShippingAddress.Variables
+                >(SET_SHIPPING_ADDRESS, {
                     input: address,
                     input: address,
                 });
                 });
 
 
-                expect(result.setOrderShippingAddress.shippingAddress).toEqual({
+                expect(setOrderShippingAddress!.shippingAddress).toEqual({
                     fullName: 'name',
                     fullName: 'name',
                     company: 'company',
                     company: 'company',
                     streetLine1: '12 the street',
                     streetLine1: '12 the street',
@@ -492,7 +578,9 @@ describe('Shop orders', () => {
             });
             });
 
 
             it('eligibleShippingMethods lists shipping methods', async () => {
             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;
                 shippingMethods = result.eligibleShippingMethods;
 
 
@@ -503,37 +591,43 @@ describe('Shop orders', () => {
             });
             });
 
 
             it('shipping is initially unset', async () => {
             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 () => {
             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,
                     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.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 () => {
             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,
                     orderItemId: activeOrder.lines[0].id,
                     quantity: 10,
                     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,
                     shippingMethods[1].description,
                 );
                 );
             });
             });
@@ -544,18 +638,24 @@ describe('Shop orders', () => {
                 'attempting add a Payment throws error when in AddingItems state',
                 'attempting add a Payment throws error when in AddingItems state',
                 assertThrowsWithMessage(
                 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`,
                     `A Payment may only be added when Order is in "ArrangingPayment" state`,
                 ),
                 ),
             );
             );
 
 
             it('transitions to the ArrangingPayment state', async () => {
             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({
                 expect(result.transitionOrderToState).toEqual({
                     id: activeOrder.id,
                     id: activeOrder.id,
                     state: 'ArrangingPayment',
                     state: 'ArrangingPayment',
@@ -566,10 +666,13 @@ describe('Shop orders', () => {
                 'attempting to add an item throws error when in ArrangingPayment state',
                 'attempting to add an item throws error when in ArrangingPayment state',
                 assertThrowsWithMessage(
                 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`,
                     `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',
                 'attempting to modify item quantity throws error when in ArrangingPayment state',
                 assertThrowsWithMessage(
                 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`,
                     `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',
                 'attempting to remove an item throws error when in ArrangingPayment state',
                 assertThrowsWithMessage(
                 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`,
                     `Order contents may only be modified when in the "AddingItems" state`,
                 ),
                 ),
             );
             );
@@ -600,16 +709,24 @@ describe('Shop orders', () => {
             it(
             it(
                 'attempting to setOrderShippingMethod throws error when in ArrangingPayment state',
                 'attempting to setOrderShippingMethod throws error when in ArrangingPayment state',
                 assertThrowsWithMessage(async () => {
                 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;
                     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`),
                 }, `Order contents may only be modified when in the "AddingItems" state`),
             );
             );
 
 
             it('adds a declined payment', async () => {
             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: {
                     input: {
                         method: testFailingPaymentMethod.code,
                         method: testFailingPaymentMethod.code,
                         metadata: {
                         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.method).toBe(testFailingPaymentMethod.code);
                 expect(payment.state).toBe('Declined');
                 expect(payment.state).toBe('Declined');
                 expect(payment.transactionId).toBe(null);
                 expect(payment.transactionId).toBe(null);
@@ -629,7 +746,10 @@ describe('Shop orders', () => {
             });
             });
 
 
             it('adds a successful payment and transitions Order state', async () => {
             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: {
                     input: {
                         method: testPaymentMethod.code,
                         method: testPaymentMethod.code,
                         metadata: {
                         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.method).toBe(testPaymentMethod.code);
                 expect(payment.state).toBe('Settled');
                 expect(payment.state).toBe('Settled');
                 expect(payment.transactionId).toBe('12345');
                 expect(payment.transactionId).toBe('12345');
@@ -654,20 +774,26 @@ describe('Shop orders', () => {
         describe('orderByCode', () => {
         describe('orderByCode', () => {
             describe('immediately after Order is placed', () => {
             describe('immediately after Order is placed', () => {
                 it('works when authenticated', async () => {
                 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 () => {
                 it('works when anonymous', async () => {
                     await shopClient.asAnonymousUser();
                     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(
                 it(
@@ -675,9 +801,12 @@ describe('Shop orders', () => {
                     assertThrowsWithMessage(async () => {
                     assertThrowsWithMessage(async () => {
                         authenticatedUserEmailAddress = customers[1].emailAddress;
                         authenticatedUserEmailAddress = customers[1].emailAddress;
                         await shopClient.asUserWithCredentials(authenticatedUserEmailAddress, password);
                         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`),
                     }, `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 gql from 'graphql-tag';
 import path from 'path';
 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 { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import {
 import {
     CreateAddressInput,
     CreateAddressInput,
-    ProductVariant,
+    GetStockMovement,
     StockMovementType,
     StockMovementType,
     UpdateProductVariantInput,
     UpdateProductVariantInput,
+    UpdateStock,
+    VariantWithStockFragment,
 } from './graphql/generated-e2e-admin-types';
 } 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 { TestAdminClient, TestShopClient } from './test-client';
 import { TestServer } from './test-server';
 import { TestServer } from './test-server';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
@@ -50,12 +48,15 @@ describe('Stock control', () => {
     });
     });
 
 
     describe('stock adjustments', () => {
     describe('stock adjustments', () => {
-        let variants: ProductVariant[];
+        let variants: VariantWithStockFragment[];
 
 
         it('stockMovements are initially empty', async () => {
         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) {
             for (const variant of variants) {
                 expect(variant.stockMovements.items).toEqual([]);
                 expect(variant.stockMovements.items).toEqual([]);
                 expect(variant.stockMovements.totalItems).toEqual(0);
                 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 () => {
         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 () => {
         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,
                 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 () => {
         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,
                 StockMovementType.ADJUSTMENT,
             );
             );
-            expect(result.updateProductVariants[0].stockMovements.items[1].quantity).toBe(-2);
+            expect(updateProductVariants[0]!.stockMovements.items[1].quantity).toBe(-2);
         });
         });
 
 
         it(
         it(
             'attempting to set a negative stockOnHand throws',
             'attempting to set a negative stockOnHand throws',
             assertThrowsWithMessage(async () => {
             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'),
             }, 'stockOnHand cannot be a negative value'),
         );
         );
     });
     });
 
 
     describe('sales', () => {
     describe('sales', () => {
         beforeAll(async () => {
         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: [
                 input: [
                     {
                     {
                         id: variant1.id,
                         id: variant1.id,
@@ -157,14 +173,20 @@ describe('Stock control', () => {
                 productVariantId: variant2.id,
                 productVariantId: variant2.id,
                 quantity: 3,
                 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: {
                 input: {
                     method: testPaymentMethod.code,
                     method: testPaymentMethod.code,
                     metadata: {},
                     metadata: {},
@@ -173,8 +195,11 @@ describe('Stock control', () => {
         });
         });
 
 
         it('creates a Sale when order completed', async () => {
         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.totalItems).toBe(2);
             expect(variant1.stockMovements.items[1].type).toBe(StockMovementType.SALE);
             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 () => {
         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(variant1.stockOnHand).toBe(5); // untracked inventory
             expect(variant2.stockOnHand).toBe(2); // tracked 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 { ZONE_FRAGMENT } from './graphql/fragments';
 import {
 import {
     AddMembersToZone,
     AddMembersToZone,
-    CreateZone,
+    CreateZone, DeleteZone,
     DeletionResult,
     DeletionResult,
     GetCountryList,
     GetCountryList,
-    GetZone,
+    GetZone, GetZones,
     RemoveMembersFromZone,
     RemoveMembersFromZone,
     UpdateZone,
     UpdateZone,
 } from './graphql/generated-e2e-admin-types';
 } from './graphql/generated-e2e-admin-types';
@@ -42,8 +42,7 @@ describe('Facet resolver', () => {
     });
     });
 
 
     it('zones', async () => {
     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);
         expect(result.zones.length).toBe(5);
         zones = result.zones;
         zones = result.zones;
         oceania = zones[0];
         oceania = zones[0];
@@ -110,19 +109,19 @@ describe('Facet resolver', () => {
 
 
     describe('deletion', () => {
     describe('deletion', () => {
         it('deletes Zone not used in any TaxRate', async () => {
         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({
             expect(result1.deleteZone).toEqual({
                 result: DeletionResult.DELETED,
                 result: DeletionResult.DELETED,
                 message: '',
                 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 () => {
         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({
             expect(result1.deleteZone).toEqual({
                 result: DeletionResult.NOT_DELETED,
                 result: DeletionResult.NOT_DELETED,
@@ -131,8 +130,8 @@ describe('Facet resolver', () => {
                     'TaxRates: Standard Tax Oceania, Reduced Tax Oceania, Zero Tax Oceania',
                     '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();
         });
         });
     });
     });
 });
 });