test-server.ts 5.2 KB

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