test-server.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import { INestApplication, INestMicroservice } from '@nestjs/common';
  2. import { NestFactory } from '@nestjs/core';
  3. import { Omit } from '@vendure/common/lib/omit';
  4. import fs from 'fs';
  5. import path from 'path';
  6. import { ConnectionOptions } from 'typeorm';
  7. import { SqljsConnectionOptions } from 'typeorm/driver/sqljs/SqljsConnectionOptions';
  8. import { populateForTesting, PopulateOptions } from '../mock-data/populate-for-testing';
  9. import { preBootstrapConfig, runPluginOnBootstrapMethods } from '../src/bootstrap';
  10. import { Mutable } from '../src/common/types/common-types';
  11. import { Logger } from '../src/config/logger/vendure-logger';
  12. import { VendureConfig } from '../src/config/vendure-config';
  13. import { testConfig } from './config/test-config';
  14. import { setTestEnvironment } from './utils/test-environment';
  15. // tslint:disable:no-console
  16. /**
  17. * A server against which the e2e tests should be run.
  18. */
  19. export class TestServer {
  20. app: INestApplication;
  21. worker?: INestMicroservice;
  22. /**
  23. * Bootstraps an instance of Vendure server and populates the database according to the options
  24. * passed in. Should be called immediately after creating the client in the `beforeAll` function.
  25. *
  26. * The populated data is saved into an .sqlite file for each test file. On subsequent runs, this file
  27. * is loaded so that the populate step can be skipped, which speeds up the tests significantly.
  28. */
  29. async init(
  30. options: Omit<PopulateOptions, 'initialDataPath'>,
  31. customConfig: Partial<VendureConfig> = {},
  32. ): Promise<void> {
  33. setTestEnvironment();
  34. const testingConfig = { ...testConfig, ...customConfig };
  35. if (options.logging) {
  36. (testingConfig.dbConnectionOptions as Mutable<ConnectionOptions>).logging = true;
  37. }
  38. const dbFilePath = this.getDbFilePath();
  39. (testingConfig.dbConnectionOptions as Mutable<SqljsConnectionOptions>).location = dbFilePath;
  40. if (!fs.existsSync(dbFilePath)) {
  41. if (options.logging) {
  42. console.log(`Test data not found. Populating database and caching...`);
  43. }
  44. await this.populateInitialData(testingConfig, options);
  45. }
  46. if (options.logging) {
  47. console.log(`Loading test data from "${dbFilePath}"`);
  48. }
  49. const [app, worker] = await this.bootstrapForTesting(testingConfig);
  50. if (app) {
  51. this.app = app;
  52. } else {
  53. console.error(`Could not bootstrap app`);
  54. process.exit(1);
  55. }
  56. if (worker) {
  57. this.worker = worker;
  58. }
  59. }
  60. /**
  61. * Destroy the Vendure instance. Should be called in the `afterAll` function.
  62. */
  63. async destroy() {
  64. await this.app.close();
  65. if (this.worker) {
  66. await this.worker.close();
  67. }
  68. }
  69. private getDbFilePath() {
  70. const dbDataDir = '__data__';
  71. // tslint:disable-next-line:no-non-null-assertion
  72. const testFilePath = module!.parent!.filename;
  73. const dbFileName = path.basename(testFilePath) + '.sqlite';
  74. const dbFilePath = path.join(path.dirname(testFilePath), dbDataDir, dbFileName);
  75. return dbFilePath;
  76. }
  77. /**
  78. * Populates an .sqlite database file based on the PopulateOptions.
  79. */
  80. private async populateInitialData(
  81. testingConfig: VendureConfig,
  82. options: Omit<PopulateOptions, 'initialDataPath'>,
  83. ): Promise<void> {
  84. (testingConfig.dbConnectionOptions as Mutable<SqljsConnectionOptions>).autoSave = true;
  85. const [app, worker] = await populateForTesting(testingConfig, this.bootstrapForTesting, {
  86. logging: false,
  87. ...{
  88. ...options,
  89. initialDataPath: path.join(__dirname, 'fixtures/e2e-initial-data.ts'),
  90. },
  91. });
  92. await app.close();
  93. if (worker) {
  94. await worker.close();
  95. }
  96. (testingConfig.dbConnectionOptions as Mutable<SqljsConnectionOptions>).autoSave = false;
  97. }
  98. /**
  99. * Bootstraps an instance of the Vendure server for testing against.
  100. */
  101. private async bootstrapForTesting(userConfig: Partial<VendureConfig>): Promise<[INestApplication, INestMicroservice | undefined]> {
  102. const config = await preBootstrapConfig(userConfig);
  103. const appModule = await import('../src/app.module');
  104. try {
  105. const app = await NestFactory.create(appModule.AppModule, {cors: config.cors, logger: false});
  106. let worker: INestMicroservice | undefined;
  107. await runPluginOnBootstrapMethods(config, app);
  108. await app.listen(config.port);
  109. if (config.workerOptions.runInMainProcess) {
  110. const workerModule = await import('../src/worker/worker.module');
  111. worker = await NestFactory.createMicroservice(workerModule.WorkerModule, {
  112. transport: config.workerOptions.transport,
  113. logger: new Logger(),
  114. options: config.workerOptions.options,
  115. });
  116. await worker.listenAsync();
  117. }
  118. return [app, worker];
  119. } catch (e) {
  120. console.log(e);
  121. throw e;
  122. }
  123. }
  124. }