text: global.handlebarsEnv = null;
  
  beforeEach(function() {
    global.handlebarsEnv = Handlebars.create();
  });
  
  describe('basic context', function() {
    it('most basic', function() {
      shouldCompileTo('{{foo}}', { foo: 'foo' }, 'foo');
    });
  
    it('escaping', function() {
      shouldCompileTo('\\{{foo}}', { foo: 'food' }, '{{foo}}');
      shouldCompileTo('content \\{{foo}}', { foo: 'food' }, 'content {{foo}}');
      shouldCompileTo('\\\\{{foo}}', { foo: 'food' }, '\\food');
      shouldCompileTo('content \\\\{{foo}}', { foo: 'food' }, 'content \\food');
      shouldCompileTo('\\\\ {{foo}}', { foo: 'food' }, '\\\\ food');
    });
  
    it('compiling with a basic context', function() {
      shouldCompileTo('Goodbye\n{{cruel}}\n{{world}}!', {cruel: 'cruel', world: 'world'}, 'Goodbye\ncruel\nworld!',
                      'It works if all the required keys are provided');
    });
  
    it('compiling with a string context', function() {
      shouldCompileTo('{{.}}{{length}}', 'bye', 'bye3');
    });
  
    it('compiling with an undefined context', function() {
      shouldCompileTo('Goodbye\n{{cruel}}\n{{world.bar}}!', undefined, 'Goodbye\n\n!');
  
      shouldCompileTo('{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}', undefined, 'Goodbye');
    });
  
    it('comments', function() {
      shouldCompileTo('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!',
        {cruel: 'cruel', world: 'world'}, 'Goodbye\ncruel\nworld!',
        'comments are ignored');
  
      shouldCompileTo('    {{~! comment ~}}      blah', {}, 'blah');
      shouldCompileTo('    {{~!-- long-comment --~}}      blah', {}, 'blah');
      shouldCompileTo('    {{! comment ~}}      blah', {}, '    blah');
      shouldCompileTo('    {{!-- long-comment --~}}      blah', {}, '    blah');
      shouldCompileTo('    {{~! comment}}      blah', {}, '      blah');
      shouldCompileTo('    {{~!-- long-comment --}}      blah', {}, '      blah');
    });
  
    it('boolean', function() {
      var string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!';
      shouldCompileTo(string, {goodbye: true, world: 'world'}, 'GOODBYE cruel world!',
                      'booleans show the contents when true');
  
      shouldCompileTo(string, {goodbye: false, world: 'world'}, 'cruel world!',
                      'booleans do not show the contents when false');
    });
  
    it('zeros', function() {
      shouldCompileTo('num1: {{num1}}, num2: {{num2}}', {num1: 42, num2: 0},
          'num1: 42, num2: 0');
      shouldCompileTo('num: {{.}}', 0, 'num: 0');
      shouldCompileTo('num: {{num1/num2}}', {num1: {num2: 0}}, 'num: 0');
    });
    it('false', function() {
      /* eslint-disable no-new-wrappers */
      shouldCompileTo('val1: {{val1}}, val2: {{val2}}', {val1: false, val2: new Boolean(false)}, 'val1: false, val2: false');
      shouldCompileTo('val: {{.}}', false, 'val: false');
      shouldCompileTo('val: {{val1/val2}}', {val1: {val2: false}}, 'val: false');
  
      shouldCompileTo('val1: {{{val1}}}, val2: {{{val2}}}', {val1: false, val2: new Boolean(false)}, 'val1: false, val2: false');
      shouldCompileTo('val: {{{val1/val2}}}', {val1: {val2: false}}, 'val: false');
      /* eslint-enable */
    });
  
    it('should handle undefined and null', function() {
      shouldCompileTo('{{awesome undefined null}}',
          {
            awesome: function(_undefined, _null, options) {
              return (_undefined === undefined) + ' ' + (_null === null) + ' ' + (typeof options);
            }
          },
          'true true object');
      shouldCompileTo('{{undefined}}',
          {
            'undefined': function() {
              return 'undefined!';
            }
          },
          'undefined!');
      shouldCompileTo('{{null}}',
          {
            'null': function() {
              return 'null!';
            }
          },
          'null!');
    });
  
    it('newlines', function() {
        shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest");
        shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest");
    });
  
    it('escaping text', function() {
      shouldCompileTo("Awesome's", {}, "Awesome's", "text is escaped so that it doesn't get caught on single quotes");
      shouldCompileTo('Awesome\\', {}, 'Awesome\\', "text is escaped so that the closing quote can't be ignored");
      shouldCompileTo('Awesome\\\\ foo', {}, 'Awesome\\\\ foo', "text is escaped so that it doesn't mess up backslashes");
      shouldCompileTo('Awesome {{foo}}', {foo: '\\'}, 'Awesome \\', "text is escaped so that it doesn't mess up backslashes");
      shouldCompileTo(" ' ' ", {}, " ' ' ", 'double quotes never produce invalid javascript');
    });
  
    it('escaping expressions', function() {
     shouldCompileTo('{{{awesome}}}', {awesome: '&\'\\<>'}, '&\'\\<>',
            "expressions with 3 handlebars aren't escaped");
  
     shouldCompileTo('{{&awesome}}', {awesome: '&\'\\<>'}, '&\'\\<>',
            "expressions with {{& handlebars aren't escaped");
  
     shouldCompileTo('{{awesome}}', {awesome: "&\"'`\\<>"}, '&amp;&quot;&#x27;&#x60;\\&lt;&gt;',
            'by default expressions should be escaped');
  
     shouldCompileTo('{{awesome}}', {awesome: 'Escaped, <b> looks like: &lt;b&gt;'}, 'Escaped, &lt;b&gt; looks like: &amp;lt;b&amp;gt;',
            'escaping should properly handle amperstands');
    });
  
    it("functions returning safestrings shouldn't be escaped", function() {
      var hash = {awesome: function() { return new Handlebars.SafeString('&\'\\<>'); }};
      shouldCompileTo('{{awesome}}', hash, '&\'\\<>',
          "functions returning safestrings aren't escaped");
    });
  
    it('functions', function() {
      shouldCompileTo('{{awesome}}', {awesome: function() { return 'Awesome'; }}, 'Awesome',
                      'functions are called and render their output');
      shouldCompileTo('{{awesome}}', {awesome: function() { return this.more; }, more: 'More awesome'}, 'More awesome',
                      'functions are bound to the context');
    });
  
    it('functions with context argument', function() {
      shouldCompileTo('{{awesome frank}}',
          {awesome: function(context) { return context; },
            frank: 'Frank'},
          'Frank', 'functions are called with context arguments');
    });
    it('pathed functions with context argument', function() {
      shouldCompileTo('{{bar.awesome frank}}',
          {bar: {awesome: function(context) { return context; }},
            frank: 'Frank'},
          'Frank', 'functions are called with context arguments');
    });
    it('depthed functions with context argument', function() {
      shouldCompileTo('{{#with frank}}{{../awesome .}}{{/with}}',
          {awesome: function(context) { return context; },
            frank: 'Frank'},
          'Frank', 'functions are called with context arguments');
    });
  
    it('block functions with context argument', function() {
      shouldCompileTo('{{#awesome 1}}inner {{.}}{{/awesome}}',
          {awesome: function(context, options) { return options.fn(context); }},
          'inner 1', 'block functions are called with context and options');
    });
  
    it('depthed block functions with context argument', function() {
      shouldCompileTo('{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}',
          {value: true, awesome: function(context, options) { return options.fn(context); }},
          'inner 1', 'block functions are called with context and options');
    });
  
    it('block functions without context argument', function() {
      shouldCompileTo('{{#awesome}}inner{{/awesome}}',
          {awesome: function(options) { return options.fn(this); }},
          'inner', 'block functions are called with options');
    });
    it('pathed block functions without context argument', function() {
      shouldCompileTo('{{#foo.awesome}}inner{{/foo.awesome}}',
          {foo: {awesome: function() { return this; }}},
          'inner', 'block functions are called with options');
    });
    it('depthed block functions without context argument', function() {
      shouldCompileTo('{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}',
          {value: true, awesome: function() { return this; }},
          'inner', 'block functions are called with options');
    });
  
  
    it('paths with hyphens', function() {
      shouldCompileTo('{{foo-bar}}', {'foo-bar': 'baz'}, 'baz', 'Paths can contain hyphens (-)');
      shouldCompileTo('{{foo.foo-bar}}', {foo: {'foo-bar': 'baz'}}, 'baz', 'Paths can contain hyphens (-)');
      shouldCompileTo('{{foo/foo-bar}}', {foo: {'foo-bar': 'baz'}}, 'baz', 'Paths can contain hyphens (-)');
    });
  
    it('nested paths', function() {
      shouldCompileTo('Goodbye {{alan/expression}} world!', {alan: {expression: 'beautiful'}},
                      'Goodbye beautiful world!', 'Nested paths access nested objects');
    });
  
    it('nested paths with empty string value', function() {
      shouldCompileTo('Goodbye {{alan/expression}} world!', {alan: {expression: ''}},
                      'Goodbye  world!', 'Nested paths access nested objects with empty string');
    });
  
    it('literal paths', function() {
      shouldCompileTo('Goodbye {{[@alan]/expression}} world!', {'@alan': {expression: 'beautiful'}},
          'Goodbye beautiful world!', 'Literal paths can be used');
      shouldCompileTo('Goodbye {{[foo bar]/expression}} world!', {'foo bar': {expression: 'beautiful'}},
          'Goodbye beautiful world!', 'Literal paths can be used');
    });
  
    it('literal references', function() {
      shouldCompileTo('Goodbye {{[foo bar]}} world!', {'foo bar': 'beautiful'}, 'Goodbye beautiful world!');
      shouldCompileTo('Goodbye {{"foo bar"}} world!', {'foo bar': 'beautiful'}, 'Goodbye beautiful world!');
      shouldCompileTo("Goodbye {{'foo bar'}} world!", {'foo bar': 'beautiful'}, 'Goodbye beautiful world!');
      shouldCompileTo('Goodbye {{"foo[bar"}} world!', {'foo[bar': 'beautiful'}, 'Goodbye beautiful world!');
      shouldCompileTo('Goodbye {{"foo\'bar"}} world!', {"foo'bar": 'beautiful'}, 'Goodbye beautiful world!');
      shouldCompileTo("Goodbye {{'foo\"bar'}} world!", {'foo"bar': 'beautiful'}, 'Goodbye beautiful world!');
    });
  
    it("that current context path ({{.}}) doesn't hit helpers", function() {
      shouldCompileTo('test: {{.}}', [null, {helper: 'awesome'}], 'test: ');
    });
  
    it('complex but empty paths', function() {
      shouldCompileTo('{{person/name}}', {person: {name: null}}, '');
      shouldCompileTo('{{person/name}}', {person: {}}, '');
    });
  
    it('this keyword in paths', function() {
      var string = '{{#goodbyes}}{{this}}{{/goodbyes}}';
      var hash = {goodbyes: ['goodbye', 'Goodbye', 'GOODBYE']};
      shouldCompileTo(string, hash, 'goodbyeGoodbyeGOODBYE',
        'This keyword in paths evaluates to current context');
  
      string = '{{#hellos}}{{this/text}}{{/hellos}}';
      hash = {hellos: [{text: 'hello'}, {text: 'Hello'}, {text: 'HELLO'}]};
      shouldCompileTo(string, hash, 'helloHelloHELLO', 'This keyword evaluates in more complex paths');
    });
  
    it('this keyword nested inside path', function() {
      shouldThrow(function() {
        CompilerContext.compile('{{#hellos}}{{text/this/foo}}{{/hellos}}');
      }, Error, 'Invalid path: text/this - 1:13');
  
      shouldCompileTo('{{[this]}}', {'this': 'bar'}, 'bar');
      shouldCompileTo('{{text/[this]}}', {text: {'this': 'bar'}}, 'bar');
    });
  
    it('this keyword in helpers', function() {
      var helpers = {foo: function(value) {
          return 'bar ' + value;
      }};
      var string = '{{#goodbyes}}{{foo this}}{{/goodbyes}}';
      var hash = {goodbyes: ['goodbye', 'Goodbye', 'GOODBYE']};
      shouldCompileTo(string, [hash, helpers], 'bar goodbyebar Goodbyebar GOODBYE',
        'This keyword in paths evaluates to current context');
  
      string = '{{#hellos}}{{foo this/text}}{{/hellos}}';
      hash = {hellos: [{text: 'hello'}, {text: 'Hello'}, {text: 'HELLO'}]};
      shouldCompileTo(string, [hash, helpers], 'bar hellobar Hellobar HELLO', 'This keyword evaluates in more complex paths');
    });
  
    it('this keyword nested inside helpers param', function() {
      var string = '{{#hellos}}{{foo text/this/foo}}{{/hellos}}';
      shouldThrow(function() {
        CompilerContext.compile(string);
      }, Error, 'Invalid path: text/this - 1:17');
  
      shouldCompileTo(
          '{{foo [this]}}',
          {foo: function(value) { return value; }, 'this': 'bar'},
          'bar');
      shouldCompileTo(
          '{{foo text/[this]}}',
          {foo: function(value) { return value; }, text: {'this': 'bar'}},
          'bar');
    });
  
    it('pass string literals', function() {
      shouldCompileTo('{{"foo"}}', {}, '');
      shouldCompileTo('{{"foo"}}', { foo: 'bar' }, 'bar');
      shouldCompileTo('{{#"foo"}}{{.}}{{/"foo"}}', { foo: ['bar', 'baz'] }, 'barbaz');
    });
  
    it('pass number literals', function() {
      shouldCompileTo('{{12}}', {}, '');
      shouldCompileTo('{{12}}', { '12': 'bar' }, 'bar');
      shouldCompileTo('{{12.34}}', {}, '');
      shouldCompileTo('{{12.34}}', { '12.34': 'bar' }, 'bar');
      shouldCompileTo('{{12.34 1}}', { '12.34': function(arg) { return 'bar' + arg; } }, 'bar1');
    });
  
    it('pass boolean literals', function() {
      shouldCompileTo('{{true}}', {}, '');
      shouldCompileTo('{{true}}', { '': 'foo' }, '');
      shouldCompileTo('{{false}}', { 'false': 'foo' }, 'foo');
    });
  
    it('should handle literals in subexpression', function() {
      var helpers = {
        foo: function(arg) {
          return arg;
        }
      };
      shouldCompileTo('{{foo (false)}}', [{ 'false': function() { return 'bar'; } }, helpers], 'bar');
    });
  });
  