Browse Source

test: All unit tests passing with vitest

Michael Bromley 2 years ago
parent
commit
8daa6b283d
65 changed files with 321 additions and 107 deletions
  1. 0 2
      e2e-common/vitest.config.ts
  2. 1 1
      packages/asset-server-plugin/package.json
  3. 2 0
      packages/asset-server-plugin/src/transform-image.spec.ts
  4. 1 1
      packages/common/package.json
  5. 2 0
      packages/common/src/filter-async.spec.ts
  6. 2 0
      packages/common/src/normalize-string.spec.ts
  7. 2 0
      packages/common/src/omit.spec.ts
  8. 2 0
      packages/common/src/pick.spec.ts
  9. 6 1
      packages/common/src/shared-utils.spec.ts
  10. 2 0
      packages/common/src/simple-deep-clone.spec.ts
  11. 2 0
      packages/common/src/unique.spec.ts
  12. 27 0
      packages/common/vitest.config.ts
  13. 1 1
      packages/core/package.json
  14. 2 0
      packages/core/src/api/common/id-codec.spec.ts
  15. 1 1
      packages/core/src/api/common/request-context.spec.ts
  16. 1 1
      packages/core/src/api/common/validate-custom-field-value.spec.ts
  17. 13 13
      packages/core/src/api/config/__snapshots__/graphql-custom-fields.spec.ts.snap
  18. 6 2
      packages/core/src/api/config/generate-list-options.spec.ts
  19. 3 1
      packages/core/src/api/config/generate-list-options.ts
  20. 1 0
      packages/core/src/api/config/graphql-custom-fields.spec.ts
  21. 2 2
      packages/core/src/bootstrap.ts
  22. 2 0
      packages/core/src/cache/request-context-cache.service.spec.ts
  23. 5 4
      packages/core/src/common/finite-state-machine/finite-state-machine.spec.ts
  24. 2 0
      packages/core/src/common/finite-state-machine/merge-transition-definitions.spec.ts
  25. 2 0
      packages/core/src/common/finite-state-machine/validate-transition-definition.spec.ts
  26. 4 2
      packages/core/src/common/self-refreshing-cache.spec.ts
  27. 2 0
      packages/core/src/common/utils.spec.ts
  28. 2 0
      packages/core/src/config/asset-naming-strategy/default-asset-naming-strategy.spec.ts
  29. 7 2
      packages/core/src/config/catalog/default-product-variant-price-calculation-strategy.spec.ts
  30. 25 5
      packages/core/src/config/config-helpers.ts
  31. 5 3
      packages/core/src/config/config.service.mock.ts
  32. 4 2
      packages/core/src/config/logger/default-logger.spec.ts
  33. 2 0
      packages/core/src/config/merge-config.spec.ts
  34. 2 0
      packages/core/src/config/order/merge-orders-strategy.spec.ts
  35. 2 0
      packages/core/src/config/order/use-existing-strategy.spec.ts
  36. 2 0
      packages/core/src/config/order/use-guest-if-existing-empty-strategy.spec.ts
  37. 2 0
      packages/core/src/config/order/use-guest-strategy.spec.ts
  38. 7 7
      packages/core/src/data-import/providers/import-parser/__snapshots__/import-parser.spec.ts.snap
  39. 7 1
      packages/core/src/data-import/providers/import-parser/import-parser.spec.ts
  40. 6 0
      packages/core/src/entity/order/order.entity.spec.ts
  41. 9 2
      packages/core/src/entity/validate-custom-fields-config.spec.ts
  42. 20 19
      packages/core/src/event-bus/event-bus.spec.ts
  43. 1 0
      packages/core/src/job-queue/in-memory-job-queue-strategy.spec.ts
  44. 2 1
      packages/core/src/job-queue/job-queue.service.spec.ts
  45. 2 0
      packages/core/src/job-queue/job.spec.ts
  46. 2 0
      packages/core/src/service/helpers/list-query-builder/parse-channel-param.spec.ts
  47. 15 13
      packages/core/src/service/helpers/list-query-builder/parse-filter-params.spec.ts
  48. 1 0
      packages/core/src/service/helpers/list-query-builder/parse-sort-params.spec.ts
  49. 3 0
      packages/core/src/service/helpers/order-calculator/order-calculator.spec.ts
  50. 2 0
      packages/core/src/service/helpers/order-calculator/prorate.spec.ts
  51. 1 0
      packages/core/src/service/helpers/order-merger/order-merger.spec.ts
  52. 1 1
      packages/core/src/service/helpers/translatable-saver/translation-differ.spec.ts
  53. 2 0
      packages/core/src/service/helpers/utils/order-utils.spec.ts
  54. 2 0
      packages/core/src/service/helpers/utils/patch-entity.spec.ts
  55. 51 8
      packages/core/src/service/helpers/utils/samples-each.spec.ts
  56. 1 0
      packages/core/src/service/helpers/utils/translate-entity.spec.ts
  57. 1 1
      packages/core/src/testing/testing-types.ts
  58. 11 0
      packages/core/vitest.config.ts
  59. 1 1
      packages/elasticsearch-plugin/package.json
  60. 1 0
      packages/elasticsearch-plugin/src/build-elastic-body.spec.ts
  61. 1 1
      packages/email-plugin/package.json
  62. 7 5
      packages/email-plugin/src/plugin.spec.ts
  63. 11 0
      packages/email-plugin/vitest.config.ts
  64. 1 1
      packages/job-queue-plugin/package.json
  65. 3 2
      packages/job-queue-plugin/src/pub-sub/pub-sub-job-queue-strategy.spec.ts

+ 0 - 2
e2e-common/vitest.config.ts

@@ -22,7 +22,5 @@ export default defineConfig({
         // See https://github.com/vitest-dev/vitest/issues/708#issuecomment-1118628479
         // Vite plugin
         swc.vite(),
-        // Rollup plugin
-        swc.rollup() as any,
     ],
 });

+ 1 - 1
packages/asset-server-plugin/package.json

@@ -11,7 +11,7 @@
     "watch": "tsc -p ./tsconfig.build.json --watch",
     "build": "rimraf lib && tsc -p ./tsconfig.build.json && node build.js",
     "lint": "tslint --fix --project ./",
-    "test": "jest --config ./jest.config.js",
+    "test": "vitest --run",
     "e2e": "cross-env PACKAGE=asset-server-plugin vitest --config ../../e2e-common/vitest.config.ts --run",
     "e2e:watch": "cross-env PACKAGE=asset-server-plugin vitest --config ../../e2e-common/vitest.config.ts"
   },

