|
|
@@ -8,6 +8,7 @@ import {
|
|
|
Order,
|
|
|
OrderStateTransitionEvent,
|
|
|
PluginCommonModule,
|
|
|
+ RequestContext,
|
|
|
VendureEvent,
|
|
|
} from '@vendure/core';
|
|
|
import path from 'path';
|
|
|
@@ -22,16 +23,18 @@ describe('EmailPlugin', () => {
|
|
|
let plugin: EmailPlugin;
|
|
|
let eventBus: EventBus;
|
|
|
let onSend: jest.Mock;
|
|
|
+ let module: TestingModule;
|
|
|
|
|
|
async function initPluginWithHandlers(
|
|
|
handlers: Array<EmailEventHandler<string, any>>,
|
|
|
options?: Partial<EmailPluginOptions>,
|
|
|
) {
|
|
|
onSend = jest.fn();
|
|
|
- const module = await Test.createTestingModule({
|
|
|
+ module = await Test.createTestingModule({
|
|
|
imports: [
|
|
|
TypeOrmModule.forRoot({
|
|
|
type: 'sqljs',
|
|
|
+ retryAttempts: 0,
|
|
|
}),
|
|
|
PluginCommonModule,
|
|
|
EmailPlugin.init({
|
|
|
@@ -53,11 +56,17 @@ describe('EmailPlugin', () => {
|
|
|
return module;
|
|
|
}
|
|
|
|
|
|
+ afterEach(async () => {
|
|
|
+ if (module) {
|
|
|
+ await module.close();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
it('setting from, recipient, subject', async () => {
|
|
|
- const ctx = {
|
|
|
- channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
- languageCode: LanguageCode.en,
|
|
|
- } as any;
|
|
|
+ const ctx = RequestContext.fromObject({
|
|
|
+ _channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
+ _languageCode: LanguageCode.en,
|
|
|
+ });
|
|
|
const handler = new EmailEventListener('test')
|
|
|
.on(MockEvent)
|
|
|
.setFrom('"test from" <noreply@test.com>')
|
|
|
@@ -65,21 +74,20 @@ describe('EmailPlugin', () => {
|
|
|
.setSubject('Hello')
|
|
|
.setTemplateVars(event => ({ subjectVar: 'foo' }));
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler]);
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
|
|
|
eventBus.publish(new MockEvent(ctx, true));
|
|
|
await pause();
|
|
|
expect(onSend.mock.calls[0][0].subject).toBe('Hello');
|
|
|
expect(onSend.mock.calls[0][0].recipient).toBe('test@test.com');
|
|
|
expect(onSend.mock.calls[0][0].from).toBe('"test from" <noreply@test.com>');
|
|
|
- await module.close();
|
|
|
});
|
|
|
|
|
|
describe('event filtering', () => {
|
|
|
- const ctx = {
|
|
|
- channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
- languageCode: LanguageCode.en,
|
|
|
- } as any;
|
|
|
+ const ctx = RequestContext.fromObject({
|
|
|
+ _channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
+ _languageCode: LanguageCode.en,
|
|
|
+ });
|
|
|
|
|
|
it('single filter', async () => {
|
|
|
const handler = new EmailEventListener('test')
|
|
|
@@ -89,7 +97,7 @@ describe('EmailPlugin', () => {
|
|
|
.setFrom('"test from" <noreply@test.com>')
|
|
|
.setSubject('test subject');
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler]);
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
|
|
|
eventBus.publish(new MockEvent(ctx, false));
|
|
|
await pause();
|
|
|
@@ -98,28 +106,28 @@ describe('EmailPlugin', () => {
|
|
|
eventBus.publish(new MockEvent(ctx, true));
|
|
|
await pause();
|
|
|
expect(onSend).toHaveBeenCalledTimes(1);
|
|
|
- await module.close();
|
|
|
});
|
|
|
|
|
|
it('multiple filters', async () => {
|
|
|
const handler = new EmailEventListener('test')
|
|
|
.on(MockEvent)
|
|
|
.filter(event => event.shouldSend === true)
|
|
|
- .filter(event => !!event.ctx.user)
|
|
|
+ .filter(event => !!event.ctx.activeUserId)
|
|
|
.setFrom('"test from" <noreply@test.com>')
|
|
|
.setRecipient(() => 'test@test.com')
|
|
|
.setSubject('test subject');
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler]);
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
|
|
|
eventBus.publish(new MockEvent(ctx, true));
|
|
|
await pause();
|
|
|
expect(onSend).not.toHaveBeenCalled();
|
|
|
|
|
|
- eventBus.publish(new MockEvent({ ...ctx, user: 'joe' }, true));
|
|
|
+ const ctxWithUser = RequestContext.fromObject({ ...ctx, _session: { user: { id: 42 } } });
|
|
|
+
|
|
|
+ eventBus.publish(new MockEvent(ctxWithUser, true));
|
|
|
await pause();
|
|
|
expect(onSend).toHaveBeenCalledTimes(1);
|
|
|
- await module.close();
|
|
|
});
|
|
|
|
|
|
it('with .loadData() after .filter()', async () => {
|
|
|
@@ -131,7 +139,7 @@ describe('EmailPlugin', () => {
|
|
|
.setFrom('"test from" <noreply@test.com>')
|
|
|
.setSubject('test subject');
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler]);
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
|
|
|
eventBus.publish(new MockEvent(ctx, false));
|
|
|
await pause();
|
|
|
@@ -140,15 +148,14 @@ describe('EmailPlugin', () => {
|
|
|
eventBus.publish(new MockEvent(ctx, true));
|
|
|
await pause();
|
|
|
expect(onSend).toHaveBeenCalledTimes(1);
|
|
|
- await module.close();
|
|
|
});
|
|
|
});
|
|
|
|
|
|
describe('templateVars', () => {
|
|
|
- const ctx = {
|
|
|
- channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
- languageCode: LanguageCode.en,
|
|
|
- } as any;
|
|
|
+ const ctx = RequestContext.fromObject({
|
|
|
+ _channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
+ _languageCode: LanguageCode.en,
|
|
|
+ });
|
|
|
|
|
|
it('interpolates subject', async () => {
|
|
|
const handler = new EmailEventListener('test')
|
|
|
@@ -158,12 +165,11 @@ describe('EmailPlugin', () => {
|
|
|
.setSubject('Hello {{ subjectVar }}')
|
|
|
.setTemplateVars(event => ({ subjectVar: 'foo' }));
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler]);
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
|
|
|
eventBus.publish(new MockEvent(ctx, true));
|
|
|
await pause();
|
|
|
expect(onSend.mock.calls[0][0].subject).toBe('Hello foo');
|
|
|
- await module.close();
|
|
|
});
|
|
|
|
|
|
it('interpolates body', async () => {
|
|
|
@@ -174,12 +180,31 @@ describe('EmailPlugin', () => {
|
|
|
.setSubject('Hello')
|
|
|
.setTemplateVars(event => ({ testVar: 'this is the test var' }));
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler]);
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
|
|
|
eventBus.publish(new MockEvent(ctx, true));
|
|
|
await pause();
|
|
|
expect(onSend.mock.calls[0][0].body).toContain('this is the test var');
|
|
|
- await module.close();
|
|
|
+ });
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Intended to test the ability for Handlebars to interpolate
|
|
|
+ * getters on the Order entity prototype.
|
|
|
+ * See https://github.com/vendure-ecommerce/vendure/issues/259
|
|
|
+ */
|
|
|
+ it('interpolates body with property from entity', async () => {
|
|
|
+ const handler = new EmailEventListener('test')
|
|
|
+ .on(MockEvent)
|
|
|
+ .setFrom('"test from" <noreply@test.com>')
|
|
|
+ .setRecipient(() => 'test@test.com')
|
|
|
+ .setSubject('Hello')
|
|
|
+ .setTemplateVars(event => ({ order: new Order({ subTotal: 123 }) }));
|
|
|
+
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
+
|
|
|
+ eventBus.publish(new MockEvent(ctx, true));
|
|
|
+ await pause();
|
|
|
+ expect(onSend.mock.calls[0][0].body).toContain('Total: 123');
|
|
|
});
|
|
|
|
|
|
it('interpolates globalTemplateVars', async () => {
|
|
|
@@ -189,14 +214,13 @@ describe('EmailPlugin', () => {
|
|
|
.setRecipient(() => 'test@test.com')
|
|
|
.setSubject('Hello {{ globalVar }}');
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler], {
|
|
|
+ await initPluginWithHandlers([handler], {
|
|
|
globalTemplateVars: { globalVar: 'baz' },
|
|
|
});
|
|
|
|
|
|
eventBus.publish(new MockEvent(ctx, true));
|
|
|
await pause();
|
|
|
expect(onSend.mock.calls[0][0].subject).toBe('Hello baz');
|
|
|
- await module.close();
|
|
|
});
|
|
|
|
|
|
it('interpolates from', async () => {
|
|
|
@@ -206,14 +230,13 @@ describe('EmailPlugin', () => {
|
|
|
.setRecipient(() => 'test@test.com')
|
|
|
.setSubject('Hello');
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler], {
|
|
|
+ await initPluginWithHandlers([handler], {
|
|
|
globalTemplateVars: { globalVar: 'baz' },
|
|
|
});
|
|
|
|
|
|
eventBus.publish(new MockEvent(ctx, true));
|
|
|
await pause();
|
|
|
expect(onSend.mock.calls[0][0].from).toBe('"test from baz" <noreply@test.com>');
|
|
|
- await module.close();
|
|
|
});
|
|
|
|
|
|
it('globalTemplateVars available in setTemplateVars method', async () => {
|
|
|
@@ -224,14 +247,13 @@ describe('EmailPlugin', () => {
|
|
|
.setSubject('Hello {{ testVar }}')
|
|
|
.setTemplateVars((event, globals) => ({ testVar: globals.globalVar + ' quux' }));
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler], {
|
|
|
+ await initPluginWithHandlers([handler], {
|
|
|
globalTemplateVars: { globalVar: 'baz' },
|
|
|
});
|
|
|
|
|
|
eventBus.publish(new MockEvent(ctx, true));
|
|
|
await pause();
|
|
|
expect(onSend.mock.calls[0][0].subject).toBe('Hello baz quux');
|
|
|
- await module.close();
|
|
|
});
|
|
|
|
|
|
it('setTemplateVars overrides globals', async () => {
|
|
|
@@ -242,20 +264,56 @@ describe('EmailPlugin', () => {
|
|
|
.setSubject('Hello {{ name }}')
|
|
|
.setTemplateVars((event, globals) => ({ name: 'quux' }));
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler], { globalTemplateVars: { name: 'baz' } });
|
|
|
+ await initPluginWithHandlers([handler], { globalTemplateVars: { name: 'baz' } });
|
|
|
|
|
|
eventBus.publish(new MockEvent(ctx, true));
|
|
|
await pause();
|
|
|
expect(onSend.mock.calls[0][0].subject).toBe('Hello quux');
|
|
|
- await module.close();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ describe('handlebars helpers', () => {
|
|
|
+ const ctx = RequestContext.fromObject({
|
|
|
+ _channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
+ _languageCode: LanguageCode.en,
|
|
|
+ });
|
|
|
+
|
|
|
+ it('formateDate', async () => {
|
|
|
+ const handler = new EmailEventListener('test-helpers')
|
|
|
+ .on(MockEvent)
|
|
|
+ .setFrom('"test from" <noreply@test.com>')
|
|
|
+ .setRecipient(() => 'test@test.com')
|
|
|
+ .setSubject('Hello')
|
|
|
+ .setTemplateVars(event => ({ myDate: new Date('2020-01-01T10:00:00.000Z'), myPrice: 0 }));
|
|
|
+
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
+
|
|
|
+ eventBus.publish(new MockEvent(ctx, true));
|
|
|
+ await pause();
|
|
|
+ expect(onSend.mock.calls[0][0].body).toContain('Date: Wed Jan 01 2020 11:00:00');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('formateMoney', async () => {
|
|
|
+ const handler = new EmailEventListener('test-helpers')
|
|
|
+ .on(MockEvent)
|
|
|
+ .setFrom('"test from" <noreply@test.com>')
|
|
|
+ .setRecipient(() => 'test@test.com')
|
|
|
+ .setSubject('Hello')
|
|
|
+ .setTemplateVars(event => ({ myDate: new Date(), myPrice: 123 }));
|
|
|
+
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
+
|
|
|
+ eventBus.publish(new MockEvent(ctx, true));
|
|
|
+ await pause();
|
|
|
+ expect(onSend.mock.calls[0][0].body).toContain('Price: 1.23');
|
|
|
});
|
|
|
});
|
|
|
|
|
|
describe('multiple configs', () => {
|
|
|
- const ctx = {
|
|
|
- channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
- languageCode: LanguageCode.en,
|
|
|
- } as any;
|
|
|
+ const ctx = RequestContext.fromObject({
|
|
|
+ _channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
+ _languageCode: LanguageCode.en,
|
|
|
+ });
|
|
|
|
|
|
it('additional LanguageCode', async () => {
|
|
|
const handler = new EmailEventListener('test')
|
|
|
@@ -271,18 +329,19 @@ describe('EmailPlugin', () => {
|
|
|
subject: 'Servus, {{ name }}!',
|
|
|
});
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler]);
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
|
|
|
- eventBus.publish(new MockEvent({ ...ctx, languageCode: LanguageCode.ta }, true));
|
|
|
+ const ctxTa = RequestContext.fromObject({ ...ctx, _languageCode: LanguageCode.ta });
|
|
|
+ eventBus.publish(new MockEvent(ctxTa, true));
|
|
|
await pause();
|
|
|
expect(onSend.mock.calls[0][0].subject).toBe('Hello, Test!');
|
|
|
expect(onSend.mock.calls[0][0].body).toContain('Default body.');
|
|
|
|
|
|
- eventBus.publish(new MockEvent({ ...ctx, languageCode: LanguageCode.de }, true));
|
|
|
+ const ctxDe = RequestContext.fromObject({ ...ctx, _languageCode: LanguageCode.de });
|
|
|
+ eventBus.publish(new MockEvent(ctxDe, true));
|
|
|
await pause();
|
|
|
expect(onSend.mock.calls[1][0].subject).toBe('Servus, Test!');
|
|
|
expect(onSend.mock.calls[1][0].body).toContain('German body.');
|
|
|
- await module.close();
|
|
|
});
|
|
|
});
|
|
|
|
|
|
@@ -299,18 +358,20 @@ describe('EmailPlugin', () => {
|
|
|
.setRecipient(() => 'test@test.com')
|
|
|
.setTemplateVars(event => ({ testData: event.data }));
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler]);
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
|
|
|
eventBus.publish(
|
|
|
new MockEvent(
|
|
|
- { channel: { code: DEFAULT_CHANNEL_CODE }, languageCode: LanguageCode.en },
|
|
|
+ RequestContext.fromObject({
|
|
|
+ _channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
+ _languageCode: LanguageCode.en,
|
|
|
+ }),
|
|
|
true,
|
|
|
),
|
|
|
);
|
|
|
await pause();
|
|
|
|
|
|
expect(onSend.mock.calls[0][0].subject).toBe('Hello, loaded data!');
|
|
|
- await module.close();
|
|
|
});
|
|
|
|
|
|
it('works when loadData is called after other setup', async () => {
|
|
|
@@ -325,11 +386,14 @@ describe('EmailPlugin', () => {
|
|
|
})
|
|
|
.setTemplateVars(event => ({ testData: event.data }));
|
|
|
|
|
|
- const module = await initPluginWithHandlers([handler]);
|
|
|
+ await initPluginWithHandlers([handler]);
|
|
|
|
|
|
eventBus.publish(
|
|
|
new MockEvent(
|
|
|
- { channel: { code: DEFAULT_CHANNEL_CODE }, languageCode: LanguageCode.en },
|
|
|
+ RequestContext.fromObject({
|
|
|
+ _channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
+ _languageCode: LanguageCode.en,
|
|
|
+ }),
|
|
|
true,
|
|
|
),
|
|
|
);
|
|
|
@@ -338,27 +402,21 @@ describe('EmailPlugin', () => {
|
|
|
expect(onSend.mock.calls[0][0].subject).toBe('Hello, loaded data!');
|
|
|
expect(onSend.mock.calls[0][0].from).toBe('"test from" <noreply@test.com>');
|
|
|
expect(onSend.mock.calls[0][0].recipient).toBe('test@test.com');
|
|
|
- await module.close();
|
|
|
});
|
|
|
});
|
|
|
|
|
|
describe('orderConfirmationHandler', () => {
|
|
|
- let module: TestingModule;
|
|
|
beforeEach(async () => {
|
|
|
module = await initPluginWithHandlers([orderConfirmationHandler], {
|
|
|
templatePath: path.join(__dirname, '../templates'),
|
|
|
});
|
|
|
});
|
|
|
|
|
|
- afterEach(async () => {
|
|
|
- await module.close();
|
|
|
+ const ctx = RequestContext.fromObject({
|
|
|
+ _channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
+ _languageCode: LanguageCode.en,
|
|
|
});
|
|
|
|
|
|
- const ctx = {
|
|
|
- channel: { code: DEFAULT_CHANNEL_CODE },
|
|
|
- languageCode: LanguageCode.en,
|
|
|
- } as any;
|
|
|
-
|
|
|
const order = ({
|
|
|
code: 'ABCDE',
|
|
|
customer: {
|
|
|
@@ -400,10 +458,10 @@ describe('EmailPlugin', () => {
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-const pause = () => new Promise(resolve => setTimeout(resolve, 50));
|
|
|
+const pause = () => new Promise(resolve => setTimeout(resolve, 100));
|
|
|
|
|
|
class MockEvent extends VendureEvent {
|
|
|
- constructor(public ctx: any, public shouldSend: boolean) {
|
|
|
+ constructor(public ctx: RequestContext, public shouldSend: boolean) {
|
|
|
super();
|
|
|
}
|
|
|
}
|