error.reporting.test.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /**
  2. * Module dependencies.
  3. */
  4. var pug = require('../');
  5. var assert = require('assert');
  6. var fs = require('fs');
  7. // Shortcut
  8. function getError(str, options){
  9. try {
  10. pug.render(str, options);
  11. } catch (ex) {
  12. return ex;
  13. }
  14. throw new Error('Input was supposed to result in an error.');
  15. }
  16. function getFileError(name, options){
  17. try {
  18. pug.renderFile(name, options);
  19. } catch (ex) {
  20. return ex;
  21. }
  22. throw new Error('Input was supposed to result in an error.');
  23. }
  24. describe('error reporting', function () {
  25. describe('compile time errors', function () {
  26. describe('with no filename', function () {
  27. it('includes detail of where the error was thrown', function () {
  28. var err = getError('foo(')
  29. expect(err.message).toMatch(/Pug:1/);
  30. expect(err.message).toMatch(/foo\(/);
  31. });
  32. });
  33. describe('with a filename', function () {
  34. it('includes detail of where the error was thrown including the filename', function () {
  35. var err = getError('foo(', {filename: 'test.pug'})
  36. expect(err.message).toMatch(/test\.pug:1/);
  37. expect(err.message).toMatch(/foo\(/);
  38. });
  39. });
  40. describe('with a layout without block declaration (syntax)', function () {
  41. it('includes detail of where the error was thrown including the filename', function () {
  42. var err = getFileError(__dirname + '/fixtures/compile.with.layout.syntax.error.pug', {})
  43. expect(err.message).toMatch(/[\\\/]layout.syntax.error.pug:2/);
  44. expect(err.message).toMatch(/foo\(/);
  45. });
  46. });
  47. describe('with a layout without block declaration (locals)', function () {
  48. it('includes detail of where the error was thrown including the filename', function () {
  49. var err = getFileError(__dirname + '/fixtures/compile.with.layout.locals.error.pug', {})
  50. expect(err.message).toMatch(/[\\\/]layout.locals.error.pug:2/);
  51. expect(err.message).toMatch(/is not a function/);
  52. });
  53. });
  54. describe('with a include (syntax)', function () {
  55. it('includes detail of where the error was thrown including the filename', function () {
  56. var err = getFileError(__dirname + '/fixtures/compile.with.include.syntax.error.pug', {})
  57. expect(err.message).toMatch(/[\\\/]include.syntax.error.pug:2/);
  58. expect(err.message).toMatch(/foo\(/);
  59. });
  60. });
  61. describe('with a include (locals)', function () {
  62. it('includes detail of where the error was thrown including the filename', function () {
  63. var err = getFileError(__dirname + '/fixtures/compile.with.include.locals.error.pug', {})
  64. expect(err.message).toMatch(/[\\\/]include.locals.error.pug:2/);
  65. expect(err.message).toMatch(/foo\(/);
  66. });
  67. });
  68. describe('with a layout (without block) with an include (syntax)', function () {
  69. it('includes detail of where the error was thrown including the filename', function () {
  70. var err = getFileError(__dirname + '/fixtures/compile.with.layout.with.include.syntax.error.pug', {})
  71. expect(err.message).toMatch(/[\\\/]include.syntax.error.pug:2/);
  72. expect(err.message).toMatch(/foo\(/);
  73. });
  74. });
  75. describe('with a layout (without block) with an include (locals)', function () {
  76. it('includes detail of where the error was thrown including the filename', function () {
  77. var err = getFileError(__dirname + '/fixtures/compile.with.layout.with.include.locals.error.pug', {})
  78. expect(err.message).toMatch(/[\\\/]include.locals.error.pug:2/);
  79. expect(err.message).toMatch(/foo\(/);
  80. });
  81. });
  82. describe('block that is never actually used', function () {
  83. it('includes detail of where the error was thrown including the filename', function () {
  84. var err = getFileError(__dirname + '/fixtures/invalid-block-in-extends.pug', {});
  85. expect(err.message).toMatch(/invalid-block-in-extends.pug:6/);;
  86. expect(err.message).toMatch(/content/);;
  87. });
  88. });
  89. describe('Unexpected character', function () {
  90. it('includes details of where the error was thrown', function () {
  91. var err = getError('ul?', {});
  92. expect(err.message).toMatch(/unexpected text \"\?\"/);
  93. });
  94. });
  95. describe('Include filtered', function () {
  96. it('includes details of where the error was thrown', function () {
  97. var err = getError('include:verbatim()!', {});
  98. assert(err.message.indexOf('unexpected text "!"') !== -1);
  99. var err = getError('include:verbatim ', {});
  100. assert(err.message.indexOf('missing path for include') !== -1);
  101. });
  102. });
  103. describe('mixin block followed by a lot of blank lines', function () {
  104. it('reports the correct line number', function () {
  105. var err = getError('mixin test\n block\n\ndiv()Test');
  106. var line = /Pug\:(\d+)/.exec(err.message);
  107. assert(line, 'Line number must be included in error message');
  108. assert(line[1] === '4', 'The error should be reported on line 4, not line ' + line[1]);
  109. });
  110. });
  111. });
  112. describe('runtime errors', function () {
  113. describe('with no filename and `compileDebug` left undefined', function () {
  114. it('just reports the line number', function () {
  115. var sentinel = new Error('sentinel');
  116. var err = getError('-foo()', {foo: function () { throw sentinel; }})
  117. expect(err.message).toMatch(/on line 1/);
  118. });
  119. });
  120. describe('with no filename and `compileDebug` set to `true`', function () {
  121. it('includes detail of where the error was thrown', function () {
  122. var sentinel = new Error('sentinel');
  123. var err = getError('-foo()', {foo: function () { throw sentinel; }, compileDebug: true})
  124. expect(err.message).toMatch(/Pug:1/);
  125. expect(err.message).toMatch(/-foo\(\)/);
  126. });
  127. });
  128. describe('with a filename that does not correspond to a real file and `compileDebug` left undefined', function () {
  129. it('just reports the line number', function () {
  130. var sentinel = new Error('sentinel');
  131. var err = getError('-foo()', {foo: function () { throw sentinel; }, filename: 'fake.pug'})
  132. expect(err.message).toMatch(/on line 1/);
  133. });
  134. });
  135. describe('with a filename that corresponds to a real file and `compileDebug` left undefined', function () {
  136. it('includes detail of where the error was thrown including the filename', function () {
  137. var sentinel = new Error('sentinel');
  138. var path = __dirname + '/fixtures/runtime.error.pug'
  139. var err = getError(fs.readFileSync(path, 'utf8'), {foo: function () { throw sentinel; }, filename: path})
  140. expect(err.message).toMatch(/fixtures[\\\/]runtime\.error\.pug:1/);
  141. expect(err.message).toMatch(/-foo\(\)/);
  142. });
  143. });
  144. describe('in a mixin', function () {
  145. it('includes detail of where the error was thrown including the filename', function () {
  146. var err = getFileError(__dirname + '/fixtures/runtime.with.mixin.error.pug', {})
  147. expect(err.message).toMatch(/mixin.error.pug:2/);
  148. expect(err.message).toMatch(/Cannot read property 'length' of null/);
  149. });
  150. });
  151. describe('in a layout', function () {
  152. it('includes detail of where the error was thrown including the filename', function () {
  153. var err = getFileError(__dirname + '/fixtures/runtime.layout.error.pug', {})
  154. expect(err.message).toMatch(/layout.with.runtime.error.pug:3/);
  155. expect(err.message).toMatch(/Cannot read property 'length' of undefined/);
  156. });
  157. });
  158. });
  159. describe('deprecated features', function () {
  160. it('warns about element-with-multiple-attributes', function () {
  161. var consoleWarn = console.warn;
  162. var log = '';
  163. console.warn = function (str) {
  164. log += str;
  165. };
  166. var res = pug.renderFile(__dirname + '/fixtures/element-with-multiple-attributes.pug');
  167. console.warn = consoleWarn;
  168. expect(log).toMatch(/element-with-multiple-attributes.pug, line 1:/);;
  169. expect(log).toMatch(/You should not have pug tags with multiple attributes/);;
  170. expect(res).toBe('<div attr="val" foo="bar"></div>');
  171. });
  172. });
  173. describe('if you throw something that isn\'t an error', function () {
  174. it('just rethrows without modification', function () {
  175. var err = getError('- throw "foo"');
  176. expect(err).toBe('foo');
  177. });
  178. });
  179. describe('import without a filename for a basedir', function () {
  180. it('throws an error', function () {
  181. var err = getError('include foo.pug');
  182. expect(err.message).toMatch(/the "filename" option is required to use/);;
  183. var err = getError('include /foo.pug');
  184. expect(err.message).toMatch(/the "basedir" option is required to use/);;
  185. })
  186. });
  187. });