"use strict"
const test = require("./util").test
const assert = require("assert")
const doT = require("..")
describe("doT", () => {
  const basictemplate = "
{{=it.foo}}
"
  const basiccompiled = doT.template(basictemplate)
  describe("#template()", () => {
    it("should return a function", () => {
      assert.equal(typeof basiccompiled, "function")
    })
  })
  describe("#()", () => {
    it("should render the template", () => {
      assert.equal(basiccompiled({foo: "http"}), "http
")
      assert.equal(basiccompiled({foo: "http://abc.com"}), "http://abc.com
")
      assert.equal(basiccompiled({}), "undefined
")
    })
  })
  describe("encoding with doNotSkipEncoded=false", () => {
    it("should not replace &", () => {
      const fn = doT.template("{{=it.foo}}
")
      assert.equal(fn({foo: "&"}), "&
")
    })
  })
  describe("interpolate 2 numbers", () => {
    it("should print numbers next to each other", () => {
      test(
        ["{{=it.one}}{{=it.two}}", "{{= it.one}}{{= it.two}}", "{{= it.one }}{{= it.two }}"],
        {one: 1, two: 2},
        "12"
      )
    })
  })
  describe("type-safe interpolation", () => {
    it("should interpolate correct types", () => {
      test(
        [
          "{{%n=it.num}}-{{%s=it.str}}-{{%b=it.bool}}",
          "{{%n= it.num}}-{{%s= it.str}}-{{%b= it.bool}}",
          "{{%n= it.num }}-{{%s= it.str }}-{{%b= it.bool }}",
        ],
        {num: 1, str: "foo", bool: true},
        "1-foo-true"
      )
    })
    it("should throw render-time exception on incorrect data types", () => {
      const numTmpl = doT.template("{{%n=it.num}}")
      assert.strictEqual(numTmpl({num: 1}), "1")
      assert.throws(() => numTmpl({num: "1"}))
      assert.throws(() => numTmpl({num: true}))
      const strTmpl = doT.template("{{%s=it.str}}")
      assert.strictEqual(strTmpl({str: "foo"}), "foo")
      assert.throws(() => strTmpl({str: 1}))
      assert.throws(() => strTmpl({str: true}))
      const boolTmpl = doT.template("{{%b=it.bool}}")
      assert.strictEqual(boolTmpl({bool: true}), "true")
      assert.throws(() => boolTmpl({bool: "true"}))
      assert.throws(() => boolTmpl({bool: 1}))
    })
  })
  describe("evaluate JavaScript", () => {
    it("should print numbers next to each other", () => {
      test(["{{ it.one = 1; it.two = 2; }}{{= it.one }}{{= it.two }}"], {}, "12")
    })
  })
  describe("no HTML encoding by default", () => {
    it("should NOT replace &", () => {
      assert.equal(doT.template("{{=it.foo}}
")({foo: "&"}), "&
")
      assert.equal(doT.template("{{=it.a}}")({a: "& < > / ' \""}), "& < > / ' \"")
      assert.equal(doT.template('{{="& < > / \' \\""}}')(), "& < > / ' \"")
    })
  })
  describe("custom encoders", () => {
    describe("selfContained: false (default)", () => {
      it("should run specified encoder", () => {
        const cfg = {
          encoders: {
            str: JSON.stringify,
            rx: (s) => new RegExp(s).toString(),
          },
        }
        assert.equal(doT.template("{{str! it}}", cfg)({foo: "bar"}), '{"foo":"bar"}')
        assert.equal(doT.template("{{rx! it.regex}}", cfg)({regex: "foo.*"}), "/foo.*/")
      })
      it("should encode HTML with provided encoder", () => {
        const encodeHTML = require("../encodeHTML")()
        test({
          encoders: {
            "": encodeHTML,
          },
        })
        function test(cfg) {
          const tmpl = doT.template("{{!it.foo}}
", cfg)
          assert.equal(tmpl({foo: "http://abc.com"}), "http://abc.com
")
          assert.equal(tmpl({foo: "&"}), "&
")
        }
      })
      it("should throw compile time exception if encoder is not specified", () => {
        const cfg = {
          encoders: {
            str: JSON.stringify,
          },
        }
        assert.doesNotThrow(() => doT.template("{{str! it}}", cfg))
        assert.throws(() => doT.template("{{rx! it}}", cfg), /unknown encoder/)
      })
    })
    describe("selfContained: true", () => {
      it("should inline specified encoders passed as strings", () => {
        const cfg = {
          selfContained: true,
          encoders: {
            str: "JSON.stringify",
            rx: "(s) => new RegExp(s).toString()",
          },
        }
        assert.equal(doT.template("{{str! it}}", cfg)({foo: "bar"}), '{"foo":"bar"}')
        assert.equal(doT.template("{{rx! it.regex}}", cfg)({regex: "foo.*"}), "/foo.*/")
      })
      it("should encode HTML with inlined HTML encoder", () => {
        const getEncodeHTML = require("../encodeHTML").toString()
        test({
          selfContained: true,
          encoders: {
            "": getEncodeHTML + "()",
          },
        })
        function test(cfg) {
          const tmpl = doT.template("{{!it.foo}}
", cfg)
          assert.equal(tmpl({foo: "http://abc.com"}), "http://abc.com
")
          assert.equal(tmpl({foo: "&"}), "&
")
        }
      })
      it("should throw compile-time exception if encoder is not specified", () => {
        const cfg = {
          selfContained: true,
          encoders: {
            str: "JSON.stringify",
          },
        }
        assert.doesNotThrow(() => doT.template("{{str! it}}", cfg))
        assert.throws(() => doT.template("{{rx! it}}", cfg), /unknown encoder/)
      })
      it("should throw compile-time exception if encoder is of incorrect type", () => {
        const cfg = {
          encoders: {
            str: JSON.stringify,
            rx: "(s) => new RegExp(s).toString()",
          },
        }
        assert.doesNotThrow(() => doT.template("{{str! it}}", cfg))
        assert.doesNotThrow(() => doT.template("{{rx! it}}", {...cfg, selfContained: true}))
        assert.throws(
          () => doT.template("{{str! it}}", {...cfg, selfContained: true}),
          /encoder type must be "string"/
        )
        assert.throws(() => doT.template("{{rx! it}}", cfg), /encoder type must be "function"/)
      })
    })
  })
  describe("context destructuring", () => {
    it('should interpolate properties without "it"', () => {
      const tmpl = doT.template("{{=foo}}{{=bar}}", {argName: ["foo", "bar"]})
      console.log(tmpl.toString())
      assert.equal(tmpl({foo: 1, bar: 2}), "12")
    })
  })
  describe("invalid JS in templates", () => {
    it("should throw exception", () => {
      assert.throws(() => {
        doT.template("{{= foo + }}
")
      })
    })
  })
})