/ packages / string-templates / test / basic.spec.ts
basic.spec.ts
  1  import {
  2    processObject,
  3    processString,
  4    isValid,
  5    makePropSafe,
  6    getManifest,
  7    encodeJSBinding,
  8    doesContainString,
  9    disableEscaping,
 10    findHBSBlocks,
 11  } from "../src/index"
 12  
 13  describe("Test that the string processing works correctly", () => {
 14    it("should process a basic template string", async () => {
 15      const output = await processString("templating is {{ adjective }}", {
 16        adjective: "easy",
 17      })
 18      expect(output).toBe("templating is easy")
 19    })
 20  
 21    it("should process a literal template", async () => {
 22      const output = await processString("derp is {{{ adjective }}}", {
 23        adjective: "derp",
 24      })
 25      expect(output).toBe("derp is derp")
 26    })
 27  
 28    it("should fail gracefully when wrong type passed in", async () => {
 29      let error = null
 30      try {
 31        await processString(null as any, null as any)
 32      } catch (err) {
 33        error = err
 34      }
 35      expect(error).not.toBeNull()
 36    })
 37  
 38    it("confirm that null properties are handled correctly", async () => {
 39      const output = await processString("hello {{ name }} I am {{ name2 }}", {
 40        name: undefined,
 41        name2: null,
 42      })
 43      expect(output).toBe("hello  I am ")
 44    })
 45  })
 46  
 47  describe("Test that the object processing works correctly", () => {
 48    it("should be able to process an object with some template strings", async () => {
 49      const output = await processObject(
 50        {
 51          first: "thing is {{ adjective }}",
 52          second: "thing is bad",
 53          third: "we are {{ adjective }} {{ noun }}",
 54        },
 55        {
 56          adjective: "easy",
 57          noun: "people",
 58        }
 59      )
 60      expect(output.first).toBe("thing is easy")
 61      expect(output.second).toBe("thing is bad")
 62      expect(output.third).toBe("we are easy people")
 63    })
 64  
 65    it("should be able to handle arrays of string templates", async () => {
 66      const output = await processObject(
 67        ["first {{ noun }}", "second {{ noun }}"],
 68        {
 69          noun: "person",
 70        }
 71      )
 72      expect(output[0]).toBe("first person")
 73      expect(output[1]).toBe("second person")
 74    })
 75  
 76    it("should fail gracefully when object passed in has cycles", async () => {
 77      let error = null
 78      try {
 79        const innerObj: any = { a: "thing {{ a }}" }
 80        innerObj.b = innerObj
 81        await processObject(innerObj, { a: 1 })
 82      } catch (err) {
 83        error = err
 84      }
 85      expect(error).not.toBeNull()
 86    })
 87  
 88    it("check objects get converted to string JSON automatically", async () => {
 89      const row = { a: 1 }
 90      const output = await processString("{{ trigger.row }}", {
 91        trigger: {
 92          row,
 93        },
 94      })
 95      expect(JSON.parse(output)).toEqual(row)
 96    })
 97  
 98    it("should be able to handle null objects", async () => {
 99      let error = null
100      try {
101        await processObject(null as any, null as any)
102      } catch (err) {
103        error = err
104      }
105      expect(error).toBeNull()
106    })
107  
108    it("should be able to handle booleans", async () => {
109      const output = await processObject(
110        {
111          first: true,
112          second: "true",
113          third: "another string",
114          forth: "with {{ template }}",
115        },
116        {
117          template: "value",
118        }
119      )
120      expect(output).toEqual({
121        first: true,
122        second: "true",
123        third: "another string",
124        forth: "with value",
125      })
126    })
127  })
128  
129  describe("check arrays", () => {
130    describe("index with square brackets", () => {
131      it.each([
132        [0, "1"],
133        [1, "2"],
134      ])("should handle an array of primitive types", async (index, expected) => {
135        const json = [1, 2, 3]
136        const output = await processString(`{{ testing.[${index}] }}`, {
137          testing: json,
138        })
139        expect(output).toEqual(expected)
140      })
141  
142      it("should handle an array of objects", async () => {
143        const json = [{ value: 1 }, { value: 2 }, { value: 3 }]
144        const output = await processString("{{ testing.[1] }}", {
145          testing: json,
146        })
147        expect(output).toEqual('{"value":2}')
148      })
149  
150      it("should handle nesting properties in an array of objects", async () => {
151        const json = [{ value: 1 }, { value: 2 }, { value: 3 }]
152        const output = await processString("{{ testing.[1].value }}", {
153          testing: json,
154        })
155        expect(output).toEqual("2")
156      })
157    })
158  
159    describe("index without square brackets", () => {
160      it("should not handle an array of primitive types", async () => {
161        const json = [1, 2, 3]
162        const output = await processString(`{{ testing.1 }}`, {
163          testing: json,
164        })
165        expect(output).toEqual("{{ testing.1 }}")
166      })
167  
168      it("should not handle an array of objects", async () => {
169        const json = [{ value: 1 }, { value: 2 }, { value: 3 }]
170        const output = await processString("{{ testing.1 }}", {
171          testing: json,
172        })
173        expect(output).toEqual("{{ testing.1 }}")
174      })
175  
176      it("should handle nesting properties in an array of object types", async () => {
177        const json = [{ value: 1 }, { value: 2 }, { value: 3 }]
178        const output = await processString("{{ testing.1.value }}", {
179          testing: json,
180        })
181        expect(output).toEqual("2")
182      })
183    })
184  })
185  
186  describe("check returning objects", () => {
187    it("should handle an array of objects", async () => {
188      const json = [{ a: 1 }, { a: 2 }]
189      const output = await processString("{{ testing }}", {
190        testing: json,
191      })
192      expect(output).toEqual(JSON.stringify(json))
193    })
194  })
195  
196  describe("check the utility functions", () => {
197    it("should return false for an invalid template string", () => {
198      const valid = isValid("{{ table1.thing prop }}")
199      expect(valid).toBe(false)
200    })
201  
202    it("should state this template is valid", () => {
203      const valid = isValid("{{ thing }}")
204      expect(valid).toBe(true)
205    })
206  
207    it("should make a property safe", () => {
208      const property = makePropSafe("thing")
209      expect(property).toEqual("[thing]")
210    })
211  
212    it("should be able to handle an input date object", async () => {
213      const date = new Date()
214      const output = await processString("{{ dateObj }}", { dateObj: date })
215      expect(date.toString()).toEqual(output)
216    })
217  })
218  
219  describe("check falsy values", () => {
220    it("should get a zero out when context contains it", async () => {
221      const output = await processString("{{ number }}", { number: 0 })
222      expect(output).toEqual("0")
223    })
224  
225    it("should get false out when context contains it", async () => {
226      const output = await processString("{{ bool }}", { bool: false })
227      expect(output).toEqual("false")
228    })
229  })
230  
231  describe("check manifest", () => {
232    it("should be able to retrieve the manifest", () => {
233      const manifest = getManifest()
234      expect(manifest.math).not.toBeNull()
235      expect(manifest.math.abs.description).toBe(
236        "<p>Return the magnitude of <code>a</code>.</p>\n"
237      )
238    })
239  })
240  
241  describe("check full stops that are safe", () => {
242    it("should allow using an escaped full stop", async () => {
243      const data = {
244        "c53a4a604fa754d33baaafd5bca4d3658-YXuUBqt5vI": {
245          "persons.firstname": "1",
246        },
247      }
248      const template =
249        "{{ [c53a4a604fa754d33baaafd5bca4d3658-YXuUBqt5vI].[persons.firstname] }}"
250      const output = await processString(template, data)
251      expect(output).toEqual("1")
252    })
253  })
254  
255  describe("check does contain string function", () => {
256    it("should work for a simple case", () => {
257      const hbs = "hello {{ name }}"
258      expect(doesContainString(hbs, "name")).toEqual(true)
259    })
260  
261    it("should reject a case where its in the string, but not the handlebars", () => {
262      const hbs = "hello {{ name }}"
263      expect(doesContainString(hbs, "hello")).toEqual(false)
264    })
265  
266    it("should handle if its in javascript", () => {
267      const js = encodeJSBinding(`return $("foo")`)
268      expect(doesContainString(js, "foo")).toEqual(true)
269    })
270  })
271  
272  describe("check that disabling escaping function works", () => {
273    it("should work for a single statement", () => {
274      expect(disableEscaping("{{ name }}")).toEqual("{{{ name }}}")
275    })
276  
277    it("should work for two statements", () => {
278      expect(disableEscaping("{{ name }} welcome to {{ platform }}")).toEqual(
279        "{{{ name }}} welcome to {{{ platform }}}"
280      )
281    })
282  
283    it("shouldn't convert triple braces", () => {
284      expect(disableEscaping("{{{ name }}}")).toEqual("{{{ name }}}")
285    })
286  
287    it("should work with a combination", () => {
288      expect(disableEscaping("{{ name }} welcome to {{{ platform }}}")).toEqual(
289        "{{{ name }}} welcome to {{{ platform }}}"
290      )
291    })
292  
293    it("should work with multiple escaped", () => {
294      expect(disableEscaping("{{ name }} welcome to {{ name }}")).toEqual(
295        "{{{ name }}} welcome to {{{ name }}}"
296      )
297    })
298  })
299  
300  describe("check find hbs blocks function", () => {
301    it("should find none", () => {
302      expect(findHBSBlocks("hello there")).toEqual([])
303    })
304  
305    it("should find two", () => {
306      expect(findHBSBlocks("{{ hello }} there {{{ name }}}")).toEqual([
307        "{{ hello }}",
308        "{{{ name }}}",
309      ])
310    })
311  })
312  
313  describe("should leave HBS blocks if not found using option", () => {
314    it("should replace one, leave one", async () => {
315      const output = await processString(
316        "{{ a }}, {{ b }}",
317        { b: 1 },
318        { onlyFound: true }
319      )
320      expect(output).toBe("{{ a }}, 1")
321    })
322  })
323  
324  describe("check multiple space behaviour", () => {
325    it("should remove whitespace and use the helper correctly", async () => {
326      const output = await processString("{{   add   num1   num2 }}", {
327        num1: 1,
328        num2: 2,
329      })
330      expect(output).toEqual("3")
331    })
332  
333    it("should ensure that whitespace within a string is respected", async () => {
334      const output = await processString("{{ trimRight 'test   string  ' }}", {
335        num1: 1,
336        num2: 2,
337      })
338      expect(output).toEqual("test   string")
339    })
340  })