Compare commits

...

6 Commits

Author SHA1 Message Date
Joey Perrott 98de185e53 WIP 2024-05-07 19:21:23 +00:00
Tom Wilkinson 0fb455f72e refactor(core): Move global dispatch behavior into Dispatcher. (#55692)
This behavior is now implemented by calling `dispatch` whether or not
the `action` is populated. The `Dispatcher` then does global dispatch
and early returns if there's no action.

PR Close #55692
2024-05-07 12:00:40 -07:00
Thomas Nguyen 67bb310376 refactor(core): Fix timing of removal of jsaction attribute to be after event replay. (#55696)
This otherwise leads to bugs where, by the time replay needs the attribute, hydration
happens and it's gone.

PR Close #55696
2024-05-07 08:16:16 -07:00
arturovt a9460d08a0 fix(zone.js): remove abort listener on a signal when actual event is removed (#55339)
This commit updates the implementation of the `addEventListener` patcher.

We're currently creating an abort event listener on the signal (when it's provided)
and never remove it. The abort event listener creates a closure which captures `task`
(tasks capture zones and other stuff too) and prevent `task`, zones and signals from
being garbage collected.

We now store the function which removes the abort event listener when the actual event
listener is being removed. The function is stored on task data since task data is already
being used to store different types of information that's necessary to be shared between
`addEventListener` and `removeEventListener`.

Closes #54739

PR Close #55339
2024-05-07 08:07:33 -07:00
Paul Gschwendtner afc057ee92 docs: properly link to developer preview section (#55711)
Fixes an invalid link to the developer preview section.
PR Close #55711
2024-05-07 08:06:42 -07:00
Angular Robot 0fffe5389f build: update all non-major dependencies (#55531)
See associated pull request for more information.

PR Close #55531
2024-05-07 08:05:57 -07:00
17 changed files with 165 additions and 352 deletions
@@ -13,7 +13,7 @@ jobs:
assistant_to_the_branch_manager:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
persist-credentials: false
- uses: angular/dev-infra/github-actions/branch-manager@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
- uses: alessbell/pull-request-comment-branch@aad01d65d6982b8eacabed5e9a684cd8ceb98da6 # v1.1
id: comment-branch
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
# Specify repository as the PR branch might be from a fork.
repository: ${{steps.comment-branch.outputs.head_owner}}/${{steps.comment-branch.outputs.head_repo}}
+2 -183
View File
@@ -5,7 +5,7 @@ on:
branches:
- main
- '[0-9]+.[0-9]+.x'
- 'remove-aio-stuff'
- 'browser-update-test'
pull_request:
types: [opened, synchronize, reopened]
@@ -20,157 +20,6 @@ defaults:
shell: bash
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
cache-node-modules: true
node-module-directories: |
./node_modules
./aio/node_modules
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Install node modules in aio
run: yarn install --frozen-lockfile --cwd aio
- name: Check code lint
run: yarn -s tslint
- name: Check for circular dependencies
run: yarn -s ts-circular-deps:check
- name: Validate pull approve configuration
run: yarn -s ng-dev pullapprove verify
- name: Validate angular robot configuration
run: yarn -s ng-dev ngbot verify
- name: Confirm code builds with typescript as expected
run: yarn -s check-tooling-setup
- name: Check commit message
# Commit message validation is only done on pull requests as its too late to validate once
# it has been merged.
if: github.event_name == 'pull_request'
run: yarn ng-dev commit-message validate-range ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}
- name: Check code format
# Code formatting checks are only done on pull requests as its too late to validate once
# it has been merged.
if: github.event_name == 'pull_request'
run: yarn ng-dev format changed --check ${{ github.event.pull_request.base.sha }}
devtools:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Run unit tests
run: yarn devtools:test
- name: Test build
run: yarn devtools:build:chrome
test:
runs-on: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Install node modules
run: yarn install --frozen-lockfile --network-timeout 100000
- name: Run CI tests for framework
run: yarn test:ci
adev:
runs-on:
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev in fast mode to ensure it continues to work
run: yarn bazel build //adev:build --fast_adev
- name: Run tests
run: yarn bazel test //adev:test
publish-snapshots:
if: github.event_name == 'push'
runs-on:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
cache-node-modules: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Install node modules
run: yarn install --frozen-lockfile
- run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials
- run: yarn build
- run: ./scripts/ci/publish-build-artifacts.sh
zone-js:
runs-on:
labels: ubuntu-latest-4core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
cache-node-modules: true
node-module-directories: |
./node_modules
./packages/zone.js/node_modules
./packages/zone.js/test/typings/node_modules
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Install node modules
run: yarn install --frozen-lockfile
- run: |
yarn bazel build \
//packages/zone.js/bundles:zone.umd.js \
//packages/zone.js:npm_package \
//packages/zone.js/test/closure:closure_js \
- run: |
rm -Rf packages/zone.js/build
rm -Rf packages/zone.js/test/extra/*.umd.js
mkdir -p packages/zone.js/build/
mkdir -p packages/zone.js/build/test/
cp dist/bin/packages/zone.js/bundles/zone.umd.js packages/zone.js/build/zone.umd.js
cp dist/bin/packages/zone.js/npm_package/bundles/zone-mix.umd.js ./packages/zone.js/test/extra/
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/
cp dist/bin/packages/zone.js/test/closure/zone.closure.mjs ./packages/zone.js/build/test/zone.closure.mjs
# Install
- run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive
# Run zone.js tools tests
- run: yarn --cwd packages/zone.js promisefinallytest
- run: yarn --cwd packages/zone.js jest:test
- run: yarn --cwd packages/zone.js jest:nodetest
- run: yarn --cwd packages/zone.js electrontest
- run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive
- run: yarn --cwd packages/zone.js/test/typings test
bazel-saucelabs:
# TODO: Replace legacy job with Bazel saucelabs job.
if: github.event_name == 'push'
@@ -247,34 +96,4 @@ jobs:
- name: Stop Saucelabs tunnel service
run: ./tools/saucelabs/sauce-service.sh stop
adev-deploy:
needs: [adev]
if: needs.adev.result == 'success' && github.event_name == 'push' && github.ref_name == 'main'
env:
DEPLOY_PROJECT: angular-dev-site
DEPLOY_SITE: angular-dev-site
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
- name: Install node modules
run: yarn install --frozen-lockfile
- name: Build adev to ensure it continues to work
run: yarn bazel build //adev:build
- name: Move generated project files into deployment directory
run: cp -r dist/bin/adev/build adev/build
- name: Setup firebase context
run: |
npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{ env.DEPLOY_PROJECT }} hosting angular-docs
npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{ env.DEPLOY_PROJECT }} hosting angular-docs ${{ env.DEPLOY_SITE }}
- name: Deploy to firebase
uses: FirebaseExtended/action-hosting-deploy@120e124148ab7016bec2374e5050f15051255ba2 # v0.7.1
with:
entryPoint: './adev'
firebaseServiceAccount: ${{ secrets.ANGULAR_DEV_SITE_DEPLOY }}
projectId: ${{ env.DEPLOY_PROJECT }}
channelId: live
+2 -2
View File
@@ -12,14 +12,14 @@ jobs:
labels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
- uses: angular/dev-infra/github-actions/commit-message-based-labels@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
post_approval_changes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
- uses: angular/dev-infra/github-actions/post-approval-changes@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
statuses: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
- uses: angular/dev-infra/github-actions/google-internal-tests@e6e98d32d7d4d350ecf4ec10ad9c7e75198daabd
with:
run-tests-guide-url: http://go/angular-g3sync-start
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
with:
# Setting `persist-credentials: false` prevents the github-action account from being the
# account that is attempted to be used for authentication, instead the remote is set to
@@ -3,7 +3,7 @@
The `output()` function declares an output in a directive or component.
Outputs allow you to emit values to parent components.
HELPFUL: The `output()` function is currently in [developer preview](/guide/releases#developer-preview).
HELPFUL: The `output()` function is currently in [developer preview](/reference/releases#developer-preview).
<docs-code language="ts" highlight="[[5], [8]]">
import {Component, output} from '@angular/core';
+4 -15
View File
@@ -22,22 +22,11 @@ const debugMode = false;
// https://saucelabs.com/platform/platform-configurator
const customLaunchers = {
'SL_ANDROID11': {
base: 'SauceLabs',
browserName: 'Chrome',
platformName: 'Android',
platformVersion: '11.0',
deviceName: 'Google Pixel 3a GoogleAPI Emulator',
appiumVersion: '1.20.2',
extendedDebugging: debugMode,
},
'SL_ANDROID12': {
base: 'SauceLabs',
browserName: 'Chrome',
platformName: 'Android',
platformVersion: '12.0',
deviceName: 'Google Pixel 4a (5G) GoogleAPI Emulator',
appiumVersion: '1.22.1',
extendedDebugging: debugMode,
browserName: 'Browser',
'appium:deviceName': 'Android GoogleAPI Emulator',
'appium:platformVersion': 'current_major',
'appium:automationName': 'UiAutomator2',
},
};
+2 -2
View File
@@ -77,7 +77,7 @@
"@types/babel__core": "7.20.5",
"@types/babel__generator": "7.6.8",
"@types/bluebird": "^3.5.27",
"@types/chrome": "^0.0.266",
"@types/chrome": "^0.0.267",
"@types/convert-source-map": "^2.0.0",
"@types/diff": "^5.0.0",
"@types/dom-view-transitions": "^1.0.1",
@@ -132,7 +132,7 @@
"rollup-plugin-sourcemaps": "^0.6.3",
"rxjs": "^7.0.0",
"selenium-webdriver": "3.5.0",
"selenium-webdriver4": "npm:selenium-webdriver@4.19.0",
"selenium-webdriver4": "npm:selenium-webdriver@4.20.0",
"semver-dsl": "^1.0.1",
"shelljs": "^0.8.5",
"source-map": "0.7.4",
@@ -39,10 +39,7 @@ export class BaseDispatcher {
* @param dispatchDelegate A function that should handle dispatching an `EventInfoWrapper` to handlers.
*/
constructor(
private readonly dispatchDelegate: (
eventInfoWrapper: EventInfoWrapper,
isGlobalDispatch?: boolean,
) => void,
private readonly dispatchDelegate: (eventInfoWrapper: EventInfoWrapper) => void,
{eventReplayer = undefined}: {eventReplayer?: Replayer} = {},
) {
this.eventReplayer = eventReplayer;
@@ -67,20 +64,18 @@ export class BaseDispatcher {
*
* @param eventInfo The info for the event that triggered this call or the
* queue of events from EventContract.
* @param isGlobalDispatch If true, dispatches a global event instead of a
* regular jsaction handler.
*/
dispatch(eventInfo: EventInfo, isGlobalDispatch?: boolean): void {
dispatch(eventInfo: EventInfo): void {
const eventInfoWrapper = new EventInfoWrapper(eventInfo);
if (eventInfoWrapper.getIsReplay()) {
if (isGlobalDispatch || !this.eventReplayer) {
if (!this.eventReplayer) {
return;
}
this.queueEventInfoWrapper(eventInfoWrapper);
this.scheduleEventReplay();
return;
}
this.dispatchDelegate(eventInfoWrapper, isGlobalDispatch);
this.dispatchDelegate(eventInfoWrapper);
}
/** Queue an `EventInfoWrapper` for replay. */
@@ -117,7 +112,7 @@ export function registerDispatcher(
eventContract: UnrenamedEventContract,
dispatcher: BaseDispatcher,
) {
eventContract.ecrd((eventInfo: EventInfo, globalDispatch?: boolean) => {
dispatcher.dispatch(eventInfo, globalDispatch);
eventContract.ecrd((eventInfo: EventInfo) => {
dispatcher.dispatch(eventInfo);
}, Restriction.I_AM_THE_JSACTION_FRAMEWORK);
}
@@ -71,8 +71,8 @@ export class Dispatcher {
) {
this.eventReplayer = eventReplayer;
this.baseDispatcher = new BaseDispatcher(
(eventInfoWrapper: EventInfoWrapper, isGlobalDispatch?: boolean) => {
this.dispatchToHandler(eventInfoWrapper, isGlobalDispatch);
(eventInfoWrapper: EventInfoWrapper) => {
this.dispatchToHandler(eventInfoWrapper);
},
{
eventReplayer: (eventInfoWrappers) => {
@@ -102,33 +102,43 @@ export class Dispatcher {
*
* @param eventInfo The info for the event that triggered this call or the
* queue of events from EventContract.
* @param isGlobalDispatch If true, dispatches a global event instead of a
* regular jsaction handler.
*/
dispatch(eventInfo: EventInfo, isGlobalDispatch?: boolean): void {
this.baseDispatcher.dispatch(eventInfo, isGlobalDispatch);
this.baseDispatcher.dispatch(eventInfo);
}
/**
* Dispatches an `EventInfoWrapper`.
*/
private dispatchToHandler(eventInfoWrapper: EventInfoWrapper, isGlobalDispatch?: boolean) {
if (isGlobalDispatch) {
private dispatchToHandler(eventInfoWrapper: EventInfoWrapper) {
if (this.globalHandlers.size) {
const globalEventInfoWrapper = eventInfoWrapper.clone();
// In some cases, `populateAction` will rewrite `click` events to
// `clickonly`. Revert back to a regular click, otherwise we won't be able
// to execute global event handlers registered on click events.
if (globalEventInfoWrapper.getEventType() === EventType.CLICKONLY) {
globalEventInfoWrapper.setEventType(EventType.CLICK);
}
// Skip everything related to jsaction handlers, and execute the global
// handlers.
const ev = eventInfoWrapper.getEvent();
const eventTypeHandlers = this.globalHandlers.get(eventInfoWrapper.getEventType());
const event = globalEventInfoWrapper.getEvent();
const eventTypeHandlers = this.globalHandlers.get(globalEventInfoWrapper.getEventType());
let shouldPreventDefault = false;
if (eventTypeHandlers) {
for (const handler of eventTypeHandlers) {
if (handler(ev) === false) {
if (handler(event) === false) {
shouldPreventDefault = true;
}
}
}
if (shouldPreventDefault) {
eventLib.preventDefault(ev);
eventLib.preventDefault(event);
}
}
const action = eventInfoWrapper.getAction();
if (!action) {
return;
}
@@ -136,8 +146,6 @@ export class Dispatcher {
stopPropagation(eventInfoWrapper);
}
const action = eventInfoWrapper.getAction()!;
let handler: EventInfoWrapperHandler | void = undefined;
if (this.getHandler) {
handler = this.getHandler(eventInfoWrapper);
@@ -307,7 +315,7 @@ export function stopPropagation(eventInfoWrapper: EventInfoWrapper) {
* Dispatcher.
*/
export function registerDispatcher(eventContract: UnrenamedEventContract, dispatcher: Dispatcher) {
eventContract.ecrd((eventInfo: EventInfo, globalDispatch?: boolean) => {
dispatcher.dispatch(eventInfo, globalDispatch);
eventContract.ecrd((eventInfo: EventInfo) => {
dispatcher.dispatch(eventInfo);
}, Restriction.I_AM_THE_JSACTION_FRAMEWORK);
}
@@ -163,29 +163,14 @@ export class EventContract implements UnrenamedEventContract {
// All events are queued when the dispatcher isn't yet loaded.
eventInfoLib.setIsReplay(eventInfo, true);
this.queuedEventInfos?.push(eventInfo);
return;
}
this.actionResolver.resolve(eventInfo);
if (!this.dispatcher) {
return;
}
const globalEventInfo: eventInfoLib.EventInfo = eventInfoLib.cloneEventInfo(eventInfo);
// In some cases, `populateAction` will rewrite `click` events to
// `clickonly`. Revert back to a regular click, otherwise we won't be able
// to execute global event handlers registered on click events.
if (eventInfoLib.getEventType(globalEventInfo) === EventType.CLICKONLY) {
eventInfoLib.setEventType(globalEventInfo, EventType.CLICK);
}
this.dispatcher(globalEventInfo, /* dispatch global event */ true);
const action = eventInfoLib.getAction(eventInfo);
if (!action) {
return;
}
if (shouldPreventDefaultBeforeDispatching(eventInfoLib.getActionElement(action), eventInfo)) {
eventLib.preventDefault(eventInfoLib.getEvent(eventInfo));
if (action) {
if (shouldPreventDefaultBeforeDispatching(eventInfoLib.getActionElement(action), eventInfo)) {
eventLib.preventDefault(eventInfoLib.getEvent(eventInfo));
}
}
this.dispatcher(eventInfo);
@@ -7,7 +7,13 @@
*/
import {Dispatcher, Replayer} from '../src/dispatcher';
import {ActionInfo, createEventInfo, EventInfo, EventInfoWrapper} from '../src/event_info';
import {
ActionInfo,
createEventInfo,
EventInfo,
EventInfoWrapper,
unsetAction,
} from '../src/event_info';
import {createEvent} from '../src/replay';
function createMockClickEvent() {
@@ -137,7 +143,8 @@ describe('dispatcher test.ts', () => {
await expectAsync(replayed).toBePending();
const eventInfo1 = createTestEventInfo({isReplay: true});
dispatcher.dispatch(eventInfo1, /* globalDispatch= */ true);
unsetAction(eventInfo1);
dispatcher.dispatch(eventInfo1);
await expectAsync(replayed).toBePending();
});
@@ -199,7 +206,9 @@ describe('dispatcher test.ts', () => {
const dispatcher = new Dispatcher();
dispatcher.registerGlobalHandler('click', handler);
dispatcher.dispatch(createTestEventInfo(), true);
const eventInfo = createTestEventInfo();
unsetAction(eventInfo);
dispatcher.dispatch(eventInfo);
expect(handler).toHaveBeenCalledTimes(1);
});
@@ -213,7 +222,8 @@ describe('dispatcher test.ts', () => {
event: createEvent({type: 'mousedown'} as Event),
eventType: 'mousedown',
});
dispatcher.dispatch(eventInfo, true);
unsetAction(eventInfo);
dispatcher.dispatch(eventInfo);
expect(handler).not.toHaveBeenCalled();
});
@@ -234,7 +244,8 @@ describe('dispatcher test.ts', () => {
container,
action: {name: 'foo', element: targetElement},
});
dispatcher.dispatch(eventInfo, true);
unsetAction(eventInfo);
dispatcher.dispatch(eventInfo);
});
targetElement.addEventListener('click', targetHandler);
const parentHandler = jasmine.createSpy('parentHandler');
@@ -263,7 +274,8 @@ describe('dispatcher test.ts', () => {
container,
action: {name: 'foo', element: targetElement},
});
dispatcher.dispatch(eventInfo, /* globalDispatch= */ true);
unsetAction(eventInfo);
dispatcher.dispatch(eventInfo);
});
targetElement.addEventListener('click', targetHandler);
const parentHandler = jasmine.createSpy('parentHandler');
@@ -427,9 +427,9 @@ describe('EventContract', () => {
const dispatcher = jasmine.createSpy<Dispatcher>('dispatcher');
eventContract.registerDispatcher(dispatcher, Restriction.I_AM_THE_JSACTION_FRAMEWORK);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe(EventType.CLICK);
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -453,9 +453,9 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe(EventType.CLICK);
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -477,9 +477,9 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe(EventType.CLICK);
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -502,7 +502,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -526,7 +526,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -558,7 +558,7 @@ describe('EventContract', () => {
// tslint:enable:deprecation
targetElement.dispatchEvent(animationEndEvent);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('animationend');
expect(eventInfoWrapper.getEvent()).toBe(animationEndEvent);
@@ -581,7 +581,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement, {shiftKey: true});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('clickmod');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -604,7 +604,7 @@ describe('EventContract', () => {
let clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
let eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -617,7 +617,7 @@ describe('EventContract', () => {
clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -640,7 +640,7 @@ describe('EventContract', () => {
let clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
let eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -655,7 +655,7 @@ describe('EventContract', () => {
clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -678,7 +678,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -701,7 +701,7 @@ describe('EventContract', () => {
const keydownEvent = dispatchKeyboardEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('keydown');
expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
@@ -724,7 +724,7 @@ describe('EventContract', () => {
const keyupEvent = dispatchKeyboardEvent(targetElement, {type: 'keyup'});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('keyup');
expect(eventInfoWrapper.getEvent()).toBe(keyupEvent);
@@ -776,7 +776,7 @@ describe('EventContract', () => {
shadowChild.click();
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
// Target element is set to the host from the event.
@@ -832,7 +832,7 @@ describe('EventContract', () => {
clickHandler('click', clickEvent, container);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe(EventType.CLICK);
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -876,7 +876,7 @@ describe('EventContract', () => {
// tslint:enable:deprecation
targetElement.dispatchEvent(customEvent);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('custom-event');
expect(eventInfoWrapper.getEvent()).toBe(customEvent);
@@ -900,7 +900,7 @@ describe('EventContract', () => {
const data = {'test': 1};
fireCustomEvent(targetElement, 'custom-event', data);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('custom-event');
const customEvent = eventInfoWrapper.getEvent() as CustomEvent;
@@ -936,7 +936,7 @@ describe('EventContract', () => {
const data = {'test': 1};
fireCustomEvent(targetElement, 'custom-event', data);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('custom-event');
const customEvent = eventInfoWrapper.getEvent() as CustomEvent;
@@ -962,7 +962,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -987,7 +987,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement, {shiftKey: true});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('clickmod');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1031,7 +1031,7 @@ describe('EventContract', () => {
const keydownEvent = dispatchKeyboardEvent(targetElement, {key: 'Enter'});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
@@ -1054,9 +1054,9 @@ describe('EventContract', () => {
const keydownEvent = dispatchKeyboardEvent(targetElement, {key: 'a'});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('keydown');
expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
@@ -1079,7 +1079,7 @@ describe('EventContract', () => {
const keydownEvent = dispatchKeyboardEvent(targetElement, {key: 'Enter'});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
@@ -1104,9 +1104,9 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('clickonly');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1129,9 +1129,9 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1158,7 +1158,7 @@ describe('EventContract', () => {
const keydownEvent = dispatchKeyboardEvent(targetElement, {key: 'Enter'});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
@@ -1183,9 +1183,9 @@ describe('EventContract', () => {
const keydownEvent = dispatchKeyboardEvent(targetElement, {key: 'a'});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('keydown');
expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
@@ -1210,7 +1210,7 @@ describe('EventContract', () => {
const keydownEvent = dispatchKeyboardEvent(targetElement, {key: 'Enter'});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(keydownEvent);
@@ -1237,9 +1237,9 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('clickonly');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1264,9 +1264,9 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(targetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1307,7 +1307,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(outerTargetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1332,7 +1332,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(innerTargetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1357,7 +1357,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(outerTargetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1382,7 +1382,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(innerTargetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1408,7 +1408,7 @@ describe('EventContract', () => {
let clickEvent = dispatchMouseEvent(innerTargetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
let eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1425,7 +1425,7 @@ describe('EventContract', () => {
clickEvent = dispatchMouseEvent(innerTargetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1457,7 +1457,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(outerTargetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1488,7 +1488,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(innerTargetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1519,7 +1519,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(outerTargetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1550,7 +1550,7 @@ describe('EventContract', () => {
const clickEvent = dispatchMouseEvent(innerTargetElement);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1587,7 +1587,7 @@ describe('EventContract', () => {
relatedTarget: container,
});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('mouseenter');
const syntheticMouseEvent = eventInfoWrapper.getEvent();
@@ -1640,7 +1640,7 @@ describe('EventContract', () => {
relatedTarget: container,
});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('mouseleave');
const syntheticMouseEvent = eventInfoWrapper.getEvent();
@@ -1693,7 +1693,7 @@ describe('EventContract', () => {
relatedTarget: container,
});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('pointerenter');
const syntheticMouseEvent = eventInfoWrapper.getEvent();
@@ -1746,7 +1746,7 @@ describe('EventContract', () => {
relatedTarget: container,
});
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('pointerleave');
const syntheticMouseEvent = eventInfoWrapper.getEvent();
@@ -1817,7 +1817,7 @@ describe('EventContract', () => {
expect(window._ejsa).toBeUndefined();
expect(removeEventListenerSpy).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1854,7 +1854,7 @@ describe('EventContract', () => {
expect(replaySink._ejsa).toBeUndefined();
expect(removeEventListenerSpy).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('focus');
expect(eventInfoWrapper.getEvent().type).toBe('focus');
@@ -1892,7 +1892,7 @@ describe('EventContract', () => {
expect(window._ejsa).toBeUndefined();
expect(removeEventListenerSpy).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('click');
expect(eventInfoWrapper.getEvent()).toBe(clickEvent);
@@ -1939,7 +1939,7 @@ describe('EventContract', () => {
expect((window as EarlyJsactionDataContainer)._ejsa).toBeUndefined();
expect(removeEventListenerSpy).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(3);
expect(dispatcher).toHaveBeenCalledTimes(2);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('mouseleave');
const syntheticMouseEvent = eventInfoWrapper.getEvent();
@@ -1981,7 +1981,7 @@ describe('EventContract', () => {
expect(window._ejsa).toBeUndefined();
expect(removeEventListenerSpy).toHaveBeenCalledTimes(1);
expect(dispatcher).toHaveBeenCalledTimes(2);
expect(dispatcher).toHaveBeenCalledTimes(1);
const eventInfoWrapper = getLastDispatchedEventInfoWrapper(dispatcher);
expect(eventInfoWrapper.getEventType()).toBe('mouseleave');
const syntheticMouseEvent = eventInfoWrapper.getEvent();
+8 -1
View File
@@ -36,6 +36,7 @@ declare global {
}
const JSACTION_ATTRIBUTE = 'jsaction';
const removeJsactionQueue: RElement[] = [];
/**
* Returns a set of providers required to setup support for event replay.
@@ -52,7 +53,9 @@ export function withEventReplay(): Provider[] {
useValue: () => {
setDisableEventReplayImpl((el: RElement) => {
if (el.hasAttribute(JSACTION_ATTRIBUTE)) {
el.removeAttribute(JSACTION_ATTRIBUTE);
// We don't immediately remove the attribute here because
// we need it for replay that happens after hydration.
removeJsactionQueue.push(el);
}
});
},
@@ -79,6 +82,10 @@ export function withEventReplay(): Provider[] {
setEventReplayer(dispatcher);
// Event replay is kicked off as a side-effect of executing this function.
registerDispatcher(eventContract, dispatcher);
for (const el of removeJsactionQueue) {
el.removeAttribute(JSACTION_ATTRIBUTE);
}
removeJsactionQueue.length = 0;
}
});
};
+23 -16
View File
@@ -427,13 +427,7 @@ export function patchEventTarget(
const passive =
passiveSupported && !!passiveEvents && passiveEvents.indexOf(eventName) !== -1;
const options = buildEventListenerOptions(arguments[2], passive);
const signal =
options &&
typeof options === 'object' &&
options.signal &&
typeof options.signal === 'object'
? options.signal
: undefined;
const signal: AbortSignal | undefined = options?.signal;
if (signal?.aborted) {
// the signal is an aborted one, just return without attaching the event listener.
return;
@@ -528,14 +522,18 @@ export function patchEventTarget(
if (signal) {
// after task is scheduled, we need to store the signal back to task.options
taskData.options.signal = signal;
nativeListener.call(
signal,
'abort',
() => {
task.zone.cancelTask(task);
},
{once: true},
);
// Wrapping `task` in a weak reference would not prevent memory leaks. Weak references are
// primarily used for preventing strong references cycles. `onAbort` is always reachable
// as it's an event listener, so its closure retains a strong reference to the `task`.
const onAbort = () => task.zone.cancelTask(task);
nativeListener.call(signal, 'abort', onAbort, {once: true});
// We need to remove the `abort` listener when the event listener is going to be removed,
// as it creates a closure that captures `task`. This closure retains a reference to the
// `task` object even after it goes out of scope, preventing `task` from being garbage
// collected.
if (data) {
(data as any).removeAbortListener = () => signal.removeEventListener('abort', onAbort);
}
}
// should clear taskData.target to avoid memory leak
@@ -620,7 +618,7 @@ export function patchEventTarget(
if (symbolEventNames) {
symbolEventName = symbolEventNames[capture ? TRUE_STR : FALSE_STR];
}
const existingTasks = symbolEventName && target[symbolEventName];
const existingTasks: Task[] = symbolEventName && target[symbolEventName];
if (existingTasks) {
for (let i = 0; i < existingTasks.length; i++) {
const existingTask = existingTasks[i];
@@ -643,6 +641,15 @@ export function patchEventTarget(
target[onPropertySymbol] = null;
}
}
// Note that `removeAllListeners` would ultimately call `removeEventListener`,
// so we're safe to remove the abort listener only once here.
const taskData = existingTask.data as any;
if (taskData?.removeAbortListener) {
taskData.removeAbortListener();
taskData.removeAbortListener = null;
}
existingTask.zone.cancelTask(existingTask);
if (returnTarget) {
return target;
+8 -17
View File
@@ -3609,10 +3609,10 @@
"@types/node" "*"
"@types/responselike" "^1.0.0"
"@types/chrome@^0.0.266":
version "0.0.266"
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.266.tgz#1ef0c3076ea05489db2a04a057657587920d2390"
integrity sha512-QSQWJTL7NjZElvq/6/E5C1+pHgEP8UAJzwoz7M4vSJ7AECt6NNehJ+tU6snnvuTqZOBjFCivvitYo5+8tNPmhg==
"@types/chrome@^0.0.267":
version "0.0.267"
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.267.tgz#eefb7d2085993437d8109eb495431dfc0e3dbda1"
integrity sha512-vnCWPpYjazSPRMNmybRH+0q4f738F+Pbbls4ZPFsPr9/4TTNJyK1OLZDpSnghnEWb4stfmIUtq/GegnlfD4sPA==
dependencies:
"@types/filesystem" "*"
"@types/har-format" "*"
@@ -13922,10 +13922,10 @@ select-hose@^2.0.0:
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==
"selenium-webdriver4@npm:selenium-webdriver@4.19.0":
version "4.19.0"
resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.19.0.tgz#bdd001b6e534033664d8c1310a84c0f12eeafc67"
integrity sha512-8XHW8m9V2XN2/SC1kr4bWzMtGvjmKUEZ6S0UBoDBqonhmwEIzKOLbzhanBd08HCOg1s1O0XrDWCD71NnA8Zt0g==
"selenium-webdriver4@npm:selenium-webdriver@4.20.0", selenium-webdriver@^4.18.1:
version "4.20.0"
resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.20.0.tgz#14941ab4a59e8956a5e4b4491a8ba2bd6619d1ac"
integrity sha512-s/G44lGQ1xB3tmtX6NNPomlkpL6CxLdmAvp/AGWWwi4qv5Te1+qji7tPSyr6gyuoPpdYiof1rKnWe3luy0MrYA==
dependencies:
jszip "^3.10.1"
tmp "^0.2.3"
@@ -13951,15 +13951,6 @@ selenium-webdriver@3.6.0, selenium-webdriver@^3.0.1:
tmp "0.0.30"
xml2js "^0.4.17"
selenium-webdriver@^4.18.1:
version "4.20.0"
resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.20.0.tgz#14941ab4a59e8956a5e4b4491a8ba2bd6619d1ac"
integrity sha512-s/G44lGQ1xB3tmtX6NNPomlkpL6CxLdmAvp/AGWWwi4qv5Te1+qji7tPSyr6gyuoPpdYiof1rKnWe3luy0MrYA==
dependencies:
jszip "^3.10.1"
tmp "^0.2.3"
ws ">=8.16.0"
selfsigned@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0"