فهرست منبع

test(core): Get all tests passing for sqljs, postgres & mysql

Relates to #207
Michael Bromley 6 سال پیش
والد
کامیت
06043a5e54
36فایلهای تغییر یافته به همراه725 افزوده شده و 907 حذف شده
  1. 42 3
      e2e-common/test-config.ts
  2. 0 39
      packages/core/e2e/__snapshots__/facet.e2e-spec.ts.snap
  3. 300 491
      packages/core/e2e/__snapshots__/import.e2e-spec.ts.snap
  4. 0 50
      packages/core/e2e/__snapshots__/product.e2e-spec.ts.snap
  5. 0 1
      packages/core/e2e/administrator.e2e-spec.ts
  6. 0 1
      packages/core/e2e/apollo-server-plugin.e2e-spec.ts
  7. 0 1
      packages/core/e2e/auth.e2e-spec.ts
  8. 4 5
      packages/core/e2e/channel.e2e-spec.ts
  9. 16 7
      packages/core/e2e/collection.e2e-spec.ts
  10. 0 1
      packages/core/e2e/country.e2e-spec.ts
  11. 106 99
      packages/core/e2e/custom-fields.e2e-spec.ts
  12. 54 40
      packages/core/e2e/customer.e2e-spec.ts
  13. 11 8
      packages/core/e2e/default-search-plugin.e2e-spec.ts
  14. 0 1
      packages/core/e2e/entity-id-strategy.e2e-spec.ts
  15. 0 1
      packages/core/e2e/entity-uuid-strategy.e2e-spec.ts
  16. 35 19
      packages/core/e2e/facet.e2e-spec.ts
  17. 35 35
      packages/core/e2e/fixtures/e2e-products-full.csv
  18. 1 17
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  19. 42 2
      packages/core/e2e/import.e2e-spec.ts
  20. 0 1
      packages/core/e2e/localization.e2e-spec.ts
  21. 0 1
      packages/core/e2e/order-promotion.e2e-spec.ts
  22. 12 10
      packages/core/e2e/order.e2e-spec.ts
  23. 0 1
      packages/core/e2e/plugin.e2e-spec.ts
  24. 0 1
      packages/core/e2e/product-option.e2e-spec.ts
  25. 49 27
      packages/core/e2e/product.e2e-spec.ts
  26. 0 1
      packages/core/e2e/promotion.e2e-spec.ts
  27. 2 2
      packages/core/e2e/role.e2e-spec.ts
  28. 0 1
      packages/core/e2e/shipping-method.e2e-spec.ts
  29. 0 4
      packages/core/e2e/shop-auth.e2e-spec.ts
  30. 6 2
      packages/core/e2e/shop-catalog.e2e-spec.ts
  31. 0 1
      packages/core/e2e/shop-customer.e2e-spec.ts
  32. 0 1
      packages/core/e2e/shop-order.e2e-spec.ts
  33. 0 1
      packages/core/e2e/stock-control.e2e-spec.ts
  34. 2 31
      packages/core/e2e/tax-rate.e2e-spec.ts
  35. 8 0
      packages/core/e2e/utils/test-order-utils.ts
  36. 0 1
      packages/core/e2e/zone.e2e-spec.ts

+ 42 - 3
e2e-common/test-config.ts

@@ -1,6 +1,13 @@
 import { mergeConfig } from '@vendure/core';
-import { testConfig as defaultTestConfig } from '@vendure/testing';
+import {
+    MysqlInitializer,
+    PostgresInitializer,
+    registerInitializer,
+    SqljsInitializer,
+    testConfig as defaultTestConfig,
+} from '@vendure/testing';
 import path from 'path';
+import { ConnectionOptions } from 'typeorm';
 
 import { getPackageDir } from './get-package-dir';
 
@@ -11,6 +18,12 @@ import { getPackageDir } from './get-package-dir';
  */
 export const TEST_SETUP_TIMEOUT_MS = process.env.E2E_DEBUG ? 1800 * 1000 : 120000;
 
