diff --git a/eslint.config.js b/eslint.config.js index f45cbae1..5b288f17 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,5 +1,6 @@ import jsdoc from "eslint-plugin-jsdoc"; import eslintjs from "@eslint/js"; +import globals from "globals"; const {configs: eslintConfigs} = eslintjs; @@ -8,9 +9,9 @@ export default [ eslintConfigs["recommended"], { languageOptions: { - // if we ever use more globals than this, pull in the `globals` package globals: { - console: false + console: false, + ...globals.browser, // EventTarget, Event } }, rules: { diff --git a/lib/chai/utils/addMethod.js b/lib/chai/utils/addMethod.js index 89092926..f05efc0f 100644 --- a/lib/chai/utils/addMethod.js +++ b/lib/chai/utils/addMethod.js @@ -5,6 +5,7 @@ */ import {addLengthGuard} from './addLengthGuard.js'; +import {PluginEvent, events} from './events.js'; import {flag} from './flag.js'; import {proxify} from './proxify.js'; import {transferFlags} from './transferFlags.js'; @@ -63,4 +64,6 @@ export function addMethod(ctx, name, method) { addLengthGuard(methodWrapper, name, false); ctx[name] = proxify(methodWrapper, name); + + events.dispatchEvent(new PluginEvent('addMethod', name, method)); } diff --git a/lib/chai/utils/addProperty.js b/lib/chai/utils/addProperty.js index a9ac3f57..17f8aa15 100644 --- a/lib/chai/utils/addProperty.js +++ b/lib/chai/utils/addProperty.js @@ -5,6 +5,7 @@ */ import {Assertion} from '../assertion.js'; +import {PluginEvent, events} from './events.js'; import {flag} from './flag.js'; import {isProxyEnabled} from './isProxyEnabled.js'; import {transferFlags} from './transferFlags.js'; @@ -67,4 +68,6 @@ export function addProperty(ctx, name, getter) { }, configurable: true }); + + events.dispatchEvent(new PluginEvent('addProperty', name, getter)); } diff --git a/lib/chai/utils/events.js b/lib/chai/utils/events.js new file mode 100644 index 00000000..3b53a9ec --- /dev/null +++ b/lib/chai/utils/events.js @@ -0,0 +1,16 @@ +/*! + * Chai - events utility + * Copyright(c) 2011-2016 Jake Luer + * MIT Licensed + */ + +// Global EventTarget instance +export const events = new EventTarget(); + +export class PluginEvent extends Event { + constructor(type, name, fn) { + super(type); + this.name = String(name); + this.fn = fn; + } +} diff --git a/lib/chai/utils/index.js b/lib/chai/utils/index.js index 70d9f4c1..8dafa682 100644 --- a/lib/chai/utils/index.js +++ b/lib/chai/utils/index.js @@ -96,6 +96,9 @@ export {isNaN} from './isNaN.js'; // getOperator method export {getOperator} from './getOperator.js'; +// events EventTarget +export {events} from './events.js'; + /** * Determines if an object is a `RegExp` * This is used since `instanceof` will not work in virtual contexts diff --git a/package-lock.json b/package-lock.json index 3b11cbfe..dc4566c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "esbuild": "^0.25.9", "eslint": "^8.56.0", "eslint-plugin-jsdoc": "^48.0.4", + "globals": "^16.3.0", "loupe": "^3.1.0", "mocha": "^10.2.0", "pathval": "^2.0.0", @@ -621,6 +622,22 @@ "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -633,6 +650,19 @@ "node": "*" } }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { "version": "9.17.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", @@ -3451,6 +3481,22 @@ "node": ">=10.13.0" } }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3484,6 +3530,19 @@ "node": ">=8" } }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -3941,27 +4000,13 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/package.json b/package.json index 213ef454..b7513c66 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "esbuild": "^0.25.9", "eslint": "^8.56.0", "eslint-plugin-jsdoc": "^48.0.4", + "globals": "^16.3.0", "loupe": "^3.1.0", "mocha": "^10.2.0", "pathval": "^2.0.0", diff --git a/test/utilities.js b/test/utilities.js index 3e956352..17a0b1ef 100644 --- a/test/utilities.js +++ b/test/utilities.js @@ -1503,4 +1503,54 @@ describe('utilities', function () { }); }); }); + + describe('eventEmitter', function() { + var eventHandler = null; + + beforeEach(function() { + if (eventHandler) { + chai.util.events.removeEventListener("addMethod", eventHandler); + chai.util.events.removeEventListener("addProperty", eventHandler); + } + eventHandler = null; + delete chai.Assertion.prototype.eqqqual; + delete chai.Assertion.prototype.tea; + }); + + it('emits addMethod', function () { + var calledTimes = 0; + + chai.use(function(_chai, _utils) { + const eqqqual = function (str) { + var object = _utils.flag(this, 'object'); + new _chai.Assertion(object).to.be.eql(str); + } + chai.util.events.addEventListener("addMethod", eventHandler = function({ name, fn }) { + if (name === 'eqqqual' && fn === eqqqual) + calledTimes++; + }); + _chai.Assertion.addMethod('eqqqual', eqqqual); + }); + + expect(calledTimes).to.equal(1); + }); + + it('emits addProperty', function () { + var calledTimes = 0; + + chai.use(function(_chai, _utils) { + const getter = function () { + return 'chai'; + } + chai.util.events.addEventListener("addProperty", eventHandler = function({ name, fn }) { + if (name === 'tea' && fn === getter) + calledTimes++; + }); + _chai.Assertion.addProperty('tea', getter); + + }); + + expect(calledTimes).to.equal(1); + }); + }); });