From 25ab1dbc2dab32df74f28c680e3641d2e8edffb2 Mon Sep 17 00:00:00 2001 From: agnik Date: Fri, 20 Feb 2026 01:40:39 +0530 Subject: [PATCH 1/2] test: add unit tests for compare, math, case, condition, and repeat helpers --- tests/helpers/helpers.spec.ts | 342 ++++++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 tests/helpers/helpers.spec.ts diff --git a/tests/helpers/helpers.spec.ts b/tests/helpers/helpers.spec.ts new file mode 100644 index 0000000..35b24d7 --- /dev/null +++ b/tests/helpers/helpers.spec.ts @@ -0,0 +1,342 @@ +import * as Handlebars from "handlebars/dist/handlebars"; + +import { DateAndTimeUtils } from "@templates/utils/dateAndTime"; +import { HelperFactory } from "@templates/helpers/index"; + +describe("Handlebars Helpers", () => { + beforeAll(() => { + const dateAndTimeUtils = new DateAndTimeUtils("en", "DD/MM/YYYY", "HH:mm"); + HelperFactory.registerHelpers(dateAndTimeUtils); + }); + + // ───────────────────────────────────────────── + // Compare Helper + // ───────────────────────────────────────────── + describe("compare helper", () => { + test("should support == operator", () => { + const template = Handlebars.compile('{{#if (compare a "==" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: 5, b: 5 })).toEqual("yes"); + expect(template({ a: 5, b: 6 })).toEqual("no"); + }); + + test("should support == with type coercion", () => { + const template = Handlebars.compile('{{#if (compare a "==" b)}}yes{{else}}no{{/if}}'); + // eslint-disable-next-line eqeqeq + expect(template({ a: 5, b: "5" })).toEqual("yes"); + }); + + test("should support === operator", () => { + const template = Handlebars.compile('{{#if (compare a "===" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: 5, b: 5 })).toEqual("yes"); + expect(template({ a: 5, b: "5" })).toEqual("no"); + }); + + test("should support != operator", () => { + const template = Handlebars.compile('{{#if (compare a "!=" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: 5, b: 6 })).toEqual("yes"); + expect(template({ a: 5, b: 5 })).toEqual("no"); + }); + + test("should support !== operator", () => { + const template = Handlebars.compile('{{#if (compare a "!==" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: 5, b: "5" })).toEqual("yes"); + expect(template({ a: 5, b: 5 })).toEqual("no"); + }); + + test("should support < operator", () => { + const template = Handlebars.compile('{{#if (compare a "<" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: 3, b: 5 })).toEqual("yes"); + expect(template({ a: 5, b: 3 })).toEqual("no"); + expect(template({ a: 5, b: 5 })).toEqual("no"); + }); + + test("should support <= operator", () => { + const template = Handlebars.compile('{{#if (compare a "<=" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: 3, b: 5 })).toEqual("yes"); + expect(template({ a: 5, b: 5 })).toEqual("yes"); + expect(template({ a: 6, b: 5 })).toEqual("no"); + }); + + test("should support > operator", () => { + const template = Handlebars.compile('{{#if (compare a ">" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: 5, b: 3 })).toEqual("yes"); + expect(template({ a: 3, b: 5 })).toEqual("no"); + }); + + test("should support >= operator", () => { + const template = Handlebars.compile('{{#if (compare a ">=" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: 5, b: 3 })).toEqual("yes"); + expect(template({ a: 5, b: 5 })).toEqual("yes"); + expect(template({ a: 3, b: 5 })).toEqual("no"); + }); + + test("should support named aliases (eq, ne, lt, lte, gt, gte)", () => { + expect(Handlebars.compile('{{#if (compare a "eq" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "seq" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "strictly-equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "ne" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 2 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "not-equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 2 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "sne" b)}}yes{{else}}no{{/if}}')({ a: 1, b: "1" })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "strictly-not-equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: "1" })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "lt" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 2 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "less-than" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 2 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "lte" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "less-than-equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "gt" b)}}yes{{else}}no{{/if}}')({ a: 2, b: 1 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "greater-than" b)}}yes{{else}}no{{/if}}')({ a: 2, b: 1 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "gte" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile('{{#if (compare a "greater-than-equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); + }); + + test("should throw on invalid operator", () => { + const template = Handlebars.compile('{{#if (compare a "invalid" b)}}yes{{/if}}'); + expect(() => template({ a: 1, b: 1 })).toThrow("Invalid comparison operator"); + }); + + test("should compare strings lexicographically", () => { + const template = Handlebars.compile('{{#if (compare a "<" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: "apple", b: "banana" })).toEqual("yes"); + expect(template({ a: "banana", b: "apple" })).toEqual("no"); + }); + + test("should handle null and undefined values", () => { + const template = Handlebars.compile('{{#if (compare a "==" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: null, b: null })).toEqual("yes"); + expect(template({ a: null, b: undefined })).toEqual("yes"); + }); + }); + + // ───────────────────────────────────────────── + // Condition Helper + // ───────────────────────────────────────────── + describe("condition helper", () => { + test("should support && (and) operator", () => { + const template = Handlebars.compile('{{#if (condition a "&&" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: true, b: true })).toEqual("yes"); + expect(template({ a: true, b: false })).toEqual("no"); + expect(template({ a: false, b: true })).toEqual("no"); + expect(template({ a: false, b: false })).toEqual("no"); + }); + + test("should support 'and' alias", () => { + const template = Handlebars.compile('{{#if (condition a "and" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: true, b: true })).toEqual("yes"); + expect(template({ a: true, b: false })).toEqual("no"); + }); + + test("should support || (or) operator", () => { + const template = Handlebars.compile('{{#if (condition a "||" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: true, b: false })).toEqual("yes"); + expect(template({ a: false, b: true })).toEqual("yes"); + expect(template({ a: false, b: false })).toEqual("no"); + }); + + test("should support 'or' alias", () => { + const template = Handlebars.compile('{{#if (condition a "or" b)}}yes{{else}}no{{/if}}'); + expect(template({ a: false, b: true })).toEqual("yes"); + expect(template({ a: false, b: false })).toEqual("no"); + }); + + test("should support ! (not) operator", () => { + const template = Handlebars.compile('{{#if (condition a "!")}}yes{{else}}no{{/if}}'); + expect(template({ a: false })).toEqual("yes"); + expect(template({ a: true })).toEqual("no"); + }); + + test("should support 'not' alias", () => { + const template = Handlebars.compile('{{#if (condition a "not")}}yes{{else}}no{{/if}}'); + expect(template({ a: false })).toEqual("yes"); + expect(template({ a: true })).toEqual("no"); + }); + + test("should throw on invalid operator", () => { + const template = Handlebars.compile('{{#if (condition a "xor" b)}}yes{{/if}}'); + expect(() => template({ a: true, b: true })).toThrow("Invalid operator"); + }); + + test("should handle truthy and falsy values (not just booleans)", () => { + const andTemplate = Handlebars.compile('{{#if (condition a "&&" b)}}yes{{else}}no{{/if}}'); + expect(andTemplate({ a: 1, b: "hello" })).toEqual("yes"); + expect(andTemplate({ a: 0, b: "hello" })).toEqual("no"); + expect(andTemplate({ a: "", b: 1 })).toEqual("no"); + + const notTemplate = Handlebars.compile('{{#if (condition a "!")}}yes{{else}}no{{/if}}'); + expect(notTemplate({ a: 0 })).toEqual("yes"); + expect(notTemplate({ a: "" })).toEqual("yes"); + expect(notTemplate({ a: 1 })).toEqual("no"); + }); + + test("should work combined with compare helper", () => { + const template = Handlebars.compile('{{#if (condition (compare a ">" 10) "&&" (compare b "<" 5))}}yes{{else}}no{{/if}}'); + expect(template({ a: 15, b: 3 })).toEqual("yes"); + expect(template({ a: 15, b: 10 })).toEqual("no"); + expect(template({ a: 5, b: 3 })).toEqual("no"); + }); + }); + + // ───────────────────────────────────────────── + // Math Helper + // ───────────────────────────────────────────── + describe("math helper", () => { + test("should support addition", () => { + const template = Handlebars.compile('{{math a "+" b}}'); + expect(template({ a: 3, b: 5 })).toEqual("8"); + }); + + test("should support subtraction", () => { + const template = Handlebars.compile('{{math a "-" b}}'); + expect(template({ a: 10, b: 4 })).toEqual("6"); + }); + + test("should support multiplication", () => { + const template = Handlebars.compile('{{math a "*" b}}'); + expect(template({ a: 3, b: 7 })).toEqual("21"); + }); + + test("should support division", () => { + const template = Handlebars.compile('{{math a "/" b}}'); + expect(template({ a: 20, b: 4 })).toEqual("5"); + }); + + test("should support exponentiation", () => { + const template = Handlebars.compile('{{math a "**" b}}'); + expect(template({ a: 2, b: 3 })).toEqual("8"); + }); + + test("should support modulo", () => { + const template = Handlebars.compile('{{math a "%" b}}'); + expect(template({ a: 10, b: 3 })).toEqual("1"); + }); + + test("should throw on modulo by zero", () => { + const template = Handlebars.compile('{{math a "%" b}}'); + expect(() => template({ a: 10, b: 0 })).toThrow("% operator used with 0"); + }); + + test("should handle floating point numbers", () => { + const template = Handlebars.compile('{{math a "+" b}}'); + expect(template({ a: 1.5, b: 2.3 })).toEqual("3.8"); + }); + + test("should handle negative numbers", () => { + const template = Handlebars.compile('{{math a "+" b}}'); + expect(template({ a: -5, b: 3 })).toEqual("-2"); + }); + + test("should throw on non-numeric inputs", () => { + const template = Handlebars.compile('{{math a "+" b}}'); + expect(() => template({ a: "abc", b: 3 })).toThrow("Can't convert"); + }); + + test("should throw on invalid operator", () => { + const template = Handlebars.compile('{{math a "^" b}}'); + expect(() => template({ a: 2, b: 3 })).toThrow("Invalid operator"); + }); + + test("should support nested math operations", () => { + const template = Handlebars.compile('{{math (math a "+" b) "*" c}}'); + expect(template({ a: 2, b: 3, c: 4 })).toEqual("20"); + }); + + test("should return Infinity for division by zero", () => { + const template = Handlebars.compile('{{math a "/" b}}'); + expect(template({ a: 10, b: 0 })).toEqual("Infinity"); + }); + + test("should parse string numbers correctly", () => { + const template = Handlebars.compile('{{math a "+" b}}'); + expect(template({ a: "5", b: "3" })).toEqual("8"); + }); + }); + + // ───────────────────────────────────────────── + // Case Helper + // ───────────────────────────────────────────── + describe("case helper", () => { + test("should convert to uppercase", () => { + const template = Handlebars.compile('{{case "upper" text}}'); + expect(template({ text: "hello world" })).toEqual("HELLO WORLD"); + }); + + test("should convert to lowercase", () => { + const template = Handlebars.compile('{{case "lower" text}}'); + expect(template({ text: "HELLO WORLD" })).toEqual("hello world"); + }); + + test("should handle mixed case input for upper", () => { + const template = Handlebars.compile('{{case "upper" text}}'); + expect(template({ text: "Hello World" })).toEqual("HELLO WORLD"); + }); + + test("should handle mixed case input for lower", () => { + const template = Handlebars.compile('{{case "lower" text}}'); + expect(template({ text: "Hello World" })).toEqual("hello world"); + }); + + test("should handle empty string", () => { + const template = Handlebars.compile('{{case "upper" text}}'); + expect(template({ text: "" })).toEqual(""); + }); + + test("should handle numbers as input", () => { + const template = Handlebars.compile('{{case "upper" text}}'); + expect(template({ text: 123 })).toEqual("123"); + }); + + test("should throw on invalid case type", () => { + const template = Handlebars.compile('{{case "title" text}}'); + expect(() => template({ text: "hello" })).toThrow("Invalid case type"); + }); + }); + + // ───────────────────────────────────────────── + // Repeat Helper + // ───────────────────────────────────────────── + describe("repeat helper", () => { + test("should repeat content n times", () => { + const template = Handlebars.compile("{{#repeat 3}}item\n{{/repeat}}"); + expect(template({})).toEqual("item\nitem\nitem\n"); + }); + + test("should provide repeat_index variable", () => { + const template = Handlebars.compile("{{#repeat 3}}{{repeat_index}}\n{{/repeat}}"); + expect(template({})).toEqual("0\n1\n2\n"); + }); + + test("should work with math helper for 1-based index", () => { + const template = Handlebars.compile('{{#repeat 3}}{{math repeat_index "+" 1}}\n{{/repeat}}'); + expect(template({})).toEqual("1\n2\n3\n"); + }); + + test("should repeat zero times for count of 0", () => { + const template = Handlebars.compile("{{#repeat 0}}item\n{{/repeat}}"); + expect(template({})).toEqual(""); + }); + + test("should work with dynamic count from context", () => { + const template = Handlebars.compile("{{#repeat count}}x{{/repeat}}"); + expect(template({ count: 4 })).toEqual("xxxx"); + }); + + test("should throw on non-numeric input", () => { + const template = Handlebars.compile("{{#repeat count}}item{{/repeat}}"); + expect(() => template({ count: "abc" })).toThrow("Can't convert"); + }); + + test("should preserve outer context variables", () => { + const template = Handlebars.compile("{{#repeat 2}}{{name}}-{{repeat_index}} {{/repeat}}"); + expect(template({ name: "test" })).toEqual("test-0 test-1 "); + }); + + test("should handle negative count by producing no output", () => { + const template = Handlebars.compile("{{#repeat count}}item{{/repeat}}"); + expect(template({ count: -1 })).toEqual(""); + }); + + test("should repeat exactly once for count of 1", () => { + const template = Handlebars.compile("{{#repeat 1}}only{{/repeat}}"); + expect(template({})).toEqual("only"); + }); + }); +}); From eb5b31c3d7273a143a7b259588288aec782d2569 Mon Sep 17 00:00:00 2001 From: alondmnt Date: Thu, 12 Mar 2026 14:07:30 +1100 Subject: [PATCH 2/2] =?UTF-8?q?style:=20fix=20lint=20errors=20in=20helpers?= =?UTF-8?q?=20test=20(single=20quotes=20=E2=86=92=20double=20quotes)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/helpers/helpers.spec.ts | 120 +++++++++++++++++----------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/tests/helpers/helpers.spec.ts b/tests/helpers/helpers.spec.ts index 35b24d7..b43a36c 100644 --- a/tests/helpers/helpers.spec.ts +++ b/tests/helpers/helpers.spec.ts @@ -14,94 +14,94 @@ describe("Handlebars Helpers", () => { // ───────────────────────────────────────────── describe("compare helper", () => { test("should support == operator", () => { - const template = Handlebars.compile('{{#if (compare a "==" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \"==\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: 5, b: 5 })).toEqual("yes"); expect(template({ a: 5, b: 6 })).toEqual("no"); }); test("should support == with type coercion", () => { - const template = Handlebars.compile('{{#if (compare a "==" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \"==\" b)}}yes{{else}}no{{/if}}"); // eslint-disable-next-line eqeqeq expect(template({ a: 5, b: "5" })).toEqual("yes"); }); test("should support === operator", () => { - const template = Handlebars.compile('{{#if (compare a "===" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \"===\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: 5, b: 5 })).toEqual("yes"); expect(template({ a: 5, b: "5" })).toEqual("no"); }); test("should support != operator", () => { - const template = Handlebars.compile('{{#if (compare a "!=" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \"!=\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: 5, b: 6 })).toEqual("yes"); expect(template({ a: 5, b: 5 })).toEqual("no"); }); test("should support !== operator", () => { - const template = Handlebars.compile('{{#if (compare a "!==" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \"!==\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: 5, b: "5" })).toEqual("yes"); expect(template({ a: 5, b: 5 })).toEqual("no"); }); test("should support < operator", () => { - const template = Handlebars.compile('{{#if (compare a "<" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \"<\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: 3, b: 5 })).toEqual("yes"); expect(template({ a: 5, b: 3 })).toEqual("no"); expect(template({ a: 5, b: 5 })).toEqual("no"); }); test("should support <= operator", () => { - const template = Handlebars.compile('{{#if (compare a "<=" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \"<=\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: 3, b: 5 })).toEqual("yes"); expect(template({ a: 5, b: 5 })).toEqual("yes"); expect(template({ a: 6, b: 5 })).toEqual("no"); }); test("should support > operator", () => { - const template = Handlebars.compile('{{#if (compare a ">" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \">\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: 5, b: 3 })).toEqual("yes"); expect(template({ a: 3, b: 5 })).toEqual("no"); }); test("should support >= operator", () => { - const template = Handlebars.compile('{{#if (compare a ">=" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \">=\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: 5, b: 3 })).toEqual("yes"); expect(template({ a: 5, b: 5 })).toEqual("yes"); expect(template({ a: 3, b: 5 })).toEqual("no"); }); test("should support named aliases (eq, ne, lt, lte, gt, gte)", () => { - expect(Handlebars.compile('{{#if (compare a "eq" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "seq" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "strictly-equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "ne" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 2 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "not-equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 2 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "sne" b)}}yes{{else}}no{{/if}}')({ a: 1, b: "1" })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "strictly-not-equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: "1" })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "lt" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 2 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "less-than" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 2 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "lte" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "less-than-equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "gt" b)}}yes{{else}}no{{/if}}')({ a: 2, b: 1 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "greater-than" b)}}yes{{else}}no{{/if}}')({ a: 2, b: 1 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "gte" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); - expect(Handlebars.compile('{{#if (compare a "greater-than-equals" b)}}yes{{else}}no{{/if}}')({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"eq\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"equals\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"seq\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"strictly-equals\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"ne\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 2 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"not-equals\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 2 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"sne\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: "1" })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"strictly-not-equals\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: "1" })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"lt\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 2 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"less-than\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 2 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"lte\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"less-than-equals\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"gt\" b)}}yes{{else}}no{{/if}}")({ a: 2, b: 1 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"greater-than\" b)}}yes{{else}}no{{/if}}")({ a: 2, b: 1 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"gte\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 1 })).toEqual("yes"); + expect(Handlebars.compile("{{#if (compare a \"greater-than-equals\" b)}}yes{{else}}no{{/if}}")({ a: 1, b: 1 })).toEqual("yes"); }); test("should throw on invalid operator", () => { - const template = Handlebars.compile('{{#if (compare a "invalid" b)}}yes{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \"invalid\" b)}}yes{{/if}}"); expect(() => template({ a: 1, b: 1 })).toThrow("Invalid comparison operator"); }); test("should compare strings lexicographically", () => { - const template = Handlebars.compile('{{#if (compare a "<" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \"<\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: "apple", b: "banana" })).toEqual("yes"); expect(template({ a: "banana", b: "apple" })).toEqual("no"); }); test("should handle null and undefined values", () => { - const template = Handlebars.compile('{{#if (compare a "==" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (compare a \"==\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: null, b: null })).toEqual("yes"); expect(template({ a: null, b: undefined })).toEqual("yes"); }); @@ -112,7 +112,7 @@ describe("Handlebars Helpers", () => { // ───────────────────────────────────────────── describe("condition helper", () => { test("should support && (and) operator", () => { - const template = Handlebars.compile('{{#if (condition a "&&" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (condition a \"&&\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: true, b: true })).toEqual("yes"); expect(template({ a: true, b: false })).toEqual("no"); expect(template({ a: false, b: true })).toEqual("no"); @@ -120,55 +120,55 @@ describe("Handlebars Helpers", () => { }); test("should support 'and' alias", () => { - const template = Handlebars.compile('{{#if (condition a "and" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (condition a \"and\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: true, b: true })).toEqual("yes"); expect(template({ a: true, b: false })).toEqual("no"); }); test("should support || (or) operator", () => { - const template = Handlebars.compile('{{#if (condition a "||" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (condition a \"||\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: true, b: false })).toEqual("yes"); expect(template({ a: false, b: true })).toEqual("yes"); expect(template({ a: false, b: false })).toEqual("no"); }); test("should support 'or' alias", () => { - const template = Handlebars.compile('{{#if (condition a "or" b)}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (condition a \"or\" b)}}yes{{else}}no{{/if}}"); expect(template({ a: false, b: true })).toEqual("yes"); expect(template({ a: false, b: false })).toEqual("no"); }); test("should support ! (not) operator", () => { - const template = Handlebars.compile('{{#if (condition a "!")}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (condition a \"!\")}}yes{{else}}no{{/if}}"); expect(template({ a: false })).toEqual("yes"); expect(template({ a: true })).toEqual("no"); }); test("should support 'not' alias", () => { - const template = Handlebars.compile('{{#if (condition a "not")}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (condition a \"not\")}}yes{{else}}no{{/if}}"); expect(template({ a: false })).toEqual("yes"); expect(template({ a: true })).toEqual("no"); }); test("should throw on invalid operator", () => { - const template = Handlebars.compile('{{#if (condition a "xor" b)}}yes{{/if}}'); + const template = Handlebars.compile("{{#if (condition a \"xor\" b)}}yes{{/if}}"); expect(() => template({ a: true, b: true })).toThrow("Invalid operator"); }); test("should handle truthy and falsy values (not just booleans)", () => { - const andTemplate = Handlebars.compile('{{#if (condition a "&&" b)}}yes{{else}}no{{/if}}'); + const andTemplate = Handlebars.compile("{{#if (condition a \"&&\" b)}}yes{{else}}no{{/if}}"); expect(andTemplate({ a: 1, b: "hello" })).toEqual("yes"); expect(andTemplate({ a: 0, b: "hello" })).toEqual("no"); expect(andTemplate({ a: "", b: 1 })).toEqual("no"); - const notTemplate = Handlebars.compile('{{#if (condition a "!")}}yes{{else}}no{{/if}}'); + const notTemplate = Handlebars.compile("{{#if (condition a \"!\")}}yes{{else}}no{{/if}}"); expect(notTemplate({ a: 0 })).toEqual("yes"); expect(notTemplate({ a: "" })).toEqual("yes"); expect(notTemplate({ a: 1 })).toEqual("no"); }); test("should work combined with compare helper", () => { - const template = Handlebars.compile('{{#if (condition (compare a ">" 10) "&&" (compare b "<" 5))}}yes{{else}}no{{/if}}'); + const template = Handlebars.compile("{{#if (condition (compare a \">\" 10) \"&&\" (compare b \"<\" 5))}}yes{{else}}no{{/if}}"); expect(template({ a: 15, b: 3 })).toEqual("yes"); expect(template({ a: 15, b: 10 })).toEqual("no"); expect(template({ a: 5, b: 3 })).toEqual("no"); @@ -180,72 +180,72 @@ describe("Handlebars Helpers", () => { // ───────────────────────────────────────────── describe("math helper", () => { test("should support addition", () => { - const template = Handlebars.compile('{{math a "+" b}}'); + const template = Handlebars.compile("{{math a \"+\" b}}"); expect(template({ a: 3, b: 5 })).toEqual("8"); }); test("should support subtraction", () => { - const template = Handlebars.compile('{{math a "-" b}}'); + const template = Handlebars.compile("{{math a \"-\" b}}"); expect(template({ a: 10, b: 4 })).toEqual("6"); }); test("should support multiplication", () => { - const template = Handlebars.compile('{{math a "*" b}}'); + const template = Handlebars.compile("{{math a \"*\" b}}"); expect(template({ a: 3, b: 7 })).toEqual("21"); }); test("should support division", () => { - const template = Handlebars.compile('{{math a "/" b}}'); + const template = Handlebars.compile("{{math a \"/\" b}}"); expect(template({ a: 20, b: 4 })).toEqual("5"); }); test("should support exponentiation", () => { - const template = Handlebars.compile('{{math a "**" b}}'); + const template = Handlebars.compile("{{math a \"**\" b}}"); expect(template({ a: 2, b: 3 })).toEqual("8"); }); test("should support modulo", () => { - const template = Handlebars.compile('{{math a "%" b}}'); + const template = Handlebars.compile("{{math a \"%\" b}}"); expect(template({ a: 10, b: 3 })).toEqual("1"); }); test("should throw on modulo by zero", () => { - const template = Handlebars.compile('{{math a "%" b}}'); + const template = Handlebars.compile("{{math a \"%\" b}}"); expect(() => template({ a: 10, b: 0 })).toThrow("% operator used with 0"); }); test("should handle floating point numbers", () => { - const template = Handlebars.compile('{{math a "+" b}}'); + const template = Handlebars.compile("{{math a \"+\" b}}"); expect(template({ a: 1.5, b: 2.3 })).toEqual("3.8"); }); test("should handle negative numbers", () => { - const template = Handlebars.compile('{{math a "+" b}}'); + const template = Handlebars.compile("{{math a \"+\" b}}"); expect(template({ a: -5, b: 3 })).toEqual("-2"); }); test("should throw on non-numeric inputs", () => { - const template = Handlebars.compile('{{math a "+" b}}'); + const template = Handlebars.compile("{{math a \"+\" b}}"); expect(() => template({ a: "abc", b: 3 })).toThrow("Can't convert"); }); test("should throw on invalid operator", () => { - const template = Handlebars.compile('{{math a "^" b}}'); + const template = Handlebars.compile("{{math a \"^\" b}}"); expect(() => template({ a: 2, b: 3 })).toThrow("Invalid operator"); }); test("should support nested math operations", () => { - const template = Handlebars.compile('{{math (math a "+" b) "*" c}}'); + const template = Handlebars.compile("{{math (math a \"+\" b) \"*\" c}}"); expect(template({ a: 2, b: 3, c: 4 })).toEqual("20"); }); test("should return Infinity for division by zero", () => { - const template = Handlebars.compile('{{math a "/" b}}'); + const template = Handlebars.compile("{{math a \"/\" b}}"); expect(template({ a: 10, b: 0 })).toEqual("Infinity"); }); test("should parse string numbers correctly", () => { - const template = Handlebars.compile('{{math a "+" b}}'); + const template = Handlebars.compile("{{math a \"+\" b}}"); expect(template({ a: "5", b: "3" })).toEqual("8"); }); }); @@ -255,37 +255,37 @@ describe("Handlebars Helpers", () => { // ───────────────────────────────────────────── describe("case helper", () => { test("should convert to uppercase", () => { - const template = Handlebars.compile('{{case "upper" text}}'); + const template = Handlebars.compile("{{case \"upper\" text}}"); expect(template({ text: "hello world" })).toEqual("HELLO WORLD"); }); test("should convert to lowercase", () => { - const template = Handlebars.compile('{{case "lower" text}}'); + const template = Handlebars.compile("{{case \"lower\" text}}"); expect(template({ text: "HELLO WORLD" })).toEqual("hello world"); }); test("should handle mixed case input for upper", () => { - const template = Handlebars.compile('{{case "upper" text}}'); + const template = Handlebars.compile("{{case \"upper\" text}}"); expect(template({ text: "Hello World" })).toEqual("HELLO WORLD"); }); test("should handle mixed case input for lower", () => { - const template = Handlebars.compile('{{case "lower" text}}'); + const template = Handlebars.compile("{{case \"lower\" text}}"); expect(template({ text: "Hello World" })).toEqual("hello world"); }); test("should handle empty string", () => { - const template = Handlebars.compile('{{case "upper" text}}'); + const template = Handlebars.compile("{{case \"upper\" text}}"); expect(template({ text: "" })).toEqual(""); }); test("should handle numbers as input", () => { - const template = Handlebars.compile('{{case "upper" text}}'); + const template = Handlebars.compile("{{case \"upper\" text}}"); expect(template({ text: 123 })).toEqual("123"); }); test("should throw on invalid case type", () => { - const template = Handlebars.compile('{{case "title" text}}'); + const template = Handlebars.compile("{{case \"title\" text}}"); expect(() => template({ text: "hello" })).toThrow("Invalid case type"); }); }); @@ -305,7 +305,7 @@ describe("Handlebars Helpers", () => { }); test("should work with math helper for 1-based index", () => { - const template = Handlebars.compile('{{#repeat 3}}{{math repeat_index "+" 1}}\n{{/repeat}}'); + const template = Handlebars.compile("{{#repeat 3}}{{math repeat_index \"+\" 1}}\n{{/repeat}}"); expect(template({})).toEqual("1\n2\n3\n"); });