+ 2 - 0
packages/asset-server-plugin/src/transform-image.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { Dimensions, Point, resizeToFocalPoint } from './transform-image';
 
 describe('resizeToFocalPoint', () => {

+ 1 - 1
packages/common/package.json

@@ -7,7 +7,7 @@
     "watch": "tsc -p ./tsconfig.build.json -w",
     "build": "rimraf lib && tsc -p ./tsconfig.build.json",
     "lint": "tslint --fix --project ./",
-    "test": "jest --config ./jest.config.js",
+    "test": "vitest --run",
     "ci": "yarn build"
   },
   "homepage": "https://www.vendure.io/",

+ 2 - 0
packages/common/src/filter-async.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { filterAsync } from './filter-async';
 
 describe('filterAsync', () => {

+ 2 - 0
packages/common/src/normalize-string.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { normalizeString } from './normalize-string';
 
 describe('normalizeString()', () => {

+ 2 - 0
packages/common/src/omit.spec.ts

@@ -1,3 +1,5 @@
+import { afterAll, beforeAll, describe, expect, it } from 'vitest';
+
 import { omit } from './omit';
 
 declare const File: any;

+ 2 - 0
packages/common/src/pick.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { pick } from './pick';
 
 describe('pick()', () => {

+ 6 - 1
packages/common/src/shared-utils.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { generateAllCombinations, isClassInstance } from './shared-utils';
 
 describe('generateAllCombinations()', () => {
@@ -12,7 +14,10 @@ describe('generateAllCombinations()', () => {
     });
 
     it('works with an input of length 2', () => {
-        const result = generateAllCombinations([['red', 'green', 'blue'], ['small', 'large']]);
+        const result = generateAllCombinations([
+            ['red', 'green', 'blue'],
+            ['small', 'large'],
+        ]);
         expect(result).toEqual([
             ['red', 'small'],
             ['red', 'large'],

+ 2 - 0
packages/common/src/simple-deep-clone.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { simpleDeepClone } from './simple-deep-clone';
 
 describe('simpleDeepClone()', () => {

+ 2 - 0
packages/common/src/unique.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { unique } from './unique';
 
 describe('unique()', () => {

+ 27 - 0
packages/common/vitest.config.ts

@@ -0,0 +1,27 @@
+import path from 'path';
+import swc from 'unplugin-swc';
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+    test: {
+        /**
+         * 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.
+         */
+        testTimeout: process.env.E2E_DEBUG ? 1800 * 1000 : process.env.CI ? 30 * 1000 : 15 * 1000,
+        // threads: false,
+        // singleThread: true,
+        // reporters: ['verbose'],
+        typecheck: {
+            tsconfig: path.join(__dirname, 'tsconfig.e2e.json'),
+        },
+    },
+    plugins: [
+        // SWC required to support decorators used in test plugins
+        // See https://github.com/vitest-dev/vitest/issues/708#issuecomment-1118628479
+        // Vite plugin
+        swc.vite(),
+        // Rollup plugin
+        swc.rollup() as any,
+    ],
+});

+ 1 - 1
packages/core/package.json

@@ -24,7 +24,7 @@
     "build": "rimraf dist && tsc -p ./build/tsconfig.build.json && tsc -p ./build/tsconfig.cli.json && gulp -f ./build/gulpfile.ts build",
     "watch": "concurrently yarn:tsc:watch yarn:gulp:watch",
     "lint": "tslint --fix --project ./",
-    "test": "jest --config ./jest.config.js",
+    "test": "vitest --config ./vitest.config.ts --run",
     "e2e": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.ts --run",
     "e2e:watch": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.ts",
     "ci": "yarn build"

+ 2 - 0
packages/core/src/api/common/id-codec.spec.ts

@@ -1,3 +1,5 @@
+import { beforeEach, describe, expect, it } from 'vitest';
+
 import { DECODED, ENCODED, MockIdStrategy } from '../../config/config.service.mock';
 
 import { IdCodec } from './id-codec';

+ 1 - 1
packages/core/src/api/common/request-context.spec.ts

@@ -1,9 +1,9 @@
 import { CurrencyCode, LanguageCode } from '@vendure/common/lib/generated-types';
+import { beforeAll, describe, expect, it } from 'vitest';
 
 import { CachedSession } from '../../config/session-cache/session-cache-strategy';
 import { Channel } from '../../entity/channel/channel.entity';
 import { Order } from '../../entity/order/order.entity';
-import { User } from '../../entity/user/user.entity';
 import { Zone } from '../../entity/zone/zone.entity';
 
 import { RequestContext, SerializedRequestContext } from './request-context';

+ 1 - 1
packages/core/src/api/common/validate-custom-field-value.spec.ts

@@ -1,8 +1,8 @@
 import { LanguageCode } from '@vendure/common/lib/generated-types';
 import { fail } from 'assert';
+import { describe, expect, it } from 'vitest';
 
 import { Injector } from '../../common/injector';
-import { CustomFieldConfig } from '../../config/custom-field/custom-field-types';
 
 import { validateCustomFieldValue } from './validate-custom-field-value';
 

+ 13 - 13
packages/core/src/api/config/__snapshots__/graphql-custom-fields.spec.ts.snap

@@ -1,6 +1,6 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
-exports[`addGraphQLCustomFields() extends OrderAddress if Address custom fields defined 1`] = `
+exports[`addGraphQLCustomFields() > extends OrderAddress if Address custom fields defined 1`] = `
 "type Address {
   id: ID
   streetLine1: String
@@ -21,7 +21,7 @@ type AddressCustomFields {
 }"
 `;
 
-exports[`addGraphQLCustomFields() extends a type 1`] = `
+exports[`addGraphQLCustomFields() > extends a type 1`] = `
 "type Product {
   id: ID
   customFields: ProductCustomFields
@@ -36,7 +36,7 @@ type ProductCustomFields {
 }"
 `;
 
-exports[`addGraphQLCustomFields() extends a type with FilterParameters 1`] = `
+exports[`addGraphQLCustomFields() > extends a type with FilterParameters 1`] = `
 "type Product {
   name: String
   customFields: ProductCustomFields
@@ -78,7 +78,7 @@ type ProductCustomFields {
 }"
 `;
 
-exports[`addGraphQLCustomFields() extends a type with SortParameters 1`] = `
+exports[`addGraphQLCustomFields() > extends a type with SortParameters 1`] = `
 "type Product {
   id: ID
   customFields: ProductCustomFields
@@ -105,7 +105,7 @@ type ProductCustomFields {
 }"
 `;
 
-exports[`addGraphQLCustomFields() extends a type with a Create input 1`] = `
+exports[`addGraphQLCustomFields() > extends a type with a Create input 1`] = `
 "type Product {
   id: ID
   customFields: ProductCustomFields
@@ -130,7 +130,7 @@ input CreateProductCustomFieldsInput {
 }"
 `;
 
-exports[`addGraphQLCustomFields() extends a type with a Create input and a translation 1`] = `
+exports[`addGraphQLCustomFields() > extends a type with a Create input and a translation 1`] = `
 "type Product {
   id: ID
   customFields: ProductCustomFields
@@ -173,7 +173,7 @@ input ProductTranslationInputCustomFields {
 }"
 `;
 
-exports[`addGraphQLCustomFields() extends a type with a translation 1`] = `
+exports[`addGraphQLCustomFields() > extends a type with a translation 1`] = `
 "type Product {
   id: ID
   translations: [ProductTranslation!]!
@@ -199,7 +199,7 @@ type ProductTranslationCustomFields {
 }"
 `;
 
-exports[`addGraphQLCustomFields() extends a type with an Update input 1`] = `
+exports[`addGraphQLCustomFields() > extends a type with an Update input 1`] = `
 "type Product {
   id: ID
   customFields: ProductCustomFields
@@ -224,7 +224,7 @@ input UpdateProductCustomFieldsInput {
 }"
 `;
 
-exports[`addGraphQLCustomFields() publicOnly = true 1`] = `
+exports[`addGraphQLCustomFields() > publicOnly = true 1`] = `
 "type Product {
   id: ID
   customFields: ProductCustomFields
@@ -239,7 +239,7 @@ type ProductCustomFields {
 }"
 `;
 
-exports[`addGraphQLCustomFields() uses JSON scalar if no custom fields defined 1`] = `
+exports[`addGraphQLCustomFields() > uses JSON scalar if no custom fields defined 1`] = `
 "type Product {
   id: ID
   customFields: JSON
@@ -250,7 +250,7 @@ scalar JSON
 scalar DateTime"
 `;
 
-exports[`addOrderLineCustomFieldsInput() Modifies the schema when the addItemToOrder & adjustOrderLine mutation is present 1`] = `
+exports[`addOrderLineCustomFieldsInput() > Modifies the schema when the addItemToOrder & adjustOrderLine mutation is present 1`] = `
 "type Mutation {
   addItemToOrder(id: ID!, quantity: Int!, customFields: OrderLineCustomFieldsInput = null): Boolean
   adjustOrderLine(id: ID!, quantity: Int, customFields: OrderLineCustomFieldsInput = null): Boolean
@@ -262,7 +262,7 @@ input OrderLineCustomFieldsInput {
 }"
 `;
 
-exports[`addRegisterCustomerCustomFieldsInput() add public writable custom fields to RegisterCustomerInput 1`] = `
+exports[`addRegisterCustomerCustomFieldsInput() > add public writable custom fields to RegisterCustomerInput 1`] = `
 "input RegisterCustomerInput {
   emailAddress: String!
   title: String

+ 6 - 2
packages/core/src/api/config/generate-list-options.spec.ts

@@ -1,4 +1,8 @@
-import { buildSchema, printType } from 'graphql';
+// Using require right now to force the commonjs version of GraphQL to be used
+// when running vitest tests. See https://github.com/vitejs/vite/issues/7879
+// tslint:disable-next-line:no-var-requires
+const { buildSchema, printType } = require('graphql');
+import { describe, expect, it } from 'vitest';
 
 import { generateListOptions } from './generate-list-options';
 // tslint:disable:no-non-null-assertion
@@ -63,7 +67,7 @@ describe('generateListOptions()', () => {
                input PersonListOptions
            `;
 
-        const result = generateListOptions(buildSchema(input));
+        const result = generateListOptions(input);
 
         expect(printType(result.getType('PersonListOptions')!)).toBe(
             removeLeadingWhitespace(`

+ 3 - 1
packages/core/src/api/config/generate-list-options.ts

@@ -21,7 +21,9 @@ import {
     isListType,
     isNonNullType,
     isObjectType,
-} from 'graphql';
+    // Importing this from graphql/index.js is a workaround for the dual-package
+    // hazard issue when testing this file in vitest. See https://github.com/vitejs/vite/issues/7879
+} from 'graphql/index.js';
 
 /**
  * Generates ListOptions inputs for queries which return PaginatedList types.

+ 1 - 0
packages/core/src/api/config/graphql-custom-fields.spec.ts

@@ -1,4 +1,5 @@
 import { printSchema } from 'graphql';
+import { describe, expect, it } from 'vitest';
 
 import { CustomFieldConfig, CustomFields } from '../../config/custom-field/custom-field-types';
 

+ 2 - 2
packages/core/src/bootstrap.ts

@@ -125,12 +125,12 @@ export async function preBootstrapConfig(
     userConfig: Partial<VendureConfig>,
 ): Promise<Readonly<RuntimeVendureConfig>> {
     if (userConfig) {
-        setConfig(userConfig);
+        await setConfig(userConfig);
     }
 
     const entities = await getAllEntities(userConfig);
     const { coreSubscribersMap } = await import('./entity/subscribers.js');
-    setConfig({
+    await setConfig({
         dbConnectionOptions: {
             entities,
             subscribers: [

+ 2 - 0
packages/core/src/cache/request-context-cache.service.spec.ts

@@ -1,3 +1,5 @@
+import { beforeEach, describe, expect, it } from 'vitest';
+
 import { RequestContext } from '../api';
 
 import { RequestContextCacheService } from './request-context-cache.service';

+ 5 - 4
packages/core/src/common/finite-state-machine/finite-state-machine.spec.ts

@@ -1,4 +1,5 @@
 import { of } from 'rxjs';
+import { describe, expect, it, vi } from 'vitest';
 
 import { FSM } from './finite-state-machine';
 import { Transitions } from './types';
@@ -62,7 +63,7 @@ describe('Finite State Machine', () => {
 
     it('onTransitionStart() is invoked before a transition takes place', () => {
         const initialState = 'DoorsClosed';
-        const spy = jest.fn();
+        const spy = vi.fn();
         const data = 123;
         let currentStateDuringCallback = '';
         const fsm = new FSM<TestState>(
@@ -83,7 +84,7 @@ describe('Finite State Machine', () => {
 
     it('onTransitionEnd() is invoked after a transition takes place', async () => {
         const initialState = 'DoorsClosed';
-        const spy = jest.fn();
+        const spy = vi.fn();
         const data = 123;
         let currentStateDuringCallback = '';
         const fsm = new FSM<TestState>(
@@ -190,7 +191,7 @@ describe('Finite State Machine', () => {
 
     it('onError() is invoked for invalid transitions', async () => {
         const initialState = 'DoorsOpen';
-        const spy = jest.fn();
+        const spy = vi.fn();
         const fsm = new FSM<TestState>(
             {
                 transitions,
@@ -205,7 +206,7 @@ describe('Finite State Machine', () => {
 
     it('onTransitionStart() invokes onError() if it returns a string', async () => {
         const initialState = 'DoorsClosed';
-        const spy = jest.fn();
+        const spy = vi.fn();
         const fsm = new FSM<TestState>(
             {
                 transitions,

+ 2 - 0
packages/core/src/common/finite-state-machine/merge-transition-definitions.spec.ts

@@ -1,3 +1,5 @@
+import { afterAll, beforeAll, describe, expect, it } from 'vitest';
+
 import { mergeTransitionDefinitions } from './merge-transition-definitions';
 import { Transitions } from './types';
 

+ 2 - 0
packages/core/src/common/finite-state-machine/validate-transition-definition.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { OrderState } from '../../service/helpers/order-state-machine/order-state';
 
 import { Transitions } from './types';

+ 4 - 2
packages/core/src/common/self-refreshing-cache.spec.ts

@@ -1,8 +1,10 @@
+import { beforeAll, describe, expect, it, vi } from 'vitest';
+
 import { createSelfRefreshingCache, SelfRefreshingCache } from './self-refreshing-cache';
 
 describe('SelfRefreshingCache', () => {
     let testCache: SelfRefreshingCache<number, [string]>;
-    const fetchFn = jest.fn().mockImplementation((arg: string) => arg.length);
+    const fetchFn = vi.fn().mockImplementation((arg: string) => arg.length);
     let currentTime = 0;
     beforeAll(async () => {
         testCache = await createSelfRefreshingCache<number, [string]>({
@@ -49,7 +51,7 @@ describe('SelfRefreshingCache', () => {
     });
 
     describe('memoization', () => {
-        const memoizedFn = jest.fn();
+        const memoizedFn = vi.fn();
         let getMemoized: (arg1: string, arg2: number) => Promise<number>;
 
         beforeAll(() => {

+ 2 - 0
packages/core/src/common/utils.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { convertRelationPaths } from './utils';
 
 describe('convertRelationPaths()', () => {

+ 2 - 0
packages/core/src/config/asset-naming-strategy/default-asset-naming-strategy.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { RequestContext } from '../../api/common/request-context';
 
 import { DefaultAssetNamingStrategy } from './default-asset-naming-strategy';

+ 7 - 2
packages/core/src/config/catalog/default-product-variant-price-calculation-strategy.spec.ts

@@ -1,3 +1,5 @@
+import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
+
 import { roundMoney } from '../../common/round-money';
 import {
     createRequestContext,
@@ -6,12 +8,11 @@ import {
     taxCategoryStandard,
     taxRateDefaultReduced,
     taxRateDefaultStandard,
-    taxRateOtherReduced,
-    taxRateOtherStandard,
     zoneDefault,
     zoneOther,
     zoneWithNoTaxRate,
 } from '../../testing/order-test-utils';
+import { ensureConfigLoaded } from '../config-helpers';
 
 import { DefaultProductVariantPriceCalculationStrategy } from './default-product-variant-price-calculation-strategy';
 
@@ -19,6 +20,10 @@ describe('DefaultProductVariantPriceCalculationStrategy', () => {
     let strategy: DefaultProductVariantPriceCalculationStrategy;
     const inputPrice = 6543;
 
+    beforeAll(async () => {
+        await ensureConfigLoaded();
+    });
+
     beforeEach(async () => {
         strategy = new DefaultProductVariantPriceCalculationStrategy();
         const mockInjector = {

+ 25 - 5
packages/core/src/config/config-helpers.ts

@@ -1,34 +1,54 @@
+import path from 'path';
+
 import { mergeConfig } from './merge-config';
 import { PartialVendureConfig, RuntimeVendureConfig } from './vendure-config';
 
 let activeConfig: RuntimeVendureConfig;
-
+const defaultConfigPath = path.join(__dirname, 'default-config');
 /**
  * Reset the activeConfig object back to the initial default state.
  */
 export function resetConfig() {
-    activeConfig = require('./default-config').defaultConfig;
+    activeConfig = require(defaultConfigPath).defaultConfig;
 }
 
 /**
  * Override the default config by merging in the supplied values. Should only be used prior to
  * bootstrapping the app.
  */
-export function setConfig(userConfig: PartialVendureConfig): void {
+export async function setConfig(userConfig: PartialVendureConfig) {
     if (!activeConfig) {
-        activeConfig = require('./default-config').defaultConfig;
+        activeConfig = (await import(defaultConfigPath)).defaultConfig;
     }
     activeConfig = mergeConfig(activeConfig, userConfig);
 }
 
+/**
+ * Ensures that the config has been loaded. This is necessary for tests which
+ * do not go through the normal bootstrap process.
+ */
+export async function ensureConfigLoaded() {
+    if (!activeConfig) {
+        activeConfig = (await import(defaultConfigPath)).defaultConfig;
+    }
+}
+
 /**
  * Returns the app config object. In general this function should only be
  * used before bootstrapping the app. In all other contexts, the {@link ConfigService}
  * should be used to access config settings.
  */
 export function getConfig(): Readonly<RuntimeVendureConfig> {
+    // @ts-ignore
     if (!activeConfig) {
-        activeConfig = require('./default-config').defaultConfig;
+        try {
+            activeConfig = require(defaultConfigPath).defaultConfig;
+        } catch (e: any) {
+            // tslint:disable-next-line:no-console
+            console.log(
+                `Error loading config. If this is a test, make sure you have called ensureConfigLoaded() before using the config.`,
+            );
+        }
     }
     return activeConfig;
 }

+ 5 - 3
packages/core/src/config/config.service.mock.ts

@@ -1,3 +1,5 @@
+import { vi } from 'vitest';
+
 import { VendureEntity } from '../entity/base/base.entity';
 import { MockClass } from '../testing/testing-types';
 
@@ -20,7 +22,7 @@ export class MockConfigService implements MockClass<ConfigService> {
     };
     authOptions: {};
     defaultChannelToken: 'channel-token';
-    defaultLanguageCode: jest.Mock<any>;
+    defaultLanguageCode: vi.Mock<any>;
     roundingStrategy: {};
     entityIdStrategy = new MockIdStrategy();
     entityOptions = {};
@@ -55,6 +57,6 @@ export const DECODED = 'decoded';
 
 export class MockIdStrategy implements EntityIdStrategy<'increment'> {
     readonly primaryKeyType = 'increment';
-    encodeId = jest.fn().mockReturnValue(ENCODED);
-    decodeId = jest.fn().mockReturnValue(DECODED);
+    encodeId = vi.fn().mockReturnValue(ENCODED);
+    decodeId = vi.fn().mockReturnValue(DECODED);
 }

+ 4 - 2
packages/core/src/config/logger/default-logger.spec.ts

@@ -1,10 +1,12 @@
+import { afterEach, beforeEach, describe, expect, it, SpyInstance, vi } from 'vitest';
+
 import { DefaultLogger } from './default-logger';
 import { Logger, LogLevel } from './vendure-logger';
 
 describe('DefaultLogger', () => {
-    let stdOutSpy: jest.SpyInstance;
+    let stdOutSpy: SpyInstance;
     beforeEach(() => {
-        stdOutSpy = jest.spyOn(process.stdout, 'write').mockImplementation(() => true);
+        stdOutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
     });
 
     afterEach(() => {

+ 2 - 0
packages/core/src/config/merge-config.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { mergeConfig } from './merge-config';
 
 describe('mergeConfig()', () => {

+ 2 - 0
packages/core/src/config/order/merge-orders-strategy.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { RequestContext } from '../../api/common/request-context';
 import { Order } from '../../entity/order/order.entity';
 import { createOrderFromLines } from '../../testing/order-test-utils';

+ 2 - 0
packages/core/src/config/order/use-existing-strategy.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { RequestContext } from '../../api/common/request-context';
 import { Order } from '../../entity/order/order.entity';
 import { createOrderFromLines } from '../../testing/order-test-utils';

+ 2 - 0
packages/core/src/config/order/use-guest-if-existing-empty-strategy.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { RequestContext } from '../../api/common/request-context';
 import { Order } from '../../entity/order/order.entity';
 import { createOrderFromLines } from '../../testing/order-test-utils';

+ 2 - 0
packages/core/src/config/order/use-guest-strategy.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { RequestContext } from '../../api/common/request-context';
 import { Order } from '../../entity/order/order.entity';
 import { createOrderFromLines } from '../../testing/order-test-utils';

+ 7 - 7
packages/core/src/data-import/providers/import-parser/__snapshots__/import-parser.spec.ts.snap

@@ -1,6 +1,6 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
-exports[`ImportParser parseProducts custom fields 1`] = `
+exports[`ImportParser > parseProducts > custom fields 1`] = `
 [
   {
     "product": {
@@ -100,7 +100,7 @@ exports[`ImportParser parseProducts custom fields 1`] = `
 ]
 `;
 
-exports[`ImportParser parseProducts multiple products with multiple variants 1`] = `
+exports[`ImportParser > parseProducts > multiple products with multiple variants 1`] = `
 [
   {
     "product": {
@@ -411,7 +411,7 @@ exports[`ImportParser parseProducts multiple products with multiple variants 1`]
 ]
 `;
 
-exports[`ImportParser parseProducts single product with a multiple variants 1`] = `
+exports[`ImportParser > parseProducts > single product with a multiple variants 1`] = `
 [
   {
     "product": {
@@ -502,7 +502,7 @@ exports[`ImportParser parseProducts single product with a multiple variants 1`]
 ]
 `;
 
-exports[`ImportParser parseProducts single product with a single variant 1`] = `
+exports[`ImportParser > parseProducts > single product with a single variant 1`] = `
 [
   {
     "product": {
@@ -573,7 +573,7 @@ exports[`ImportParser parseProducts single product with a single variant 1`] = `
 ]
 `;
 
-exports[`ImportParser parseProducts works with multilingual input 1`] = `
+exports[`ImportParser > parseProducts > works with multilingual input 1`] = `
 [
   {
     "product": {
@@ -748,7 +748,7 @@ exports[`ImportParser parseProducts works with multilingual input 1`] = `
 ]
 `;
 
-exports[`ImportParser parseProducts works with streamed input 1`] = `
+exports[`ImportParser > parseProducts > works with streamed input 1`] = `
 [
   {
     "product": {

+ 7 - 1
packages/core/src/data-import/providers/import-parser/import-parser.spec.ts

@@ -1,7 +1,9 @@
+import { LanguageCode } from '@vendure/common/lib/generated-types';
 import fs from 'fs-extra';
 import path from 'path';
+import { beforeAll, describe, expect, it } from 'vitest';
 
-import { LanguageCode } from '../../..';
+import { ensureConfigLoaded } from '../../../config/config-helpers';
 import { ConfigService } from '../../../config/config.service';
 
 import { ImportParser } from './import-parser';
@@ -30,6 +32,10 @@ const mockConfigService = {
 } as ConfigService;
 
 describe('ImportParser', () => {
+    beforeAll(async () => {
+        await ensureConfigLoaded();
+    });
+
     describe('parseProducts', () => {
         it('single product with a single variant', async () => {
             const importParser = new ImportParser(mockConfigService);

+ 6 - 0
packages/core/src/entity/order/order.entity.spec.ts

@@ -1,12 +1,18 @@
 import { AdjustmentType } from '@vendure/common/lib/generated-types';
 import { summate } from '@vendure/common/lib/shared-utils';
+import { beforeAll, describe, expect, it } from 'vitest';
 
+import { ensureConfigLoaded } from '../../config/config-helpers';
 import { createOrder, createRequestContext, taxCategoryStandard } from '../../testing/order-test-utils';
 import { ShippingLine } from '../shipping-line/shipping-line.entity';
 
 import { Order } from './order.entity';
 
 describe('Order entity methods', () => {
+    beforeAll(async () => {
+        await ensureConfigLoaded();
+    });
+
     describe('taxSummary', () => {
         it('single rate across items', () => {
             const ctx = createRequestContext({ pricesIncludeTax: false });

+ 9 - 2
packages/core/src/entity/validate-custom-fields-config.spec.ts

@@ -1,4 +1,5 @@
 import { Type } from '@vendure/common/lib/shared-types';
+import { describe, expect, it } from 'vitest';
 
 import { CustomFields } from '../config/custom-field/custom-field-types';
 
@@ -10,7 +11,10 @@ describe('validateCustomFieldsConfig()', () => {
 
     it('valid config', () => {
         const config: CustomFields = {
-            Product: [{ name: 'foo', type: 'string' }, { name: 'bar', type: 'localeString' }],
+            Product: [
+                { name: 'foo', type: 'string' },
+                { name: 'bar', type: 'localeString' },
+            ],
         };
         const result = validateCustomFieldsConfig(config, allEntities);
 
@@ -20,7 +24,10 @@ describe('validateCustomFieldsConfig()', () => {
 
     it('invalid localeString', () => {
         const config: CustomFields = {
-            User: [{ name: 'foo', type: 'string' }, { name: 'bar', type: 'localeString' }],
+            User: [
+                { name: 'foo', type: 'string' },
+                { name: 'bar', type: 'localeString' },
+            ],
         };
         const result = validateCustomFieldsConfig(config, allEntities);
 

+ 20 - 19
packages/core/src/event-bus/event-bus.spec.ts

@@ -1,4 +1,5 @@
 import { QueryRunner } from 'typeorm';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
 
 import { EventBus } from './event-bus';
 import { VendureEvent } from './vendure-event';
@@ -24,7 +25,7 @@ describe('EventBus', () => {
 
     describe('ofType()', () => {
         it('single handler is called once', async () => {
-            const handler = jest.fn();
+            const handler = vi.fn();
             const event = new TestEvent('foo');
             eventBus.ofType(TestEvent).subscribe(handler);
 
@@ -36,7 +37,7 @@ describe('EventBus', () => {
         });
 
         it('single handler is called on multiple events', async () => {
-            const handler = jest.fn();
+            const handler = vi.fn();
             const event1 = new TestEvent('foo');
             const event2 = new TestEvent('bar');
             const event3 = new TestEvent('baz');
@@ -54,9 +55,9 @@ describe('EventBus', () => {
         });
 
         it('multiple handlers are called', async () => {
-            const handler1 = jest.fn();
-            const handler2 = jest.fn();
-            const handler3 = jest.fn();
+            const handler1 = vi.fn();
+            const handler2 = vi.fn();
+            const handler3 = vi.fn();
             const event = new TestEvent('foo');
             eventBus.ofType(TestEvent).subscribe(handler1);
             eventBus.ofType(TestEvent).subscribe(handler2);
@@ -71,7 +72,7 @@ describe('EventBus', () => {
         });
 
         it('handler is not called for other events', async () => {
-            const handler = jest.fn();
+            const handler = vi.fn();
             const event = new OtherTestEvent('foo');
             eventBus.ofType(TestEvent).subscribe(handler);
 
@@ -82,7 +83,7 @@ describe('EventBus', () => {
         });
 
         it('ofType() returns a subscription', async () => {
-            const handler = jest.fn();
+            const handler = vi.fn();
             const event = new TestEvent('foo');
             const subscription = eventBus.ofType(TestEvent).subscribe(handler);
 
@@ -101,8 +102,8 @@ describe('EventBus', () => {
         });
 
         it('unsubscribe() only unsubscribes own handler', async () => {
-            const handler1 = jest.fn();
-            const handler2 = jest.fn();
+            const handler1 = vi.fn();
+            const handler2 = vi.fn();
             const event = new TestEvent('foo');
             const subscription1 = eventBus.ofType(TestEvent).subscribe(handler1);
             const subscription2 = eventBus.ofType(TestEvent).subscribe(handler2);
@@ -126,7 +127,7 @@ describe('EventBus', () => {
 
     describe('filter()', () => {
         it('single handler is called once', async () => {
-            const handler = jest.fn();
+            const handler = vi.fn();
             const event = new TestEvent('foo');
             eventBus.filter(vendureEvent => vendureEvent instanceof TestEvent).subscribe(handler);
 
@@ -138,7 +139,7 @@ describe('EventBus', () => {
         });
 
         it('single handler is called on multiple events', async () => {
-            const handler = jest.fn();
+            const handler = vi.fn();
             const event1 = new TestEvent('foo');
             const event2 = new TestEvent('bar');
             const event3 = new TestEvent('baz');
@@ -156,9 +157,9 @@ describe('EventBus', () => {
         });
 
         it('multiple handlers are called', async () => {
-            const handler1 = jest.fn();
-            const handler2 = jest.fn();
-            const handler3 = jest.fn();
+            const handler1 = vi.fn();
+            const handler2 = vi.fn();
+            const handler3 = vi.fn();
             const event = new TestEvent('foo');
             eventBus.filter(vendureEvent => vendureEvent instanceof TestEvent).subscribe(handler1);
             eventBus.filter(vendureEvent => vendureEvent instanceof TestEvent).subscribe(handler2);
@@ -173,7 +174,7 @@ describe('EventBus', () => {
         });
 
         it('handler is not called for other events', async () => {
-            const handler = jest.fn();
+            const handler = vi.fn();
             const event = new OtherTestEvent('foo');
             eventBus.filter(vendureEvent => vendureEvent instanceof TestEvent).subscribe(handler);
 
@@ -184,7 +185,7 @@ describe('EventBus', () => {
         });
 
         it('handler is called for instance of child classes', async () => {
-            const handler = jest.fn();
+            const handler = vi.fn();
             const event = new ChildTestEvent('bar', 'foo');
             eventBus.filter(vendureEvent => vendureEvent instanceof TestEvent).subscribe(handler);
 
@@ -195,7 +196,7 @@ describe('EventBus', () => {
         });
 
         it('filter() returns a subscription', async () => {
-            const handler = jest.fn();
+            const handler = vi.fn();
             const event = new TestEvent('foo');
             const subscription = eventBus
                 .filter(vendureEvent => vendureEvent instanceof TestEvent)
@@ -216,8 +217,8 @@ describe('EventBus', () => {
         });
 
         it('unsubscribe() only unsubscribes own handler', async () => {
-            const handler1 = jest.fn();
-            const handler2 = jest.fn();
+            const handler1 = vi.fn();
+            const handler2 = vi.fn();
             const event = new TestEvent('foo');
             const subscription1 = eventBus
                 .filter(vendureEvent => vendureEvent instanceof TestEvent)

+ 1 - 0
packages/core/src/job-queue/in-memory-job-queue-strategy.spec.ts

@@ -1,5 +1,6 @@
 /* tslint:disable:no-non-null-assertion */
 import { JobListOptions, SortOrder } from '@vendure/common/lib/generated-types';
+import { afterEach, beforeEach, describe, expect, it } from 'vitest';
 
 import { InMemoryJobQueueStrategy } from './in-memory-job-queue-strategy';
 import { Job } from './job';

+ 2 - 1
packages/core/src/job-queue/job-queue.service.spec.ts

@@ -5,6 +5,7 @@ import { Test, TestingModule } from '@nestjs/testing';
 import { JobState } from '@vendure/common/lib/generated-types';
 import { Subject } from 'rxjs';
 import { take } from 'rxjs/operators';
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
 
 import { assertFound, Injector } from '../common';
 import { ConfigService } from '../config/config.service';
@@ -19,7 +20,7 @@ import { JobQueueService } from './job-queue.service';
 import { TestingJobQueueStrategy } from './testing-job-queue-strategy';
 
 const queuePollInterval = 10;
-const backoffStrategySpy = jest.fn();
+const backoffStrategySpy = vi.fn();
 const testJobQueueStrategy = new TestingJobQueueStrategy({
     concurrency: 1,
     pollInterval: queuePollInterval,

+ 2 - 0
packages/core/src/job-queue/job.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { Job } from './job';
 
 describe('Job class', () => {

+ 2 - 0
packages/core/src/service/helpers/list-query-builder/parse-channel-param.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { Channel } from '../../../entity/channel/channel.entity';
 import { Customer } from '../../../entity/customer/customer.entity';
 import { Product } from '../../../entity/product/product.entity';

+ 15 - 13
packages/core/src/service/helpers/list-query-builder/parse-filter-params.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { FilterParameter } from '../../../common/types/common-types';
 import { ProductTranslation } from '../../../entity/product/product-translation.entity';
 import { Product } from '../../../entity/product/product.entity';
@@ -70,7 +72,7 @@ describe('parseFilterParams()', () => {
     });
 
     describe('string operators', () => {
-        describe('eq', () => {
+        it('eq', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'name', type: String }]);
             const filterParams: FilterParameter<Product> = {
@@ -83,7 +85,7 @@ describe('parseFilterParams()', () => {
             expect(result[0].parameters).toEqual({ arg1: 'foo' });
         });
 
-        describe('contains', () => {
+        it('contains', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'name', type: String }]);
             const filterParams: FilterParameter<Product> = {
@@ -98,7 +100,7 @@ describe('parseFilterParams()', () => {
     });
 
     describe('number operators', () => {
-        describe('eq', () => {
+        it('eq', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'price', type: Number }]);
             const filterParams: FilterParameter<Product & { price: number }> = {
@@ -111,7 +113,7 @@ describe('parseFilterParams()', () => {
             expect(result[0].parameters).toEqual({ arg1: 123 });
         });
 
-        describe('lt', () => {
+        it('lt', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'price', type: Number }]);
             const filterParams: FilterParameter<Product & { price: number }> = {
@@ -124,7 +126,7 @@ describe('parseFilterParams()', () => {
             expect(result[0].parameters).toEqual({ arg1: 123 });
         });
 
-        describe('lte', () => {
+        it('lte', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'price', type: Number }]);
             const filterParams: FilterParameter<Product & { price: number }> = {
@@ -137,7 +139,7 @@ describe('parseFilterParams()', () => {
             expect(result[0].parameters).toEqual({ arg1: 123 });
         });
 
-        describe('gt', () => {
+        it('gt', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'price', type: Number }]);
             const filterParams: FilterParameter<Product & { price: number }> = {
@@ -150,7 +152,7 @@ describe('parseFilterParams()', () => {
             expect(result[0].parameters).toEqual({ arg1: 123 });
         });
 
-        describe('gte', () => {
+        it('gte', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'price', type: Number }]);
             const filterParams: FilterParameter<Product & { price: number }> = {
@@ -163,7 +165,7 @@ describe('parseFilterParams()', () => {
             expect(result[0].parameters).toEqual({ arg1: 123 });
         });
 
-        describe('between', () => {
+        it('between', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'price', type: Number }]);
             const filterParams: FilterParameter<Product & { price: number }> = {
@@ -181,7 +183,7 @@ describe('parseFilterParams()', () => {
     });
 
     describe('date operators', () => {
-        describe('eq', () => {
+        it('eq', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'createdAt', type: 'datetime' }]);
             const filterParams: FilterParameter<Product> = {
@@ -194,7 +196,7 @@ describe('parseFilterParams()', () => {
             expect(result[0].parameters).toEqual({ arg1: '2018-01-01 10:00:00.000' });
         });
 
-        describe('before', () => {
+        it('before', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'createdAt', type: 'datetime' }]);
             const filterParams: FilterParameter<Product> = {
@@ -207,7 +209,7 @@ describe('parseFilterParams()', () => {
             expect(result[0].parameters).toEqual({ arg1: '2018-01-01 10:00:00.000' });
         });
 
-        describe('after', () => {
+        it('after', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'createdAt', type: 'datetime' }]);
             const filterParams: FilterParameter<Product> = {
@@ -220,7 +222,7 @@ describe('parseFilterParams()', () => {
             expect(result[0].parameters).toEqual({ arg1: '2018-01-01 10:00:00.000' });
         });
 
-        describe('between', () => {
+        it('between', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'createdAt', type: 'datetime' }]);
             const filterParams: FilterParameter<Product> = {
@@ -241,7 +243,7 @@ describe('parseFilterParams()', () => {
     });
 
     describe('boolean operators', () => {
-        describe('eq', () => {
+        it('eq', () => {
             const connection = new MockConnection();
             connection.setColumns(Product, [{ propertyName: 'available', type: 'tinyint' }]);
             const filterParams: FilterParameter<Product & { available: boolean }> = {

+ 1 - 0
packages/core/src/service/helpers/list-query-builder/parse-sort-params.spec.ts

@@ -2,6 +2,7 @@ import { Type } from '@vendure/common/lib/shared-types';
 import { DefaultNamingStrategy } from 'typeorm';
 import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
 import { RelationMetadata } from 'typeorm/metadata/RelationMetadata';
+import { describe, expect, it } from 'vitest';
 
 import { SortParameter } from '../../../common/types/common-types';
 import { CustomFieldConfig } from '../../../config/custom-field/custom-field-types';

+ 3 - 0
packages/core/src/service/helpers/order-calculator/order-calculator.spec.ts

@@ -1,10 +1,12 @@
 import { Test } from '@nestjs/testing';
 import { AdjustmentType, LanguageCode, TaxLine } from '@vendure/common/lib/generated-types';
 import { summate } from '@vendure/common/lib/shared-utils';
+import { beforeAll, describe, expect, it } from 'vitest';
 
 import { RequestContext } from '../../../api/common/request-context';
 import { RequestContextCacheService } from '../../../cache/request-context-cache.service';
 import { PromotionItemAction, PromotionOrderAction, PromotionShippingAction } from '../../../config';
+import { ensureConfigLoaded } from '../../../config/config-helpers';
 import { ConfigService } from '../../../config/config.service';
 import { MockConfigService } from '../../../config/config.service.mock';
 import { PromotionCondition } from '../../../config/promotion/promotion-condition';
@@ -41,6 +43,7 @@ describe('OrderCalculator', () => {
     let orderCalculator: OrderCalculator;
 
     beforeAll(async () => {
+        ensureConfigLoaded();
         const module = await createTestModule();
         orderCalculator = module.get(OrderCalculator);
         const mockConfigService = module.get<ConfigService, MockConfigService>(ConfigService);

+ 2 - 0
packages/core/src/service/helpers/order-calculator/prorate.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { prorate } from './prorate';
 
 describe('prorate()', () => {

+ 1 - 0
packages/core/src/service/helpers/order-merger/order-merger.spec.ts

@@ -1,4 +1,5 @@
 import { Test } from '@nestjs/testing';
+import { beforeEach, describe, expect, it } from 'vitest';
 
 import { RequestContext } from '../../../api/common/request-context';
 import { ConfigService } from '../../../config/config.service';

+ 1 - 1
packages/core/src/service/helpers/translatable-saver/translation-differ.spec.ts

@@ -1,7 +1,7 @@
 import { LanguageCode } from '@vendure/common/lib/generated-types';
+import { beforeEach, describe, expect, it } from 'vitest';
 
 import { TranslationInput } from '../../../common/types/locale-types';
-import { TransactionalConnection } from '../../../connection/index';
 import { ProductTranslation } from '../../../entity/product/product-translation.entity';
 import { Product } from '../../../entity/product/product.entity';
 

+ 2 - 0
packages/core/src/service/helpers/utils/order-utils.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { Order } from '../../../entity/order/order.entity';
 import { Payment } from '../../../entity/payment/payment.entity';
 

+ 2 - 0
packages/core/src/service/helpers/utils/patch-entity.spec.ts

@@ -1,3 +1,5 @@
+import { describe, expect, it } from 'vitest';
+
 import { patchEntity } from './patch-entity';
 
 describe('patchEntity()', () => {

+ 51 - 8
packages/core/src/service/helpers/utils/samples-each.spec.ts

@@ -1,7 +1,8 @@
+import { describe, expect, it } from 'vitest';
+
 import { samplesEach } from './samples-each';
 
 describe('samplesEach()', () => {
-
     it('single group match', () => {
         const result = samplesEach([1], [[1]]);
         expect(result).toBe(true);
@@ -13,33 +14,69 @@ describe('samplesEach()', () => {
     });
 
     it('does not sample all groups', () => {
-        const result = samplesEach([1, 3], [[0, 1, 3], [2, 5, 4]]);
+        const result = samplesEach(
+            [1, 3],
+            [
+                [0, 1, 3],
+                [2, 5, 4],
+            ],
+        );
         expect(result).toBe(false);
     });
 
     it('two groups in order', () => {
-        const result = samplesEach([1, 4], [[0, 1, 3], [2, 5, 4]]);
+        const result = samplesEach(
+            [1, 4],
+            [
+                [0, 1, 3],
+                [2, 5, 4],
+            ],
+        );
         expect(result).toBe(true);
     });
 
     it('two groups not in order', () => {
-        const result = samplesEach([1, 4], [[2, 5, 4], [0, 1, 3]]);
+        const result = samplesEach(
+            [1, 4],
+            [
+                [2, 5, 4],
+                [0, 1, 3],
+            ],
+        );
         expect(result).toBe(true);
     });
 
     it('three groups in order', () => {
-        const result = samplesEach([1, 4, 'a'], [[0, 1, 3], [2, 5, 4], ['b', 'a']]);
+        const result = samplesEach(
+            [1, 4, 'a'],
+            [
+                [0, 1, 3],
+                [2, 5, 4],
+                ['b', 'a'],
+            ],
+        );
         expect(result).toBe(true);
     });
 
     it('three groups not in order', () => {
-        const result = samplesEach([1, 4, 'a'], [[2, 5, 4], ['b', 'a'], [0, 1, 3]]);
+        const result = samplesEach(
+            [1, 4, 'a'],
+            [
+                [2, 5, 4],
+                ['b', 'a'],
+                [0, 1, 3],
+            ],
+        );
         expect(result).toBe(true);
     });
 
     it('input is unchanged', () => {
         const input = [1, 4, 'a'];
-        const result = samplesEach(input, [[2, 5, 4], ['b', 'a'], [0, 1, 3]]);
+        const result = samplesEach(input, [
+            [2, 5, 4],
+            ['b', 'a'],
+            [0, 1, 3],
+        ]);
         expect(result).toBe(true);
         expect(input).toEqual([1, 4, 'a']);
     });
@@ -50,7 +87,13 @@ describe('samplesEach()', () => {
     });
 
     it('length mismatch', () => {
-        const result = samplesEach([1, 4, 5], [[2, 5, 4], [0, 1, 3]]);
+        const result = samplesEach(
+            [1, 4, 5],
+            [
+                [2, 5, 4],
+                [0, 1, 3],
+            ],
+        );
         expect(result).toBe(false);
     });
 });

+ 1 - 0
packages/core/src/service/helpers/utils/translate-entity.spec.ts

@@ -1,4 +1,5 @@
 import { LanguageCode } from '@vendure/common/lib/generated-types';
+import { beforeEach, describe, expect, it } from 'vitest';
 
 import { Translatable, Translation } from '../../../common/types/locale-types';
 import { VendureEntity } from '../../../entity/base/base.entity';

+ 1 - 1
packages/core/src/testing/testing-types.ts

@@ -1 +1 @@
-export type MockClass<T> = { [K in keyof T]: jest.Mock<any> | any };
+export type MockClass<T> = { [K in keyof T]: vi.Mock<any> | any };

+ 11 - 0
packages/core/vitest.config.ts

@@ -0,0 +1,11 @@
+import swc from 'unplugin-swc';
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+    plugins: [
+        // SWC required to support decorators used in test plugins
+        // See https://github.com/vitest-dev/vitest/issues/708#issuecomment-1118628479
+        // Vite plugin
+        swc.vite(),
+    ],
+});

+ 1 - 1
packages/elasticsearch-plugin/package.json

@@ -11,7 +11,7 @@
     "watch": "tsc -p ./tsconfig.build.json --watch",
     "build": "rimraf lib && tsc -p ./tsconfig.build.json",
     "lint": "tslint --fix --project ./",
-    "test": "jest --config ./jest.config.js",
+    "test": "vitest --run",
     "e2e": "cross-env PACKAGE=elasticsearch-plugin vitest --config ../../e2e-common/vitest.config.ts --run",
     "e2e:watch": "cross-env PACKAGE=elasticsearch-plugin vitest --config ../../e2e-common/vitest.config.ts"
   },

+ 1 - 0
packages/elasticsearch-plugin/src/build-elastic-body.spec.ts

@@ -1,5 +1,6 @@
 import { LanguageCode, LogicalOperator, SortOrder } from '@vendure/common/lib/generated-types';
 import { DeepRequired } from '@vendure/core';
+import { describe, expect, it } from 'vitest';
 
 import { buildElasticBody } from './build-elastic-body';
 import { defaultOptions, SearchConfig } from './options';

+ 1 - 1
packages/email-plugin/package.json

@@ -13,7 +13,7 @@
     "watch": "tsc -p ./tsconfig.build.json --watch",
     "build": "rimraf lib && tsc -p ./tsconfig.build.json",
     "lint": "tslint --fix --project ./",
-    "test": "jest --config ./jest.config.js"
+    "test": "vitest --config ./vitest.config.ts --run"
   },
   "homepage": "https://www.vendure.io/",
   "funding": "https://github.com/sponsors/michaelbromley",

+ 7 - 5
packages/email-plugin/src/plugin.spec.ts

@@ -12,11 +12,12 @@ import {
     RequestContext,
     VendureEvent,
 } from '@vendure/core';
+import { ensureConfigLoaded } from '@vendure/core/dist/config/config-helpers';
 import { TestingLogger } from '@vendure/testing';
 import { createReadStream, readFileSync } from 'fs';
-import { readFile } from 'fs-extra';
 import path from 'path';
 import { Readable } from 'stream';
+import { afterEach, beforeEach, describe, expect, it, Mock, vi } from 'vitest';
 
 import { orderConfirmationHandler } from './default-email-handlers';
 import { EmailSender } from './email-sender';
@@ -27,16 +28,17 @@ import { EmailDetails, EmailPluginOptions, EmailTransportOptions } from './types
 
 describe('EmailPlugin', () => {
     let eventBus: EventBus;
-    let onSend: jest.Mock;
+    let onSend: Mock;
     let module: TestingModule;
 
-    const testingLogger = new TestingLogger((...args) => jest.fn(...args));
+    const testingLogger = new TestingLogger((...args) => vi.fn(...args));
 
     async function initPluginWithHandlers(
         handlers: Array<EmailEventHandler<string, any>>,
         options?: Partial<EmailPluginOptions>,
     ) {
-        onSend = jest.fn();
+        await ensureConfigLoaded();
+        onSend = vi.fn();
         module = await Test.createTestingModule({
             imports: [
                 TypeOrmModule.forRoot({
@@ -790,7 +792,7 @@ describe('EmailPlugin', () => {
                 .setTemplateVars(event => ({ subjectVar: 'foo' }));
 
             const fakeSender = new FakeCustomSender();
-            const send = jest.fn();
+            const send = vi.fn();
             fakeSender.send = send;
 
             await initPluginWithHandlers([handler], {

+ 11 - 0
packages/email-plugin/vitest.config.ts

@@ -0,0 +1,11 @@
+import swc from 'unplugin-swc';
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+    plugins: [
+        // SWC required to support decorators used in test plugins
+        // See https://github.com/vitest-dev/vitest/issues/708#issuecomment-1118628479
+        // Vite plugin
+        swc.vite(),
+    ],
+});

+ 1 - 1
packages/job-queue-plugin/package.json

@@ -12,7 +12,7 @@
     "watch": "tsc -p ./tsconfig.build.json --watch",
     "build": "rimraf package && tsc -p ./tsconfig.build.json",
     "lint": "tslint --fix --project ./",
-    "test": "jest --config ./jest.config.js",
+    "test": "vitest --run",
     "e2e-wip": "node e2e/check-connection.js || jest --config ../../e2e-common/jest-config.js --runInBand --package=job-queue-plugin",
     "ci": "yarn build"
   },

+ 3 - 2
packages/job-queue-plugin/src/pub-sub/pub-sub-job-queue-strategy.spec.ts

@@ -2,6 +2,7 @@ import { PubSub } from '@google-cloud/pubsub';
 import { ModuleRef } from '@nestjs/core';
 import { Test } from '@nestjs/testing';
 import { Injector, Job } from '@vendure/core';
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
 
 import { PUB_SUB_OPTIONS } from './constants';
 import { PubSubOptions } from './options';
@@ -14,10 +15,10 @@ describe('PubSubJobQueueStrategy', () => {
 
     beforeEach(async () => {
         topic = {
-            publish: jest.fn(),
+            publish: vi.fn(),
         };
         pubsub = {
-            topic: jest.fn(() => {
+            topic: vi.fn(() => {
                 return topic;
             }),
         };