plugin.spec.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /* tslint:disable:no-non-null-assertion */
  2. import { DEFAULT_CHANNEL_CODE } from '@vendure/common/lib/shared-constants';
  3. import { LanguageCode } from '@vendure/common/src/generated-types';
  4. import { EventBus, Order, OrderStateTransitionEvent, VendureEvent } from '@vendure/core';
  5. import path from 'path';
  6. import { orderConfirmationHandler } from './default-email-handlers';
  7. import { EmailEventHandler, EmailEventListener } from './event-listener';
  8. import { EmailPlugin } from './plugin';
  9. import { EmailPluginOptions } from './types';
  10. describe('EmailPlugin', () => {
  11. let plugin: EmailPlugin;
  12. let eventBus: EventBus;
  13. let onSend: jest.Mock;
  14. async function initPluginWithHandlers(handlers: Array<EmailEventHandler<string, any>>, options?: Partial<EmailPluginOptions>) {
  15. eventBus = new EventBus();
  16. onSend = jest.fn();
  17. plugin = new EmailPlugin({
  18. templatePath: path.join(__dirname, '../test-templates'),
  19. transport: {
  20. type: 'testing',
  21. onSend,
  22. },
  23. handlers,
  24. ...options,
  25. });
  26. const inject = (token: any): any => {
  27. if (token === EventBus) {
  28. return eventBus;
  29. } else {
  30. throw new Error(`Was not expecting to inject the token ${token}`);
  31. }
  32. };
  33. await plugin.onBootstrap(inject);
  34. }
  35. describe('event filtering', () => {
  36. const ctx = {
  37. channel: { code: DEFAULT_CHANNEL_CODE },
  38. languageCode: LanguageCode.en,
  39. } as any;
  40. it('single filter', async () => {
  41. const handler = new EmailEventListener('test')
  42. .on(MockEvent)
  43. .filter(event => event.shouldSend === true)
  44. .setRecipient(() => 'test@test.com')
  45. .setSubject('test subject');
  46. await initPluginWithHandlers([handler]);
  47. eventBus.publish(new MockEvent(ctx, false));
  48. await pause();
  49. expect(onSend).not.toHaveBeenCalled();
  50. eventBus.publish(new MockEvent(ctx, true));
  51. await pause();
  52. expect(onSend).toHaveBeenCalledTimes(1);
  53. });
  54. it('multiple filters', async () => {
  55. const handler = new EmailEventListener('test')
  56. .on(MockEvent)
  57. .filter(event => event.shouldSend === true)
  58. .filter(event => !!event.ctx.user)
  59. .setRecipient(() => 'test@test.com')
  60. .setSubject('test subject');
  61. await initPluginWithHandlers([handler]);
  62. eventBus.publish(new MockEvent(ctx, true));
  63. await pause();
  64. expect(onSend).not.toHaveBeenCalled();
  65. eventBus.publish(new MockEvent({ ...ctx, user: 'joe' }, true));
  66. await pause();
  67. expect(onSend).toHaveBeenCalledTimes(1);
  68. });
  69. });
  70. describe('templateVars', () => {
  71. const ctx = {
  72. channel: { code: DEFAULT_CHANNEL_CODE },
  73. languageCode: LanguageCode.en,
  74. } as any;
  75. it('interpolates subject', async () => {
  76. const handler = new EmailEventListener('test')
  77. .on(MockEvent)
  78. .setRecipient(() => 'test@test.com')
  79. .setSubject('Hello {{ subjectVar }}')
  80. .setTemplateVars(event => ({ subjectVar: 'foo' }));
  81. await initPluginWithHandlers([handler]);
  82. eventBus.publish(new MockEvent(ctx, true));
  83. await pause();
  84. expect(onSend.mock.calls[0][0].subject).toBe('Hello foo');
  85. });
  86. it('interpolates body', async () => {
  87. const handler = new EmailEventListener('test')
  88. .on(MockEvent)
  89. .setRecipient(() => 'test@test.com')
  90. .setSubject('Hello')
  91. .setTemplateVars(event => ({ testVar: 'this is the test var' }));
  92. await initPluginWithHandlers([handler]);
  93. eventBus.publish(new MockEvent(ctx, true));
  94. await pause();
  95. expect(onSend.mock.calls[0][0].body).toContain('this is the test var');
  96. });
  97. it('interpolates globalTemplateVars', async () => {
  98. const handler = new EmailEventListener('test')
  99. .on(MockEvent)
  100. .setRecipient(() => 'test@test.com')
  101. .setSubject('Hello {{ globalVar }}');
  102. await initPluginWithHandlers([handler], { globalTemplateVars: { globalVar: 'baz' } });
  103. eventBus.publish(new MockEvent(ctx, true));
  104. await pause();
  105. expect(onSend.mock.calls[0][0].subject).toBe('Hello baz');
  106. });
  107. it('globalTemplateVars available in setTemplateVars method', async () => {
  108. const handler = new EmailEventListener('test')
  109. .on(MockEvent)
  110. .setRecipient(() => 'test@test.com')
  111. .setSubject('Hello {{ testVar }}')
  112. .setTemplateVars((event, globals) => ({ testVar: globals.globalVar + ' quux' }));
  113. await initPluginWithHandlers([handler], { globalTemplateVars: { globalVar: 'baz' } });
  114. eventBus.publish(new MockEvent(ctx, true));
  115. await pause();
  116. expect(onSend.mock.calls[0][0].subject).toBe('Hello baz quux');
  117. });
  118. it('setTemplateVars overrides globals', async () => {
  119. const handler = new EmailEventListener('test')
  120. .on(MockEvent)
  121. .setRecipient(() => 'test@test.com')
  122. .setSubject('Hello {{ name }}')
  123. .setTemplateVars((event, globals) => ({ name: 'quux' }));
  124. await initPluginWithHandlers([handler], { globalTemplateVars: { name: 'baz' } });
  125. eventBus.publish(new MockEvent(ctx, true));
  126. await pause();
  127. expect(onSend.mock.calls[0][0].subject).toBe('Hello quux');
  128. });
  129. });
  130. describe('multiple configs', () => {
  131. const ctx = {
  132. channel: { code: DEFAULT_CHANNEL_CODE },
  133. languageCode: LanguageCode.en,
  134. } as any;
  135. it('additional LanguageCode', async () => {
  136. const handler = new EmailEventListener('test')
  137. .on(MockEvent)
  138. .setSubject('Hello, {{ name }}!')
  139. .setRecipient(() => 'test@test.com')
  140. .setTemplateVars(() => ({ name: 'Test' }))
  141. .addTemplate({
  142. channelCode: 'default',
  143. languageCode: LanguageCode.de,
  144. templateFile: 'body.de.hbs',
  145. subject: 'Servus, {{ name }}!',
  146. });
  147. await initPluginWithHandlers([handler]);
  148. eventBus.publish(new MockEvent({ ...ctx, languageCode: LanguageCode.ta }, true));
  149. await pause();
  150. expect(onSend.mock.calls[0][0].subject).toBe('Hello, Test!');
  151. expect(onSend.mock.calls[0][0].body).toContain('Default body.');
  152. eventBus.publish(new MockEvent({ ...ctx, languageCode: LanguageCode.de }, true));
  153. await pause();
  154. expect(onSend.mock.calls[1][0].subject).toBe('Servus, Test!');
  155. expect(onSend.mock.calls[1][0].body).toContain('German body.');
  156. });
  157. });
  158. describe('orderConfirmationHandler', () => {
  159. beforeEach(async () => {
  160. await initPluginWithHandlers([orderConfirmationHandler], { templatePath: path.join(__dirname, '../templates') });
  161. });
  162. const ctx = {
  163. channel: { code: DEFAULT_CHANNEL_CODE },
  164. languageCode: LanguageCode.en,
  165. } as any;
  166. const order = {
  167. code: 'ABCDE',
  168. customer: {
  169. emailAddress: 'test@test.com',
  170. },
  171. } as Partial<Order> as any;
  172. it('filters events with wrong order state', async () => {
  173. eventBus.publish(new OrderStateTransitionEvent('AddingItems', 'ArrangingPayment', ctx, order));
  174. await pause();
  175. expect(onSend).not.toHaveBeenCalled();
  176. eventBus.publish(new OrderStateTransitionEvent('AddingItems', 'Cancelled', ctx, order));
  177. await pause();
  178. expect(onSend).not.toHaveBeenCalled();
  179. eventBus.publish(new OrderStateTransitionEvent('AddingItems', 'OrderComplete', ctx, order));
  180. await pause();
  181. expect(onSend).not.toHaveBeenCalled();
  182. eventBus.publish(new OrderStateTransitionEvent('ArrangingPayment', 'PaymentSettled', ctx, order));
  183. await pause();
  184. expect(onSend).toHaveBeenCalledTimes(1);
  185. });
  186. it('sets the Order Customer emailAddress as recipient', async () => {
  187. eventBus.publish(new OrderStateTransitionEvent('ArrangingPayment', 'PaymentSettled', ctx, order));
  188. await pause();
  189. expect(onSend.mock.calls[0][0].recipient).toBe(order.customer!.emailAddress);
  190. });
  191. it('sets the subject', async () => {
  192. eventBus.publish(new OrderStateTransitionEvent('ArrangingPayment', 'PaymentSettled', ctx, order));
  193. await pause();
  194. expect(onSend.mock.calls[0][0].subject).toBe(`Order confirmation for #${order.code}`);
  195. });
  196. });
  197. });
  198. const pause = () => new Promise(resolve => setTimeout(resolve, 50));
  199. class MockEvent extends VendureEvent {
  200. constructor(public ctx: any, public shouldSend: boolean) {
  201. super();
  202. }
  203. }