license import { patchFilteredProperties } from '../../lib/browser/property-descriptor' ; import { patchEventTarget } from '../../lib/common/events' ; import { isIEOrEdge , zoneSymbol } from '../../lib/common/utils' ; import { getEdgeVersion , getIEVersion , ifEnvSupports , ifEnvSupportsWithDone , isEdge } from '../test-util' ; import Spy = jasmine . Spy ; declare const global ; const noop = function ( ) { } ; function windowPrototype ( ) { return ! ! ( global [ 'Window' ] && global [ 'Window' ] . prototype ) ; } function promiseUnhandleRejectionSupport ( ) { return ! ! global [ 'PromiseRejectionEvent' ] ; } function canPatchOnProperty ( obj , prop ) { const func = function ( ) { if ( ! obj ) { return false ; } const desc = Object . getOwnPropertyDescriptor ( obj , prop ) ; if ( ! desc || ! desc . configurable ) { return false ; } return true ; } ; ( func as any ) . message = 'patchOnProperties' ; return func ; } let supportsPassive = false ; try { const opts = Object . defineProperty ( { } , 'passive' , { get : function ( ) { supportsPassive = true ; } } ) ; window . addEventListener ( 'test' , opts as any , opts ) ; window . removeEventListener ( 'test' , opts as any , opts ) ; } catch ( e ) { } function supportEventListenerOptions ( ) { return supportsPassive ; } ( supportEventListenerOptions as any ) . message = 'supportsEventListenerOptions' ; function supportCanvasTest ( ) { const HTMLCanvasElement = ( window as any ) [ 'HTMLCanvasElement' ] ; const supportCanvas = typeof HTMLCanvasElement !== 'undefined' && HTMLCanvasElement . prototype && HTMLCanvasElement . prototype . toBlob ; const FileReader = ( window as any ) [ 'FileReader' ] ; const supportFileReader = typeof FileReader !== 'undefined' ; return supportCanvas && supportFileReader ; } ( supportCanvasTest as any ) . message = 'supportCanvasTest' ; function ieOrEdge ( ) { return isIEOrEdge ( ) ; } ( ieOrEdge as any ) . message = 'IE/Edge Test' ; class TestEventListener { logs : string [ ] = [ ] ; addEventListener ( eventName , listener , options ) { this . logs . push ( options ) ; } removeEventListener ( eventName , listener , options ) { } } describe ( 'Zone' , function ( ) { const rootZone = Zone . current ; ( Zone as any ) [ zoneSymbol ( 'ignoreConsoleErrorUncaughtError' ) ] = true ; describe ( 'hooks' , function ( ) { it ( 'should allow you to override alert/prompt/confirm' , function ( ) { const alertSpy = jasmine . createSpy ( 'alert' ) ; const promptSpy = jasmine . createSpy ( 'prompt' ) ; const confirmSpy = jasmine . createSpy ( 'confirm' ) ; const spies = { 'alert' : alertSpy , 'prompt' : promptSpy , 'confirm' : confirmSpy } ; const myZone = Zone . current . fork ( { name : 'spy' , onInvoke : ( parentZoneDelegate , currentZone , targetZone , callback , applyThis ? , applyArgs ? , source ? ) : any => { if ( source ) { spies [ source ] . apply ( null , applyArgs ) ; } else { return parentZoneDelegate . invoke ( targetZone , callback , applyThis , applyArgs , source ) ; } } } ) ; myZone . run ( function ( ) { alert ( 'alertMsg' ) ; prompt ( 'promptMsg' , 'default' ) ; confirm ( 'confirmMsg' ) ; } ) ; expect ( alertSpy ) . toHaveBeenCalledWith ( 'alertMsg' ) ; expect ( promptSpy ) . toHaveBeenCalledWith ( 'promptMsg' , 'default' ) ; expect ( confirmSpy ) . toHaveBeenCalledWith ( 'confirmMsg' ) ; } ) ; describe ( 'DOM onProperty hooks' , ifEnvSupports ( canPatchOnProperty ( HTMLElement . prototype , 'onclick' ) , function ( ) { let mouseEvent = document . createEvent ( 'Event' ) ; let hookSpy , eventListenerSpy ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; beforeEach ( function ( ) { mouseEvent . initEvent ( 'mousedown' , true , true ) ; hookSpy = jasmine . createSpy ( 'hook' ) ; eventListenerSpy = jasmine . createSpy ( 'eventListener' ) ; } ) ; function checkIsOnPropertiesPatched ( target , ignoredProperties ? ) { for ( let prop in target ) { if ( ignoredProperties && ignoredProperties . filter ( ignoreProp => ignoreProp === prop ) . length > 0 ) { continue ; } if ( prop . substr ( 0 , 2 ) === 'on' && prop . length > 2 ) { target [ prop ] = noop ; if ( ! target [ Zone . __symbol__ ( 'ON_PROPERTY' + prop . substr ( 2 ) ) ] ) { console . log ( 'onProp is null:' , prop ) ; } else { target [ prop ] = null ; expect ( ! target [ Zone . __symbol__ ( 'ON_PROPERTY' + prop . substr ( 2 ) ) ] ) . toBeTruthy ( ) ; } } } } it ( 'should patch all possbile on properties on element' , function ( ) { const htmlElementTagNames = [ 'a' , 'area' , 'audio' , 'base' , 'basefont' , 'blockquote' , 'br' , 'button' , 'canvas' , 'caption' , 'col' , 'colgroup' , 'data' , 'datalist' , 'del' , 'dir' , 'div' , 'dl' , 'embed' , 'fieldset' , 'font' , 'form' , 'frame' , 'frameset' , 'h1' , 'h2' , 'h3' , 'h4' , 'h5' , 'h6' , 'head' , 'hr' , 'html' , 'iframe' , 'img' , 'input' , 'ins' , 'isindex' , 'label' , 'legend' , 'li' , 'link' , 'listing' , 'map' , 'marquee' , 'menu' , 'meta' , 'meter' , 'nextid' , 'ol' , 'optgroup' , 'option' , 'output' , 'p' , 'param' , 'picture' , 'pre' , 'progress' , 'q' , 'script' , 'select' , 'source' , 'span' , 'style' , 'table' , 'tbody' , 'td' , 'template' , 'textarea' , 'tfoot' , 'th' , 'thead' , 'time' , 'title' , 'tr' , 'track' , 'ul' , 'video' ] ; htmlElementTagNames . forEach ( tagName => { checkIsOnPropertiesPatched ( document . createElement ( tagName ) , [ 'onorientationchange' ] ) ; } ) ; } ) ; it ( 'should patch all possbile on properties on body' , function ( ) { checkIsOnPropertiesPatched ( document . body , [ 'onorientationchange' ] ) ; } ) ; it ( 'should patch all possbile on properties on Document' , function ( ) { checkIsOnPropertiesPatched ( document , [ 'onorientationchange' ] ) ; } ) ; it ( 'should patch all possbile on properties on Window' , function ( ) { checkIsOnPropertiesPatched ( window , [ 'onvrdisplayactivate' , 'onvrdisplayblur' , 'onvrdisplayconnect' , 'onvrdisplaydeactivate' , 'onvrdisplaydisconnect' , 'onvrdisplayfocus' , 'onvrdisplaypointerrestricted' , 'onvrdisplaypointerunrestricted' , 'onorientationchange' , 'onerror' ] ) ; } ) ; it ( 'should patch all possbile on properties on xhr' , function ( ) { checkIsOnPropertiesPatched ( new XMLHttpRequest ( ) ) ; } ) ; it ( 'should not patch ignored on properties' , function ( ) { const TestTarget = ( window as any ) [ 'TestTarget' ] ; patchFilteredProperties ( TestTarget . prototype , [ 'prop1' , 'prop2' ] , global [ '__Zone_ignore_on_properties' ] ) ; const testTarget = new TestTarget ( ) ; Zone . current . fork ( { name : 'test' } ) . run ( ( ) => { testTarget . onprop1 = function ( ) { expect ( Zone . current . name ) . toEqual ( 'test1' ) ; } ; testTarget . onprop2 = function ( ) { expect ( Zone . current . name ) . toEqual ( 'test' ) ; } ; } ) ; Zone . current . fork ( { name : 'test1' } ) . run ( ( ) => { testTarget . dispatchEvent ( 'prop1' ) ; testTarget . dispatchEvent ( 'prop2' ) ; } ) ; } ) ; it ( 'should not patch ignored eventListener' , function ( ) { let scrollEvent = document . createEvent ( 'Event' ) ; scrollEvent . initEvent ( 'scroll' , true , true ) ; const zone = Zone . current . fork ( { name : 'run' } ) ; Zone . current . fork ( { name : 'scroll' } ) . run ( ( ) => { document . addEventListener ( 'scroll' , ( ) => { expect ( Zone . current . name ) . toEqual ( zone . name ) ; } ) ; } ) ; zone . run ( ( ) => { document . dispatchEvent ( scrollEvent ) ; } ) ; } ) ; it ( 'should be able to clear on handler added before load zone.js' , function ( ) { const TestTarget = ( window as any ) [ 'TestTarget' ] ; patchFilteredProperties ( TestTarget . prototype , [ 'prop3' ] , global [ '__Zone_ignore_on_properties' ] ) ; const testTarget = new TestTarget ( ) ; Zone . current . fork ( { name : 'test' } ) . run ( ( ) => { expect ( testTarget . onprop3 ) . toBeTruthy ( ) ; const newProp3Handler = function ( ) { } ; testTarget . onprop3 = newProp3Handler ; expect ( testTarget . onprop3 ) . toBe ( newProp3Handler ) ; testTarget . onprop3 = null ; expect ( ! testTarget . onprop3 ) . toBeTruthy ( ) ; testTarget . onprop3 = function ( ) { expect ( Zone . current . name ) . toEqual ( 'test' ) ; } ; } ) ; Zone . current . fork ( { name : 'test1' } ) . run ( ( ) => { testTarget . dispatchEvent ( 'prop3' ) ; } ) ; } ) ; it ( 'window onclick should be in zone' , ifEnvSupports ( canPatchOnProperty ( window , 'onmousedown' ) , function ( ) { zone . run ( function ( ) { window . onmousedown = eventListenerSpy ; } ) ; window . dispatchEvent ( mouseEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( eventListenerSpy ) . toHaveBeenCalled ( ) ; window . removeEventListener ( 'mousedown' , eventListenerSpy ) ; } ) ) ; it ( 'window onresize should be patched' , ifEnvSupports ( canPatchOnProperty ( window , 'onmousedown' ) , function ( ) { window . onresize = eventListenerSpy ; const innerResizeProp = ( window as any ) [ zoneSymbol ( 'ON_PROPERTYresize' ) ] ; expect ( innerResizeProp ) . toBeTruthy ( ) ; innerResizeProp ( ) ; expect ( eventListenerSpy ) . toHaveBeenCalled ( ) ; window . removeEventListener ( 'resize' , eventListenerSpy ) ; } ) ) ; it ( 'document onclick should be in zone' , ifEnvSupports ( canPatchOnProperty ( Document . prototype , 'onmousedown' ) , function ( ) { zone . run ( function ( ) { document . onmousedown = eventListenerSpy ; } ) ; document . dispatchEvent ( mouseEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( eventListenerSpy ) . toHaveBeenCalled ( ) ; document . removeEventListener ( 'mousedown' , eventListenerSpy ) ; } ) ) ; it ( 'event handler with null context should use event.target' , ifEnvSupports ( canPatchOnProperty ( Document . prototype , 'onmousedown' ) , function ( ) { const ieVer = getIEVersion ( ) ; if ( ieVer && ieVer === 9 ) { return ; } const logs = [ ] ; const EventTarget = ( window as any ) [ 'EventTarget' ] ; let oriAddEventListener = EventTarget && EventTarget . prototype ? ( EventTarget . prototype as any ) [ zoneSymbol ( 'addEventListener' ) ] : ( HTMLSpanElement . prototype as any ) [ zoneSymbol ( 'addEventListener' ) ] ; if ( ! oriAddEventListener ) { return ; } let handler1 ; let handler2 ; const listener = function ( ) { logs . push ( 'listener1' ) ; } ; const listener1 = function ( ) { logs . push ( 'listener2' ) ; } ; HTMLSpanElement . prototype . addEventListener = function ( eventName , callback ) { if ( eventName === 'click' ) { handler1 = callback ; } else if ( eventName === 'mousedown' ) { handler2 = callback ; } return oriAddEventListener . apply ( this , arguments ) ; } ; ( HTMLSpanElement . prototype as any ) [ zoneSymbol ( 'addEventListener' ) ] = null ; patchEventTarget ( window , [ HTMLSpanElement . prototype ] ) ; const span = document . createElement ( 'span' ) ; document . body . appendChild ( span ) ; zone . run ( function ( ) { span . addEventListener ( 'click' , listener ) ; span . onmousedown = listener1 ; } ) ; expect ( handler1 ! ) . toBe ( handler2 ! ) ; handler1 ! . apply ( undefined , [ { type : 'click' , target : span } ] ) ; handler2 ! . apply ( undefined , [ { type : 'mousedown' , target : span } ] ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' ] ) ; document . body . removeChild ( span ) ; if ( EventTarget ) { ( EventTarget . prototype as any ) [ zoneSymbol ( 'addEventListener' ) ] = oriAddEventListener ; } else { ( HTMLSpanElement . prototype as any ) [ zoneSymbol ( 'addEventListener' ) ] = oriAddEventListener ; } } ) ) ; it ( 'SVGElement onclick should be in zone' , ifEnvSupports ( canPatchOnProperty ( SVGElement && SVGElement . prototype , 'onmousedown' ) , function ( ) { const svg = document . createElementNS ( 'http://www.w3.org/2000/svg' , 'svg' ) ; document . body . appendChild ( svg ) ; zone . run ( function ( ) { svg . onmousedown = eventListenerSpy ; } ) ; svg . dispatchEvent ( mouseEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( eventListenerSpy ) . toHaveBeenCalled ( ) ; svg . removeEventListener ( 'mouse' , eventListenerSpy ) ; document . body . removeChild ( svg ) ; } ) ) ; it ( 'get window onerror should not throw error' , ifEnvSupports ( canPatchOnProperty ( window , 'onerror' ) , function ( ) { const testFn = function ( ) { let onerror = window . onerror ; window . onerror = function ( ) { } ; onerror = window . onerror ; } ; expect ( testFn ) . not . toThrow ( ) ; } ) ) ; it ( 'window.onerror callback signiture should be (message, source, lineno, colno, error)' , ifEnvSupportsWithDone ( canPatchOnProperty ( window , 'onerror' ) , function ( done ) { let testError = new Error ( 'testError' ) ; window . onerror = function ( message , source ? , lineno ? , colno ? , error ? ) { expect ( message ) . toContain ( 'testError' ) ; if ( getEdgeVersion ( ) !== 14 ) { expect ( error ) . toBe ( testError ) ; } ( window as any ) . onerror = null ; setTimeout ( done ) ; return true ; } ; setTimeout ( ( ) => { throw testError ; } , 100 ) ; } ) ) ; } ) ) ; describe ( 'eventListener hooks' , function ( ) { let button ; let clickEvent ; beforeEach ( function ( ) { button = document . createElement ( 'button' ) ; clickEvent = document . createEvent ( 'Event' ) ; clickEvent . initEvent ( 'click' , true , true ) ; document . body . appendChild ( button ) ; } ) ; afterEach ( function ( ) { document . body . removeChild ( button ) ; } ) ; it ( 'should support addEventListener' , function ( ) { const hookSpy = jasmine . createSpy ( 'hook' ) ; const eventListenerSpy = jasmine . createSpy ( 'eventListener' ) ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( function ( ) { button . addEventListener ( 'click' , eventListenerSpy ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( eventListenerSpy ) . toHaveBeenCalled ( ) ; } ) ; it ( 'should be able to access addEventListener information in onScheduleTask' , function ( ) { const hookSpy = jasmine . createSpy ( 'hook' ) ; const eventListenerSpy = jasmine . createSpy ( 'eventListener' ) ; let scheduleButton ; let scheduleEventName ; let scheduleCapture ; let scheduleTask ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; scheduleButton = ( task . data as any ) . taskData . target ; scheduleEventName = ( task . data as any ) . taskData . eventName ; scheduleCapture = ( task . data as any ) . taskData . capture ; scheduleTask = task ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( function ( ) { button . addEventListener ( 'click' , eventListenerSpy ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( eventListenerSpy ) . toHaveBeenCalled ( ) ; expect ( scheduleButton ) . toBe ( button ) ; expect ( scheduleEventName ) . toBe ( 'click' ) ; expect ( scheduleCapture ) . toBe ( false ) ; expect ( scheduleTask && ( scheduleTask as any ) . data . taskData ) . toBe ( null ) ; } ) ; it ( 'should support addEventListener on window' , ifEnvSupports ( windowPrototype , function ( ) { const hookSpy = jasmine . createSpy ( 'hook' ) ; const eventListenerSpy = jasmine . createSpy ( 'eventListener' ) ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( function ( ) { window . addEventListener ( 'click' , eventListenerSpy ) ; } ) ; window . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( eventListenerSpy ) . toHaveBeenCalled ( ) ; } ) ) ; it ( 'should support removeEventListener' , function ( ) { const hookSpy = jasmine . createSpy ( 'hook' ) ; const eventListenerSpy = jasmine . createSpy ( 'eventListener' ) ; const zone = rootZone . fork ( { name : 'spy' , onCancelTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . cancelTask ( targetZone , task ) ; } } ) ; zone . run ( function ( ) { button . addEventListener ( 'click' , eventListenerSpy ) ; button . removeEventListener ( 'click' , eventListenerSpy ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( eventListenerSpy ) . not . toHaveBeenCalled ( ) ; } ) ; describe ( 'should support addEventListener/removeEventListener with AddEventListenerOptions with capture setting' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let hookSpy ; let cancelSpy ; let logs ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } , onCancelTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { cancelSpy ( ) ; return parentZoneDelegate . cancelTask ( targetZone , task ) ; } } ) ; const docListener = ( ) => { logs . push ( 'document' ) ; } ; const btnListener = ( ) => { logs . push ( 'button' ) ; } ; beforeEach ( ( ) => { logs = [ ] ; hookSpy = jasmine . createSpy ( 'hook' ) ; cancelSpy = jasmine . createSpy ( 'cancel' ) ; } ) ; it ( 'should handle child event when addEventListener with capture true' , ( ) => { zone . run ( function ( ) { ( document as any ) . addEventListener ( 'click' , docListener , { capture : true } ) ; button . addEventListener ( 'click' , btnListener ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( logs ) . toEqual ( [ 'document' , 'button' ] ) ; logs = [ ] ; ( document as any ) . removeEventListener ( 'click' , docListener , { capture : true } ) ; button . removeEventListener ( 'click' , btnListener ) ; expect ( cancelSpy ) . toHaveBeenCalled ( ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; } ) ; it ( 'should handle child event when addEventListener with capture true' , ( ) => { zone . run ( function ( ) { ( document as any ) . addEventListener ( 'click' , docListener , { capture : false } ) ; button . addEventListener ( 'click' , btnListener ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( logs ) . toEqual ( [ 'button' , 'document' ] ) ; logs = [ ] ; ( document as any ) . removeEventListener ( 'click' , docListener , { capture : false } ) ; button . removeEventListener ( 'click' , btnListener ) ; expect ( cancelSpy ) . toHaveBeenCalled ( ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; } ) ; } ) ) ; describe ( 'should ignore duplicate event handler' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let hookSpy ; let cancelSpy ; let logs ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } , onCancelTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { cancelSpy ( ) ; return parentZoneDelegate . cancelTask ( targetZone , task ) ; } } ) ; const docListener = ( ) => { logs . push ( 'document options' ) ; } ; beforeEach ( ( ) => { logs = [ ] ; hookSpy = jasmine . createSpy ( 'hook' ) ; cancelSpy = jasmine . createSpy ( 'cancel' ) ; } ) ; const testDuplicate = function ( args1 ? , args2 ? ) { zone . run ( function ( ) { if ( args1 ) { ( document as any ) . addEventListener ( 'click' , docListener , args1 ) ; } else { ( document as any ) . addEventListener ( 'click' , docListener ) ; } if ( args2 ) { ( document as any ) . addEventListener ( 'click' , docListener , args2 ) ; } else { ( document as any ) . addEventListener ( 'click' , docListener ) ; } } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( logs ) . toEqual ( [ 'document options' ] ) ; logs = [ ] ; ( document as any ) . removeEventListener ( 'click' , docListener , args1 ) ; expect ( cancelSpy ) . toHaveBeenCalled ( ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; } ; it ( 'should ignore duplicate handler' , ( ) => { let captureFalse = [ undefined , false , { capture : false } , { capture : false , passive : false } , { passive : false } , { } ] ; let captureTrue = [ true , { capture : true } , { capture : true , passive : false } ] ; for ( let i = 0 ; i < captureFalse . length ; i ++ ) { for ( let j = 0 ; j < captureFalse . length ; j ++ ) { testDuplicate ( captureFalse [ i ] , captureFalse [ j ] ) ; } } for ( let i = 0 ; i < captureTrue . length ; i ++ ) { for ( let j = 0 ; j < captureTrue . length ; j ++ ) { testDuplicate ( captureTrue [ i ] , captureTrue [ j ] ) ; } } } ) ; } ) ) ; describe ( 'should support mix useCapture with AddEventListenerOptions capture' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let hookSpy ; let cancelSpy ; let logs ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } , onCancelTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { cancelSpy ( ) ; return parentZoneDelegate . cancelTask ( targetZone , task ) ; } } ) ; const docListener = ( ) => { logs . push ( 'document options' ) ; } ; const docListener1 = ( ) => { logs . push ( 'document useCapture' ) ; } ; const btnListener = ( ) => { logs . push ( 'button' ) ; } ; beforeEach ( ( ) => { logs = [ ] ; hookSpy = jasmine . createSpy ( 'hook' ) ; cancelSpy = jasmine . createSpy ( 'cancel' ) ; } ) ; const testAddRemove = function ( args1 ? , args2 ? ) { zone . run ( function ( ) { if ( args1 ) { ( document as any ) . addEventListener ( 'click' , docListener , args1 ) ; } else { ( document as any ) . addEventListener ( 'click' , docListener ) ; } if ( args2 ) { ( document as any ) . removeEventListener ( 'click' , docListener , args2 ) ; } else { ( document as any ) . removeEventListener ( 'click' , docListener ) ; } } ) ; button . dispatchEvent ( clickEvent ) ; expect ( cancelSpy ) . toHaveBeenCalled ( ) ; expect ( logs ) . toEqual ( [ ] ) ; } ; it ( 'should be able to add/remove same handler with mix options and capture' , function ( ) { let captureFalse = [ undefined , false , { capture : false } , { capture : false , passive : false } , { passive : false } , { } ] ; let captureTrue = [ true , { capture : true } , { capture : true , passive : false } ] ; for ( let i = 0 ; i < captureFalse . length ; i ++ ) { for ( let j = 0 ; j < captureFalse . length ; j ++ ) { testAddRemove ( captureFalse [ i ] , captureFalse [ j ] ) ; } } for ( let i = 0 ; i < captureTrue . length ; i ++ ) { for ( let j = 0 ; j < captureTrue . length ; j ++ ) { testAddRemove ( captureTrue [ i ] , captureTrue [ j ] ) ; } } } ) ; const testDifferent = function ( args1 ? , args2 ? ) { zone . run ( function ( ) { if ( args1 ) { ( document as any ) . addEventListener ( 'click' , docListener , args1 ) ; } else { ( document as any ) . addEventListener ( 'click' , docListener ) ; } if ( args2 ) { ( document as any ) . addEventListener ( 'click' , docListener1 , args2 ) ; } else { ( document as any ) . addEventListener ( 'click' , docListener1 ) ; } } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( logs . sort ( ) ) . toEqual ( [ 'document options' , 'document useCapture' ] ) ; logs = [ ] ; if ( args1 ) { ( document as any ) . removeEventListener ( 'click' , docListener , args1 ) ; } else { ( document as any ) . removeEventListener ( 'click' , docListener ) ; } button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ 'document useCapture' ] ) ; logs = [ ] ; if ( args2 ) { ( document as any ) . removeEventListener ( 'click' , docListener1 , args2 ) ; } else { ( document as any ) . removeEventListener ( 'click' , docListener1 ) ; } button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; } ; it ( 'should be able to add different handlers for same event' , function ( ) { let captureFalse = [ undefined , false , { capture : false } , { capture : false , passive : false } , { passive : false } , { } ] ; let captureTrue = [ true , { capture : true } , { capture : true , passive : false } ] ; for ( let i = 0 ; i < captureFalse . length ; i ++ ) { for ( let j = 0 ; j < captureTrue . length ; j ++ ) { testDifferent ( captureFalse [ i ] , captureTrue [ j ] ) ; } } for ( let i = 0 ; i < captureTrue . length ; i ++ ) { for ( let j = 0 ; j < captureFalse . length ; j ++ ) { testDifferent ( captureTrue [ i ] , captureFalse [ j ] ) ; } } } ) ; it ( 'should handle options.capture true with capture true correctly' , function ( ) { zone . run ( function ( ) { ( document as any ) . addEventListener ( 'click' , docListener , { capture : true } ) ; document . addEventListener ( 'click' , docListener1 , true ) ; button . addEventListener ( 'click' , btnListener ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( logs ) . toEqual ( [ 'document options' , 'document useCapture' , 'button' ] ) ; logs = [ ] ; ( document as any ) . removeEventListener ( 'click' , docListener , { capture : true } ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ 'document useCapture' , 'button' ] ) ; logs = [ ] ; document . removeEventListener ( 'click' , docListener1 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ 'button' ] ) ; logs = [ ] ; button . removeEventListener ( 'click' , btnListener ) ; expect ( cancelSpy ) . toHaveBeenCalled ( ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; } ) ; } ) ) ; it ( 'should support addEventListener with AddEventListenerOptions once setting' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let hookSpy = jasmine . createSpy ( 'hook' ) ; let logs = [ ] ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( function ( ) { ( button as any ) . addEventListener ( 'click' , function ( ) { logs . push ( 'click' ) ; } , { once : true } ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'click' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 0 ) ; } ) ) ; it ( 'should support addEventListener with AddEventListenerOptions once setting and capture' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let hookSpy = jasmine . createSpy ( 'hook' ) ; let logs = [ ] ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( function ( ) { ( button as any ) . addEventListener ( 'click' , function ( ) { logs . push ( 'click' ) ; } , { once : true , capture : true } ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'click' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 0 ) ; } ) ) ; it ( 'should support add multipe listeners with AddEventListenerOptions once setting and same capture after normal listener' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let logs = [ ] ; button . addEventListener ( 'click' , function ( ) { logs . push ( 'click' ) ; } , true ) ; ( button as any ) . addEventListener ( 'click' , function ( ) { logs . push ( 'once click' ) ; } , { once : true , capture : true } ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'click' , 'once click' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'click' ] ) ; } ) ) ; it ( 'should support add multipe listeners with AddEventListenerOptions once setting and mixed capture after normal listener' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let logs = [ ] ; button . addEventListener ( 'click' , function ( ) { logs . push ( 'click' ) ; } ) ; ( button as any ) . addEventListener ( 'click' , function ( ) { logs . push ( 'once click' ) ; } , { once : true , capture : true } ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'click' , 'once click' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'click' ] ) ; } ) ) ; it ( 'should support add multipe listeners with AddEventListenerOptions once setting before normal listener' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let logs = [ ] ; ( button as any ) . addEventListener ( 'click' , function ( ) { logs . push ( 'once click' ) ; } , { once : true } ) ; button . addEventListener ( 'click' , function ( ) { logs . push ( 'click' ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'once click' , 'click' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'click' ] ) ; } ) ) ; it ( 'should support add multipe listeners with AddEventListenerOptions once setting with same capture before normal listener' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let logs = [ ] ; ( button as any ) . addEventListener ( 'click' , function ( ) { logs . push ( 'once click' ) ; } , { once : true , capture : true } ) ; button . addEventListener ( 'click' , function ( ) { logs . push ( 'click' ) ; } , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'once click' , 'click' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'click' ] ) ; } ) ) ; it ( 'should support add multipe listeners with AddEventListenerOptions once setting with mixed capture before normal listener' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let logs = [ ] ; ( button as any ) . addEventListener ( 'click' , function ( ) { logs . push ( 'once click' ) ; } , { once : true , capture : true } ) ; button . addEventListener ( 'click' , function ( ) { logs . push ( 'click' ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'once click' , 'click' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'click' ] ) ; } ) ) ; it ( 'should change options to boolean if not support passive' , ( ) => { patchEventTarget ( window , [ TestEventListener . prototype ] ) ; const testEventListener = new TestEventListener ( ) ; const listener = function ( ) { } ; testEventListener . addEventListener ( 'test' , listener , { passive : true } ) ; testEventListener . addEventListener ( 'test1' , listener , { once : true } ) ; testEventListener . addEventListener ( 'test2' , listener , { capture : true } ) ; testEventListener . addEventListener ( 'test3' , listener , { passive : false } ) ; testEventListener . addEventListener ( 'test4' , listener , { once : false } ) ; testEventListener . addEventListener ( 'test5' , listener , { capture : false } ) ; if ( ! supportsPassive ) { expect ( testEventListener . logs ) . toEqual ( [ false , false , true , false , false , false ] ) ; } else { expect ( testEventListener . logs ) . toEqual ( [ { passive : true } , { once : true } , { capture : true } , { passive : false } , { once : false } , { capture : false } ] ) ; } } ) ; it ( 'should change options to boolean if not support passive on HTMLElement' , ( ) => { const logs = [ ] ; const listener = ( e ) => { logs . push ( 'clicked' ) ; } ; ( button as any ) . addEventListener ( 'click' , listener , { once : true } ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ 'clicked' ] ) ; button . dispatchEvent ( clickEvent ) ; if ( supportsPassive ) { expect ( logs ) . toEqual ( [ 'clicked' ] ) ; } else { expect ( logs ) . toEqual ( [ 'clicked' , 'clicked' ] ) ; } button . removeEventListener ( 'click' , listener ) ; } ) ; it ( 'should support addEventListener with AddEventListenerOptions passive setting' , ifEnvSupports ( supportEventListenerOptions , function ( ) { const hookSpy = jasmine . createSpy ( 'hook' ) ; const logs = [ ] ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; const listener = ( e ) => { logs . push ( e . defaultPrevented . toString ( ) ) ; e . preventDefault ( ) ; logs . push ( e . defaultPrevented . toString ( ) ) ; } ; zone . run ( function ( ) { ( button as any ) . addEventListener ( 'click' , listener , { passive : true } ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( logs ) . toEqual ( [ 'false' , 'false' ] ) ; button . removeEventListener ( 'click' , listener ) ; } ) ) ; it ( 'should support Event.stopImmediatePropagation' , ifEnvSupports ( supportEventListenerOptions , function ( ) { const hookSpy = jasmine . createSpy ( 'hook' ) ; const logs = [ ] ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; const listener1 = ( e ) => { logs . push ( 'listener1' ) ; e . stopImmediatePropagation ( ) ; } ; const listener2 = ( e ) => { logs . push ( 'listener2' ) ; } ; zone . run ( function ( ) { ( button as any ) . addEventListener ( 'click' , listener1 ) ; ( button as any ) . addEventListener ( 'click' , listener2 ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . toHaveBeenCalled ( ) ; expect ( logs ) . toEqual ( [ 'listener1' ] ) ; button . removeEventListener ( 'click' , listener1 ) ; button . removeEventListener ( 'click' , listener2 ) ; } ) ) ; it ( 'should support remove event listener by call zone.cancelTask directly' , function ( ) { let logs = [ ] ; let eventTask ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { eventTask = task ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( ( ) => { button . addEventListener ( 'click' , function ( ) { logs . push ( 'click' ) ; } ) ; } ) ; let listeners = ( button as any ) . eventListeners ( 'click' ) ; expect ( listeners . length ) . toBe ( 1 ) ; eventTask ! . zone . cancelTask ( eventTask ! ) ; listeners = ( button as any ) . eventListeners ( 'click' ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 0 ) ; expect ( listeners . length ) . toBe ( 0 ) ; } ) ; it ( 'should support remove event listener by call zone.cancelTask directly with capture=true' , function ( ) { let logs = [ ] ; let eventTask ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { eventTask = task ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( ( ) => { button . addEventListener ( 'click' , function ( ) { logs . push ( 'click' ) ; } , true ) ; } ) ; let listeners = ( button as any ) . eventListeners ( 'click' ) ; expect ( listeners . length ) . toBe ( 1 ) ; eventTask ! . zone . cancelTask ( eventTask ! ) ; listeners = ( button as any ) . eventListeners ( 'click' ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 0 ) ; expect ( listeners . length ) . toBe ( 0 ) ; } ) ; it ( 'should support remove event listeners by call zone.cancelTask directly with multiple listeners' , function ( ) { let logs = [ ] ; let eventTask ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { eventTask = task ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( ( ) => { button . addEventListener ( 'click' , function ( ) { logs . push ( 'click1' ) ; } ) ; } ) ; button . addEventListener ( 'click' , function ( ) { logs . push ( 'click2' ) ; } ) ; let listeners = ( button as any ) . eventListeners ( 'click' ) ; expect ( listeners . length ) . toBe ( 2 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'click1' , 'click2' ] ) ; eventTask ! . zone . cancelTask ( eventTask ! ) ; logs = [ ] ; listeners = ( button as any ) . eventListeners ( 'click' ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( listeners . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'click2' ] ) ; } ) ; it ( 'should support remove event listeners by call zone.cancelTask directly with multiple listeners with same capture=true' , function ( ) { let logs = [ ] ; let eventTask ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { eventTask = task ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( ( ) => { button . addEventListener ( 'click' , function ( ) { logs . push ( 'click1' ) ; } , true ) ; } ) ; button . addEventListener ( 'click' , function ( ) { logs . push ( 'click2' ) ; } , true ) ; let listeners = ( button as any ) . eventListeners ( 'click' ) ; expect ( listeners . length ) . toBe ( 2 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'click1' , 'click2' ] ) ; eventTask ! . zone . cancelTask ( eventTask ! ) ; logs = [ ] ; listeners = ( button as any ) . eventListeners ( 'click' ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( listeners . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'click2' ] ) ; } ) ; it ( 'should support remove event listeners by call zone.cancelTask directly with multiple listeners with mixed capture' , function ( ) { let logs = [ ] ; let eventTask ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { eventTask = task ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( ( ) => { button . addEventListener ( 'click' , function ( ) { logs . push ( 'click1' ) ; } , true ) ; } ) ; button . addEventListener ( 'click' , function ( ) { logs . push ( 'click2' ) ; } ) ; let listeners = ( button as any ) . eventListeners ( 'click' ) ; expect ( listeners . length ) . toBe ( 2 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'click1' , 'click2' ] ) ; eventTask ! . zone . cancelTask ( eventTask ! ) ; logs = [ ] ; listeners = ( button as any ) . eventListeners ( 'click' ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( listeners . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'click2' ] ) ; } ) ; it ( 'should support reschedule eventTask' , ifEnvSupports ( supportEventListenerOptions , function ( ) { let hookSpy1 = jasmine . createSpy ( 'spy1' ) ; let hookSpy2 = jasmine . createSpy ( 'spy2' ) ; let hookSpy3 = jasmine . createSpy ( 'spy3' ) ; let logs = [ ] ; const isBlacklistedEvent = function ( source ) { return source . lastIndexOf ( 'click' ) !== - 1 ; } ; const zone1 = Zone . current . fork ( { name : 'zone1' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { if ( ( task . type === 'eventTask' || task . type === 'macroTask' ) && isBlacklistedEvent ( task . source ) ) { task . cancelScheduleRequest ( ) ; return zone2 . scheduleTask ( task ) ; } else { return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } , onInvokeTask ( parentZoneDelegate , currentZone , targetZone , task , applyThis , applyArgs ) { hookSpy1 ( ) ; return parentZoneDelegate . invokeTask ( targetZone , task , applyThis , applyArgs ) ; } } ) ; const zone2 = Zone . current . fork ( { name : 'zone2' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy2 ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } , onInvokeTask ( parentZoneDelegate , currentZone , targetZone , task , applyThis , applyArgs ) { hookSpy3 ( ) ; return parentZoneDelegate . invokeTask ( targetZone , task , applyThis , applyArgs ) ; } } ) ; const listener = function ( ) { logs . push ( Zone . current . name ) ; } ; zone1 . run ( ( ) => { button . addEventListener ( 'click' , listener ) ; button . addEventListener ( 'mouseover' , listener ) ; } ) ; const clickEvent = document . createEvent ( 'Event' ) ; clickEvent . initEvent ( 'click' , true , true ) ; const mouseEvent = document . createEvent ( 'Event' ) ; mouseEvent . initEvent ( 'mouseover' , true , true ) ; button . dispatchEvent ( clickEvent ) ; button . removeEventListener ( 'click' , listener ) ; expect ( logs ) . toEqual ( [ 'zone2' ] ) ; expect ( hookSpy1 ) . not . toHaveBeenCalled ( ) ; expect ( hookSpy2 ) . toHaveBeenCalled ( ) ; expect ( hookSpy3 ) . toHaveBeenCalled ( ) ; logs = [ ] ; hookSpy2 = jasmine . createSpy ( 'hookSpy2' ) ; hookSpy3 = jasmine . createSpy ( 'hookSpy3' ) ; button . dispatchEvent ( mouseEvent ) ; button . removeEventListener ( 'mouseover' , listener ) ; expect ( logs ) . toEqual ( [ 'zone1' ] ) ; expect ( hookSpy1 ) . toHaveBeenCalled ( ) ; expect ( hookSpy2 ) . not . toHaveBeenCalled ( ) ; expect ( hookSpy3 ) . not . toHaveBeenCalled ( ) ; } ) ) ; it ( 'should support inline event handler attributes' , function ( ) { const hookSpy = jasmine . createSpy ( 'hook' ) ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( function ( ) { button . setAttribute ( 'onclick' , 'return' ) ; expect ( button . onclick ) . not . toBe ( null ) ; } ) ; } ) ; describe ( 'should be able to remove eventListener during eventListener callback' , function ( ) { it ( 'should be able to remove eventListener during eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { button . removeEventListener ( 'click' , listener1 ) ; logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener2' , 'listener3' ] ) ; button . removeEventListener ( 'click' , listener2 ) ; button . removeEventListener ( 'click' , listener3 ) ; } ) ; it ( 'should be able to remove eventListener during eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { button . removeEventListener ( 'click' , listener1 , true ) ; logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener2' , 'listener3' ] ) ; button . removeEventListener ( 'click' , listener2 , true ) ; button . removeEventListener ( 'click' , listener3 , true ) ; } ) ; it ( 'should be able to remove handleEvent eventListener during eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; button . removeEventListener ( 'click' , listener3 ) ; } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' ] ) ; button . removeEventListener ( 'click' , listener1 ) ; button . removeEventListener ( 'click' , listener2 ) ; } ) ; it ( 'should be able to remove handleEvent eventListener during eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; button . removeEventListener ( 'click' , listener3 , true ) ; } } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' ] ) ; button . removeEventListener ( 'click' , listener1 , true ) ; button . removeEventListener ( 'click' , listener2 , true ) ; } ) ; it ( 'should be able to remove multiple eventListeners during eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; button . removeEventListener ( 'click' , listener2 ) ; button . removeEventListener ( 'click' , listener3 ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'listener1' ] ) ; button . removeEventListener ( 'click' , listener1 ) ; } ) ; it ( 'should be able to remove multiple eventListeners during eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; button . removeEventListener ( 'click' , listener2 , true ) ; button . removeEventListener ( 'click' , listener3 , true ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'listener1' ] ) ; button . removeEventListener ( 'click' , listener1 , true ) ; } ) ; it ( 'should be able to remove part of other eventListener during eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; button . removeEventListener ( 'click' , listener2 ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener3' ] ) ; button . removeEventListener ( 'click' , listener1 ) ; button . removeEventListener ( 'click' , listener3 ) ; } ) ; it ( 'should be able to remove part of other eventListener during eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; button . removeEventListener ( 'click' , listener2 , true ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener3' ] ) ; button . removeEventListener ( 'click' , listener1 , true ) ; button . removeEventListener ( 'click' , listener3 , true ) ; } ) ; it ( 'should be able to remove all beforeward and afterward eventListener during eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; button . removeEventListener ( 'click' , listener1 ) ; button . removeEventListener ( 'click' , listener3 ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'listener2' ] ) ; button . removeEventListener ( 'click' , listener2 ) ; } ) ; it ( 'should be able to remove all beforeward and afterward eventListener during eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; button . removeEventListener ( 'click' , listener1 , true ) ; button . removeEventListener ( 'click' , listener3 , true ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'listener2' ] ) ; button . removeEventListener ( 'click' , listener2 , true ) ; } ) ; it ( 'should be able to remove part of beforeward and afterward eventListener during eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; button . removeEventListener ( 'click' , listener2 ) ; button . removeEventListener ( 'click' , listener4 ) ; } } ; const listener4 = function ( ) { logs . push ( 'listener4' ) ; } ; const listener5 = function ( ) { logs . push ( 'listener5' ) ; } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . addEventListener ( 'click' , listener4 ) ; button . addEventListener ( 'click' , listener5 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 4 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' , 'listener5' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener3' , 'listener5' ] ) ; button . removeEventListener ( 'click' , listener1 ) ; button . removeEventListener ( 'click' , listener3 ) ; button . removeEventListener ( 'click' , listener5 ) ; } ) ; it ( 'should be able to remove part of beforeward and afterward eventListener during eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; button . removeEventListener ( 'click' , listener2 , true ) ; button . removeEventListener ( 'click' , listener4 , true ) ; } } ; const listener4 = function ( ) { logs . push ( 'listener4' ) ; } ; const listener5 = function ( ) { logs . push ( 'listener5' ) ; } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . addEventListener ( 'click' , listener4 , true ) ; button . addEventListener ( 'click' , listener5 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 4 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' , 'listener5' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener3' , 'listener5' ] ) ; button . removeEventListener ( 'click' , listener1 , true ) ; button . removeEventListener ( 'click' , listener3 , true ) ; button . removeEventListener ( 'click' , listener5 , true ) ; } ) ; it ( 'should be able to remove all beforeward eventListener during eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; button . removeEventListener ( 'click' , listener1 ) ; button . removeEventListener ( 'click' , listener2 ) ; } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'listener3' ] ) ; button . removeEventListener ( 'click' , listener3 ) ; } ) ; it ( 'should be able to remove all beforeward eventListener during eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; button . removeEventListener ( 'click' , listener1 , true ) ; button . removeEventListener ( 'click' , listener2 , true ) ; } } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'listener3' ] ) ; button . removeEventListener ( 'click' , listener3 , true ) ; } ) ; it ( 'should be able to remove part of beforeward eventListener during eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; button . removeEventListener ( 'click' , listener1 ) ; } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener2' , 'listener3' ] ) ; button . removeEventListener ( 'click' , listener2 ) ; button . removeEventListener ( 'click' , listener3 ) ; } ) ; it ( 'should be able to remove part of beforeward eventListener during eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; button . removeEventListener ( 'click' , listener1 , true ) ; } } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener2' , 'listener3' ] ) ; button . removeEventListener ( 'click' , listener2 , true ) ; button . removeEventListener ( 'click' , listener3 , true ) ; } ) ; it ( 'should be able to remove all eventListeners during first eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { ( button as any ) . removeAllListeners ( 'click' ) ; logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'listener1' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 0 ) ; } ) ; it ( 'should be able to remove all eventListeners during first eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { ( button as any ) . removeAllListeners ( 'click' ) ; logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 1 ) ; expect ( logs ) . toEqual ( [ 'listener1' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 0 ) ; } ) ; it ( 'should be able to remove all eventListeners during middle eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { ( button as any ) . removeAllListeners ( 'click' ) ; logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 0 ) ; } ) ; it ( 'should be able to remove all eventListeners during middle eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { ( button as any ) . removeAllListeners ( 'click' ) ; logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 2 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 0 ) ; } ) ; it ( 'should be able to remove all eventListeners during last eventListener callback' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; ( button as any ) . removeAllListeners ( 'click' ) ; } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 0 ) ; } ) ; it ( 'should be able to remove all eventListeners during last eventListener callback with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; ( button as any ) . removeAllListeners ( 'click' ) ; } } ; button . addEventListener ( 'click' , listener1 , true ) ; button . addEventListener ( 'click' , listener2 , true ) ; button . addEventListener ( 'click' , listener3 , true ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 3 ) ; expect ( logs ) . toEqual ( [ 'listener1' , 'listener2' , 'listener3' ] ) ; logs = [ ] ; button . dispatchEvent ( clickEvent ) ; expect ( logs . length ) . toBe ( 0 ) ; } ) ; } ) ; it ( 'should be able to get eventListeners of specified event form EventTarget' , function ( ) { const listener1 = function ( ) { } ; const listener2 = function ( ) { } ; const listener3 = { handleEvent : function ( event ) { } } ; const listener4 = function ( ) { } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; button . addEventListener ( 'mouseover' , listener4 ) ; const listeners = ( button as any ) . eventListeners ( 'click' ) ; expect ( listeners . length ) . toBe ( 3 ) ; expect ( listeners ) . toEqual ( [ listener1 , listener2 , listener3 ] ) ; button . removeEventListener ( 'click' , listener1 ) ; button . removeEventListener ( 'click' , listener2 ) ; button . removeEventListener ( 'click' , listener3 ) ; } ) ; it ( 'should be able to get all eventListeners form EventTarget without eventName' , function ( ) { const listener1 = function ( ) { } ; const listener2 = function ( ) { } ; const listener3 = { handleEvent : function ( event ) { } } ; button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'mouseover' , listener2 ) ; button . addEventListener ( 'mousehover' , listener3 ) ; const listeners = ( button as any ) . eventListeners ( ) ; expect ( listeners . length ) . toBe ( 3 ) ; expect ( listeners ) . toEqual ( [ listener1 , listener2 , listener3 ] ) ; button . removeEventListener ( 'click' , listener1 ) ; button . removeEventListener ( 'mouseover' , listener2 ) ; button . removeEventListener ( 'mousehover' , listener3 ) ; } ) ; it ( 'should be able to remove all listeners of specified event form EventTarget' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; const listener4 = function ( ) { logs . push ( 'listener4' ) ; } ; button . addEventListener ( 'mouseover' , listener1 ) ; button . addEventListener ( 'mouseover' , listener2 ) ; button . addEventListener ( 'mouseover' , listener3 ) ; button . addEventListener ( 'click' , listener4 ) ; ( button as any ) . removeAllListeners ( 'mouseover' ) ; const listeners = ( button as any ) . eventListeners ( 'mouseove' ) ; expect ( listeners . length ) . toBe ( 0 ) ; const mouseEvent = document . createEvent ( 'Event' ) ; mouseEvent . initEvent ( 'mouseover' , true , true ) ; button . dispatchEvent ( mouseEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ 'listener4' ] ) ; button . removeEventListener ( 'click' , listener4 ) ; } ) ; it ( 'should be able to remove all listeners of specified event form EventTarget with capture=true' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; const listener4 = function ( ) { logs . push ( 'listener4' ) ; } ; button . addEventListener ( 'mouseover' , listener1 , true ) ; button . addEventListener ( 'mouseover' , listener2 , true ) ; button . addEventListener ( 'mouseover' , listener3 , true ) ; button . addEventListener ( 'click' , listener4 , true ) ; ( button as any ) . removeAllListeners ( 'mouseover' ) ; const listeners = ( button as any ) . eventListeners ( 'mouseove' ) ; expect ( listeners . length ) . toBe ( 0 ) ; const mouseEvent = document . createEvent ( 'Event' ) ; mouseEvent . initEvent ( 'mouseover' , true , true ) ; button . dispatchEvent ( mouseEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ 'listener4' ] ) ; button . removeEventListener ( 'click' , listener4 ) ; } ) ; it ( 'should be able to remove all listeners of specified event form EventTarget with mixed capture' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; const listener4 = function ( ) { logs . push ( 'listener4' ) ; } ; button . addEventListener ( 'mouseover' , listener1 , true ) ; button . addEventListener ( 'mouseover' , listener2 , false ) ; button . addEventListener ( 'mouseover' , listener3 , true ) ; button . addEventListener ( 'click' , listener4 , true ) ; ( button as any ) . removeAllListeners ( 'mouseover' ) ; const listeners = ( button as any ) . eventListeners ( 'mouseove' ) ; expect ( listeners . length ) . toBe ( 0 ) ; const mouseEvent = document . createEvent ( 'Event' ) ; mouseEvent . initEvent ( 'mouseover' , true , true ) ; button . dispatchEvent ( mouseEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ 'listener4' ] ) ; button . removeEventListener ( 'click' , listener4 ) ; } ) ; it ( 'should be able to remove all listeners of all events form EventTarget' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; const listener4 = function ( ) { logs . push ( 'listener4' ) ; } ; button . addEventListener ( 'mouseover' , listener1 ) ; button . addEventListener ( 'mouseover' , listener2 ) ; button . addEventListener ( 'mouseover' , listener3 ) ; button . addEventListener ( 'click' , listener4 ) ; ( button as any ) . removeAllListeners ( ) ; const listeners = ( button as any ) . eventListeners ( 'mouseover' ) ; expect ( listeners . length ) . toBe ( 0 ) ; const mouseEvent = document . createEvent ( 'Event' ) ; mouseEvent . initEvent ( 'mouseover' , true , true ) ; button . dispatchEvent ( mouseEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; } ) ; it ( 'should be able to remove listener which was added outside of zone ' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; const listener4 = function ( ) { logs . push ( 'listener4' ) ; } ; button . addEventListener ( 'mouseover' , listener1 ) ; ( button as any ) [ Zone . __symbol__ ( 'addEventListener' ) ] ( 'mouseover' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; ( button as any ) [ Zone . __symbol__ ( 'addEventListener' ) ] ( 'click' , listener4 ) ; button . removeEventListener ( 'mouseover' , listener1 ) ; button . removeEventListener ( 'mouseover' , listener2 ) ; button . removeEventListener ( 'click' , listener3 ) ; button . removeEventListener ( 'click' , listener4 ) ; const listeners = ( button as any ) . eventListeners ( 'mouseover' ) ; expect ( listeners . length ) . toBe ( 0 ) ; const mouseEvent = document . createEvent ( 'Event' ) ; mouseEvent . initEvent ( 'mouseover' , true , true ) ; button . dispatchEvent ( mouseEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ ] ) ; } ) ; it ( 'should be able to remove all listeners which were added inside of zone ' , function ( ) { let logs = [ ] ; const listener1 = function ( ) { logs . push ( 'listener1' ) ; } ; const listener2 = function ( ) { logs . push ( 'listener2' ) ; } ; const listener3 = { handleEvent : function ( event ) { logs . push ( 'listener3' ) ; } } ; const listener4 = function ( ) { logs . push ( 'listener4' ) ; } ; button . addEventListener ( 'mouseover' , listener1 ) ; ( button as any ) [ Zone . __symbol__ ( 'addEventListener' ) ] ( 'mouseover' , listener2 ) ; button . addEventListener ( 'click' , listener3 ) ; ( button as any ) [ Zone . __symbol__ ( 'addEventListener' ) ] ( 'click' , listener4 ) ; ( button as any ) . removeAllListeners ( ) ; const listeners = ( button as any ) . eventListeners ( 'mouseover' ) ; expect ( listeners . length ) . toBe ( 0 ) ; const mouseEvent = document . createEvent ( 'Event' ) ; mouseEvent . initEvent ( 'mouseover' , true , true ) ; button . dispatchEvent ( mouseEvent ) ; expect ( logs ) . toEqual ( [ 'listener2' ] ) ; button . dispatchEvent ( clickEvent ) ; expect ( logs ) . toEqual ( [ 'listener2' , 'listener4' ] ) ; } ) ; it ( 'should bypass addEventListener of FunctionWrapper and __BROWSERTOOLS_CONSOLE_SAFEFUNC of IE/Edge' , ifEnvSupports ( ieOrEdge , function ( ) { const hookSpy = jasmine . createSpy ( 'hook' ) ; const zone = rootZone . fork ( { name : 'spy' , onScheduleTask : ( parentZoneDelegate , currentZone , targetZone , task ) : any => { hookSpy ( ) ; return parentZoneDelegate . scheduleTask ( targetZone , task ) ; } } ) ; let logs = [ ] ; const listener1 = function ( ) { logs . push ( Zone . current . name ) ; } ; ( listener1 as any ) . toString = function ( ) { return '[object FunctionWrapper]' ; } ; const listener2 = function ( ) { logs . push ( Zone . current . name ) ; } ; ( listener2 as any ) . toString = function ( ) { return 'function __BROWSERTOOLS_CONSOLE_SAFEFUNC() { [native code] }' ; } ; zone . run ( ( ) => { button . addEventListener ( 'click' , listener1 ) ; button . addEventListener ( 'click' , listener2 ) ; } ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . not . toHaveBeenCalled ( ) ; expect ( logs ) . toEqual ( [ 'ProxyZone' , 'ProxyZone' ] ) ; logs = [ ] ; button . removeEventListener ( 'click' , listener1 ) ; button . removeEventListener ( 'click' , listener2 ) ; button . dispatchEvent ( clickEvent ) ; expect ( hookSpy ) . not . toHaveBeenCalled ( ) ; expect ( logs ) . toEqual ( [ ] ) ; } ) ) ; } ) ; describe ( 'unhandle promise rejection' , ( ) => { const AsyncTestZoneSpec = ( Zone as any ) [ 'AsyncTestZoneSpec' ] ; const asyncTest = function ( testFn ) { return ( done ) => { let asyncTestZone = Zone . current . fork ( new AsyncTestZoneSpec ( done , ( error ) => { fail ( error ) ; } , 'asyncTest' ) ) ; asyncTestZone . run ( testFn ) ; } ; } ; it ( 'should support window.addEventListener(unhandledrejection)' , asyncTest ( ( ) => { if ( ! promiseUnhandleRejectionSupport ( ) ) { return ; } ( Zone as any ) [ zoneSymbol ( 'ignoreConsoleErrorUncaughtError' ) ] = true ; Zone . root . fork ( { name : 'promise' } ) . run ( function ( ) { const listener = ( evt ) => { window . removeEventListener ( 'unhandledrejection' , listener ) ; expect ( evt . type ) . toEqual ( 'unhandledrejection' ) ; expect ( evt . promise . constructor . name ) . toEqual ( 'Promise' ) ; expect ( evt . reason . message ) . toBe ( 'promise error' ) ; } ; window . addEventListener ( 'unhandledrejection' , listener ) ; new Promise ( ( resolve , reject ) => { throw new Error ( 'promise error' ) ; } ) ; } ) ; } ) ) ; it ( 'should support window.addEventListener(rejectionhandled)' , asyncTest ( ( ) => { if ( ! promiseUnhandleRejectionSupport ( ) ) { return ; } ( Zone as any ) [ zoneSymbol ( 'ignoreConsoleErrorUncaughtError' ) ] = true ; Zone . root . fork ( { name : 'promise' } ) . run ( function ( ) { const listener = ( evt ) => { window . removeEventListener ( 'unhandledrejection' , listener ) ; p . catch ( reason => { } ) ; } ; window . addEventListener ( 'unhandledrejection' , listener ) ; const handledListener = ( evt ) => { window . removeEventListener ( 'rejectionhandled' , handledListener ) ; expect ( evt . type ) . toEqual ( 'rejectionhandled' ) ; expect ( evt . promise . constructor . name ) . toEqual ( 'Promise' ) ; expect ( evt . reason . message ) . toBe ( 'promise error' ) ; } ; window . addEventListener ( 'rejectionhandled' , handledListener ) ; const p = new Promise ( ( resolve , reject ) => { throw new Error ( 'promise error' ) ; } ) ; } ) ; } ) ) ; it ( 'should support multiple window.addEventListener(unhandledrejection)' , asyncTest ( ( ) => { if ( ! promiseUnhandleRejectionSupport ( ) ) { return ; } ( Zone as any ) [ zoneSymbol ( 'ignoreConsoleErrorUncaughtError' ) ] = true ; Zone . root . fork ( { name : 'promise' } ) . run ( function ( ) { const listener1 = ( evt ) => { window . removeEventListener ( 'unhandledrejection' , listener1 ) ; expect ( evt . type ) . toEqual ( 'unhandledrejection' ) ; expect ( evt . promise . constructor . name ) . toEqual ( 'Promise' ) ; expect ( evt . reason . message ) . toBe ( 'promise error' ) ; } ; const listener2 = ( evt ) => { window . removeEventListener ( 'unhandledrejection' , listener2 ) ; expect ( evt . type ) . toEqual ( 'unhandledrejection' ) ; expect ( evt . promise . constructor . name ) . toEqual ( 'Promise' ) ; expect ( evt . reason . message ) . toBe ( 'promise error' ) ; } ; window . addEventListener ( 'unhandledrejection' , listener1 ) ; window . addEventListener ( 'unhandledrejection' , listener2 ) ; new Promise ( ( resolve , reject ) => { throw new Error ( 'promise error' ) ; } ) ; } ) ; } ) ) ; } ) ; xit ( 'IntersectionObserver should run callback in zone' , ifEnvSupportsWithDone ( 'IntersectionObserver' , ( done ) => { const div = document . createElement ( 'div' ) ; document . body . appendChild ( div ) ; const options = { threshold : 0.5 } ; const zone = Zone . current . fork ( { name : 'intersectionObserverZone' } ) ; zone . run ( ( ) => { const observer = new IntersectionObserver ( ( ) => { expect ( Zone . current . name ) . toEqual ( zone . name ) ; observer . unobserve ( div ) ; done ( ) ; } , options ) ; observer . observe ( div ) ; } ) ; div . style . display = 'none' ; div . style . visibility = 'block' ; } ) ) ; it ( 'HTMLCanvasElement.toBlob should be a ZoneAware MacroTask' , ifEnvSupportsWithDone ( supportCanvasTest , ( done ) => { const canvas = document . createElement ( 'canvas' ) ; const d = canvas . width ; const ctx = canvas . getContext ( '2d' ) ! ; ctx . beginPath ( ) ; ctx . moveTo ( d / 2 , 0 ) ; ctx . lineTo ( d , d ) ; ctx . lineTo ( 0 , d ) ; ctx . closePath ( ) ; ctx . fillStyle = 'yellow' ; ctx . fill ( ) ; const scheduleSpy = jasmine . createSpy ( 'scheduleSpy' ) ; const zone = Zone . current . fork ( { name : 'canvas' , onScheduleTask : ( delegate , currentZone , targetZone , task ) => { scheduleSpy ( ) ; return delegate . scheduleTask ( targetZone , task ) ; } } ) ; zone . run ( ( ) => { const canvasData = canvas . toDataURL ( ) ; canvas . toBlob ( function ( blob ) { expect ( Zone . current . name ) . toEqual ( 'canvas' ) ; expect ( scheduleSpy ) . toHaveBeenCalled ( ) ; const reader = new FileReader ( ) ; reader . readAsDataURL ( blob ! ) ; reader . onloadend = function ( ) { const base64data = reader . result ; expect ( base64data ) . toEqual ( canvasData ) ; done ( ) ; } ; } ) ; } ) ; } ) ) ; describe ( 'ResizeObserver' , ifEnvSupports ( 'ResizeObserver' , ( ) => { it ( 'ResizeObserver callback should be in zone' , ( done ) => { const ResizeObserver = ( window as any ) [ 'ResizeObserver' ] ; const div = document . createElement ( 'div' ) ; const zone = Zone . current . fork ( { name : 'observer' } ) ; const observer = new ResizeObserver ( ( entries , ob ) => { expect ( Zone . current . name ) . toEqual ( zone . name ) ; expect ( entries . length ) . toBe ( 1 ) ; expect ( entries [ 0 ] . target ) . toBe ( div ) ; done ( ) ; } ) ; zone . run ( ( ) => { observer . observe ( div ) ; } ) ; document . body . appendChild ( div ) ; } ) ; it ( 'ResizeObserver callback should be able to in different zones which when they were observed' , ( done ) => { const ResizeObserver = ( window as any ) [ 'ResizeObserver' ] ; const div1 = document . createElement ( 'div' ) ; const div2 = document . createElement ( 'div' ) ; const zone = Zone . current . fork ( { name : 'observer' } ) ; let count = 0 ; const observer = new ResizeObserver ( ( entries , ob ) => { entries . forEach ( ( entry ) => { if ( entry . target === div1 ) { expect ( Zone . current . name ) . toEqual ( zone . name ) ; } else { expect ( Zone . current . name ) . toEqual ( '<root>' ) ; } } ) ; count ++ ; if ( count === 2 ) { done ( ) ; } } ) ; zone . run ( ( ) => { observer . observe ( div1 ) ; } ) ; Zone . root . run ( ( ) => { observer . observe ( div2 ) ; } ) ; document . body . appendChild ( div1 ) ; document . body . appendChild ( div2 ) ; } ) ; } ) ) ; xdescribe ( 'getUserMedia' , ( ) => { it ( 'navigator.mediaDevices.getUserMedia should in zone' , ifEnvSupportsWithDone ( ( ) => { return ! isEdge ( ) && navigator && navigator . mediaDevices && typeof navigator . mediaDevices . getUserMedia === 'function' ; } , ( done ) => { const zone = Zone . current . fork ( { name : 'media' } ) ; zone . run ( ( ) => { const constraints = { audio : true , video : { width : 1280 , height : 720 } } ; navigator . mediaDevices . getUserMedia ( constraints ) . then ( function ( mediaStream ) { expect ( Zone . current . name ) . toEqual ( zone . name ) ; done ( ) ; } ) . catch ( function ( err ) { console . log ( err . name + ': ' + err . message ) ; expect ( Zone . current . name ) . toEqual ( zone . name ) ; done ( ) ; } ) ; } ) ; } ) ) ; it ( 'navigator.getUserMedia should in zone' , ifEnvSupportsWithDone ( ( ) => { return ! isEdge ( ) && navigator && typeof navigator . getUserMedia === 'function' ; } , ( done ) => { const zone = Zone . current . fork ( { name : 'media' } ) ; zone . run ( ( ) => { const constraints = { audio : true , video : { width : 1280 , height : 720 } } ; navigator . getUserMedia ( constraints , ( ) => { expect ( Zone . current . name ) . toEqual ( zone . name ) ; done ( ) ; } , ( ) => { expect ( Zone . current . name ) . toEqual ( zone . name ) ; done ( ) ; } ) ; } ) ; } ) ) ; } ) ; } ) ; } ) ;