serialize-error.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. 'use strict';
  2. require('../lib/chalk').set();
  3. require('../lib/worker/options').set({});
  4. const fs = require('fs');
  5. const path = require('path');
  6. const sourceMapFixtures = require('source-map-fixtures');
  7. const sourceMapSupport = require('source-map-support');
  8. const tempWrite = require('temp-write');
  9. const uniqueTempDir = require('unique-temp-dir');
  10. const test = require('tap').test;
  11. const avaAssert = require('../lib/assert');
  12. const beautifyStack = require('../lib/beautify-stack');
  13. const serializeError = require('../lib/serialize-error');
  14. const serialize = err => serializeError('Test', true, err);
  15. // Needed to test stack traces from source map fixtures.
  16. sourceMapSupport.install({environment: 'node'});
  17. test('serialize standard props', t => {
  18. const err = new Error('Hello');
  19. const serializedErr = serialize(err);
  20. t.is(Object.keys(serializedErr).length, 8);
  21. t.is(serializedErr.avaAssertionError, false);
  22. t.is(serializedErr.nonErrorObject, false);
  23. t.deepEqual(serializedErr.object, {});
  24. t.is(serializedErr.name, 'Error');
  25. t.is(serializedErr.stack, beautifyStack(err.stack));
  26. t.is(serializedErr.message, 'Hello');
  27. t.is(serializedErr.summary, 'Error: Hello');
  28. t.is(typeof serializedErr.source.isDependency, 'boolean');
  29. t.is(typeof serializedErr.source.isWithinProject, 'boolean');
  30. t.is(typeof serializedErr.source.file, 'string');
  31. t.is(typeof serializedErr.source.line, 'number');
  32. t.end();
  33. });
  34. test('additional error properties are preserved', t => {
  35. const serializedErr = serialize(Object.assign(new Error(), {foo: 'bar'}));
  36. t.deepEqual(serializedErr.object, {foo: 'bar'});
  37. t.end();
  38. });
  39. test('source file is an absolute path', t => {
  40. const err = new Error('Hello');
  41. const serializedErr = serialize(err);
  42. t.is(serializedErr.source.file, __filename);
  43. t.end();
  44. });
  45. test('source file is an absolute path, after source map correction', t => {
  46. const fixture = sourceMapFixtures.mapFile('throws');
  47. try {
  48. fixture.require().run();
  49. t.fail('Fixture should have thrown');
  50. } catch (err) {
  51. const serializedErr = serialize(err);
  52. t.is(serializedErr.source.file, fixture.sourceFile);
  53. t.end();
  54. }
  55. });
  56. test('source file is an absolute path, after source map correction, even if already absolute', t => {
  57. const fixture = sourceMapFixtures.mapFile('throws');
  58. const map = JSON.parse(fs.readFileSync(fixture.file + '.map'));
  59. const tmp = uniqueTempDir({create: true});
  60. const sourceRoot = path.join(tmp, 'src');
  61. const expectedSourceFile = path.join(sourceRoot, map.file);
  62. const tmpFile = path.join(tmp, path.basename(fixture.file));
  63. fs.writeFileSync(tmpFile, fs.readFileSync(fixture.file));
  64. fs.writeFileSync(tmpFile + '.map', JSON.stringify(Object.assign(map, {sourceRoot}), null, 2));
  65. try {
  66. require(tmpFile).run();
  67. t.fail('Fixture should have thrown');
  68. } catch (err) {
  69. const serializedErr = serialize(err);
  70. t.is(serializedErr.source.file, expectedSourceFile);
  71. t.end();
  72. }
  73. });
  74. test('determines whether source file is within the project', t => {
  75. const file = tempWrite.sync('module.exports = () => { throw new Error("hello") }');
  76. try {
  77. require(file)();
  78. t.fail('Should have thrown');
  79. } catch (err) {
  80. const serializedErr = serialize(err);
  81. t.is(serializedErr.source.file, file);
  82. t.is(serializedErr.source.isWithinProject, false);
  83. }
  84. const err = new Error('Hello');
  85. const serializedErr = serialize(err);
  86. t.is(serializedErr.source.file, __filename);
  87. t.is(serializedErr.source.isWithinProject, true);
  88. t.end();
  89. });
  90. test('determines whether source file, if within the project, is a dependency', t => {
  91. const fixture = sourceMapFixtures.mapFile('throws');
  92. try {
  93. fixture.require().run();
  94. t.fail('Fixture should have thrown');
  95. } catch (err) {
  96. const serializedErr = serialize(err);
  97. t.is(serializedErr.source.file, fixture.sourceFile);
  98. t.is(serializedErr.source.isWithinProject, true);
  99. t.is(serializedErr.source.isDependency, true);
  100. }
  101. const err = new Error('Hello');
  102. const serializedErr = serialize(err);
  103. t.is(serializedErr.source.file, __filename);
  104. t.is(serializedErr.source.isDependency, false);
  105. t.end();
  106. });
  107. test('sets avaAssertionError to true if indeed an assertion error', t => {
  108. const err = new avaAssert.AssertionError({});
  109. const serializedErr = serialize(err);
  110. t.true(serializedErr.avaAssertionError);
  111. t.end();
  112. });
  113. test('includes statements of assertion errors', t => {
  114. const err = new avaAssert.AssertionError({
  115. assertion: 'true'
  116. });
  117. err.statements = [
  118. ['actual.a[0]', '1'],
  119. ['actual.a', '[1]'],
  120. ['actual', '{a: [1]}']
  121. ];
  122. const serializedErr = serialize(err);
  123. t.is(serializedErr.statements, err.statements);
  124. t.end();
  125. });
  126. test('includes values of assertion errors', t => {
  127. const err = new avaAssert.AssertionError({
  128. assertion: 'is',
  129. values: [{label: 'actual:', formatted: '1'}, {label: 'expected:', formatted: 'a'}]
  130. });
  131. const serializedErr = serialize(err);
  132. t.is(serializedErr.values, err.values);
  133. t.end();
  134. });
  135. test('remove non-string error properties', t => {
  136. const err = {
  137. name: [42],
  138. stack: /re/g
  139. };
  140. const serializedErr = serialize(err);
  141. t.is(serializedErr.name, undefined);
  142. t.is(serializedErr.stack, undefined);
  143. t.end();
  144. });
  145. test('creates multiline summaries for syntax errors', t => {
  146. const err = new SyntaxError();
  147. Object.defineProperty(err, 'stack', {
  148. value: 'Hello\nThere\nSyntaxError here\nIgnore me'
  149. });
  150. const serializedErr = serialize(err);
  151. t.is(serializedErr.name, 'SyntaxError');
  152. t.is(serializedErr.summary, 'Hello\nThere\nSyntaxError here');
  153. t.end();
  154. });