+const packageDir = getPackageDir();
+
+registerInitializer('sqljs', new SqljsInitializer(path.join(packageDir, '__data__')));
+registerInitializer('postgres', new PostgresInitializer());
+registerInitializer('mysql', new MysqlInitializer());
+
 /**
  * For local debugging of the e2e tests, we set a very long timeout value otherwise tests will
  * automatically fail for going over the 5 second default timeout.
@@ -21,10 +34,36 @@ if (process.env.E2E_DEBUG) {
     jest.setTimeout(1800 * 1000);
 }
 
-const packageDir = getPackageDir();
-
 export const testConfig = mergeConfig(defaultTestConfig, {
     importExportOptions: {
         importAssetsDir: path.join(packageDir, 'fixtures/assets'),
     },
+    dbConnectionOptions: getDbConfig(),
 });
+
+function getDbConfig(): ConnectionOptions {
+    const dbType = process.env.DB || 'sqljs';
+    switch (dbType) {
+        case 'postgres':
+            return {
+                synchronize: true,
+                type: 'postgres',
+                host: '127.0.0.1',
+                port: 5432,
+                username: 'postgres',
+                password: 'Be70',
+            };
+        case 'mysql':
+            return {
+                synchronize: true,
+                type: 'mysql',
+                host: '192.168.99.100',
+                port: 3306,
+                username: 'root',
+                password: '',
+            };
+        case 'sqljs':
+        default:
+            return defaultTestConfig.dbConnectionOptions;
+    }
+}

+ 0 - 39
packages/core/e2e/__snapshots__/facet.e2e-spec.ts.snap

@@ -35,42 +35,3 @@ Object {
   ],
 }
 `;
-
-exports[`Facet resolver createFacetValues 1`] = `
-Array [
-  Object {
-    "code": "pc",
-    "facet": Object {
-      "id": "T_2",
-      "name": "Speaker Category",
-    },
-    "id": "T_9",
-    "languageCode": "en",
-    "name": "PC Speakers",
-    "translations": Array [
-      Object {
-        "id": "T_9",
-        "languageCode": "en",
-        "name": "PC Speakers",
-      },
-    ],
-  },
-  Object {
-    "code": "hi-fi",
-    "facet": Object {
-      "id": "T_2",
-      "name": "Speaker Category",
-    },
-    "id": "T_8",
-    "languageCode": "en",
-    "name": "Hi Fi Speakers",
-    "translations": Array [
-      Object {
-        "id": "T_8",
-        "languageCode": "en",
-        "name": "Hi Fi Speakers",
-      },
-    ],
-  },
-]
-`;

+ 300 - 491
packages/core/e2e/__snapshots__/import.e2e-spec.ts.snap

@@ -1,537 +1,346 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`Import resolver imports products 1`] = `
-Array [
-  Object {
-    "assets": Array [
-      Object {
-        "id": "T_1",
-        "name": "pps1.jpg",
-        "preview": "test-url/test-assets/pps1__preview.jpg",
-        "source": "test-url/test-assets/pps1.jpg",
-      },
-      Object {
-        "id": "T_2",
-        "name": "pps2.jpg",
-        "preview": "test-url/test-assets/pps2__preview.jpg",
-        "source": "test-url/test-assets/pps2.jpg",
-      },
-    ],
-    "customFields": Object {
-      "pageType": "default",
-    },
-    "description": "A great device for stretching paper.",
-    "facetValues": Array [],
-    "featuredAsset": Object {
+Object {
+  "assets": Array [
+    Object {
       "id": "T_1",
       "name": "pps1.jpg",
       "preview": "test-url/test-assets/pps1__preview.jpg",
       "source": "test-url/test-assets/pps1.jpg",
     },
+    Object {
+      "id": "T_2",
+      "name": "pps2.jpg",
+      "preview": "test-url/test-assets/pps2__preview.jpg",
+      "source": "test-url/test-assets/pps2.jpg",
+    },
+  ],
+  "customFields": Object {
+    "pageType": "default",
+  },
+  "description": "A great device for stretching paper.",
+  "featuredAsset": Object {
     "id": "T_1",
-    "name": "Perfect Paper Stretcher",
-    "optionGroups": Array [
-      Object {
-        "code": "perfect-paper-stretcher-size",
+    "name": "pps1.jpg",
+    "preview": "test-url/test-assets/pps1__preview.jpg",
+    "source": "test-url/test-assets/pps1.jpg",
+  },
+  "id": "T_1",
+  "name": "Perfect Paper Stretcher",
+  "optionGroups": Array [
+    Object {
+      "code": "perfect-paper-stretcher-size",
+      "id": "T_1",
+      "name": "size",
+    },
+  ],
+  "slug": "perfect-paper-stretcher",
+  "variants": Array [
+    Object {
+      "assets": Array [],
+      "customFields": Object {
+        "weight": 100,
+      },
+      "featuredAsset": null,
+      "id": "T_1",
+      "name": "Perfect Paper Stretcher Half Imperial",
+      "price": 4530,
+      "sku": "PPS12",
+      "stockMovements": Object {
+        "items": Array [],
+      },
+      "stockOnHand": 0,
+      "taxCategory": Object {
         "id": "T_1",
-        "name": "size",
-      },
-    ],
-    "slug": "perfect-paper-stretcher",
-    "variants": Array [
-      Object {
-        "assets": Array [],
-        "customFields": Object {
-          "weight": 100,
-        },
-        "facetValues": Array [
-          Object {
-            "code": "kb",
-            "facet": Object {
-              "id": "T_1",
-              "name": "Brand",
-            },
-            "id": "T_1",
-            "name": "KB",
-          },
-          Object {
-            "code": "accessory",
-            "facet": Object {
-              "id": "T_2",
-              "name": "Type",
-            },
-            "id": "T_2",
-            "name": "Accessory",
-          },
-        ],
-        "featuredAsset": null,
+        "name": "Standard Tax",
+      },
+      "trackInventory": false,
+    },
+    Object {
+      "assets": Array [],
+      "customFields": Object {
+        "weight": 100,
+      },
+      "featuredAsset": null,
+      "id": "T_2",
+      "name": "Perfect Paper Stretcher Quarter Imperial",
+      "price": 3250,
+      "sku": "PPS14",
+      "stockMovements": Object {
+        "items": Array [],
+      },
+      "stockOnHand": 0,
+      "taxCategory": Object {
         "id": "T_1",
-        "name": "Perfect Paper Stretcher Half Imperial",
-        "options": Array [
-          Object {
-            "code": "half-imperial",
-            "id": "T_1",
-          },
-        ],
-        "price": 4530,
-        "sku": "PPS12",
-        "stockMovements": Object {
-          "items": Array [],
-        },
-        "stockOnHand": 0,
-        "taxCategory": Object {
-          "id": "T_1",
-          "name": "Standard Tax",
-        },
-        "trackInventory": false,
+        "name": "Standard Tax",
       },
-      Object {
-        "assets": Array [],
-        "customFields": Object {
-          "weight": 100,
-        },
-        "facetValues": Array [
-          Object {
-            "code": "kb",
-            "facet": Object {
-              "id": "T_1",
-              "name": "Brand",
-            },
-            "id": "T_1",
-            "name": "KB",
-          },
-          Object {
-            "code": "accessory",
-            "facet": Object {
-              "id": "T_2",
-              "name": "Type",
-            },
-            "id": "T_2",
-            "name": "Accessory",
-          },
-        ],
-        "featuredAsset": null,
-        "id": "T_2",
-        "name": "Perfect Paper Stretcher Quarter Imperial",
-        "options": Array [
-          Object {
-            "code": "quarter-imperial",
-            "id": "T_2",
-          },
-        ],
-        "price": 3250,
-        "sku": "PPS14",
-        "stockMovements": Object {
-          "items": Array [],
-        },
-        "stockOnHand": 0,
-        "taxCategory": Object {
-          "id": "T_1",
-          "name": "Standard Tax",
-        },
-        "trackInventory": false,
+      "trackInventory": false,
+    },
+    Object {
+      "assets": Array [],
+      "customFields": Object {
+        "weight": 100,
       },
-      Object {
-        "assets": Array [],
-        "customFields": Object {
-          "weight": 100,
-        },
-        "facetValues": Array [
+      "featuredAsset": null,
+      "id": "T_3",
+      "name": "Perfect Paper Stretcher Full Imperial",
+      "price": 5950,
+      "sku": "PPSF",
+      "stockMovements": Object {
+        "items": Array [
           Object {
-            "code": "kb",
-            "facet": Object {
-              "id": "T_1",
-              "name": "Brand",
-            },
             "id": "T_1",
-            "name": "KB",
-          },
-          Object {
-            "code": "accessory",
-            "facet": Object {
-              "id": "T_2",
-              "name": "Type",
-            },
-            "id": "T_2",
-            "name": "Accessory",
+            "quantity": -10,
+            "type": "ADJUSTMENT",
           },
         ],
-        "featuredAsset": null,
-        "id": "T_3",
-        "name": "Perfect Paper Stretcher Full Imperial",
-        "options": Array [
-          Object {
-            "code": "full-imperial",
-            "id": "T_3",
-          },
-        ],
-        "price": 5950,
-        "sku": "PPSF",
-        "stockMovements": Object {
-          "items": Array [
-            Object {
-              "id": "T_1",
-              "quantity": -10,
-              "type": "ADJUSTMENT",
-            },
-          ],
-        },
-        "stockOnHand": -10,
-        "taxCategory": Object {
-          "id": "T_1",
-          "name": "Standard Tax",
-        },
-        "trackInventory": false,
       },
-    ],
-  },
-  Object {
-    "assets": Array [],
-    "customFields": Object {
-      "pageType": "expanded",
+      "stockOnHand": -10,
+      "taxCategory": Object {
+        "id": "T_1",
+        "name": "Standard Tax",
+      },
+      "trackInventory": false,
     },
-    "description": "Mabef description",
-    "facetValues": Array [],
-    "featuredAsset": null,
-    "id": "T_2",
-    "name": "Mabef M/02 Studio Easel",
-    "optionGroups": Array [],
-    "slug": "mabef-m02-studio-easel",
-    "variants": Array [
-      Object {
-        "assets": Array [],
-        "customFields": Object {
-          "weight": 300,
-        },
-        "facetValues": Array [
-          Object {
-            "code": "mabef",
-            "facet": Object {
-              "id": "T_1",
-              "name": "Brand",
-            },
-            "id": "T_3",
-            "name": "Mabef",
-          },
+  ],
+}
+`;
+
+exports[`Import resolver imports products 2`] = `
+Object {
+  "assets": Array [],
+  "customFields": Object {
+    "pageType": "expanded",
+  },
+  "description": "Mabef description",
+  "featuredAsset": null,
+  "id": "T_2",
+  "name": "Mabef M/02 Studio Easel",
+  "optionGroups": Array [],
+  "slug": "mabef-m02-studio-easel",
+  "variants": Array [
+    Object {
+      "assets": Array [],
+      "customFields": Object {
+        "weight": 300,
+      },
+      "featuredAsset": null,
+      "id": "T_4",
+      "name": "Mabef M/02 Studio Easel",
+      "price": 91070,
+      "sku": "M02",
+      "stockMovements": Object {
+        "items": Array [
           Object {
-            "code": "easel",
-            "facet": Object {
-              "id": "T_2",
-              "name": "Type",
-            },
-            "id": "T_4",
-            "name": "Easel",
+            "id": "T_2",
+            "quantity": 100,
+            "type": "ADJUSTMENT",
           },
         ],
-        "featuredAsset": null,
-        "id": "T_4",
-        "name": "Mabef M/02 Studio Easel",
-        "options": Array [],
-        "price": 91070,
-        "sku": "M02",
-        "stockMovements": Object {
-          "items": Array [
-            Object {
-              "id": "T_2",
-              "quantity": 100,
-              "type": "ADJUSTMENT",
-            },
-          ],
-        },
-        "stockOnHand": 100,
-        "taxCategory": Object {
-          "id": "T_1",
-          "name": "Standard Tax",
-        },
-        "trackInventory": false,
       },
-    ],
+      "stockOnHand": 100,
+      "taxCategory": Object {
+        "id": "T_1",
+        "name": "Standard Tax",
+      },
+      "trackInventory": false,
+    },
+  ],
+}
+`;
+
+exports[`Import resolver imports products 3`] = `
+Object {
+  "assets": Array [],
+  "customFields": Object {
+    "pageType": "default",
   },
-  Object {
-    "assets": Array [],
-    "customFields": Object {
-      "pageType": "default",
+  "description": "Really mega pencils",
+  "featuredAsset": null,
+  "id": "T_3",
+  "name": "Giotto Mega Pencils",
+  "optionGroups": Array [
+    Object {
+      "code": "giotto-mega-pencils-box-size",
+      "id": "T_2",
+      "name": "box size",
     },
-    "description": "Really mega pencils",
-    "facetValues": Array [],
-    "featuredAsset": null,
-    "id": "T_3",
-    "name": "Giotto Mega Pencils",
-    "optionGroups": Array [
-      Object {
-        "code": "giotto-mega-pencils-box-size",
-        "id": "T_2",
-        "name": "box size",
-      },
-    ],
-    "slug": "giotto-mega-pencils",
-    "variants": Array [
-      Object {
-        "assets": Array [
-          Object {
-            "id": "T_3",
-            "name": "box-of-8.jpg",
-            "preview": "test-url/test-assets/box-of-8__preview.jpg",
-            "source": "test-url/test-assets/box-of-8.jpg",
-          },
-        ],
-        "customFields": Object {
-          "weight": 200,
-        },
-        "facetValues": Array [
-          Object {
-            "code": "xmas-sale",
-            "facet": Object {
-              "id": "T_3",
-              "name": "Collection",
-            },
-            "id": "T_5",
-            "name": "Xmas Sale",
-          },
-        ],
-        "featuredAsset": Object {
+  ],
+  "slug": "giotto-mega-pencils",
+  "variants": Array [
+    Object {
+      "assets": Array [
+        Object {
           "id": "T_3",
           "name": "box-of-8.jpg",
           "preview": "test-url/test-assets/box-of-8__preview.jpg",
           "source": "test-url/test-assets/box-of-8.jpg",
         },
-        "id": "T_5",
-        "name": "Giotto Mega Pencils Box of 8",
-        "options": Array [
-          Object {
-            "code": "box-of-8",
-            "id": "T_4",
-          },
-        ],
-        "price": 416,
-        "sku": "225400",
-        "stockMovements": Object {
-          "items": Array [],
-        },
-        "stockOnHand": 0,
-        "taxCategory": Object {
-          "id": "T_1",
-          "name": "Standard Tax",
-        },
-        "trackInventory": false,
+      ],
+      "customFields": Object {
+        "weight": 200,
       },
-      Object {
-        "assets": Array [
-          Object {
-            "id": "T_4",
-            "name": "box-of-12.jpg",
-            "preview": "test-url/test-assets/box-of-12__preview.jpg",
-            "source": "test-url/test-assets/box-of-12.jpg",
-          },
-        ],
-        "customFields": Object {
-          "weight": 200,
-        },
-        "facetValues": Array [
-          Object {
-            "code": "xmas-sale",
-            "facet": Object {
-              "id": "T_3",
-              "name": "Collection",
-            },
-            "id": "T_5",
-            "name": "Xmas Sale",
-          },
-        ],
-        "featuredAsset": Object {
+      "featuredAsset": Object {
+        "id": "T_3",
+        "name": "box-of-8.jpg",
+        "preview": "test-url/test-assets/box-of-8__preview.jpg",
+        "source": "test-url/test-assets/box-of-8.jpg",
+      },
+      "id": "T_5",
+      "name": "Giotto Mega Pencils Box of 8",
+      "price": 416,
+      "sku": "225400",
+      "stockMovements": Object {
+        "items": Array [],
+      },
+      "stockOnHand": 0,
+      "taxCategory": Object {
+        "id": "T_1",
+        "name": "Standard Tax",
+      },
+      "trackInventory": false,
+    },
+    Object {
+      "assets": Array [
+        Object {
           "id": "T_4",
           "name": "box-of-12.jpg",
           "preview": "test-url/test-assets/box-of-12__preview.jpg",
           "source": "test-url/test-assets/box-of-12.jpg",
         },
-        "id": "T_6",
-        "name": "Giotto Mega Pencils Box of 12",
-        "options": Array [
-          Object {
-            "code": "box-of-12",
-            "id": "T_5",
-          },
-        ],
-        "price": 624,
-        "sku": "225600",
-        "stockMovements": Object {
-          "items": Array [],
-        },
-        "stockOnHand": 0,
-        "taxCategory": Object {
-          "id": "T_1",
-          "name": "Standard Tax",
-        },
-        "trackInventory": false,
+      ],
+      "customFields": Object {
+        "weight": 200,
+      },
+      "featuredAsset": Object {
+        "id": "T_4",
+        "name": "box-of-12.jpg",
+        "preview": "test-url/test-assets/box-of-12__preview.jpg",
+        "source": "test-url/test-assets/box-of-12.jpg",
+      },
+      "id": "T_6",
+      "name": "Giotto Mega Pencils Box of 12",
+      "price": 624,
+      "sku": "225600",
+      "stockMovements": Object {
+        "items": Array [],
+      },
+      "stockOnHand": 0,
+      "taxCategory": Object {
+        "id": "T_1",
+        "name": "Standard Tax",
       },
-    ],
+      "trackInventory": false,
+    },
+  ],
+}
+`;
+
+exports[`Import resolver imports products 4`] = `
+Object {
+  "assets": Array [],
+  "customFields": Object {
+    "pageType": "default",
   },
-  Object {
-    "assets": Array [],
-    "customFields": Object {
-      "pageType": "default",
+  "description": "Keeps the paint off the clothes",
+  "featuredAsset": null,
+  "id": "T_4",
+  "name": "Artists Smock",
+  "optionGroups": Array [
+    Object {
+      "code": "artists-smock-size",
+      "id": "T_3",
+      "name": "size",
     },
-    "description": "Keeps the paint off the clothes",
-    "facetValues": Array [
-      Object {
-        "facet": Object {
-          "id": "T_4",
-          "name": "Material",
-        },
-        "id": "T_6",
-        "name": "Denim",
+    Object {
+      "code": "artists-smock-colour",
+      "id": "T_4",
+      "name": "colour",
+    },
+  ],
+  "slug": "artists-smock",
+  "variants": Array [
+    Object {
+      "assets": Array [],
+      "customFields": Object {
+        "weight": 500,
       },
-      Object {
-        "facet": Object {
-          "id": "T_3",
-          "name": "Collection",
-        },
-        "id": "T_7",
-        "name": "clothes",
-      },
-    ],
-    "featuredAsset": null,
-    "id": "T_4",
-    "name": "Artists Smock",
-    "optionGroups": Array [
-      Object {
-        "code": "artists-smock-size",
-        "id": "T_3",
-        "name": "size",
+      "featuredAsset": null,
+      "id": "T_7",
+      "name": "Artists Smock small beige",
+      "price": 1199,
+      "sku": "10112",
+      "stockMovements": Object {
+        "items": Array [],
       },
-      Object {
-        "code": "artists-smock-colour",
-        "id": "T_4",
-        "name": "colour",
-      },
-    ],
-    "slug": "artists-smock",
-    "variants": Array [
-      Object {
-        "assets": Array [],
-        "customFields": Object {
-          "weight": 500,
-        },
-        "facetValues": Array [],
-        "featuredAsset": null,
-        "id": "T_7",
-        "name": "Artists Smock small beige",
-        "options": Array [
-          Object {
-            "code": "small",
-            "id": "T_6",
-          },
-          Object {
-            "code": "beige",
-            "id": "T_8",
-          },
-        ],
-        "price": 1199,
-        "sku": "10112",
-        "stockMovements": Object {
-          "items": Array [],
-        },
-        "stockOnHand": 0,
-        "taxCategory": Object {
-          "id": "T_2",
-          "name": "Reduced Tax",
-        },
-        "trackInventory": false,
+      "stockOnHand": 0,
+      "taxCategory": Object {
+        "id": "T_2",
+        "name": "Reduced Tax",
       },
-      Object {
-        "assets": Array [],
-        "customFields": Object {
-          "weight": 500,
-        },
-        "facetValues": Array [],
-        "featuredAsset": null,
-        "id": "T_8",
-        "name": "Artists Smock large beige",
-        "options": Array [
-          Object {
-            "code": "large",
-            "id": "T_7",
-          },
-          Object {
-            "code": "beige",
-            "id": "T_8",
-          },
-        ],
-        "price": 1199,
-        "sku": "10113",
-        "stockMovements": Object {
-          "items": Array [],
-        },
-        "stockOnHand": 0,
-        "taxCategory": Object {
-          "id": "T_2",
-          "name": "Reduced Tax",
-        },
-        "trackInventory": false,
+      "trackInventory": false,
+    },
+    Object {
+      "assets": Array [],
+      "customFields": Object {
+        "weight": 500,
       },
-      Object {
-        "assets": Array [],
-        "customFields": Object {
-          "weight": 500,
-        },
-        "facetValues": Array [],
-        "featuredAsset": null,
-        "id": "T_9",
-        "name": "Artists Smock small navy",
-        "options": Array [
-          Object {
-            "code": "small",
-            "id": "T_6",
-          },
-          Object {
-            "code": "navy",
-            "id": "T_9",
-          },
-        ],
-        "price": 1199,
-        "sku": "10114",
-        "stockMovements": Object {
-          "items": Array [],
-        },
-        "stockOnHand": 0,
-        "taxCategory": Object {
-          "id": "T_2",
-          "name": "Reduced Tax",
-        },
-        "trackInventory": false,
+      "featuredAsset": null,
+      "id": "T_8",
+      "name": "Artists Smock large beige",
+      "price": 1199,
+      "sku": "10113",
+      "stockMovements": Object {
+        "items": Array [],
       },
-      Object {
-        "assets": Array [],
-        "customFields": Object {
-          "weight": 500,
-        },
-        "facetValues": Array [],
-        "featuredAsset": null,
-        "id": "T_10",
-        "name": "Artists Smock large navy",
-        "options": Array [
-          Object {
-            "code": "large",
-            "id": "T_7",
-          },
-          Object {
-            "code": "navy",
-            "id": "T_9",
-          },
-        ],
-        "price": 1199,
-        "sku": "10115",
-        "stockMovements": Object {
-          "items": Array [],
-        },
-        "stockOnHand": 0,
-        "taxCategory": Object {
-          "id": "T_2",
-          "name": "Reduced Tax",
-        },
-        "trackInventory": false,
+      "stockOnHand": 0,
+      "taxCategory": Object {
+        "id": "T_2",
+        "name": "Reduced Tax",
       },
-    ],
-  },
-]
+      "trackInventory": false,
+    },
+    Object {
+      "assets": Array [],
+      "customFields": Object {
+        "weight": 500,
+      },
+      "featuredAsset": null,
+      "id": "T_9",
+      "name": "Artists Smock small navy",
+      "price": 1199,
+      "sku": "10114",
+      "stockMovements": Object {
+        "items": Array [],
+      },
+      "stockOnHand": 0,
+      "taxCategory": Object {
+        "id": "T_2",
+        "name": "Reduced Tax",
+      },
+      "trackInventory": false,
+    },
+    Object {
+      "assets": Array [],
+      "customFields": Object {
+        "weight": 500,
+      },
+      "featuredAsset": null,
+      "id": "T_10",
+      "name": "Artists Smock large navy",
+      "price": 1199,
+      "sku": "10115",
+      "stockMovements": Object {
+        "items": Array [],
+      },
+      "stockOnHand": 0,
+      "taxCategory": Object {
+        "id": "T_2",
+        "name": "Reduced Tax",
+      },
+      "trackInventory": false,
+    },
+  ],
+}
 `;

+ 0 - 50
packages/core/e2e/__snapshots__/product.e2e-spec.ts.snap

@@ -18,56 +18,6 @@ Object {
   "name": "en Baked Potato",
   "optionGroups": Array [],
   "slug": "en-baked-potato",
-  "translations": Array [
-    Object {
-      "description": "Eine baked Erdapfel",
-      "languageCode": "de",
-      "name": "de Baked Potato",
-      "slug": "de-baked-potato",
-    },
-    Object {
-      "description": "A baked potato",
-      "languageCode": "en",
-      "name": "en Baked Potato",
-      "slug": "en-baked-potato",
-    },
-  ],
-  "variants": Array [],
-}
-`;
-
-exports[`Product resolver product mutation updateProduct updates a Product 1`] = `
-Object {
-  "assets": Array [],
-  "channels": Array [
-    Object {
-      "code": "__default_channel__",
-      "id": "T_1",
-    },
-  ],
-  "description": "A blob of mashed potato",
-  "enabled": true,
-  "facetValues": Array [],
-  "featuredAsset": null,
-  "id": "T_21",
-  "languageCode": "en",
-  "name": "en Mashed Potato",
-  "optionGroups": Array [],
-  "slug": "en-mashed-potato",
-  "translations": Array [
-    Object {
-      "description": "Eine blob von gemashed Erdapfel",
-      "languageCode": "de",
-      "name": "de Mashed Potato",
-      "slug": "de-mashed-potato",
-    },
-    Object {
-      "description": "A blob of mashed potato",
-      "languageCode": "en",
-      "name": "en Mashed Potato",
-      "slug": "en-mashed-potato",
-    },
-  ],
   "variants": Array [],
 }
 `;

+ 0 - 1
packages/core/e2e/administrator.e2e-spec.ts

@@ -22,7 +22,6 @@ describe('Administrator resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,

+ 0 - 1
packages/core/e2e/apollo-server-plugin.e2e-spec.ts

@@ -45,7 +45,6 @@ describe('custom apolloServerPlugins', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,

+ 0 - 1
packages/core/e2e/auth.e2e-spec.ts

@@ -33,7 +33,6 @@ describe('Authorization & permissions', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,

+ 4 - 5
packages/core/e2e/channel.e2e-spec.ts

@@ -44,7 +44,6 @@ describe('Channels', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 1,
@@ -289,7 +288,7 @@ describe('Channels', () => {
                 },
             });
 
-            expect(assignProductsToChannel[0].channels.map(c => c.id)).toEqual(['T_1', 'T_2']);
+            expect(assignProductsToChannel[0].channels.map(c => c.id).sort()).toEqual(['T_1', 'T_2']);
             await adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
             const { product } = await adminClient.query<
                 GetProductWithVariants.Query,
@@ -318,7 +317,7 @@ describe('Channels', () => {
                 },
             });
 
-            expect(assignProductsToChannel[0].channels.map(c => c.id)).toEqual(['T_1', 'T_2']);
+            expect(assignProductsToChannel[0].channels.map(c => c.id).sort()).toEqual(['T_1', 'T_2']);
         });
 
         it(
@@ -365,7 +364,7 @@ describe('Channels', () => {
                 productIds: [PROD_ID],
             },
         });
-        expect(assignProductsToChannel[0].channels.map(c => c.id)).toEqual(['T_1', 'T_2']);
+        expect(assignProductsToChannel[0].channels.map(c => c.id).sort()).toEqual(['T_1', 'T_2']);
 
         const { deleteChannel } = await adminClient.query<DeleteChannel.Mutation, DeleteChannel.Variables>(
             DELETE_CHANNEL,
@@ -377,7 +376,7 @@ describe('Channels', () => {
         expect(deleteChannel.result).toBe(DeletionResult.DELETED);
 
         const { channels } = await adminClient.query<GetChannels.Query>(GET_CHANNELS);
-        expect(channels.map(c => c.id)).toEqual(['T_1', 'T_3']);
+        expect(channels.map(c => c.id).sort()).toEqual(['T_1', 'T_3']);
 
         const { product } = await adminClient.query<
             GetProductWithVariants.Query,

+ 16 - 7
packages/core/e2e/collection.e2e-spec.ts

@@ -61,7 +61,6 @@ describe('Collection resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-collections.csv'),
             customerCount: 1,
@@ -846,6 +845,7 @@ describe('Collection resolver', () => {
                         ],
                     },
                 });
+                await awaitRunningJobs(adminClient);
                 return createCollection;
             }
 
@@ -931,12 +931,13 @@ describe('Collection resolver', () => {
             beforeAll(async () => {
                 const result = await adminClient.query<GetProductsWithVariantIds.Query>(gql`
                     query GetProductsWithVariantIds {
-                        products {
+                        products(options: { sort: { id: ASC } }) {
                             items {
                                 id
                                 name
                                 variants {
                                     id
+                                    name
                                 }
                             }
                         }
@@ -975,13 +976,15 @@ describe('Collection resolver', () => {
             });
 
             it('updates contents when ProductVariant is updated', async () => {
-                const gamingPcFirstVariant = products.find(p => p.name === 'Gaming PC')!.variants[0];
+                const gamingPc240GB = products
+                    .find(p => p.name === 'Gaming PC')!
+                    .variants.find(v => v.name.includes('240GB'))!;
                 await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
                     UPDATE_PRODUCT_VARIANTS,
                     {
                         input: [
                             {
-                                id: gamingPcFirstVariant.id,
+                                id: gamingPc240GB.id,
                                 facetValueIds: [getFacetValueId('pear')],
                             },
                         ],
@@ -1007,18 +1010,23 @@ describe('Collection resolver', () => {
             });
 
             it('correctly filters when ProductVariant and Product both have matching FacetValue', async () => {
-                const gamingPcFirstVariant = products.find(p => p.name === 'Gaming PC')!.variants[0];
+                const gamingPc240GB = products
+                    .find(p => p.name === 'Gaming PC')!
+                    .variants.find(v => v.name.includes('240GB'))!;
                 await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
                     UPDATE_PRODUCT_VARIANTS,
                     {
                         input: [
                             {
-                                id: gamingPcFirstVariant.id,
+                                id: gamingPc240GB.id,
                                 facetValueIds: [getFacetValueId('electronics'), getFacetValueId('pear')],
                             },
                         ],
                     },
                 );
+
+                await awaitRunningJobs(adminClient);
+
                 const result = await adminClient.query<
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Variables
@@ -1037,6 +1045,7 @@ describe('Collection resolver', () => {
         });
 
         it('filter inheritance of nested collections (issue #158)', async () => {
+            const a = 1;
             const { createCollection: pearElectronics } = await adminClient.query<
                 CreateCollectionSelectVariants.Mutation,
                 CreateCollectionSelectVariants.Variables
@@ -1189,7 +1198,7 @@ const GET_COLLECTIONS = gql`
 const GET_COLLECTION_PRODUCT_VARIANTS = gql`
     query GetCollectionProducts($id: ID!) {
         collection(id: $id) {
-            productVariants {
+            productVariants(options: { sort: { id: ASC } }) {
                 items {
                     id
                     name

+ 0 - 1
packages/core/e2e/country.e2e-spec.ts

@@ -27,7 +27,6 @@ describe('Facet resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,

+ 106 - 99
packages/core/e2e/custom-fields.e2e-spec.ts

@@ -1,7 +1,14 @@
 // Force the timezone to avoid tests failing in other locales
-process.env.TZ = 'UTC';
+// process.env.TZ = 'UTC';
+if (process.env.DB === 'postgres') {
+    // Hack to fix pg driver date handling. See https://github.com/typeorm/typeorm/issues/2622#issuecomment-476416712
+    // tslint:disable-next-line:no-var-requires
+    const pg = require('pg');
+    pg.types.setTypeParser(1114, (stringValue: string) => new Date(`${stringValue}+0000`));
+}
 
 import { LanguageCode } from '@vendure/common/lib/generated-types';
+import { mergeConfig } from '@vendure/core';
 import { CustomFields } from '@vendure/core/dist/config/custom-field/custom-field-types';
 import { createTestEnvironment } from '@vendure/testing';
 import gql from 'graphql-tag';
@@ -14,111 +21,111 @@ import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
 
 // tslint:disable:no-non-null-assertion
 
-const customConfig = {
-    ...testConfig,
-    ...{
-        customFields: {
-            Product: [
-                { name: 'nullable', type: 'string' },
-                { name: 'notNullable', type: 'string', nullable: false, defaultValue: '' },
-                { name: 'stringWithDefault', type: 'string', defaultValue: 'hello' },
-                { name: 'localeStringWithDefault', type: 'localeString', defaultValue: 'hola' },
-                { name: 'intWithDefault', type: 'int', defaultValue: 5 },
-                { name: 'floatWithDefault', type: 'float', defaultValue: 5.5 },
-                { name: 'booleanWithDefault', type: 'boolean', defaultValue: true },
-                {
-                    name: 'dateTimeWithDefault',
-                    type: 'datetime',
-                    defaultValue: new Date('2019-04-30T12:59:16.4158386Z'),
-                },
-                { name: 'validateString', type: 'string', pattern: '^[0-9][a-z]+$' },
-                { name: 'validateLocaleString', type: 'localeString', pattern: '^[0-9][a-z]+$' },
-                { name: 'validateInt', type: 'int', min: 0, max: 10 },
-                { name: 'validateFloat', type: 'float', min: 0.5, max: 10.5 },
-                {
-                    name: 'validateDateTime',
-                    type: 'datetime',
-                    min: '2019-01-01T08:30',
-                    max: '2019-06-01T08:30',
-                },
-                {
-                    name: 'validateFn1',
-                    type: 'string',
-                    validate: value => {
-                        if (value !== 'valid') {
-                            return `The value ['${value}'] is not valid`;
-                        }
-                    },
-                },
-                {
-                    name: 'validateFn2',
-                    type: 'string',
-                    validate: value => {
-                        if (value !== 'valid') {
-                            return [
-                                {
-                                    languageCode: LanguageCode.en,
-                                    value: `The value ['${value}'] is not valid`,
-                                },
-                            ];
-                        }
-                    },
-                },
-                {
-                    name: 'stringWithOptions',
-                    type: 'string',
-                    options: [{ value: 'small' }, { value: 'medium' }, { value: 'large' }],
-                },
-                {
-                    name: 'nonPublic',
-                    type: 'string',
-                    defaultValue: 'hi!',
-                    public: false,
-                },
-                {
-                    name: 'public',
-                    type: 'string',
-                    defaultValue: 'ho!',
-                    public: true,
-                },
-                {
-                    name: 'longString',
-                    type: 'string',
-                },
-                {
-                    name: 'readonlyString',
-                    type: 'string',
-                    readonly: true,
-                },
-                {
-                    name: 'internalString',
-                    type: 'string',
-                    internal: true,
-                },
-            ],
-            Facet: [
-                {
-                    name: 'translated',
-                    type: 'localeString',
+const customConfig = mergeConfig(testConfig, {
+    dbConnectionOptions: {
+        timezone: 'Z',
+    },
+    customFields: {
+        Product: [
+            { name: 'nullable', type: 'string' },
+            { name: 'notNullable', type: 'string', nullable: false, defaultValue: '' },
+            { name: 'stringWithDefault', type: 'string', defaultValue: 'hello' },
+            { name: 'localeStringWithDefault', type: 'localeString', defaultValue: 'hola' },
+            { name: 'intWithDefault', type: 'int', defaultValue: 5 },
+            { name: 'floatWithDefault', type: 'float', defaultValue: 5.5 },
+            { name: 'booleanWithDefault', type: 'boolean', defaultValue: true },
+            {
+                name: 'dateTimeWithDefault',
+                type: 'datetime',
+                defaultValue: new Date('2019-04-30T12:59:16.4158386Z'),
+            },
+            { name: 'validateString', type: 'string', pattern: '^[0-9][a-z]+$' },
+            { name: 'validateLocaleString', type: 'localeString', pattern: '^[0-9][a-z]+$' },
+            { name: 'validateInt', type: 'int', min: 0, max: 10 },
+            { name: 'validateFloat', type: 'float', min: 0.5, max: 10.5 },
+            {
+                name: 'validateDateTime',
+                type: 'datetime',
+                min: '2019-01-01T08:30',
+                max: '2019-06-01T08:30',
+            },
+            {
+                name: 'validateFn1',
+                type: 'string',
+                validate: value => {
+                    if (value !== 'valid') {
+                        return `The value ['${value}'] is not valid`;
+                    }
                 },
-            ],
-            Customer: [
-                {
-                    name: 'score',
-                    type: 'int',
-                    readonly: true,
+            },
+            {
+                name: 'validateFn2',
+                type: 'string',
+                validate: value => {
+                    if (value !== 'valid') {
+                        return [
+                            {
+                                languageCode: LanguageCode.en,
+                                value: `The value ['${value}'] is not valid`,
+                            },
+                        ];
+                    }
                 },
-            ],
-        } as CustomFields,
-    },
-};
+            },
+            {
+                name: 'stringWithOptions',
+                type: 'string',
+                options: [{ value: 'small' }, { value: 'medium' }, { value: 'large' }],
+            },
+            {
+                name: 'nonPublic',
+                type: 'string',
+                defaultValue: 'hi!',
+                public: false,
+            },
+            {
+                name: 'public',
+                type: 'string',
+                defaultValue: 'ho!',
+                public: true,
+            },
+            {
+                name: 'longString',
+                type: 'string',
+                length: 60000,
+            },
+            {
+                name: 'readonlyString',
+                type: 'string',
+                readonly: true,
+            },
+            {
+                name: 'internalString',
+                type: 'string',
+                internal: true,
+            },
+        ],
+        Facet: [
+            {
+                name: 'translated',
+                type: 'localeString',
+            },
+        ],
+        Customer: [
+            {
+                name: 'score',
+                type: 'int',
+                readonly: true,
+            },
+        ],
+    } as CustomFields,
+});
 
 describe('Custom fields', () => {
     const { server, adminClient, shopClient } = createTestEnvironment(customConfig);
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,
@@ -310,7 +317,7 @@ describe('Custom fields', () => {
     );
 
     it('string length allows long strings', async () => {
-        const longString = Array.from({ length: 5000 }, v => 'hello there!').join(' ');
+        const longString = Array.from({ length: 4000 }, v => 'hello there!').join(' ');
         const result = await adminClient.query(
             gql`
                 mutation($stringValue: String!) {

+ 54 - 40
packages/core/e2e/customer.e2e-spec.ts

@@ -63,7 +63,6 @@ describe('Customer resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 5,
@@ -165,7 +164,7 @@ describe('Customer resolver', () => {
             });
 
             expect(result.customer!.addresses!.length).toBe(2);
-            firstCustomerAddressIds = result.customer!.addresses!.map(a => a.id);
+            firstCustomerAddressIds = result.customer!.addresses!.map(a => a.id).sort();
         });
 
         it('updateCustomerAddress updates the country', async () => {
@@ -199,12 +198,15 @@ describe('Customer resolver', () => {
             expect(result1.updateCustomerAddress.defaultShippingAddress).toBe(true);
             expect(result1.updateCustomerAddress.defaultBillingAddress).toBe(true);
 
-            // assert the first customer's first address is not default
+            // assert the first customer's other addresse is not default
             const result2 = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
                 id: firstCustomer.id,
             });
-            expect(result2.customer!.addresses![0].defaultShippingAddress).toBe(false);
-            expect(result2.customer!.addresses![0].defaultBillingAddress).toBe(false);
+            const otherAddress = result2.customer!.addresses!.filter(
+                a => a.id !== firstCustomerAddressIds[1],
+            )[0]!;
+            expect(otherAddress.defaultShippingAddress).toBe(false);
+            expect(otherAddress.defaultBillingAddress).toBe(false);
 
             // set the first customer's first address to be default
             const result3 = await adminClient.query<UpdateAddress.Mutation, UpdateAddress.Variables>(
@@ -224,8 +226,11 @@ describe('Customer resolver', () => {
             const result4 = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
                 id: firstCustomer.id,
             });
-            expect(result4.customer!.addresses![1].defaultShippingAddress).toBe(false);
-            expect(result4.customer!.addresses![1].defaultBillingAddress).toBe(false);
+            const otherAddress2 = result4.customer!.addresses!.filter(
+                a => a.id !== firstCustomerAddressIds[0],
+            )[0]!;
+            expect(otherAddress2.defaultShippingAddress).toBe(false);
+            expect(otherAddress2.defaultBillingAddress).toBe(false);
 
             // get the second customer's address id
             const result5 = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
@@ -258,19 +263,19 @@ describe('Customer resolver', () => {
         });
 
         it('createCustomerAddress with true defaults unsets existing defaults', async () => {
-            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,
-                    },
+            const { createCustomerAddress } = 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(createCustomerAddress, ['id'])).toEqual({
                 fullName: '',
                 company: '',
                 streetLine1: 'new default streetline',
@@ -287,21 +292,23 @@ describe('Customer resolver', () => {
                 defaultBillingAddress: true,
             });
 
-            const result2 = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
-                id: firstCustomer.id,
-            });
-            expect(result2.customer!.addresses![0].defaultShippingAddress).toBe(false);
-            expect(result2.customer!.addresses![0].defaultBillingAddress).toBe(false);
-            expect(result2.customer!.addresses![1].defaultShippingAddress).toBe(false);
-            expect(result2.customer!.addresses![1].defaultBillingAddress).toBe(false);
-            expect(result2.customer!.addresses![2].defaultShippingAddress).toBe(true);
-            expect(result2.customer!.addresses![2].defaultBillingAddress).toBe(true);
-
-            firstCustomerThirdAddressId = result2.customer!.addresses![2].id;
+            const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
+                GET_CUSTOMER,
+                {
+                    id: firstCustomer.id,
+                },
+            );
+            for (const address of customer!.addresses!) {
+                const shouldBeDefault = address.id === createCustomerAddress.id;
+                expect(address.defaultShippingAddress).toEqual(shouldBeDefault);
+                expect(address.defaultBillingAddress).toEqual(shouldBeDefault);
+            }
+
+            firstCustomerThirdAddressId = createCustomerAddress.id;
         });
 
         it('deleteCustomerAddress on default address resets defaults', async () => {
-            const result = await adminClient.query<
+            const { deleteCustomerAddress } = await adminClient.query<
                 DeleteCustomerAddress.Mutation,
                 DeleteCustomerAddress.Variables
             >(
@@ -313,16 +320,23 @@ describe('Customer resolver', () => {
                 { id: firstCustomerThirdAddressId },
             );
 
-            expect(result.deleteCustomerAddress).toBe(true);
+            expect(deleteCustomerAddress).toBe(true);
 
-            const result2 = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(GET_CUSTOMER, {
-                id: firstCustomer.id,
-            });
-            expect(result2.customer!.addresses!.length).toBe(2);
-            expect(result2.customer!.addresses![0].defaultShippingAddress).toBe(true);
-            expect(result2.customer!.addresses![0].defaultBillingAddress).toBe(true);
-            expect(result2.customer!.addresses![1].defaultShippingAddress).toBe(false);
-            expect(result2.customer!.addresses![1].defaultBillingAddress).toBe(false);
+            const { customer } = await adminClient.query<GetCustomer.Query, GetCustomer.Variables>(
+                GET_CUSTOMER,
+                {
+                    id: firstCustomer.id,
+                },
+            );
+            expect(customer!.addresses!.length).toBe(2);
+            const defaultAddress = customer!.addresses!.filter(
+                a => a.defaultBillingAddress && a.defaultShippingAddress,
+            );
+            const otherAddress = customer!.addresses!.filter(
+                a => !a.defaultBillingAddress && !a.defaultShippingAddress,
+            );
+            expect(defaultAddress.length).toBe(1);
+            expect(otherAddress.length).toBe(1);
         });
     });
 

+ 11 - 8
packages/core/e2e/default-search-plugin.e2e-spec.ts

@@ -21,6 +21,7 @@ import {
     SearchFacetValues,
     SearchGetPrices,
     SearchInput,
+    SortOrder,
     UpdateCollection,
     UpdateProduct,
     UpdateProductVariants,
@@ -50,7 +51,6 @@ describe('Default search plugin', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 1,
@@ -99,13 +99,16 @@ describe('Default search plugin', () => {
                 input: {
                     term: 'camera',
                     groupByProduct: true,
+                    sort: {
+                        name: SortOrder.ASC,
+                    },
                 },
             },
         );
         expect(result.search.items.map(i => i.productName)).toEqual([
-            'Instant Camera',
             'Camera Lens',
-            'SLR Camera',
+            'Instant Camera',
+            'Slr Camera',
         ]);
     }
 
@@ -536,7 +539,7 @@ describe('Default search plugin', () => {
                     'Instant Camera',
                     'Camera Lens',
                     'Tripod',
-                    'SLR Camera',
+                    'Slr Camera',
                 ]);
             });
 
@@ -567,7 +570,7 @@ describe('Default search plugin', () => {
                 ]);
             });
 
-            it('returns disabled field when not grouped', async () => {
+            it('returns enabled field when not grouped', async () => {
                 const result = await doAdminSearchQuery({ groupByProduct: false, take: 3 });
                 expect(result.search.items.map(pick(['productVariantId', 'enabled']))).toEqual([
                     { productVariantId: 'T_1', enabled: true },
@@ -576,7 +579,7 @@ describe('Default search plugin', () => {
                 ]);
             });
 
-            it('when grouped, disabled is false if at least one variant is enabled', async () => {
+            it('when grouped, enabled is true if at least one variant is enabled', async () => {
                 await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
                     UPDATE_PRODUCT_VARIANTS,
                     {
@@ -592,7 +595,7 @@ describe('Default search plugin', () => {
                 ]);
             });
 
-            it('when grouped, disabled is true if all variants are disabled', async () => {
+            it('when grouped, enabled is false if all variants are disabled', async () => {
                 await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
                     UPDATE_PRODUCT_VARIANTS,
                     {
@@ -608,7 +611,7 @@ describe('Default search plugin', () => {
                 ]);
             });
 
-            it('when grouped, disabled is true product is disabled', async () => {
+            it('when grouped, enabled is false if product is disabled', async () => {
                 await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
                     input: {
                         id: 'T_3',

+ 0 - 1
packages/core/e2e/entity-id-strategy.e2e-spec.ts

@@ -23,7 +23,6 @@ describe('EntityIdStrategy', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 1,

+ 0 - 1
packages/core/e2e/entity-uuid-strategy.e2e-spec.ts

@@ -25,7 +25,6 @@ describe('UuidIdStrategy', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 1,

+ 35 - 19
packages/core/e2e/facet.e2e-spec.ts

@@ -1,3 +1,4 @@
+import { pick } from '@vendure/common/lib/pick';
 import { createTestEnvironment } from '@vendure/testing';
 import gql from 'graphql-tag';
 import path from 'path';
@@ -42,7 +43,6 @@ describe('Facet resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 1,
@@ -85,25 +85,41 @@ describe('Facet resolver', () => {
     });
 
     it('createFacetValues', async () => {
-        const result = await adminClient.query<CreateFacetValues.Mutation, CreateFacetValues.Variables>(
-            CREATE_FACET_VALUES,
-            {
-                input: [
-                    {
-                        facetId: speakerTypeFacet.id,
-                        code: 'pc',
-                        translations: [{ languageCode: LanguageCode.en, name: 'PC Speakers' }],
-                    },
-                    {
-                        facetId: speakerTypeFacet.id,
-                        code: 'hi-fi',
-                        translations: [{ languageCode: LanguageCode.en, name: 'Hi Fi Speakers' }],
-                    },
-                ],
-            },
-        );
+        const { createFacetValues } = await adminClient.query<
+            CreateFacetValues.Mutation,
+            CreateFacetValues.Variables
+        >(CREATE_FACET_VALUES, {
+            input: [
+                {
+                    facetId: speakerTypeFacet.id,
+                    code: 'pc',
+                    translations: [{ languageCode: LanguageCode.en, name: 'PC Speakers' }],
+                },
+                {
+                    facetId: speakerTypeFacet.id,
+                    code: 'hi-fi',
+                    translations: [{ languageCode: LanguageCode.en, name: 'Hi Fi Speakers' }],
+                },
+            ],
+        });
 
-        expect(result.createFacetValues).toMatchSnapshot();
+        expect(createFacetValues.length).toBe(2);
+        expect(pick(createFacetValues[0], ['code', 'facet', 'name'])).toEqual({
+            code: 'pc',
+            facet: {
+                id: 'T_2',
+                name: 'Speaker Category',
+            },
+            name: 'PC Speakers',
+        });
+        expect(pick(createFacetValues[1], ['code', 'facet', 'name'])).toEqual({
+            code: 'hi-fi',
+            facet: {
+                id: 'T_2',
+                name: 'Speaker Category',
+            },
+            name: 'Hi Fi Speakers',
+        });
     });
 
     it('updateFacetValues', async () => {

+ 35 - 35
packages/core/e2e/fixtures/e2e-products-full.csv

@@ -1,35 +1,35 @@
-name               , slug               , description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   , assets                             , facets                                  , optionGroups      , optionValues        , sku          , price   , taxCategory , stockOnHand , trackInventory , variantAssets , variantFacets
-Laptop             , laptop             , "Now equipped with seventh-generation Intel Core processors, Laptop is snappier than ever. From daily tasks like launching apps and opening files to more advanced computing, you can power through your day thanks to faster SSDs and Turbo Boost processing up to 3.6GHz."                                                                                                                                                                                                                  , derick-david-409858-unsplash.jpg   , category:electronics|category:computers , "screen size|RAM" , "13 inch|8GB"       , L2201308     , 1299.00 , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , "15 inch|8GB"       , L2201508     , 1399.00 , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , "13 inch|16GB"      , L2201316     , 2199.00 , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , "15 inch|16GB"      , L2201516     , 2299.00 , standard    , 100         , false          ,               ,
-Curvy Monitor      , curvy-monitor      , "Discover a truly immersive viewing experience with this monitor curved more deeply than any other. Wrapping around your field of vision the 1,800 R screencreates a wider field of view, enhances depth perception, and minimises peripheral distractions to draw you deeper in to your content."                                                                                                                                                                                            , alexandru-acea-686569-unsplash.jpg , category:electronics|category:computers , monitor size      , 24 inch             , C24F390      , 143.74  , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , 27 inch             , C27F390      , 169.94  , standard    , 100         , false          ,               ,
-Gaming PC          , gaming-pc          , "This pc is optimised for gaming, and is also VR ready. The Intel Core-i7 CPU and High Performance GPU give the computer the raw power it needs to function at a high level."                                                                                                                                                                                                                                                                                                                 , florian-olivo-1166419-unsplash.jpg , category:electronics|category:computers , "cpu|HDD"         , "i7-8700|240GB SSD" , CGS480VR1063 , 1087.20 , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , "R7-2700|240GB SSD" , CGS480VR1064 , 1099.95 , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , "i7-8700|120GB SSD" , CGS480VR1065 , 931.20  , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , "R7-2700|120GB SSD" , CGS480VR1066 , 949.20  , standard    , 100         , false          ,               ,
-Hard Drive         , hard-drive         , "Boost your PC storage with this internal hard drive, designed just for desktop and all-in-one PCs."                                                                                                                                                                                                                                                                                                                                                                                          , vincent-botta-736919-unsplash.jpg  , category:electronics|category:computers , "HDD capacity"    , 1TB                 , IHD455T1     , 37.99   , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , 2TB                 , IHD455T2     , 53.74   , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , 3TB                 , IHD455T3     , 78.96   , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , 4TB                 , IHD455T4     , 92.99   , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , 6TB                 , IHD455T6     , 134.35  , standard    , 100         , false          ,               ,
-Clacky Keyboard    , clacky-keyboard    , "Let all your colleagues know that you are typing on this exclusive, colorful klicky-klacky keyboard. Huge travel on each keypress ensures maximum klack on each and every keystroke."                                                                                                                                                                                                                                                                                                        ,                                    , category:electronics|category:computers ,                   ,                     , A4TKLA45535  , 74.89   , standard    , 100         , false          ,               ,
-USB Cable          , usb-cable          , "Solid conductors eliminate strand-interaction distortion and reduce jitter. As the surface is made of high-purity silver, the performance is very close to that of a solid silver cable, but priced much closer to solid copper cable."                                                                                                                                                                                                                                                      ,                                    , category:electronics|category:computers ,                   ,                     , USBCIN01.5MI , 69.00   , standard    , 100         , false          ,               ,
-Instant Camera     , instant-camera     , "With its nostalgic design and simple point-and-shoot functionality, the Instant Camera is the perfect pick to get started with instant photography."                                                                                                                                                                                                                                                                                                                                         ,                                    , category:electronics|category:photo     ,                   ,                     , IC22MWDD     , 174.99  , standard    , 100         , false          ,               ,
-Camera Lens        , camera-lens        , "This lens is a Di type lens using an optical system with improved multi-coating designed to function with digital SLR cameras as well as film cameras."                                                                                                                                                                                                                                                                                                                                      ,                                    , category:electronics|category:photo     ,                   ,                     , B0012UUP02   , 104.00  , standard    , 100         , false          ,               ,
-Tripod             , tripod             , "Capture vivid, professional-style photographs with help from this lightweight tripod. The adjustable-height tripod makes it easy to achieve reliable stability and score just the right angle when going after that award-winning shot."                                                                                                                                                                                                                                                     ,                                    , category:electronics|category:photo     ,                   ,                     , B00XI87KV8   , 14.98   , standard    , 100         , false          ,               ,
-SLR Camera         , slr-camera         , "Retro styled, portable in size and built around a powerful 24-megapixel APS-C CMOS sensor, this digital camera is the ideal companion for creative everyday photography. Packed full of high spec features such as an advanced hybrid autofocus system able to keep pace with even the most active subjects, a speedy 6fps continuous-shooting mode, high-resolution electronic viewfinder and intuitive swivelling touchscreen, it brings professional image making into everyone’s grasp." ,                                    , category:electronics|category:photo     ,                   ,                     , B07D75V44S   , 521.00  , standard    , 100         , false          ,               ,
-Road Bike          , road-bike          , "Featuring a full carbon chassis - complete with cyclocross-specific carbon fork - and a component setup geared for hard use on the race circuit, it's got the low weight, exceptional efficiency and brilliant handling you'll need to stay at the front of the pack."                                                                                                                                                                                                                       ,                                    , category:sports equipment               ,                   ,                     , RB000844334  , 2499.00 , standard    , 100         , false          ,               ,
-Skipping Rope      , skipping-rope      , "When you're working out you need a quality rope that doesn't tangle at every couple of jumps and with this sipping rope you won't have this problem."                                                                                                                                                                                                                                                                                                                                        ,                                    , category:sports equipment               ,                   ,                     , B07CNGXVXT   , 7.99    , standard    , 100         , false          ,               ,
-Boxing Gloves      , boxing-gloves      , "Training gloves designed for optimum training. Our gloves promote proper punching technique because they are conformed to the natural shape of your fist. Dense, innovative two-layer foam provides better shock absorbency and full padding on the front, back and wrist to promote proper punching technique."                                                                                                                                                                             ,                                    , category:sports equipment               ,                   ,                     , B000ZYLPPU   , 33.04   , standard    , 100         , false          ,               ,
-Tent               , tent               , "With tons of space inside (for max. 4 persons), full head height throughout the entire tent and an unusual and striking shape, this tent offers you everything you need."                                                                                                                                                                                                                                                                                                                    ,                                    , category:sports equipment               ,                   ,                     , 2000023510   , 214.93  , standard    , 100         , false          ,               ,
-Cruiser Skateboard , cruiser-skateboard , "Based on the 1970s iconic shape, but made to a larger 69cm size, with updated, quality component, these skateboards are great for beginners to learn the foot spacing required, and are perfect for all-day cruising."                                                                                                                                                                                                                                                                       ,                                    , category:sports equipment               ,                   ,                     , 799872520    , 24.99   , standard    , 100         , false          ,               ,
-Football           , football           , "This football features high-contrast graphics for high-visibility during play, while its machine-stitched tpu casing offers consistent performance."                                                                                                                                                                                                                                                                                                                                         ,                                    , category:sports equipment               ,                   ,                     , SC3137-056   , 57.07   , standard    , 100         , false          ,               ,
-Running Shoe       , running-shoe       , "With its ultra-light, uber-responsive magic foam and a carbon fiber plate that feels like it’s propelling you forward, the Running Shoe is ready to push you to victories both large and small"                                                                                                                                                                                                                                                                                              ,                                    , category:sports equipment               , shoe size         , Size 40             , RS0040       , 99.99   , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , Size 42             , RS0042       , 99.99   , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , Size 44             , RS0044       , 99.99   , standard    , 100         , false          ,               ,
-                   ,                    ,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ,                                    ,                                         ,                   , Size 46             , RS0046       , 99.99   , standard    , 100         , false          ,               ,
-Spiky Cactus       , spiky-cactus       , "A spiky yet elegant house cactus - perfect for the home or office. Origin and habitat: Probably native only to the Andes of Peru"                                                                                                                                                                                                                                                                                                                                                            ,                                    , category:home & garden|category:plants  ,                   ,                     , SC011001     , 15.50   , standard    , 100         , false          ,               ,
-Orchid             , orchid             , "Gloriously elegant. It can go along with any interior as it is a neutral color and the most popular Phalaenopsis overall. 2 to 3 foot stems host large white flowers that can last for over 2 months."                                                                                                                                                                                                                                                                                       ,                                    , category:home & garden|category:plants  ,                   ,                     , ROR00221     , 65.00   , standard    , 100         , false          ,               ,
-Bonsai Tree        , bonsai-tree        , "Excellent semi-evergreen bonsai. Indoors or out but needs some winter protection. All trees sent will leave the nursery in excellent condition and will be of equal quality or better than the photograph shown."                                                                                                                                                                                                                                                                            ,                                    , category:home & garden|category:plants  ,                   ,                     , B01MXFLUSV   , 19.99   , standard    , 100         , false          ,               ,
+name,slug,description,assets,facets,optionGroups,optionValues,sku,price,taxCategory,stockOnHand,trackInventory,variantAssets,variantFacets
+Laptop,laptop,"Now equipped with seventh-generation Intel Core processors, Laptop is snappier than ever. From daily tasks like launching apps and opening files to more advanced computing, you can power through your day thanks to faster SSDs and Turbo Boost processing up to 3.6GHz.",derick-david-409858-unsplash.jpg,category:electronics|category:computers,screen size|RAM,13 inch|8GB,L2201308,1299.00,standard,100,false,,
+,,,,,,15 inch|8GB,L2201508,1399.00,standard,100,false,,
+,,,,,,13 inch|16GB,L2201316,2199.00,standard,100,false,,
+,,,,,,15 inch|16GB,L2201516,2299.00,standard,100,false,,
+Curvy Monitor,curvy-monitor,"Discover a truly immersive viewing experience with this monitor curved more deeply than any other. Wrapping around your field of vision the 1,800 R screencreates a wider field of view, enhances depth perception, and minimises peripheral distractions to draw you deeper in to your content.",alexandru-acea-686569-unsplash.jpg,category:electronics|category:computers,monitor size,24 inch,C24F390,143.74,standard,100,false,,
+,,,,,,27 inch,C27F390,169.94,standard,100,false,,
+Gaming PC,gaming-pc,"This pc is optimised for gaming, and is also VR ready. The Intel Core-i7 CPU and High Performance GPU give the computer the raw power it needs to function at a high level.",florian-olivo-1166419-unsplash.jpg,category:electronics|category:computers,cpu|HDD,i7-8700|240GB SSD,CGS480VR1063,1087.20,standard,100,false,,
+,,,,,,R7-2700|240GB SSD,CGS480VR1064,1099.95,standard,100,false,,
+,,,,,,i7-8700|120GB SSD,CGS480VR1065,931.20,standard,100,false,,
+,,,,,,R7-2700|120GB SSD,CGS480VR1066,949.20,standard,100,false,,
+Hard Drive,hard-drive,"Boost your PC storage with this internal hard drive, designed just for desktop and all-in-one PCs.",vincent-botta-736919-unsplash.jpg,category:electronics|category:computers,HDD capacity,1TB,IHD455T1,37.99,standard,100,false,,
+,,,,,,2TB,IHD455T2,53.74,standard,100,false,,
+,,,,,,3TB,IHD455T3,78.96,standard,100,false,,
+,,,,,,4TB,IHD455T4,92.99,standard,100,false,,
+,,,,,,6TB,IHD455T6,134.35,standard,100,false,,
+Clacky Keyboard,clacky-keyboard,"Let all your colleagues know that you are typing on this exclusive, colorful klicky-klacky keyboard. Huge travel on each keypress ensures maximum klack on each and every keystroke.",,category:electronics|category:computers,,,A4TKLA45535,74.89,standard,100,false,,
+USB Cable,usb-cable,"Solid conductors eliminate strand-interaction distortion and reduce jitter. As the surface is made of high-purity silver, the performance is very close to that of a solid silver cable, but priced much closer to solid copper cable.",,category:electronics|category:computers,,,USBCIN01.5MI,69.00,standard,100,false,,
+Instant Camera,instant-camera,"With its nostalgic design and simple point-and-shoot functionality, the Instant Camera is the perfect pick to get started with instant photography.",,category:electronics|category:photo,,,IC22MWDD,174.99,standard,100,false,,
+Camera Lens,camera-lens,This lens is a Di type lens using an optical system with improved multi-coating designed to function with digital SLR cameras as well as film cameras.,,category:electronics|category:photo,,,B0012UUP02,104.00,standard,100,false,,
+Tripod,tripod,"Capture vivid, professional-style photographs with help from this lightweight tripod. The adjustable-height tripod makes it easy to achieve reliable stability and score just the right angle when going after that award-winning shot.",,category:electronics|category:photo,,,B00XI87KV8,14.98,standard,100,false,,
+Slr Camera,slr-camera,"Retro styled, portable in size and built around a powerful 24-megapixel APS-C CMOS sensor, this digital camera is the ideal companion for creative everyday photography. Packed full of high spec features such as an advanced hybrid autofocus system able to keep pace with even the most active subjects, a speedy 6fps continuous-shooting mode, high-resolution electronic viewfinder and intuitive swivelling touchscreen, it brings professional image making into everyone’s grasp.",,category:electronics|category:photo,,,B07D75V44S,521.00,standard,100,false,,
+Road Bike,road-bike,"Featuring a full carbon chassis - complete with cyclocross-specific carbon fork - and a component setup geared for hard use on the race circuit, it's got the low weight, exceptional efficiency and brilliant handling you'll need to stay at the front of the pack.",,category:sports equipment,,,RB000844334,2499.00,standard,100,false,,
+Skipping Rope,skipping-rope,When you're working out you need a quality rope that doesn't tangle at every couple of jumps and with this sipping rope you won't have this problem.,,category:sports equipment,,,B07CNGXVXT,7.99,standard,100,false,,
+Boxing Gloves,boxing-gloves,"Training gloves designed for optimum training. Our gloves promote proper punching technique because they are conformed to the natural shape of your fist. Dense, innovative two-layer foam provides better shock absorbency and full padding on the front, back and wrist to promote proper punching technique.",,category:sports equipment,,,B000ZYLPPU,33.04,standard,100,false,,
+Tent,tent,"With tons of space inside (for max. 4 persons), full head height throughout the entire tent and an unusual and striking shape, this tent offers you everything you need.",,category:sports equipment,,,2000023510,214.93,standard,100,false,,
+Cruiser Skateboard,cruiser-skateboard,"Based on the 1970s iconic shape, but made to a larger 69cm size, with updated, quality component, these skateboards are great for beginners to learn the foot spacing required, and are perfect for all-day cruising.",,category:sports equipment,,,799872520,24.99,standard,100,false,,
+Football,football,"This football features high-contrast graphics for high-visibility during play, while its machine-stitched tpu casing offers consistent performance.",,category:sports equipment,,,SC3137-056,57.07,standard,100,false,,
+Running Shoe,running-shoe,"With its ultra-light, uber-responsive magic foam and a carbon fiber plate that feels like it’s propelling you forward, the Running Shoe is ready to push you to victories both large and small",,category:sports equipment,shoe size,Size 40,RS0040,99.99,standard,100,false,,
+,,,,,,Size 42,RS0042,99.99,standard,100,false,,
+,,,,,,Size 44,RS0044,99.99,standard,100,false,,
+,,,,,,Size 46,RS0046,99.99,standard,100,false,,
+Spiky Cactus,spiky-cactus,A spiky yet elegant house cactus - perfect for the home or office. Origin and habitat: Probably native only to the Andes of Peru,,category:home & garden|category:plants,,,SC011001,15.50,standard,100,false,,
+Orchid,orchid,Gloriously elegant. It can go along with any interior as it is a neutral color and the most popular Phalaenopsis overall. 2 to 3 foot stems host large white flowers that can last for over 2 months.,,category:home & garden|category:plants,,,ROR00221,65.00,standard,100,false,,
+Bonsai Tree,bonsai-tree,Excellent semi-evergreen bonsai. Indoors or out but needs some winter protection. All trees sent will leave the nursery in excellent condition and will be of equal quality or better than the photograph shown.,,category:home & garden|category:plants,,,B01MXFLUSV,19.99,standard,100,false,,

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

@@ -3498,7 +3498,7 @@ export type GetProductsWithVariantIdsQuery = { __typename?: 'Query' } & {
     products: { __typename?: 'ProductList' } & {
         items: Array<
             { __typename?: 'Product' } & Pick<Product, 'id' | 'name'> & {
-                    variants: Array<{ __typename?: 'ProductVariant' } & Pick<ProductVariant, 'id'>>;
+                    variants: Array<{ __typename?: 'ProductVariant' } & Pick<ProductVariant, 'id' | 'name'>>;
                 }
         >;
     };
@@ -5091,15 +5091,6 @@ export type UpdateStockMutation = { __typename?: 'Mutation' } & {
     updateProductVariants: Array<Maybe<{ __typename?: 'ProductVariant' } & VariantWithStockFragment>>;
 };
 
-export type TaxRateFragment = { __typename?: 'TaxRate' } & Pick<
-    TaxRate,
-    'id' | 'name' | 'value' | 'enabled'
-> & {
-        zone: { __typename?: 'Zone' } & Pick<Zone, 'id' | 'name'>;
-        category: { __typename?: 'TaxCategory' } & Pick<TaxCategory, 'id' | 'name'>;
-        customerGroup: Maybe<{ __typename?: 'CustomerGroup' } & Pick<CustomerGroup, 'id' | 'name'>>;
-    };
-
 export type GetTaxRatesQueryVariables = {};
 
 export type GetTaxRatesQuery = { __typename?: 'Query' } & {
@@ -6325,13 +6316,6 @@ export namespace UpdateStock {
     export type UpdateProductVariants = VariantWithStockFragment;
 }
 
-export namespace TaxRate {
-    export type Fragment = TaxRateFragment;
-    export type Zone = TaxRateFragment['zone'];
-    export type Category = TaxRateFragment['category'];
-    export type CustomerGroup = NonNullable<TaxRateFragment['customerGroup']>;
-}
-
 export namespace GetTaxRates {
     export type Variables = GetTaxRatesQueryVariables;
     export type Query = GetTaxRatesQuery;

+ 42 - 2
packages/core/e2e/import.e2e-spec.ts

@@ -1,3 +1,4 @@
+import { omit } from '@vendure/common/lib/omit';
 import { createTestEnvironment } from '@vendure/testing';
 import gql from 'graphql-tag';
 import path from 'path';
@@ -16,7 +17,6 @@ describe('Import resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-empty.csv'),
             customerCount: 0,
@@ -160,6 +160,46 @@ describe('Import resolver', () => {
         );
 
         expect(productResult.products.totalItems).toBe(4);
-        expect(productResult.products.items).toMatchSnapshot();
+
+        const paperStretcher = productResult.products.items.find(
+            (p: any) => p.name === 'Perfect Paper Stretcher',
+        );
+        const easel = productResult.products.items.find((p: any) => p.name === 'Mabef M/02 Studio Easel');
+        const pencils = productResult.products.items.find((p: any) => p.name === 'Giotto Mega Pencils');
+        const smock = productResult.products.items.find((p: any) => p.name === 'Artists Smock');
+
+        // Omit FacetValues & options due to variations in the ordering between different DB engines
+        expect(omit(paperStretcher, ['facetValues', 'options'], true)).toMatchSnapshot();
+        expect(omit(easel, ['facetValues', 'options'], true)).toMatchSnapshot();
+        expect(omit(pencils, ['facetValues', 'options'], true)).toMatchSnapshot();
+        expect(omit(smock, ['facetValues', 'options'], true)).toMatchSnapshot();
+
+        const byName = (e: { name: string }) => e.name;
+        const byCode = (e: { code: string }) => e.code;
+
+        expect(paperStretcher.facetValues).toEqual([]);
+        expect(easel.facetValues).toEqual([]);
+        expect(pencils.facetValues).toEqual([]);
+        expect(smock.facetValues.map(byName).sort()).toEqual(['Denim', 'clothes']);
+
+        expect(paperStretcher.variants[0].facetValues.map(byName).sort()).toEqual(['Accessory', 'KB']);
+        expect(paperStretcher.variants[1].facetValues.map(byName).sort()).toEqual(['Accessory', 'KB']);
+        expect(paperStretcher.variants[2].facetValues.map(byName).sort()).toEqual(['Accessory', 'KB']);
+        expect(paperStretcher.variants[0].options.map(byCode).sort()).toEqual(['half-imperial']);
+        expect(paperStretcher.variants[1].options.map(byCode).sort()).toEqual(['quarter-imperial']);
+        expect(paperStretcher.variants[2].options.map(byCode).sort()).toEqual(['full-imperial']);
+        expect(easel.variants[0].facetValues.map(byName).sort()).toEqual(['Easel', 'Mabef']);
+        expect(pencils.variants[0].facetValues.map(byName).sort()).toEqual(['Xmas Sale']);
+        expect(pencils.variants[1].facetValues.map(byName).sort()).toEqual(['Xmas Sale']);
+        expect(pencils.variants[0].options.map(byCode).sort()).toEqual(['box-of-8']);
+        expect(pencils.variants[1].options.map(byCode).sort()).toEqual(['box-of-12']);
+        expect(smock.variants[0].facetValues.map(byName).sort()).toEqual([]);
+        expect(smock.variants[1].facetValues.map(byName).sort()).toEqual([]);
+        expect(smock.variants[2].facetValues.map(byName).sort()).toEqual([]);
+        expect(smock.variants[3].facetValues.map(byName).sort()).toEqual([]);
+        expect(smock.variants[0].options.map(byCode).sort()).toEqual(['beige', 'small']);
+        expect(smock.variants[1].options.map(byCode).sort()).toEqual(['beige', 'large']);
+        expect(smock.variants[2].options.map(byCode).sort()).toEqual(['navy', 'small']);
+        expect(smock.variants[3].options.map(byCode).sort()).toEqual(['large', 'navy']);
     }, 20000);
 });

+ 0 - 1
packages/core/e2e/localization.e2e-spec.ts

@@ -20,7 +20,6 @@ describe('Role resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,

+ 0 - 1
packages/core/e2e/order-promotion.e2e-spec.ts

@@ -67,7 +67,6 @@ describe('Promotions applied to Orders', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-promotions.csv'),
             customerCount: 2,

+ 12 - 10
packages/core/e2e/order.e2e-spec.ts

@@ -43,7 +43,7 @@ import {
 } from './graphql/shared-definitions';
 import { ADD_ITEM_TO_ORDER, GET_ACTIVE_ORDER } from './graphql/shop-definitions';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
-import { addPaymentToOrder, proceedToArrangingPayment } from './utils/test-order-utils';
+import { addPaymentToOrder, proceedToArrangingPayment, sortById } from './utils/test-order-utils';
 
 describe('Orders resolver', () => {
     const { server, adminClient, shopClient } = createTestEnvironment({
@@ -61,7 +61,6 @@ describe('Orders resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 3,
@@ -314,10 +313,14 @@ describe('Orders resolver', () => {
             });
 
             expect(result.order!.state).toBe('PartiallyFulfilled');
+
             expect(result.order!.lines[0].items[0].fulfillment!.id).toBe(fulfillOrder!.id);
-            expect(result.order!.lines[1].items[2].fulfillment!.id).toBe(fulfillOrder!.id);
-            expect(result.order!.lines[1].items[1].fulfillment).toBeNull();
-            expect(result.order!.lines[1].items[0].fulfillment).toBeNull();
+            expect(
+                result.order!.lines[1].items.filter(
+                    i => i.fulfillment && i.fulfillment.id === fulfillOrder.id,
+                ).length,
+            ).toBe(1);
+            expect(result.order!.lines[1].items.filter(i => i.fulfillment == null).length).toBe(2);
         });
 
         it('creates a second partial fulfillment', async () => {
@@ -341,11 +344,9 @@ describe('Orders resolver', () => {
             const result = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
                 id: 'T_2',
             });
-            // expect(result.order!.lines).toEqual({});
             expect(result.order!.state).toBe('PartiallyFulfilled');
-            expect(result.order!.lines[1].items[2].fulfillment).not.toBeNull();
-            expect(result.order!.lines[1].items[1].fulfillment).not.toBeNull();
-            expect(result.order!.lines[1].items[0].fulfillment).toBeNull();
+            expect(result.order!.lines[1].items.filter(i => i.fulfillment != null).length).toBe(2);
+            expect(result.order!.lines[1].items.filter(i => i.fulfillment == null).length).toBe(1);
         });
 
         it(
@@ -382,6 +383,7 @@ describe('Orders resolver', () => {
                 (items, line) => [...items, ...line.items],
                 [] as OrderItemFragment[],
             );
+            const unfulfilledItem = order!.lines[1].items.find(i => i.fulfillment == null)!;
 
             const { fulfillOrder } = await adminClient.query<
                 CreateFulfillment.Mutation,
@@ -401,7 +403,7 @@ describe('Orders resolver', () => {
 
             expect(fulfillOrder!.method).toBe('Test3');
             expect(fulfillOrder!.trackingCode).toBe('333');
-            expect(fulfillOrder!.orderItems).toEqual([{ id: orderItems[1].id }]);
+            expect(fulfillOrder!.orderItems).toEqual([{ id: unfulfilledItem.id }]);
 
             const result = await adminClient.query<GetOrder.Query, GetOrder.Variables>(GET_ORDER, {
                 id: 'T_2',

+ 0 - 1
packages/core/e2e/plugin.e2e-spec.ts

@@ -42,7 +42,6 @@ describe('Plugins', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 1,

+ 0 - 1
packages/core/e2e/product-option.e2e-spec.ts

@@ -25,7 +25,6 @@ describe('ProductOption resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             customerCount: 1,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),

+ 49 - 27
packages/core/e2e/product.e2e-spec.ts

@@ -40,6 +40,7 @@ import {
     UPDATE_PRODUCT_VARIANTS,
 } from './graphql/shared-definitions';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
+import { sortById } from './utils/test-order-utils';
 
 // tslint:disable:no-non-null-assertion
 
@@ -48,7 +49,6 @@ describe('Product resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             customerCount: 1,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
@@ -151,8 +151,8 @@ describe('Product resolver', () => {
                 'Orchid',
                 'Road Bike',
                 'Running Shoe',
-                'SLR Camera',
                 'Skipping Rope',
+                'Slr Camera',
                 'Spiky Cactus',
                 'Tent',
                 'Tripod',
@@ -205,8 +205,8 @@ describe('Product resolver', () => {
                 'Orchid',
                 'Road Bike',
                 'Running Shoe',
-                'SLR Camera',
                 'Skipping Rope',
+                'Slr Camera',
                 'Spiky Cactus',
                 'Tent',
                 'Tripod',
@@ -338,7 +338,11 @@ describe('Product resolver', () => {
                     },
                 },
             );
-            expect(result.createProduct).toMatchSnapshot();
+            expect(omit(result.createProduct, ['translations'])).toMatchSnapshot();
+            expect(result.createProduct.translations.map(t => t.description).sort()).toEqual([
+                'A baked potato',
+                'Eine baked Erdapfel',
+            ]);
             newProduct = result.createProduct;
         });
 
@@ -394,7 +398,10 @@ describe('Product resolver', () => {
                     },
                 },
             );
-            expect(result.updateProduct).toMatchSnapshot();
+            expect(result.updateProduct.translations.map(t => t.description).sort()).toEqual([
+                'A blob of mashed potato',
+                'Eine blob von gemashed Erdapfel',
+            ]);
         });
 
         it('slug is normalized to be url-safe', async () => {
@@ -490,9 +497,15 @@ describe('Product resolver', () => {
                 },
             );
             expect(result.updateProduct.translations.length).toBe(2);
-            expect(result.updateProduct.translations[0].name).toBe('de Mashed Potato');
-            expect(result.updateProduct.translations[1].name).toBe('en Very Mashed Potato');
-            expect(result.updateProduct.translations[1].description).toBe('Possibly the final baked potato');
+            expect(
+                result.updateProduct.translations.find(t => t.languageCode === LanguageCode.de)!.name,
+            ).toBe('de Mashed Potato');
+            expect(
+                result.updateProduct.translations.find(t => t.languageCode === LanguageCode.en)!.name,
+            ).toBe('en Very Mashed Potato');
+            expect(
+                result.updateProduct.translations.find(t => t.languageCode === LanguageCode.en)!.description,
+            ).toBe('Possibly the final baked potato');
         });
 
         it('updateProduct adds Assets to a product and sets featured asset', async () => {
@@ -777,10 +790,12 @@ describe('Product resolver', () => {
                     ],
                 });
                 expect(createProductVariants[0]!.name).toBe('Variant 1');
-                expect(createProductVariants[0]!.options.map(pick(['id']))).toEqual([
-                    { id: optionGroup2.options[0].id },
-                    { id: optionGroup3.options[0].id },
-                ]);
+                expect(createProductVariants[0]!.options.map(pick(['id']))).toContainEqual({
+                    id: optionGroup2.options[0].id,
+                });
+                expect(createProductVariants[0]!.options.map(pick(['id']))).toContainEqual({
+                    id: optionGroup3.options[0].id,
+                });
             });
 
             it('createProductVariants adds multiple variants at once', async () => {
@@ -803,16 +818,13 @@ describe('Product resolver', () => {
                         },
                     ],
                 });
-                expect(createProductVariants[0]!.name).toBe('Variant 2');
-                expect(createProductVariants[1]!.name).toBe('Variant 3');
-                expect(createProductVariants[0]!.options.map(pick(['id']))).toEqual([
-                    { id: optionGroup2.options[1].id },
-                    { id: optionGroup3.options[0].id },
-                ]);
-                expect(createProductVariants[1]!.options.map(pick(['id']))).toEqual([
-                    { id: optionGroup2.options[1].id },
-                    { id: optionGroup3.options[1].id },
-                ]);
+                const variant2 = createProductVariants.find(v => v!.name === 'Variant 2')!;
+                const variant3 = createProductVariants.find(v => v!.name === 'Variant 3')!;
+                expect(variant2.options.map(pick(['id']))).toContainEqual({ id: optionGroup2.options[1].id });
+                expect(variant2.options.map(pick(['id']))).toContainEqual({ id: optionGroup3.options[0].id });
+                expect(variant3.options.map(pick(['id']))).toContainEqual({ id: optionGroup2.options[1].id });
+                expect(variant3.options.map(pick(['id']))).toContainEqual({ id: optionGroup3.options[1].id });
+
                 variants = createProductVariants.filter(notNullOrUndefined);
             });
 
@@ -832,7 +844,7 @@ describe('Product resolver', () => {
                             ],
                         },
                     );
-                }, 'A ProductVariant already exists with the options: 16gb, 24-inch'),
+                }, 'A ProductVariant already exists with the options:'),
             );
 
             it('updateProductVariants updates variants', async () => {
@@ -978,13 +990,14 @@ describe('Product resolver', () => {
                 >(GET_PRODUCT_WITH_VARIANTS, {
                     id: newProduct.id,
                 });
-                expect(result1.product!.variants.map(v => v.id)).toEqual(['T_35', 'T_36', 'T_37']);
+                const sortedVariantIds = result1.product!.variants.map(v => v.id).sort();
+                expect(sortedVariantIds).toEqual(['T_35', 'T_36', 'T_37']);
 
                 const { deleteProductVariant } = await adminClient.query<
                     DeleteProductVariant.Mutation,
                     DeleteProductVariant.Variables
                 >(DELETE_PRODUCT_VARIANT, {
-                    id: result1.product!.variants[0].id,
+                    id: sortedVariantIds[0],
                 });
 
                 expect(deleteProductVariant.result).toBe(DeletionResult.DELETED);
@@ -995,7 +1008,7 @@ describe('Product resolver', () => {
                 >(GET_PRODUCT_WITH_VARIANTS, {
                     id: newProduct.id,
                 });
-                expect(result2.product!.variants.map(v => v.id)).toEqual(['T_36', 'T_37']);
+                expect(result2.product!.variants.map(v => v.id).sort()).toEqual(['T_36', 'T_37']);
             });
         });
     });
@@ -1005,7 +1018,16 @@ describe('Product resolver', () => {
         let productToDelete: GetProductList.Items;
 
         beforeAll(async () => {
-            const result = await adminClient.query<GetProductList.Query>(GET_PRODUCT_LIST);
+            const result = await adminClient.query<GetProductList.Query, GetProductList.Variables>(
+                GET_PRODUCT_LIST,
+                {
+                    options: {
+                        sort: {
+                            id: SortOrder.ASC,
+                        },
+                    },
+                },
+            );
             allProducts = result.products.items;
         });
 

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

@@ -50,7 +50,6 @@ describe('Promotion resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,

+ 2 - 2
packages/core/e2e/role.e2e-spec.ts

@@ -27,6 +27,7 @@ import {
 } from './graphql/generated-e2e-admin-types';
 import { CREATE_CHANNEL, CREATE_ROLE } from './graphql/shared-definitions';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
+import { sortById } from './utils/test-order-utils';
 
 describe('Role resolver', () => {
     const { server, adminClient } = createTestEnvironment(testConfig);
@@ -35,7 +36,6 @@ describe('Role resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,
@@ -339,7 +339,7 @@ describe('Role resolver', () => {
                 },
             );
 
-            expect(updateRole.channels).toEqual([
+            expect(updateRole.channels.sort(sortById)).toEqual([
                 {
                     code: DEFAULT_CHANNEL_CODE,
                     id: 'T_1',

+ 0 - 1
packages/core/e2e/shipping-method.e2e-spec.ts

@@ -54,7 +54,6 @@ describe('ShippingMethod resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 1,

+ 0 - 4
packages/core/e2e/shop-auth.e2e-spec.ts

@@ -85,7 +85,6 @@ describe('Shop auth & accounts', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 2,
@@ -545,7 +544,6 @@ describe('Expiring tokens', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,
@@ -634,7 +632,6 @@ describe('Registration without email verification', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,
@@ -708,7 +705,6 @@ describe('Updating email address without email verification', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,

+ 6 - 2
packages/core/e2e/shop-catalog.e2e-spec.ts

@@ -39,13 +39,13 @@ import {
     UPDATE_PRODUCT_VARIANTS,
 } from './graphql/shared-definitions';
 import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
+import { awaitRunningJobs } from './utils/await-running-jobs';
 
 describe('Shop catalog', () => {
     const { server, adminClient, shopClient } = createTestEnvironment(testConfig);
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 1,
@@ -262,6 +262,7 @@ describe('Shop catalog', () => {
                 },
             });
             collection = createCollection;
+            await awaitRunningJobs(adminClient);
         });
 
         it('returns collection with variants', async () => {
@@ -287,6 +288,7 @@ describe('Shop catalog', () => {
             await adminClient.query<DisableProduct.Mutation, DisableProduct.Variables>(DISABLE_PRODUCT, {
                 id: 'T_17',
             });
+            await awaitRunningJobs(adminClient);
 
             const result = await shopClient.query<
                 GetCollectionVariants.Query,
@@ -302,13 +304,14 @@ describe('Shop catalog', () => {
             ]);
         });
 
-        it('omits variants from disabled products', async () => {
+        it('omits disabled product variants', async () => {
             await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
                 UPDATE_PRODUCT_VARIANTS,
                 {
                     input: [{ id: 'T_22', enabled: false }],
                 },
             );
+            await awaitRunningJobs(adminClient);
 
             const result = await shopClient.query<
                 GetCollectionVariants.Query,
@@ -342,6 +345,7 @@ describe('Shop catalog', () => {
                     },
                 },
             );
+            await awaitRunningJobs(adminClient);
             const result = await shopClient.query<GetCollectionList.Query>(GET_COLLECTION_LIST);
 
             expect(result.collections.items).toEqual([{ id: 'T_2', name: 'Plants' }]);

+ 0 - 1
packages/core/e2e/shop-customer.e2e-spec.ts

@@ -33,7 +33,6 @@ describe('Shop customers', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 2,

+ 0 - 1
packages/core/e2e/shop-order.e2e-spec.ts

@@ -78,7 +78,6 @@ describe('Shop orders', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-full.csv'),
             customerCount: 2,

+ 0 - 1
packages/core/e2e/stock-control.e2e-spec.ts

@@ -44,7 +44,6 @@ describe('Stock control', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-stock-control.csv'),
             customerCount: 2,

+ 2 - 31
packages/core/e2e/tax-rate.e2e-spec.ts

@@ -7,14 +7,15 @@ import path from 'path';
 import { initialData } from '../../../e2e-common/e2e-initial-data';
 import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
 
+import { TAX_RATE_FRAGMENT } from './graphql/fragments';
 import { CreateTaxRate, GetTaxRate, GetTaxRates, UpdateTaxRate } from './graphql/generated-e2e-admin-types';
+import { UPDATE_TAX_RATE } from './graphql/shared-definitions';
 
 describe('TaxRate resolver', () => {
     const { server, adminClient, shopClient } = createTestEnvironment(testConfig);
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 2,
@@ -80,27 +81,6 @@ describe('TaxRate resolver', () => {
     });
 });
 
-export const TAX_RATE_FRAGMENT = gql`
-    fragment TaxRate on TaxRate {
-        id
-        name
-        value
-        enabled
-        zone {
-            id
-            name
-        }
-        category {
-            id
-            name
-        }
-        customerGroup {
-            id
-            name
-        }
-    }
-`;
-
 export const GET_TAX_RATES_LIST = gql`
     query GetTaxRates {
         taxRates {
@@ -130,12 +110,3 @@ export const CREATE_TAX_RATE = gql`
     }
     ${TAX_RATE_FRAGMENT}
 `;
-
-export const UPDATE_TAX_RATE = gql`
-    mutation UpdateTaxRate($input: UpdateTaxRateInput!) {
-        updateTaxRate(input: $input) {
-            ...TaxRate
-        }
-    }
-    ${TAX_RATE_FRAGMENT}
-`;

+ 8 - 0
packages/core/e2e/utils/test-order-utils.ts

@@ -63,3 +63,11 @@ export async function addPaymentToOrder(
     const order = result.addPaymentToOrder!;
     return order as any;
 }
+
+/**
+ * Sorts an array of entities by the id key. Useful for compensating for the fact that different DBs
+ * return arrays in different orders.
+ */
+export function sortById<T extends { id: string | number }>(a: T, b: T): 1 | -1 {
+    return a.id < b.id ? -1 : 1;
+}

+ 0 - 1
packages/core/e2e/zone.e2e-spec.ts

@@ -30,7 +30,6 @@ describe('Facet resolver', () => {
 
     beforeAll(async () => {
         await server.init({
-            dataDir: path.join(__dirname, '__data__'),
             initialData,
             productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
             customerCount: 1,