@peter.naydenov/morph 0.0.1

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.
@@ -0,0 +1,470 @@
1
+ import morphAPI from '../src/main.js'
2
+ import { expect } from 'chai'
3
+
4
+
5
+
6
+ describe ( 'transformer: build', () => {
7
+
8
+
9
+ it ( 'Simple mustache like placeholders, no actions', () => {
10
+ // spaces inside placeholders are ignored
11
+ const myTpl = {
12
+ template: `Your name is {{ name }}. Your age is {{age}}.`
13
+ }
14
+ const templateFn = morphAPI.build ( myTpl )
15
+ const result = templateFn({
16
+ name: 'Peter'
17
+ , age: 50
18
+ })
19
+ expect ( result ).to.be.equal ( 'Your name is Peter. Your age is 50.' )
20
+ }) // it simple mustache like placeholders, no actions
21
+
22
+
23
+ it ( 'Mixing actions', () => {
24
+ // Mix action starts with '[]'. If no name after '[]' it will be like arr.join('').
25
+ // If name, it is the helper function used. In our case it is 'coma'.
26
+ const myTpl = {
27
+ template : `My friends are {{ names : []coma }}.`
28
+ , helpers : {
29
+ coma: (res) => res.join(', ')
30
+ }
31
+ };
32
+ const templateFn = morphAPI.build ( myTpl );
33
+ const result = templateFn({ names: ['Peter', 'Ivan'] });
34
+ expect ( result ).to.be.equal ( 'My friends are Peter, Ivan.' );
35
+ }) // it mixing actions
36
+
37
+
38
+
39
+ it ( 'No placeholders', () => {
40
+ const myTpl = {
41
+ template : `My name is Peter.`
42
+ }
43
+ const templateFn = morphAPI.build ( myTpl );
44
+ const result = templateFn(); // No data required
45
+ expect ( result ).to.be.equal ( 'My name is Peter.' );
46
+ }) // it No placeholders
47
+
48
+
49
+
50
+ it ( 'Data action to change the result', () => {
51
+ const myTpl = {
52
+ // placeholder will be fullfiled with data[job], but data function 'jobPossible' will filter the result
53
+ template : `My job is {{ job : >jobPossible }}.`
54
+ , helpers : {
55
+ jobPossible: (text) => {
56
+ if ( text === 'Software Engineer' ) return 'hidden'
57
+ else return text
58
+ }
59
+ }
60
+ }
61
+ const templateFn = morphAPI.build ( myTpl );
62
+ const result = templateFn({ job: 'Software Engineer' });
63
+ const result2 = templateFn({ job: 'doctor' });
64
+
65
+ expect ( result ).to.be.equal ( 'My job is hidden.' );
66
+ expect ( result2 ).to.be.equal ( 'My job is doctor.' );
67
+ }) // it data action to change the result
68
+
69
+
70
+
71
+ it ( 'Keep a placeholder', () => {
72
+ const myTpl = {
73
+ // placeholder will be fullfiled with data[job], but data function 'jobPossible' will filter the result
74
+ template : `My job is {{ job : >jobPossible }}.`
75
+ , helpers : {
76
+ jobPossible: (text) => {
77
+ if ( text === 'Software Engineer' ) return null // null means: Do not render this field
78
+ else return text
79
+ }
80
+ }
81
+ }
82
+ const templateFn = morphAPI.build ( myTpl );
83
+ const result = templateFn({ job: 'Software Engineer' });
84
+ expect ( result ).to.be.equal ( 'My job is {{ job : >jobPossible }}.' );
85
+ }) // it Keep placeholder
86
+
87
+
88
+
89
+ it ( 'Long chain with variaty of actions: Data, render, and mixing', () => {
90
+ const myTpl = {
91
+ template: `Hello, {{ name : ul, [], li, >fromList }}! {{ more }}`
92
+ , helpers: {
93
+ fromList: name => (['Peter', 'Ivan'].includes(name) ? name : 'stranger')
94
+
95
+ , ul : `<ul>{{text}}</ul>`
96
+ , li : `<li>{{text}}</li>`
97
+ }
98
+ };
99
+ const templateFn = morphAPI.build ( myTpl )
100
+ const result = templateFn({
101
+ name: [ 'Peter', 'Stoyan' ]
102
+ , more: `extended version`
103
+ })
104
+ expect ( result ).to.be.equal ( 'Hello, <ul><li>Peter</li><li>stranger</li></ul>! extended version' )
105
+ }) // it Long chain with variaty of actions
106
+
107
+
108
+
109
+ it ( 'Data action on array of objects', () => {
110
+ const myTpl = {
111
+ template: `My friends: {{ friends : ul, [], li, >people }}`
112
+ , helpers: {
113
+ li: `<li>{{name}}: {{state}}</li>`,
114
+ ul: `<ul>{{text}}</ul>`,
115
+ people: (res) => {
116
+ if ( res.age < 30 ) res.state = 'young'
117
+ else res.state = 'old'
118
+ return res
119
+ }
120
+ }
121
+ }
122
+ const templateFn = morphAPI.build ( myTpl )
123
+ const result = templateFn({
124
+ friends: [
125
+ { name: 'Peter', age: 30 }
126
+ , { name: 'Ivan', age: 25 }
127
+ , { name: 'Stoyan', age: 35 }
128
+ ]
129
+ })
130
+ expect ( result ).to.be.equal ( 'My friends: <ul><li>Peter: old</li><li>Ivan: young</li><li>Stoyan: old</li></ul>' )
131
+ }) // it data action on array of objects
132
+
133
+
134
+
135
+ it ( 'Data array', () => {
136
+ // If input data is an array, we render each element with the template
137
+ const myTpl = {
138
+ template: `My name is {{ name }}. My age is {{ age }}.`
139
+ }
140
+ const templateFn = morphAPI.build ( myTpl )
141
+ const result = templateFn([
142
+ { name: 'Peter', age: 30 }
143
+ , { name: 'Ivan', age: 25 }
144
+ , { name: 'Stoyan', age: 35 }
145
+ ])
146
+ // Result is array of rendered templates
147
+ expect ( result ).to.have.length ( 3 )
148
+ expect ( result[0] ).to.be.equal ( 'My name is Peter. My age is 30.' )
149
+ expect ( result[1] ).to.be.equal ( 'My name is Ivan. My age is 25.' )
150
+ expect ( result[2] ).to.be.equal ( 'My name is Stoyan. My age is 35.' )
151
+ }) // it data array
152
+
153
+
154
+
155
+ it ( 'Use all data: @all, @root', () => { // *** Address full data to placeholder -> @all
156
+ // Both are equivalent: @all === @root
157
+ const myTpl = {
158
+ template : `My name is {{ name }}. My web page is {{ @all : a, >morphWeb }}. Contact me on {{ @root : a , >morphMail }}.`
159
+ , helpers : {
160
+ a : `<a href="{{link}}">{{text}}</a>`
161
+ , morphWeb : ( data ) => {
162
+ data.link = data.web
163
+ data.text = data.web
164
+ return data
165
+ }
166
+ , morphMail: ( data ) => {
167
+ data.link = data.email
168
+ data.text = 'e-mail'
169
+ return data
170
+ }
171
+ }
172
+ }
173
+ const templateFn = morphAPI.build ( myTpl )
174
+ const result = templateFn({
175
+ name : 'Peter'
176
+ , web : 'example.com'
177
+ , email : 'peter@example.com'
178
+ })
179
+ expect ( result ).to.be.equal ( 'My name is Peter. My web page is <a href="example.com">example.com</a>. Contact me on <a href="peter@example.com">e-mail</a>.' )
180
+ }) // it use all data @all
181
+
182
+
183
+
184
+ it ( 'Ignore rendering if data is null', () => {
185
+ // Ignore rendering [] lines that have missing data
186
+ const myTpl = {
187
+ template : `People and their jobs: {{ jobList : ul, [], li, >jobItems }}`
188
+ , helpers : {
189
+ ul : `<ul>{{text}}</ul>`
190
+ , li : `<li>{{name}} - {{job}}</li>`
191
+ , jobItems : ( data ) => { // It's a data function
192
+ // If data-function returns null, it will be ignored in template render
193
+ if ( !data.name || !data.job ) return null
194
+ else return data
195
+ }
196
+ }
197
+ };
198
+ const data = {
199
+ jobList: [
200
+ {name: 'Peter', job: 'Software Engineer'}
201
+ , {name: 'Ivan' } // No job - ignored in template render
202
+ , {name: 'Stoyan', job: 'designer' }
203
+ ]
204
+ };
205
+ const templateFn = morphAPI.build ( myTpl );
206
+ const result = templateFn(data);
207
+ expect ( result ).to.be.equal ( 'People and their jobs: <ul><li>Peter - Software Engineer</li><li>Stoyan - designer</li></ul>' )
208
+
209
+ }) // it Ignore rendering if data is null
210
+
211
+
212
+
213
+ it ( 'Ignore rendering fields that have no data', () => {
214
+ const myTpl = {
215
+ template : `My name is {{ name }}.`
216
+
217
+ };
218
+ const templateFn = morphAPI.build ( myTpl );
219
+ const result = templateFn({ person: 'Peter' });
220
+ expect ( result ).to.be.equal ( 'My name is {{ name }}.' )
221
+ }) // it Ignore rendering fields that have no data
222
+
223
+
224
+
225
+ it ( 'How to hide a placeholder', () => {
226
+ // Hide a placeholder - provide an empty string
227
+ const myTpl = {
228
+ template : `My name is {{ name }}.{{ extraData}}`
229
+ }
230
+ const templateFn = morphAPI.build ( myTpl );
231
+ const result = templateFn({
232
+ name: 'Peter'
233
+ , extraData: '' // Provide an empty string to hide a placeholder
234
+ });
235
+ expect ( result ).to.be.equal ( 'My name is Peter.' )
236
+ }) // it how to hide a placeholder
237
+
238
+
239
+
240
+ it ( 'Data as null or undefined', () => {
241
+ // If data is null or undefined, should return the template without any changes
242
+ const myTpl = {
243
+ template : `My name is {{ name }}.{{ extraData}}`
244
+ }
245
+ const templateFn = morphAPI.build ( myTpl );
246
+ const result = templateFn( null );
247
+ expect ( result ).to.be.equal ( myTpl.template )
248
+ }) // it data as null or undefined
249
+
250
+
251
+
252
+ it ( 'Sequence of render processes with object', () => {
253
+ // Write result of render to field 'text' and preserve other existing fields
254
+ const myTpl = {
255
+ template : `Here is - {{ website: a, setText }}.`
256
+ , helpers : {
257
+ a : `<a href="{{link}}">{{text}}</a>`
258
+ , setText : `{{about}}: {{domain}}`
259
+ }
260
+ }
261
+ const templateFn = morphAPI.build ( myTpl );
262
+ const result = templateFn({
263
+ website : {
264
+ domain: `example.com`
265
+ , about : `My portfolio`
266
+ , link : `http://example.com`
267
+ }
268
+ });
269
+ expect ( result ).to.be.equal ( 'Here is - <a href="http://example.com">My portfolio: example.com</a>.' )
270
+ }) // it sequence of render processes with object
271
+
272
+
273
+
274
+ it ( 'Action only', () => {
275
+ // Use helper functions to insert dynamic data from programming environment(state, variable, etc.)
276
+ // Templates that are using actions only, don't need a input data object. It's a dynamic data.
277
+ const myState = 'active'
278
+ const myTpl = {
279
+ template: `Profile {{ : state }}.`
280
+ , helpers: {
281
+ state: () => myState
282
+ }
283
+ };
284
+ const templateFn = morphAPI.build ( myTpl );
285
+ const result = templateFn();
286
+ expect ( result ).to.be.equal ( 'Profile active.' )
287
+ }) // it action only
288
+
289
+
290
+
291
+ it ( 'Mixing a string', () => {
292
+ // Providing a string to a mixing function should not break the result.
293
+ const myTpl = {
294
+ template: `My name is {{ : [], >name }}`
295
+ , helpers: {
296
+ name: ( data ) => data.name
297
+ }
298
+ }
299
+ const templateFn = morphAPI.build ( myTpl );
300
+ const result = templateFn({ name: 'Stoyan'});
301
+ expect ( result ).to.be.equal ( 'My name is Stoyan' )
302
+ }) // it mixing a string
303
+
304
+
305
+
306
+ it ( 'Broken template', () => {
307
+ // If template is broken, the result should be a string, representation of the error.
308
+ const myTpl = {
309
+ template : `My {{ {{ welcome }}`
310
+ };
311
+ const templateFn = morphAPI.build ( myTpl );
312
+ const result = templateFn();
313
+ expect ( result ).to.be.equal ( 'Error: Nested placeholders. Close placeholder before open new one.' )
314
+ }) // it broken template
315
+
316
+
317
+
318
+ it ( 'Data only - object. Default field is "text"', () => {
319
+ // If provided data is object - default field is 'text'
320
+ const myTpl = {
321
+ template : `My name is {{ person }}.`
322
+ };
323
+ const templateFn = morphAPI.build ( myTpl );
324
+ const result = templateFn({ person: { text: 'Ivan', age: 25 }});
325
+ expect ( result ).to.be.equal ( 'My name is Ivan.' )
326
+ })
327
+
328
+
329
+
330
+ it ( 'Data only - array. Should render first element', () => {
331
+ // If provided data is array - default field is the first element of the array
332
+ const myTpl = {
333
+ template : `My name is {{ person }}.`
334
+ };
335
+ const templateFn = morphAPI.build ( myTpl );
336
+ const result = templateFn({ person: [ 'John', 'Milen', 'Vladislav' ]});
337
+ expect ( result ).to.be.equal ( 'My name is John.' )
338
+ })
339
+
340
+
341
+
342
+ it ( 'Auto mixing array results', () => {
343
+ // Auto mix if in the end of the action-list the result is still an array
344
+ const myTpl = {
345
+ template: `My friends are {{ friendsList: li }}.`
346
+ , helpers : {
347
+ li: `<li>{{ text }}</li>`
348
+ }
349
+ };
350
+ const templateFn = morphAPI.build ( myTpl );
351
+ const result = templateFn({ friendsList: [ 'John', 'Milen', 'Vladislav' ]});
352
+ expect ( result ).to.be.equal ( 'My friends are <li>John</li><li>Milen</li><li>Vladislav</li>.' )
353
+ })
354
+
355
+
356
+
357
+ it ( 'Nested templates', () => {
358
+ // Use helper functions to render other templates
359
+
360
+ const secondaryTpl = { // Template for nesting
361
+ template : `My friends are {{ names }}.`
362
+ }
363
+ const secTemplateFn = morphAPI.build ( secondaryTpl );
364
+
365
+ const myTpl = { // Top level template
366
+ template : `My name is {{ name }}. {{ friends: friendListing, []coma }}`
367
+ , helpers: {
368
+ friendListing: ( d ) => {
369
+ return secTemplateFn ({ names : d }) // Nested template render
370
+ }
371
+ , coma: ( res ) => res.join(', ')
372
+ }
373
+ }
374
+ const templateFn = morphAPI.build ( myTpl );
375
+ const result = templateFn({
376
+ name : 'Peter'
377
+ , friends: [ 'John', 'Milen', 'Vladislav' ]
378
+ });
379
+ expect ( result ).to.be.equal ( 'My name is Peter. My friends are John, Milen, Vladislav.' )
380
+ }) // it nested templates
381
+
382
+
383
+
384
+ it ( 'List - Data deep object', () => {
385
+ const myTpl = {
386
+ template : `My list: {{ list: ul , [], li, #, line }}.`
387
+ , helpers: {
388
+ line: `{{ name }} - {{ age }}`
389
+ , ul: `<ul>{{ text }}</ul>`
390
+ , li: `<li>{{ text }}</li>`
391
+ }
392
+
393
+ };
394
+ const data = {
395
+ list : [
396
+ 'John'
397
+ , {
398
+ name: 'Milen'
399
+ , age: 25
400
+ }
401
+ , 'Vladislav'
402
+ , {
403
+ name: 'Stoyan'
404
+ , age: 30}
405
+ ]
406
+ };
407
+ const templateFn = morphAPI.build ( myTpl );
408
+ const result = templateFn ( data );
409
+ expect ( result ).to.be.equal ( 'My list: <ul><li>John</li><li>Milen - 25</li><li>Vladislav</li><li>Stoyan - 30</li></ul>.' )
410
+ }) // it list - data deep object
411
+
412
+
413
+
414
+ it ( 'Object - Data deep object', () => {
415
+ const myTpl = {
416
+ template : `Profile: {{ me: ul , [], li, #, line }}.`
417
+ , helpers: {
418
+ line: `({{ height}}cm,{{ weight}}kg)`
419
+ , ul: `<ul>{{ text }}</ul>`
420
+ , li: `<li>{{ name }} - {{stats}}</li>`
421
+ }
422
+ };
423
+ const data = {
424
+ me : {
425
+ name: 'Peter'
426
+ , stats : {
427
+ age: 50
428
+ , height: 180
429
+ , weight: 66
430
+ }
431
+ }
432
+ };
433
+ const templateFn = morphAPI.build ( myTpl );
434
+ const result = templateFn ( data );
435
+ expect ( result ).to.be.equal ( 'Profile: <ul><li>Peter - (180cm,66kg)</li></ul>.' )
436
+ }) // it object - data deep object
437
+
438
+
439
+
440
+ it ( 'Use extra renders: "+" ', () => {
441
+ let touch = false;
442
+ const myTpl = {
443
+ template : `Profile: {{ me: +line }}.`
444
+ , helpers: {
445
+ line: ( x ) => {
446
+ expect ( x ).to.have.property ( 'stats' )
447
+ expect ( x ).to.have.property ( 'name' )
448
+ const { height, weight } = x.stats;
449
+ touch = true
450
+ return `${x.name} - (${height}cm,${weight}kg)`
451
+ } // line helper
452
+ } // helpers
453
+ };
454
+ const data = {
455
+ me : {
456
+ name: 'Peter'
457
+ , stats : {
458
+ age: 50
459
+ , height: 180
460
+ , weight: 66
461
+ }
462
+ }
463
+ };
464
+ const templateFn = morphAPI.build ( myTpl );
465
+ const result = templateFn ( data );
466
+ expect ( result ).to.be.equal ( 'Profile: Peter - (180cm,66kg).' )
467
+ expect ( touch ).to.be.true
468
+ }) // it use extra renders "+"
469
+
470
+ }) // Describe
@@ -0,0 +1,78 @@
1
+ import morph from '../src/main.js'
2
+ import { expect } from 'chai'
3
+
4
+
5
+ describe ( 'morph: storage', () => {
6
+
7
+
8
+
9
+ it ( 'Add template to default storage', () => {
10
+ const myTpl = {
11
+ template : `My name is {{ name }}.`
12
+ };
13
+ morph.add ( 'myTpl', morph.build ( myTpl ) );
14
+ const result = morph.get ( 'myTpl' )({ name: 'Peter' })
15
+ expect ( result ).to.be.equal ( 'My name is Peter.' )
16
+ })
17
+
18
+
19
+
20
+ it ( 'Add template definition to default storage', () => {
21
+ morph.clear ()
22
+ const myTpl = {
23
+ template : `My name is {{ name }}.`
24
+ };
25
+ // Can we check if the template is already in the storage?
26
+ morph.add ( 'myName', myTpl );
27
+ const result = morph.get ( 'myName' )({ name: 'Peter' })
28
+ expect ( result ).to.be.equal ( 'My name is Peter.' )
29
+ })
30
+
31
+
32
+
33
+ it ( 'Wrong template description', () => {
34
+ //
35
+ morph.clear ()
36
+ console.error = ( str ) => { // Override console.error to avoid console output
37
+ expect ( str ).to.be.equal ( 'Error: Template "fake" looks broken and is not added to storage.' )
38
+ }
39
+ const fakeTpl = { a: 12, b: 'Hello' };
40
+ morph.add ( 'fake', fakeTpl );
41
+ }) // it wrong template description
42
+
43
+
44
+
45
+ it ( 'Add component to default storage', () => {
46
+ morph.clear ()
47
+ const myTpl = { template: `My name is {{ name }}.` };
48
+ const myTplFn = morph.build ( myTpl );
49
+ morph.add ( 'myTpl', myTplFn );
50
+ const result = morph.get ( 'myTpl' )({ name: 'Peter' })
51
+ expect ( result ).to.be.equal ( 'My name is Peter.' )
52
+ }) // it add component to default storage
53
+
54
+
55
+
56
+ it ( 'Add template definition to custom storage', () => {
57
+ // Using a custom named storage if preffer to organize templates somehow
58
+ morph.clear () // Clear the storage from previous tests
59
+ const myTpl = { template: `My name is {{ name }}.` };
60
+ morph.add ( 'myName', myTpl, 'hidden' ); // Provide a custom storage name as 3rd argument
61
+
62
+ let result = morph.get ('myName')({ name: 'Peter' });
63
+ // result of morph.get() is a function that returns a error.
64
+ // Error will popup as a rendering result
65
+
66
+ expect ( result ).to.be.equal ( 'Error: Template "myName" does not exist in storage "default".' )
67
+ result = morph.get ('myName', 'hidden')({ name: 'Peter' })
68
+ expect ( result ).to.be.equal ( 'My name is Peter.' )
69
+
70
+ let list = morph.list ();
71
+ expect ( list ).to.have.length ( 0 )
72
+
73
+ list = morph.list ('hidden');
74
+ expect ( list ).to.have.length ( 1 )
75
+ expect ( list[0] ).to.be.equal ( 'myName' )
76
+ }) // it add template definition to custom storage
77
+
78
+ }) // describe
@@ -0,0 +1,73 @@
1
+ import morph from '../src/main.js'
2
+ import { expect } from 'chai'
3
+
4
+
5
+
6
+ describe ( 'morph: commands', () => {
7
+
8
+
9
+
10
+ it ( 'Request a template', () => {
11
+ // Command 'raw'
12
+ const myTpl = { template : `My name is {{ name }}.` };
13
+ morph.add ( 'myName', myTpl );
14
+ const result = morph.get ( 'myName' )('raw')
15
+ expect ( result ).to.be.equal ( 'My name is {{ name }}.' )
16
+ }) // it request a template
17
+
18
+
19
+
20
+ it ( 'Request a handshake', () => {
21
+ let demo = { name: 'Stoyan' }
22
+ const myTpl = {
23
+ template : `My name is {{ name }}.`
24
+ , handshake : demo
25
+ };
26
+ morph.add ( 'myName', myTpl );
27
+ const result = morph.get ( 'myName' )('handshake')
28
+ expect ( result ).to.be.deep.equal ( demo )
29
+ }) // it request a handshake
30
+
31
+
32
+
33
+ it ( 'Request a demo', () => {
34
+ // Render a template with the handshake
35
+ let demo = { name: 'Stoyan' }
36
+ const myTpl = {
37
+ template : `My name is {{ name }}.`
38
+ , handshake : demo
39
+ };
40
+ morph.add ( 'myName', myTpl );
41
+ const result = morph.get ( 'myName' )('demo')
42
+ expect ( result ).to.be.equal ( 'My name is Stoyan.' )
43
+ }) // it request a demo
44
+
45
+
46
+
47
+
48
+ it ( 'Wrong command to component', () => {
49
+ // Commands other than 'raw', 'handshake' and 'demo' should return an error.
50
+ let demo = { name: 'Stoyan' }
51
+ const myTpl = {
52
+ template : `My name is {{ name }}.`
53
+ , handshake : demo
54
+ };
55
+ morph.add ( 'myName', myTpl );
56
+ const result = morph.get ( 'myName' )('fake')
57
+ expect ( result ).to.be.equal ( 'Error: Wrong command "fake". Available commands: raw, demo, handshake, placeholders.' )
58
+ }) // it wrong command to component
59
+
60
+
61
+ it ( 'See placeholders', () => {
62
+ let demo = { name: 'Stoyan', age: 30 }
63
+ const myTpl = {
64
+ template : `My name is {{ name }}. Age {{ age }}.`
65
+ , handshake : demo
66
+ };
67
+ morph.add ( 'myName', myTpl );
68
+ const result = morph.get ( 'myName' )('placeholders')
69
+ expect ( result ).to.be.equal ( '{{ name }}, {{ age }}' )
70
+ }) // it see placeholders
71
+
72
+
73
+ }) // describe