diff --git a/spec/core/ExceptionFormatterSpec.js b/spec/core/ExceptionFormatterSpec.js index d8c2d94a..f468928b 100644 --- a/spec/core/ExceptionFormatterSpec.js +++ b/spec/core/ExceptionFormatterSpec.js @@ -350,48 +350,52 @@ describe('ExceptionFormatter', function() { describe('when the error has an errors array (AggregateError)', function() { it('includes all aggregated errors in the stack trace', function() { const subject = new privateUnderTest.ExceptionFormatter(); - const error1 = new Error('first error'); - const error2 = new Error('second error'); - const error3 = new Error('third error'); - const aggregateError = new Error('Multiple errors occurred'); - aggregateError.errors = [error1, error2, error3]; + const error1 = (function fn1() { + return new Error('first error'); + })(); + const error2 = (function fn2() { + return new Error('second error'); + })(); + const aggregateError = (function fn3() { + return new Error('Multiple errors occurred'); + })(); + aggregateError.errors = [error1, error2]; const lines = subject.stack(aggregateError).split('\n'); + // TODO: be consistent across environments about whether the message is + // included in the stack trace + if (lines[0] === 'Error: Multiple errors occurred') { + lines.shift(); + } + + for (let i = 0; i < lines.length; i++) { + jasmine.debugLog(`Line ${i}: ${lines[i]}`); + } + + expect(lines[0]) + .withContext('first stack frame of the overall error') + .toMatch(/fn3.*core\/ExceptionFormatterSpec\.js/); + const error1MsgIx = lines.findIndex(line => line.includes('Error 1: Error: first error') ); expect(error1MsgIx) - .withContext('first error message') + .withContext('first nested error message') .toBeGreaterThan(-1); + expect(lines[error1MsgIx + 1]) + .withContext('first stack frame of first nested error') + .toMatch(/fn1.*core\/ExceptionFormatterSpec\.js/); const error2MsgIx = lines.findIndex(line => line.includes('Error 2: Error: second error') ); expect(error2MsgIx) - .withContext('second error message') + .withContext('second nested error message') .toBeGreaterThan(error1MsgIx); - - const error3MsgIx = lines.findIndex(line => - line.includes('Error 3: Error: third error') - ); - expect(error3MsgIx) - .withContext('third error message') - .toBeGreaterThan(error2MsgIx); - }); - - it('handles AggregateError with single error', function() { - const subject = new privateUnderTest.ExceptionFormatter(); - const error1 = new Error('single error'); - const aggregateError = new Error('One error occurred'); - aggregateError.errors = [error1]; - - const lines = subject.stack(aggregateError).split('\n'); - - const error1MsgIx = lines.findIndex(line => - line.includes('Error 1: Error: single error') - ); - expect(error1MsgIx).toBeGreaterThan(-1); + expect(lines[error2MsgIx + 1]) + .withContext('first stack frame of second nested error') + .toMatch(/fn2.*core\/ExceptionFormatterSpec\.js/); }); it('handles empty errors array', function() { diff --git a/spec/core/integration/ExceptionFormattingSpec.js b/spec/core/integration/ExceptionFormattingSpec.js deleted file mode 100644 index f8664228..00000000 --- a/spec/core/integration/ExceptionFormattingSpec.js +++ /dev/null @@ -1,84 +0,0 @@ -describe('Exception formatting (integration)', function() { - let env; - - beforeEach(function() { - specHelpers.registerIntegrationMatchers(); - env = new privateUnderTest.Env(); - }); - - afterEach(function() { - env.cleanup_(); - }); - - describe('AggregateError formatting', function() { - it('formats AggregateError with individual errors', async function() { - env.it('should format AggregateError with individual errors', function() { - const errors = [ - new Error('Database connection failed'), - new Error('Invalid configuration'), - new Error('Service unavailable') - ]; - throw new AggregateError(errors, 'Multiple initialization errors'); - }); - - const reporter = jasmine.createSpyObj('reporter', ['specDone']); - env.addReporter(reporter); - await env.execute(); - - expect(reporter.specDone).toHaveBeenCalledTimes(1); - const result = reporter.specDone.calls.argsFor(0)[0]; - expect(result.status).toEqual('failed'); - expect(result.failedExpectations.length).toEqual(1); - - const failure = result.failedExpectations[0]; - expect(failure.message).toContain('AggregateError'); - expect(failure.message).toContain('Multiple initialization errors'); - - expect(failure.stack).toContain( - 'Error 1: Error: Database connection failed' - ); - expect(failure.stack).toContain('Error 2: Error: Invalid configuration'); - expect(failure.stack).toContain('Error 3: Error: Service unavailable'); - }); - - it('formats nested AggregateError', async function() { - env.it('should format nested AggregateError', function() { - const innerErrors = [ - new Error('Inner error 1'), - new Error('Inner error 2') - ]; - const innerAggregate = new AggregateError( - innerErrors, - 'Inner operation failed' - ); - - const outerErrors = [ - innerAggregate, - new Error('Outer error'), - new Error('Other outer error') - ]; - throw new AggregateError(outerErrors, 'Multiple operations failed'); - }); - - const reporter = jasmine.createSpyObj('reporter', ['specDone']); - env.addReporter(reporter); - await env.execute(); - - expect(reporter.specDone).toHaveBeenCalledTimes(1); - const result = reporter.specDone.calls.argsFor(0)[0]; - expect(result.status).toEqual('failed'); - - const failure = result.failedExpectations[0]; - - // Firefox & Safari don't preserve types for nested errors - expect(failure.stack).toMatch( - /Error 1: (AggregateError|Error): Inner operation failed/ - ); - expect(failure.stack).toContain('Error 2: Error: Outer error'); - expect(failure.stack).toContain('Error 3: Error: Other outer error'); - - expect(failure.stack).toContain('Error 1: Error: Inner error 1'); - expect(failure.stack).toContain('Error 2: Error: Inner error 2'); - }); - }); -});