@ackplus/nest-dynamic-templates 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/{index.ts → index.d.ts} +2 -14
- package/src/index.d.ts.map +1 -0
- package/src/lib/constant.d.ts +3 -0
- package/src/lib/constant.d.ts.map +1 -0
- package/src/lib/dto/create-template-layout.dto.d.ts +16 -0
- package/src/lib/dto/create-template-layout.dto.d.ts.map +1 -0
- package/src/lib/dto/create-template.dto.d.ts +18 -0
- package/src/lib/dto/create-template.dto.d.ts.map +1 -0
- package/src/lib/dto/render-content-template-layout.dto.d.ts +8 -0
- package/src/lib/dto/render-content-template-layout.dto.d.ts.map +1 -0
- package/src/lib/dto/render-content-template.dto.d.ts +9 -0
- package/src/lib/dto/render-content-template.dto.d.ts.map +1 -0
- package/src/lib/dto/render-template-layout.dto.d.ts +11 -0
- package/src/lib/dto/render-template-layout.dto.d.ts.map +1 -0
- package/src/lib/dto/render-template.dto.d.ts +15 -0
- package/src/lib/dto/render-template.dto.d.ts.map +1 -0
- package/src/lib/dto/template-filter.dto.d.ts +9 -0
- package/src/lib/dto/template-filter.dto.d.ts.map +1 -0
- package/src/lib/dto/template-layout-filter.dto.d.ts +8 -0
- package/src/lib/dto/template-layout-filter.dto.d.ts.map +1 -0
- package/src/lib/engines/language/html.engine.d.ts +10 -0
- package/src/lib/engines/language/html.engine.d.ts.map +1 -0
- package/src/lib/engines/language/{index.ts → index.d.ts} +1 -0
- package/src/lib/engines/language/index.d.ts.map +1 -0
- package/src/lib/engines/language/markdown.engine.d.ts +10 -0
- package/src/lib/engines/language/markdown.engine.d.ts.map +1 -0
- package/src/lib/engines/language/mjml.engine.d.ts +10 -0
- package/src/lib/engines/language/mjml.engine.d.ts.map +1 -0
- package/src/lib/engines/language/text.engine.d.ts +8 -0
- package/src/lib/engines/language/text.engine.d.ts.map +1 -0
- package/src/lib/engines/{language-engine.ts → language-engine.d.ts} +2 -7
- package/src/lib/engines/language-engine.d.ts.map +1 -0
- package/src/lib/engines/template/ejs.engine.d.ts +11 -0
- package/src/lib/engines/template/ejs.engine.d.ts.map +1 -0
- package/src/lib/engines/template/handlebars.engine.d.ts +11 -0
- package/src/lib/engines/template/handlebars.engine.d.ts.map +1 -0
- package/src/lib/engines/template/{index.ts → index.d.ts} +1 -0
- package/src/lib/engines/template/index.d.ts.map +1 -0
- package/src/lib/engines/template/nunjucks.engine.d.ts +17 -0
- package/src/lib/engines/template/nunjucks.engine.d.ts.map +1 -0
- package/src/lib/engines/template/pug.engine.d.ts +13 -0
- package/src/lib/engines/template/pug.engine.d.ts.map +1 -0
- package/src/lib/engines/{template-engine.ts → template-engine.d.ts} +2 -6
- package/src/lib/engines/template-engine.d.ts.map +1 -0
- package/src/lib/entities/template-layout.entity.d.ts +21 -0
- package/src/lib/entities/template-layout.entity.d.ts.map +1 -0
- package/src/lib/entities/template.entity.d.ts +22 -0
- package/src/lib/entities/template.entity.d.ts.map +1 -0
- package/src/lib/errors/template.errors.d.ts +20 -0
- package/src/lib/errors/template.errors.d.ts.map +1 -0
- package/src/lib/interfaces/{module-config.interface.ts → module-config.interface.d.ts} +2 -10
- package/src/lib/interfaces/module-config.interface.d.ts.map +1 -0
- package/src/lib/interfaces/template.types.d.ts +23 -0
- package/src/lib/interfaces/template.types.d.ts.map +1 -0
- package/src/lib/nest-dynamic-templates.module.d.ts +11 -0
- package/src/lib/nest-dynamic-templates.module.d.ts.map +1 -0
- package/src/lib/services/template-config.service.d.ts +55 -0
- package/src/lib/services/template-config.service.d.ts.map +1 -0
- package/src/lib/services/template-engine.registry.d.ts +22 -0
- package/src/lib/services/template-engine.registry.d.ts.map +1 -0
- package/src/lib/services/template-layout.service.d.ts +37 -0
- package/src/lib/services/template-layout.service.d.ts.map +1 -0
- package/src/lib/services/template.service.d.ts +39 -0
- package/src/lib/services/template.service.d.ts.map +1 -0
- package/src/test/helpers.d.ts +5 -0
- package/src/test/helpers.d.ts.map +1 -0
- package/src/test/test-database.config.d.ts +5 -0
- package/src/test/test-database.config.d.ts.map +1 -0
- package/src/test/test.setup.d.ts +4 -0
- package/src/test/test.setup.d.ts.map +1 -0
- package/eslint.config.mjs +0 -22
- package/jest.config.ts +0 -10
- package/project.json +0 -38
- package/src/lib/constant.ts +0 -2
- package/src/lib/dto/create-template-layout.dto.ts +0 -65
- package/src/lib/dto/create-template.dto.ts +0 -80
- package/src/lib/dto/render-content-template-layout.dto.ts +0 -32
- package/src/lib/dto/render-content-template.dto.ts +0 -37
- package/src/lib/dto/render-template-layout.dto.ts +0 -55
- package/src/lib/dto/render-template.dto.ts +0 -74
- package/src/lib/dto/template-filter.dto.ts +0 -52
- package/src/lib/dto/template-layout-filter.dto.ts +0 -51
- package/src/lib/engines/language/html.engine.ts +0 -49
- package/src/lib/engines/language/markdown.engine.ts +0 -37
- package/src/lib/engines/language/mjml.engine.ts +0 -44
- package/src/lib/engines/language/text.engine.ts +0 -15
- package/src/lib/engines/template/ejs.engine.ts +0 -33
- package/src/lib/engines/template/handlebars.engine.ts +0 -35
- package/src/lib/engines/template/nunjucks.engine.ts +0 -70
- package/src/lib/engines/template/pug.engine.ts +0 -43
- package/src/lib/entities/template-layout.entity.ts +0 -99
- package/src/lib/entities/template.entity.ts +0 -105
- package/src/lib/errors/template.errors.ts +0 -73
- package/src/lib/interfaces/template.types.ts +0 -25
- package/src/lib/nest-dynamic-templates.module.ts +0 -143
- package/src/lib/services/template-config.service.ts +0 -112
- package/src/lib/services/template-engine.registry.ts +0 -109
- package/src/lib/services/template-layout.service.ts +0 -407
- package/src/lib/services/template.service.ts +0 -476
- package/src/test/helpers.ts +0 -31
- package/src/test/nunjucks.service.spec.ts +0 -157
- package/src/test/pug.service.spec-temp +0 -254
- package/src/test/template-layout.service.spec.ts +0 -422
- package/src/test/template.service.spec.ts +0 -862
- package/src/test/test-database.config.ts +0 -24
- package/src/test/test-database.d.ts +0 -6
- package/src/test/test.setup.ts +0 -34
- package/src/types/ioredis.d.ts +0 -6
- package/src/types/mjml.d.ts +0 -5
- package/tsconfig.json +0 -17
- package/tsconfig.lib.json +0 -14
- package/tsconfig.spec.json +0 -15
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
import { DataSource, Repository } from 'typeorm';
|
|
2
|
-
import { createTestModule } from './test.setup';
|
|
3
|
-
import { TemplateService } from '../lib/services/template.service';
|
|
4
|
-
import { NestDynamicTemplate } from '../lib/entities/template.entity';
|
|
5
|
-
import { TemplateTypeEnum, TemplateEngineEnum, TemplateLanguageEnum } from '../lib/interfaces/template.types';
|
|
6
|
-
import { CreateTemplateDto } from '../lib/dto/create-template.dto';
|
|
7
|
-
import { engineFilters } from './helpers';
|
|
8
|
-
|
|
9
|
-
describe('Pug Engine', () => {
|
|
10
|
-
let service: TemplateService;
|
|
11
|
-
let dataSource: DataSource;
|
|
12
|
-
let templateRepository: Repository<NestDynamicTemplate>;
|
|
13
|
-
|
|
14
|
-
const testTemplate: CreateTemplateDto = {
|
|
15
|
-
name: 'test-template',
|
|
16
|
-
displayName: 'Test Template',
|
|
17
|
-
content: 'p Hello #{name}!',
|
|
18
|
-
type: TemplateTypeEnum.EMAIL,
|
|
19
|
-
engine: TemplateEngineEnum.PUG,
|
|
20
|
-
language: TemplateLanguageEnum.HTML,
|
|
21
|
-
scope: 'system',
|
|
22
|
-
scopeId: null,
|
|
23
|
-
locale: 'en',
|
|
24
|
-
isActive: true
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
beforeEach(async () => {
|
|
28
|
-
const moduleRef = await createTestModule({
|
|
29
|
-
engines: {
|
|
30
|
-
template: [TemplateEngineEnum.PUG],
|
|
31
|
-
},
|
|
32
|
-
enginesOptions: {
|
|
33
|
-
filters: engineFilters,
|
|
34
|
-
template: {
|
|
35
|
-
[TemplateEngineEnum.PUG]: {
|
|
36
|
-
pretty: true,
|
|
37
|
-
compileDebug: true
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
service = moduleRef.get<TemplateService>(TemplateService);
|
|
44
|
-
dataSource = moduleRef.get<DataSource>(DataSource);
|
|
45
|
-
templateRepository = dataSource.getRepository(NestDynamicTemplate);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
afterEach(async () => {
|
|
49
|
-
// Clean up templates
|
|
50
|
-
await templateRepository.delete('1 = 1');
|
|
51
|
-
|
|
52
|
-
// Clean up database after each test
|
|
53
|
-
await dataSource.synchronize(true);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
afterAll(async () => {
|
|
57
|
-
// Close the connection after all tests
|
|
58
|
-
await dataSource.destroy();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('Pug Custom Filters', () => {
|
|
62
|
-
it('should format date using custom date filter', async () => {
|
|
63
|
-
const template = await service.createTemplate({
|
|
64
|
-
...testTemplate,
|
|
65
|
-
name: 'date-template',
|
|
66
|
-
content: 'p Today is #{formatDate(date, "YYYY-MM-DD")}',
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const today = new Date();
|
|
70
|
-
const expectedDate = today.toISOString().split('T')[0];
|
|
71
|
-
|
|
72
|
-
const result = await service.render({
|
|
73
|
-
type: TemplateTypeEnum.EMAIL,
|
|
74
|
-
name: 'date-template',
|
|
75
|
-
scope: 'system',
|
|
76
|
-
context: {
|
|
77
|
-
date: today
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
expect(result.content).toBe(`<p>Today is ${expectedDate}</p>`);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should format currency using custom currency filter', async () => {
|
|
85
|
-
const template = await service.createTemplate({
|
|
86
|
-
...testTemplate,
|
|
87
|
-
name: 'currency-template',
|
|
88
|
-
content: 'p Price: #{formatCurrency(amount, "USD")}',
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const result = await service.render({
|
|
92
|
-
type: TemplateTypeEnum.EMAIL,
|
|
93
|
-
name: 'currency-template',
|
|
94
|
-
scope: 'system',
|
|
95
|
-
context: {
|
|
96
|
-
amount: 1234.56
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
expect(result.content).toBe('<p>Price: $1,234.56</p>');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should handle multiple filters in the same template', async () => {
|
|
104
|
-
const template = await service.createTemplate({
|
|
105
|
-
...testTemplate,
|
|
106
|
-
name: 'multi-filter-template',
|
|
107
|
-
content: 'p Order placed on #{formatDate(date, "YYYY-MM-DD")} for #{formatCurrency(amount, "USD")}',
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const today = new Date();
|
|
111
|
-
const expectedDate = today.toISOString().split('T')[0];
|
|
112
|
-
|
|
113
|
-
const result = await service.render({
|
|
114
|
-
type: TemplateTypeEnum.EMAIL,
|
|
115
|
-
name: 'multi-filter-template',
|
|
116
|
-
scope: 'system',
|
|
117
|
-
context: {
|
|
118
|
-
date: today,
|
|
119
|
-
amount: 1234.56
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
expect(result.content).toBe(`<p>Order placed on ${expectedDate} for $1,234.56</p>`);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should handle different currency formats', async () => {
|
|
127
|
-
const template = await service.createTemplate({
|
|
128
|
-
...testTemplate,
|
|
129
|
-
name: 'currency-formats-template',
|
|
130
|
-
content: 'p USD: #{formatCurrency(usd, "USD")}, EUR: #{formatCurrency(eur, "EUR")}',
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
const result = await service.render({
|
|
134
|
-
type: TemplateTypeEnum.EMAIL,
|
|
135
|
-
name: 'currency-formats-template',
|
|
136
|
-
scope: 'system',
|
|
137
|
-
context: {
|
|
138
|
-
usd: 1234.56,
|
|
139
|
-
eur: 1234.56
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
expect(result.content).toBe('<p>USD: $1,234.56, EUR: €1,234.56</p>');
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should handle different date formats', async () => {
|
|
147
|
-
const template = await service.createTemplate({
|
|
148
|
-
...testTemplate,
|
|
149
|
-
name: 'date-formats-template',
|
|
150
|
-
content: 'p Short: #{formatDate(date, "MM/DD/YY")}, Long: #{formatDate(date, "MMMM D, YYYY")}, ISO: #{formatDate(date, "YYYY-MM-DD")}',
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
const date = new Date('2024-03-15');
|
|
154
|
-
const result = await service.render({
|
|
155
|
-
type: TemplateTypeEnum.EMAIL,
|
|
156
|
-
name: 'date-formats-template',
|
|
157
|
-
scope: 'system',
|
|
158
|
-
context: {
|
|
159
|
-
date: date
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
expect(result.content).toBe('<p>Short: 03/15/24, Long: March 15, 2024, ISO: 2024-03-15</p>');
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
describe('Pug HTML Features', () => {
|
|
168
|
-
// Custom matcher to check HTML structure
|
|
169
|
-
const expectHtmlStructure = (received: string, expected: string) => {
|
|
170
|
-
const normalizeHtml = (html: string) => {
|
|
171
|
-
return html
|
|
172
|
-
.replace(/\s+/g, ' ')
|
|
173
|
-
.replace(/>\s+</g, '><')
|
|
174
|
-
.trim();
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const receivedNormalized = normalizeHtml(received);
|
|
178
|
-
const expectedNormalized = normalizeHtml(expected);
|
|
179
|
-
|
|
180
|
-
// Extract attributes and compare them regardless of order
|
|
181
|
-
const getAttributes = (html: string) => {
|
|
182
|
-
const match = html.match(/<[^>]+>/);
|
|
183
|
-
if (!match) return [];
|
|
184
|
-
return match[0]
|
|
185
|
-
.replace(/<[^\s>]+/, '')
|
|
186
|
-
.replace(/>$/, '')
|
|
187
|
-
.trim()
|
|
188
|
-
.split(/\s+/)
|
|
189
|
-
.sort();
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const receivedAttrs = getAttributes(receivedNormalized);
|
|
193
|
-
const expectedAttrs = getAttributes(expectedNormalized);
|
|
194
|
-
|
|
195
|
-
expect(receivedAttrs).toEqual(expectedAttrs);
|
|
196
|
-
expect(receivedNormalized.replace(/<[^>]+>/, '')).toBe(expectedNormalized.replace(/<[^>]+>/, ''));
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
it('should render nested HTML tags', async () => {
|
|
200
|
-
const template = await service.createTemplate({
|
|
201
|
-
...testTemplate,
|
|
202
|
-
name: 'nested-html-template',
|
|
203
|
-
content: 'div\n h1 Title\n p This is a paragraph.'
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
const result = await service.render({
|
|
207
|
-
type: TemplateTypeEnum.EMAIL,
|
|
208
|
-
name: 'nested-html-template',
|
|
209
|
-
scope: 'system',
|
|
210
|
-
context: {}
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
expect(result.content.replace(/\n/g, ''))
|
|
214
|
-
.toBe('<div><h1>Title</h1><p>This is a paragraph.</p></div>');
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('should render HTML with attributes', async () => {
|
|
218
|
-
const template = await service.createTemplate({
|
|
219
|
-
...testTemplate,
|
|
220
|
-
name: 'attributes-html-template',
|
|
221
|
-
content: 'a(href="https://example.com" target="_blank") Example Link'
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
const result = await service.render({
|
|
225
|
-
type: TemplateTypeEnum.EMAIL,
|
|
226
|
-
name: 'attributes-html-template',
|
|
227
|
-
scope: 'system',
|
|
228
|
-
context: {}
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
expect(result.content).toBe('<a href="https://example.com" target="_blank">Example Link</a>');
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it('should render HTML with classes and ids', async () => {
|
|
235
|
-
const template = await service.createTemplate({
|
|
236
|
-
...testTemplate,
|
|
237
|
-
name: 'class-id-html-template',
|
|
238
|
-
content: 'div#main.container Main content'
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
const result = await service.render({
|
|
242
|
-
type: TemplateTypeEnum.EMAIL,
|
|
243
|
-
name: 'class-id-html-template',
|
|
244
|
-
scope: 'system',
|
|
245
|
-
context: {}
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
expectHtmlStructure(
|
|
249
|
-
result.content,
|
|
250
|
-
'<div class="container" id="main">Main content</div>'
|
|
251
|
-
);
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
});
|
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
import { DataSource, Repository } from 'typeorm';
|
|
2
|
-
import { createTestModule } from './test.setup';
|
|
3
|
-
import { TemplateTypeEnum, TemplateEngineEnum, TemplateLanguageEnum } from '../lib/interfaces/template.types';
|
|
4
|
-
import { CreateTemplateDto } from '../lib/dto/create-template.dto';
|
|
5
|
-
import { ForbiddenException, NotFoundException } from '@nestjs/common';
|
|
6
|
-
import { NestDynamicTemplateLayout } from '../lib/entities/template-layout.entity';
|
|
7
|
-
import { TemplateLayoutService } from '../lib/services/template-layout.service';
|
|
8
|
-
|
|
9
|
-
describe('Template Layout Service', () => {
|
|
10
|
-
let service: TemplateLayoutService;
|
|
11
|
-
let dataSource: DataSource;
|
|
12
|
-
let templateLayoutRepository: Repository<NestDynamicTemplateLayout>;
|
|
13
|
-
|
|
14
|
-
const testTemplate: CreateTemplateDto = {
|
|
15
|
-
name: 'test-template',
|
|
16
|
-
displayName: 'Test Template',
|
|
17
|
-
content: 'Hello {{name}}!',
|
|
18
|
-
type: TemplateTypeEnum.EMAIL,
|
|
19
|
-
engine: TemplateEngineEnum.NUNJUCKS,
|
|
20
|
-
language: TemplateLanguageEnum.HTML,
|
|
21
|
-
scope: 'system',
|
|
22
|
-
scopeId: null,
|
|
23
|
-
locale: 'en',
|
|
24
|
-
isActive: true
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
beforeEach(async () => {
|
|
28
|
-
const moduleRef = await createTestModule();
|
|
29
|
-
|
|
30
|
-
service = moduleRef.get<TemplateLayoutService>(TemplateLayoutService);
|
|
31
|
-
dataSource = moduleRef.get<DataSource>(DataSource);
|
|
32
|
-
templateLayoutRepository = dataSource.getRepository(NestDynamicTemplateLayout);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
afterEach(async () => {
|
|
36
|
-
// Clean up templates
|
|
37
|
-
await templateLayoutRepository.delete('1 = 1');
|
|
38
|
-
|
|
39
|
-
// Clean up database after each test
|
|
40
|
-
await dataSource.synchronize(true);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
afterAll(async () => {
|
|
44
|
-
// Close the connection after all tests
|
|
45
|
-
await dataSource.destroy();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe('System Template Layout Management', () => {
|
|
49
|
-
it('should create system template layout', async () => {
|
|
50
|
-
const result = await service.createTemplateLayout(testTemplate);
|
|
51
|
-
|
|
52
|
-
expect(result).toBeDefined();
|
|
53
|
-
expect(result.name).toBe(testTemplate.name);
|
|
54
|
-
expect(result.scope).toBe('system');
|
|
55
|
-
expect(result.scopeId).toBeNull();
|
|
56
|
-
|
|
57
|
-
// Verify template was saved in database
|
|
58
|
-
const savedTemplate = await templateLayoutRepository.findOne({ where: { id: result.id } });
|
|
59
|
-
expect(savedTemplate).toBeDefined();
|
|
60
|
-
expect(savedTemplate.name).toBe(testTemplate.name);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should not create other then system template layout', async () => {
|
|
64
|
-
await expect(service.createTemplateLayout({
|
|
65
|
-
...testTemplate,
|
|
66
|
-
scope: 'custom',
|
|
67
|
-
scopeId: '123',
|
|
68
|
-
content: 'Custom content for {{name}}'
|
|
69
|
-
})).rejects.toThrow(ForbiddenException);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should find system template', async () => {
|
|
73
|
-
// First create a template
|
|
74
|
-
const created = await service.createTemplateLayout(testTemplate);
|
|
75
|
-
|
|
76
|
-
// Then find it
|
|
77
|
-
const found = await service.findTemplateLayout(
|
|
78
|
-
testTemplate.name,
|
|
79
|
-
testTemplate.scope,
|
|
80
|
-
testTemplate.scopeId,
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
expect(found).toBeDefined();
|
|
84
|
-
expect(found.id).toBe(created.id);
|
|
85
|
-
expect(found.name).toBe(testTemplate.name);
|
|
86
|
-
expect(found.scope).toBe(testTemplate.scope);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should find system template layout when template layout not found with given scope', async () => {
|
|
90
|
-
// Create system template layout
|
|
91
|
-
const created = await service.createTemplateLayout(testTemplate);
|
|
92
|
-
|
|
93
|
-
// Then find it with custom scope
|
|
94
|
-
const found = await service.findTemplateLayout(
|
|
95
|
-
testTemplate.name,
|
|
96
|
-
'custom',
|
|
97
|
-
'123',
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
// Verify template was found and is the system template
|
|
101
|
-
expect(found).toBeDefined();
|
|
102
|
-
expect(found).not.toBeNull();
|
|
103
|
-
expect(found.id).toBe(created.id);
|
|
104
|
-
expect(found.name).toBe(testTemplate.name);
|
|
105
|
-
expect(found.scope).toBe('system');
|
|
106
|
-
expect(found.scopeId).toBeNull();
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should not find template layout when template layout not found in any scope and system', async () => {
|
|
110
|
-
// Try to find non-existent template layout
|
|
111
|
-
const result = await service.findTemplateLayout(
|
|
112
|
-
'non-existent-template',
|
|
113
|
-
'custom',
|
|
114
|
-
'123',
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
expect(result).toBeNull();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should not find template layout when template layout not found in system scope', async () => {
|
|
121
|
-
// Try to find non-existent template layout in system scope
|
|
122
|
-
const result = await service.findTemplateLayout(
|
|
123
|
-
'non-existent-template',
|
|
124
|
-
'system',
|
|
125
|
-
null,
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
expect(result).toBeNull();
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should update system template when canUpdateSystemTemplate is true', async () => {
|
|
132
|
-
// First create a template
|
|
133
|
-
const created = await service.createTemplateLayout(testTemplate);
|
|
134
|
-
|
|
135
|
-
// Then update it
|
|
136
|
-
const result = await service.updateTemplateLayout(created.id, {
|
|
137
|
-
content: 'Updated system content for {{name}}'
|
|
138
|
-
}, true);
|
|
139
|
-
|
|
140
|
-
expect(result).toBeDefined();
|
|
141
|
-
expect(result.content).toBe('Updated system content for {{name}}');
|
|
142
|
-
expect(result.scope).toBe('system');
|
|
143
|
-
|
|
144
|
-
// Verify update in database
|
|
145
|
-
const updatedTemplate = await templateLayoutRepository.findOne({ where: { id: created.id } });
|
|
146
|
-
expect(updatedTemplate.content).toBe('Updated system content for {{name}}');
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should not update system template layout when canUpdateSystemTemplate is false, it should throw ForbiddenException', async () => {
|
|
150
|
-
// First create a template layout
|
|
151
|
-
const created = await service.createTemplateLayout(testTemplate);
|
|
152
|
-
|
|
153
|
-
// Then update it with canUpdateSystemTemplate set to false
|
|
154
|
-
await expect(service.updateTemplateLayout(created.id, {
|
|
155
|
-
content: 'Updated system content for {{name}}'
|
|
156
|
-
}, false)).rejects.toThrow(ForbiddenException);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// Verify template was not updated
|
|
160
|
-
const updatedTemplate = await templateLayoutRepository.findOne({ where: { id: created.id } });
|
|
161
|
-
expect(updatedTemplate.content).toBe(testTemplate.content);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
it('should not update system template layout when canUpdateSystemTemplate is false, but scope is not system then overwrite the template layout', async () => {
|
|
166
|
-
// First create a template layout
|
|
167
|
-
const created = await service.createTemplateLayout(testTemplate);
|
|
168
|
-
|
|
169
|
-
// Then update it with canUpdateSystemTemplate set to false
|
|
170
|
-
await service.updateTemplateLayout(created.id, {
|
|
171
|
-
scope: 'custom',
|
|
172
|
-
scopeId: '123',
|
|
173
|
-
content: 'Updated system content for {{name}}'
|
|
174
|
-
}, false);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// Verify system template was not updated
|
|
178
|
-
const updatedTemplate = await templateLayoutRepository.findOne({ where: { id: created.id } });
|
|
179
|
-
expect(updatedTemplate.content).toBe(testTemplate.content);
|
|
180
|
-
|
|
181
|
-
// Verify overwrite template was created
|
|
182
|
-
const overwrittenTemplate = await service.findTemplateLayout(
|
|
183
|
-
testTemplate.name,
|
|
184
|
-
'custom',
|
|
185
|
-
'123',
|
|
186
|
-
);
|
|
187
|
-
expect(overwrittenTemplate.content).toBe('Updated system content for {{name}}');
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should delete system template layout when canDeleteSystemTemplate is true', async () => {
|
|
191
|
-
// First create a template layout
|
|
192
|
-
const created = await service.createTemplateLayout(testTemplate);
|
|
193
|
-
|
|
194
|
-
// Then delete it
|
|
195
|
-
await service.deleteTemplateLayout(created.id, true);
|
|
196
|
-
|
|
197
|
-
// Verify template was deleted
|
|
198
|
-
const deletedTemplate = await templateLayoutRepository.findOne({ where: { id: created.id } });
|
|
199
|
-
expect(deletedTemplate).toBeNull();
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('should not delete system template when canDeleteSystemTemplate is false, it should throw ForbiddenException', async () => {
|
|
203
|
-
// First create a template
|
|
204
|
-
const created = await service.createTemplateLayout(testTemplate);
|
|
205
|
-
|
|
206
|
-
// Then delete it
|
|
207
|
-
await expect(service.deleteTemplateLayout(created.id, false)).rejects.toThrow(ForbiddenException);
|
|
208
|
-
|
|
209
|
-
// Verify template was not deleted
|
|
210
|
-
const deletedTemplate = await templateLayoutRepository.findOne({ where: { id: created.id } });
|
|
211
|
-
expect(deletedTemplate).toBeDefined();
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
describe('Render System Template Layout', () => {
|
|
216
|
-
it('should render system template layout', async () => {
|
|
217
|
-
// Create system template layout
|
|
218
|
-
await service.createTemplateLayout(testTemplate);
|
|
219
|
-
|
|
220
|
-
// Render template
|
|
221
|
-
const renderedResult = await service.render({
|
|
222
|
-
name: testTemplate.name,
|
|
223
|
-
scope: testTemplate.scope,
|
|
224
|
-
scopeId: testTemplate.scopeId,
|
|
225
|
-
context: { name: 'John' }
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Verify template was rendered correctly
|
|
229
|
-
expect(renderedResult).toEqual({
|
|
230
|
-
subject: undefined,
|
|
231
|
-
content: 'Hello John!'
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('should not render system template layout when not found', async () => {
|
|
236
|
-
// Render template
|
|
237
|
-
await expect(service.render({
|
|
238
|
-
name: testTemplate.name,
|
|
239
|
-
scope: testTemplate.scope,
|
|
240
|
-
scopeId: testTemplate.scopeId,
|
|
241
|
-
context: { name: 'John' }
|
|
242
|
-
})).rejects.toThrow(NotFoundException);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it('should render system template layout even scope not system and template layout not found with given scope', async () => {
|
|
246
|
-
// Create system template layout
|
|
247
|
-
await service.createTemplateLayout(testTemplate);
|
|
248
|
-
|
|
249
|
-
// Render template
|
|
250
|
-
const renderedResult = await service.render({
|
|
251
|
-
name: testTemplate.name,
|
|
252
|
-
scope: 'custom',
|
|
253
|
-
scopeId: '123',
|
|
254
|
-
context: { name: 'John' }
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
expect(renderedResult).toEqual({
|
|
258
|
-
subject: undefined,
|
|
259
|
-
content: 'Hello John!'
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
describe('Custom Scope Template Layout Management', () => {
|
|
265
|
-
it('should create overwrite template layout for custom scope', async () => {
|
|
266
|
-
// Create system template layout
|
|
267
|
-
const systemTemplateLayout = await service.createTemplateLayout(testTemplate);
|
|
268
|
-
|
|
269
|
-
// Then create overwrite template layout
|
|
270
|
-
const overwriteTemplateLayout = await service.overwriteSystemTemplateLayout(systemTemplateLayout.id, {
|
|
271
|
-
...testTemplate,
|
|
272
|
-
scope: 'custom',
|
|
273
|
-
scopeId: '123',
|
|
274
|
-
content: 'Custom content for {{name}}'
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
expect(overwriteTemplateLayout).toBeDefined();
|
|
278
|
-
expect(overwriteTemplateLayout.scope).toBe('custom');
|
|
279
|
-
expect(overwriteTemplateLayout.scopeId).toBe('123');
|
|
280
|
-
expect(overwriteTemplateLayout.content).toBe('Custom content for {{name}}');
|
|
281
|
-
|
|
282
|
-
// Verify system template is not affected by overwrite template
|
|
283
|
-
const oldSystemTemplate = await templateLayoutRepository.findOne({
|
|
284
|
-
where: { name: testTemplate.name, scope: 'system' }
|
|
285
|
-
});
|
|
286
|
-
expect(oldSystemTemplate).toBeDefined();
|
|
287
|
-
|
|
288
|
-
// Verify system template and overwrite template are different
|
|
289
|
-
expect(systemTemplateLayout.id).not.toBe(overwriteTemplateLayout.id);
|
|
290
|
-
|
|
291
|
-
// Verify system template content is not affected by overwrite template
|
|
292
|
-
expect(oldSystemTemplate.id).toBe(systemTemplateLayout.id);
|
|
293
|
-
expect(oldSystemTemplate.content).toBe(systemTemplateLayout.content);
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it('should update overwrite template layout without affecting system template layout', async () => {
|
|
297
|
-
// Create system template layout
|
|
298
|
-
const systemTemplateLayout = await service.createTemplateLayout(testTemplate);
|
|
299
|
-
|
|
300
|
-
// Create overwrite template layout
|
|
301
|
-
const overwriteTemplateLayout = await service.overwriteSystemTemplateLayout(systemTemplateLayout.id, {
|
|
302
|
-
...testTemplate,
|
|
303
|
-
scope: 'custom',
|
|
304
|
-
scopeId: '123',
|
|
305
|
-
content: 'Custom content for {{name}}'
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
// Update overwrite template layout
|
|
309
|
-
const result = await service.updateTemplateLayout(overwriteTemplateLayout.id, {
|
|
310
|
-
content: 'Updated custom content for {{name}}'
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
expect(result).toBeDefined();
|
|
314
|
-
expect(result.scope).toBe('custom');
|
|
315
|
-
expect(result.scopeId).toBe('123');
|
|
316
|
-
expect(result.content).toBe('Updated custom content for {{name}}');
|
|
317
|
-
|
|
318
|
-
// Verify system template remains unchanged
|
|
319
|
-
const oldSystemTemplate = await templateLayoutRepository.findOne({
|
|
320
|
-
where: { name: testTemplate.name, scope: 'system' }
|
|
321
|
-
});
|
|
322
|
-
expect(oldSystemTemplate.content).toBe(systemTemplateLayout.content);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it('should not overwrite already overwritten template layout, it should return the already overwritten template layout but with updated fields', async () => {
|
|
326
|
-
// Create system template layout
|
|
327
|
-
const systemTemplateLayout = await service.createTemplateLayout(testTemplate);
|
|
328
|
-
|
|
329
|
-
// Create overwrite template layout
|
|
330
|
-
const overwrittenTemplateLayout = await service.overwriteSystemTemplateLayout(systemTemplateLayout.id, {
|
|
331
|
-
...testTemplate,
|
|
332
|
-
scope: 'custom',
|
|
333
|
-
scopeId: '123',
|
|
334
|
-
content: 'Custom content for {{name}}'
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
// Try to overwrite again
|
|
338
|
-
const result = await service.overwriteSystemTemplateLayout(systemTemplateLayout.id, {
|
|
339
|
-
...testTemplate,
|
|
340
|
-
scope: 'custom',
|
|
341
|
-
scopeId: '123',
|
|
342
|
-
content: 'Updated custom content for {{name}}'
|
|
343
|
-
})
|
|
344
|
-
|
|
345
|
-
// Verify the result is the same as the already overwritten template
|
|
346
|
-
expect(result).toBeDefined();
|
|
347
|
-
expect(result.id).toBe(overwrittenTemplateLayout.id);
|
|
348
|
-
expect(result.content).toBe('Updated custom content for {{name}}');
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it('should find overwrite template when available', async () => {
|
|
352
|
-
// Create system template layout
|
|
353
|
-
const systemTemplateLayout = await service.createTemplateLayout(testTemplate);
|
|
354
|
-
|
|
355
|
-
// Create overwrite template layout
|
|
356
|
-
await service.overwriteSystemTemplateLayout(systemTemplateLayout.id, {
|
|
357
|
-
...testTemplate,
|
|
358
|
-
scope: 'custom',
|
|
359
|
-
scopeId: '123',
|
|
360
|
-
content: 'Custom content for {{name}}'
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
// Find overwrite template layout
|
|
364
|
-
const found = await service.findTemplateLayout(
|
|
365
|
-
testTemplate.name,
|
|
366
|
-
'custom',
|
|
367
|
-
'123',
|
|
368
|
-
testTemplate.language
|
|
369
|
-
);
|
|
370
|
-
|
|
371
|
-
expect(found).toBeDefined();
|
|
372
|
-
expect(found.content).toBe('Custom content for {{name}}');
|
|
373
|
-
expect(found.scope).toBe('custom');
|
|
374
|
-
expect(found.scopeId).toBe('123');
|
|
375
|
-
})
|
|
376
|
-
|
|
377
|
-
it('should render overwrite template when available', async () => {
|
|
378
|
-
// Create system template layout
|
|
379
|
-
const systemTemplateLayout = await service.createTemplateLayout(testTemplate);
|
|
380
|
-
|
|
381
|
-
// Create overwrite template layout
|
|
382
|
-
await service.overwriteSystemTemplateLayout(systemTemplateLayout.id, {
|
|
383
|
-
...testTemplate,
|
|
384
|
-
scope: 'custom',
|
|
385
|
-
scopeId: '123',
|
|
386
|
-
content: 'Custom content for {{name}}'
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
const result = await service.render({
|
|
390
|
-
name: testTemplate.name,
|
|
391
|
-
scope: 'custom',
|
|
392
|
-
scopeId: '123',
|
|
393
|
-
locale: testTemplate.language,
|
|
394
|
-
context: { name: 'John' }
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
expect(result).toEqual({
|
|
398
|
-
subject: undefined,
|
|
399
|
-
content: 'Custom content for John'
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
it('should not fail render when overwrite template layout not found', async () => {
|
|
404
|
-
// Create only system template layout
|
|
405
|
-
await service.createTemplateLayout(testTemplate);
|
|
406
|
-
|
|
407
|
-
const result = await service.render({
|
|
408
|
-
name: testTemplate.name,
|
|
409
|
-
scope: 'custom',
|
|
410
|
-
scopeId: '123',
|
|
411
|
-
locale: testTemplate.language,
|
|
412
|
-
context: { name: 'John' }
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
expect(result).toEqual({
|
|
416
|
-
subject: undefined,
|
|
417
|
-
content: 'Hello John!'
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
});
|