@@ -258,12 +258,161 @@ describe('ZoneAwareError', () => {
258258 expect ( outsideFrames [ 0 ] ) . toMatch ( / t e s t F n .* [ < r o o t > ] / ) ;
259259
260260 expect ( insideFrames [ 0 ] ) . toMatch ( / i n s i d e R u n .* [ I n n e r Z o n e ] ] / ) ;
261- expect ( insideFrames [ 2 ] ) . toMatch ( / t e s t F n .* [ < r o o t > ] ] / ) ;
261+ expect ( insideFrames [ 1 ] ) . toMatch ( / t e s t F n .* [ < r o o t > ] ] / ) ;
262262
263263 expect ( outsideWithoutNewFrames [ 0 ] ) . toMatch ( / t e s t F n .* [ < r o o t > ] / ) ;
264264
265265 expect ( insideWithoutNewFrames [ 0 ] ) . toMatch ( / i n s i d e R u n .* [ I n n e r Z o n e ] ] / ) ;
266- expect ( insideWithoutNewFrames [ 2 ] ) . toMatch ( / t e s t F n .* [ < r o o t > ] ] / ) ;
266+ expect ( insideWithoutNewFrames [ 1 ] ) . toMatch ( / t e s t F n .* [ < r o o t > ] ] / ) ;
267267 }
268268 } ) ;
269+
270+ const zoneAwareFrames = [
271+ 'Zone.run' , 'Zone.runGuarded' , 'Zone.scheduleEventTask' , 'Zone.scheduleMicroTask' ,
272+ 'Zone.scheduleMacroTask' , 'Zone.runTask' , 'ZoneDelegate.scheduleTask' ,
273+ 'ZoneDelegate.invokeTask' , 'zoneAwareAddListener'
274+ ] ;
275+
276+ function assertStackDoesNotContainZoneFrames ( err : Error ) {
277+ const frames = err . stack . split ( '\n' ) ;
278+ for ( let i = 0 ; i < frames . length ; i ++ ) {
279+ expect ( zoneAwareFrames . filter ( f => frames [ i ] . indexOf ( f ) !== - 1 ) ) . toEqual ( [ ] ) ;
280+ }
281+ } ;
282+
283+ const errorZoneSpec = {
284+ name : 'errorZone' ,
285+ done : < ( ) => void > null ,
286+ onHandleError :
287+ ( parentDelegate : ZoneDelegate , currentZone : Zone , targetZone : Zone , error : Error ) => {
288+ assertStackDoesNotContainZoneFrames ( error ) ;
289+ setTimeout ( ( ) => {
290+ errorZoneSpec . done && errorZoneSpec . done ( ) ;
291+ } , 0 ) ;
292+ return false ;
293+ }
294+ } ;
295+
296+ const errorZone = Zone . root . fork ( errorZoneSpec ) ;
297+
298+ const assertStackDoesNotContainZoneFramesTest = function ( testFn : Function ) {
299+ return function ( done : ( ) => void ) {
300+ errorZoneSpec . done = done ;
301+ errorZone . run ( testFn ) ;
302+ } ;
303+ } ;
304+
305+ describe ( 'Error stack' , ( ) => {
306+ it ( 'Error with new which occurs in setTimeout callback should not have zone frames visible' ,
307+ assertStackDoesNotContainZoneFramesTest ( ( ) => {
308+ setTimeout ( ( ) => {
309+ throw new Error ( 'timeout test error' ) ;
310+ } , 10 ) ;
311+ } ) ) ;
312+
313+ it ( 'Error without new which occurs in setTimeout callback should not have zone frames visible' ,
314+ assertStackDoesNotContainZoneFramesTest ( ( ) => {
315+ setTimeout ( ( ) => {
316+ throw Error ( 'test error' ) ;
317+ } , 10 ) ;
318+ } ) ) ;
319+
320+ it ( 'Error with new which cause by promise rejection should not have zone frames visible' ,
321+ ( done ) => {
322+ const p = new Promise ( ( resolve , reject ) => {
323+ reject ( new Error ( 'test error' ) ) ;
324+ } ) ;
325+ p . catch ( err => {
326+ assertStackDoesNotContainZoneFrames ( err ) ;
327+ done ( ) ;
328+ } ) ;
329+ } ) ;
330+
331+ it ( 'Error without new which cause by promise rejection should not have zone frames visible' ,
332+ ( done ) => {
333+ const p = new Promise ( ( resolve , reject ) => {
334+ reject ( Error ( 'test error' ) ) ;
335+ } ) ;
336+ p . catch ( err => {
337+ assertStackDoesNotContainZoneFrames ( err ) ;
338+ done ( ) ;
339+ } ) ;
340+ } ) ;
341+
342+ it ( 'Error with new which occurs in eventTask callback should not have zone frames visible' ,
343+ assertStackDoesNotContainZoneFramesTest ( ( ) => {
344+ const task = Zone . current . scheduleEventTask ( 'errorEvent' , ( ) => {
345+ throw new Error ( 'test error' ) ;
346+ } , null , ( ) => null , null ) ;
347+ task . invoke ( ) ;
348+ } ) ) ;
349+
350+ it ( 'Error without new which occurs in eventTask callback should not have zone frames visible' ,
351+ assertStackDoesNotContainZoneFramesTest ( ( ) => {
352+ const task = Zone . current . scheduleEventTask ( 'errorEvent' , ( ) => {
353+ throw Error ( 'test error' ) ;
354+ } , null , ( ) => null , null ) ;
355+ task . invoke ( ) ;
356+ } ) ) ;
357+
358+ it ( 'Error with new which occurs in longStackTraceZone should not have zone frames and longStackTraceZone frames visible' ,
359+ assertStackDoesNotContainZoneFramesTest ( ( ) => {
360+ const task = Zone . current . fork ( ( Zone as any ) [ 'longStackTraceZoneSpec' ] )
361+ . scheduleEventTask ( 'errorEvent' , ( ) => {
362+ throw new Error ( 'test error' ) ;
363+ } , null , ( ) => null , null ) ;
364+ task . invoke ( ) ;
365+ } ) ) ;
366+
367+ it ( 'Error without new which occurs in longStackTraceZone should not have zone frames and longStackTraceZone frames visible' ,
368+ assertStackDoesNotContainZoneFramesTest ( ( ) => {
369+ const task = Zone . current . fork ( ( Zone as any ) [ 'longStackTraceZoneSpec' ] )
370+ . scheduleEventTask ( 'errorEvent' , ( ) => {
371+ throw Error ( 'test error' ) ;
372+ } , null , ( ) => null , null ) ;
373+ task . invoke ( ) ;
374+ } ) ) ;
375+
376+ it ( 'stack frames of the callback in user customized zoneSpec should be kept' ,
377+ assertStackDoesNotContainZoneFramesTest ( ( ) => {
378+ const task = Zone . current . fork ( ( Zone as any ) [ 'longStackTraceZoneSpec' ] )
379+ . fork ( {
380+ name : 'customZone' ,
381+ onScheduleTask : ( parentDelegate , currentZone , targetZone , task ) => {
382+ return parentDelegate . scheduleTask ( targetZone , task ) ;
383+ } ,
384+ onHandleError : ( parentDelegate , currentZone , targetZone , error ) => {
385+ parentDelegate . handleError ( targetZone , error ) ;
386+ const containsCustomZoneSpecStackTrace =
387+ error . stack . indexOf ( 'onScheduleTask' ) !== - 1 ;
388+ expect ( containsCustomZoneSpecStackTrace ) . toBeTruthy ( ) ;
389+ return false ;
390+ }
391+ } )
392+ . scheduleEventTask ( 'errorEvent' , ( ) => {
393+ throw new Error ( 'test error' ) ;
394+ } , null , ( ) => null , null ) ;
395+ task . invoke ( ) ;
396+ } ) ) ;
397+
398+ it ( 'should be able to generate zone free stack even NativeError stack is readonly' , function ( ) {
399+ const _global : any =
400+ typeof window === 'object' && window || typeof self === 'object' && self || global ;
401+ const NativeError = _global [ '__zone_symbol__Error' ] ;
402+ const desc = Object . getOwnPropertyDescriptor ( NativeError . prototype , 'stack' ) ;
403+ if ( desc ) {
404+ const originalSet : ( value : any ) => void = desc . set ;
405+ // make stack readonly
406+ desc . set = null ;
407+
408+ try {
409+ const error = new Error ( 'test error' ) ;
410+ expect ( error . stack ) . toBeTruthy ( ) ;
411+ assertStackDoesNotContainZoneFrames ( error ) ;
412+ } finally {
413+ desc . set = originalSet ;
414+ }
415+ }
416+ } ) ;
417+ } ) ;
269418} ) ;
0 commit comments