diff --git a/AGENTS.md b/AGENTS.md index 7ce4243..55f9515 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ This file provides guidance to AI agents when working with code in this reposito ## Commands ```bash -npm test # Run all tests (mocha) +npm test # Run all tests (node:test) npm run lint # ESLint (flat config in eslint.config.js) npm run build # Generate JSDoc documentation into build/ npm run docs # Build the docs and open them in the browser (macOS `open`) @@ -16,7 +16,7 @@ The docs are a static site that works straight from disk, no server needed. Off Run single test file: ```bash -npx mocha test/onOff.js +node --test test/onOff.js ``` ## Architecture @@ -68,10 +68,16 @@ inputClusters: [0x0006] ### Test Pattern +Tests run on `node:test`, so `describe`, `it` and `mock` are imported, not globals. A +callback-style test takes the test context first: `it('...', function(t, done) { ... })`. +With `function(done)` node:test sees a synchronous test, passes it immediately and never +runs the assertions. + Tests use mock nodes from `test/util/mockNode.js`: **Single node with loopback** (command → same node's BoundCluster): ```javascript +const { describe, it } = require('node:test'); const { createMockNode } = require('./util'); const OnOffCluster = require('../lib/clusters/onOff'); diff --git a/README.md b/README.md index 0a8a5ad..ff4ef98 100644 --- a/README.md +++ b/README.md @@ -339,5 +339,5 @@ The workflow commits updated types back to `develop` if changes are detected. Great if you'd like to contribute to this project, a few things to take note of before submitting a PR: - This project enforces ESLint, validate by running `npm run lint`. -- This project implements a basic test framework based on mocha, see [test](https://github.com/athombv/node-zigbee-clusters/blob/production/test) directory. +- This project implements a basic test framework based on node:test, see [test](https://github.com/athombv/node-zigbee-clusters/blob/production/test) directory. - This project uses several [GitHub Action workflows](https://github.com/athombv/node-zigbee-clusters/blob/production/.github/workflows) (e.g. ESLint, running test and versioning/publishing). diff --git a/eslint.config.js b/eslint.config.js index d26c20d..54c72dc 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -28,12 +28,4 @@ module.exports = defineConfig( ], }, }, - { - files: ['test/**/*.js'], - languageOptions: { - globals: { - ...globals.mocha, - }, - }, - }, ); diff --git a/package-lock.json b/package-lock.json index 10bb82a..7aa1511 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,14 +16,11 @@ "@athombv/jsdoc-template": "^1.7.0", "@eslint/js": "^10.0.1", "@types/node": "^25.0.10", - "@types/sinon": "^17.0.3", "eslint": "^10.13.0", "globals": "^17.13.0", "jsdoc": "^4.0.5", "jsdoc-ts-utils": "^6.0.0", - "mocha": "^12.0.3", "prettier": "^3.9.10", - "sinon": "^19.0.2", "typescript": "^5.9.3" }, "engines": { @@ -450,47 +447,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", - "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1", - "type-detect": "^4.1.0" - } - }, - "node_modules/@sinonjs/samsam/node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -547,23 +503,6 @@ "undici-types": ">=7.24.0 <7.24.7" } }, - "node_modules/@types/sinon": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz", - "integrity": "sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/sinonjs__fake-timers": "*" - } - }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-15.0.1.tgz", - "integrity": "sha512-Ko2tjWJq8oozHzHV+reuvS5KYIRAokHnGbDwGh/J64LntgpbuylF74ipEL24HCyRjf9FOlBiBHWBR1RlVKsI1w==", - "dev": true, - "license": "MIT" - }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -882,13 +821,6 @@ "dev": true, "license": "MIT" }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true, - "license": "ISC" - }, "node_modules/browserslist": { "version": "4.29.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.4.tgz", @@ -981,22 +913,6 @@ "node": ">= 10" } }, - "node_modules/chokidar": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", - "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/chrome-trace-event": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", @@ -1097,16 +1013,6 @@ "dev": true, "license": "MIT" }, - "node_modules/diff": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", - "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.463", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.463.tgz", @@ -1456,19 +1362,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-up-simple": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", - "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/flat-cache": { "version": "6.1.23", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", @@ -1488,24 +1381,6 @@ "dev": true, "license": "ISC" }, - "node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -1519,45 +1394,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/globals": { "version": "17.13.0", "resolved": "https://registry.npmjs.org/globals/-/globals-17.13.0.tgz", @@ -1584,6 +1420,7 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -1674,32 +1511,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-path-inside": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", - "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1843,13 +1654,6 @@ "node": ">=6" } }, - "node_modules/just-extend": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", - "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", - "dev": true, - "license": "MIT" - }, "node_modules/keyv": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", @@ -1942,16 +1746,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lru-cache": { - "version": "11.5.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", - "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, "node_modules/markdown-it": { "version": "14.3.3", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.3.tgz", @@ -2183,155 +1977,6 @@ } } }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mocha": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-12.0.3.tgz", - "integrity": "sha512-beammieDdiWdkjANWeWPpkxUROYGi5FN88fPOkLiticz7HTg3Y+Pho3EYE6SUdcuWnZ3h2WD8EXCA55JF4o6wg==", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^5.0.0", - "debug": "^4.3.5", - "diff": "^9.0.0", - "find-up-simple": "^1.0.1", - "glob": "^13.0.0", - "is-path-inside": "^4.0.0", - "is-unicode-supported": "^0.1.0", - "js-yaml": "^5.0.0", - "minimatch": "^10.2.2", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^7.1.1", - "strip-json-comments": "^5.0.3", - "supports-color": "^8.1.1", - "workerpool": "^10.0.0" - }, - "bin": { - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/mocha/node_modules/argparse": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-3.0.3.tgz", - "integrity": "sha512-Sn98ftV3KfsO8xfLPHG6oT8y+b0yvbrLgZjgPDXsharNY15tTAjqIsIju4S32nl6/iEUNNE4v+yL2Zhm8ooP3w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "PSF-2.0" - }, - "node_modules/mocha/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", - "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.5.0.tgz", - "integrity": "sha512-paNr4LFIFPjeeSmwNYdrb8FafLLG/Cr2Af3GjbsHJ5CxIjH4r0c2emlmRdAtpPAieP1gRVzk6buBbVP5gBMGAg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^3.0.3" - }, - "bin": { - "js-yaml": "bin/js-yaml.mjs" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", - "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2364,40 +2009,6 @@ "dev": true, "license": "MIT" }, - "node_modules/nise": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.5.tgz", - "integrity": "sha512-SnRDPDBjxZZoU2n0+gzzLtSvo1OZo7j6jnbXsoh3AFxEGhaFU7ZF0TmefuKERq79wxR2U+MPn7ArW+Tl+clC3A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^15.1.1", - "just-extend": "^6.2.0", - "path-to-regexp": "^8.3.0" - } - }, - "node_modules/nise/node_modules/@sinonjs/fake-timers": { - "version": "15.4.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", - "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/nise/node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/node-releases": { "version": "2.0.60", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz", @@ -2479,23 +2090,6 @@ "node": ">=8" } }, - "node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2682,20 +2276,6 @@ "dev": true, "license": "MIT" }, - "node_modules/readdirp": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", - "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -2749,16 +2329,6 @@ "node": ">=10" } }, - "node_modules/serialize-javascript": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.1.2.tgz", - "integrity": "sha512-GL2BWwVa6JydKO6l/ljVgjAZF4QJ3S7dWDWi53s5GZT8PJzD2fNxi67HANQGKAqPrwEpL5ba7gUBGi0Ls/sEoQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2782,35 +2352,6 @@ "node": ">=8" } }, - "node_modules/sinon": { - "version": "19.0.5", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.5.tgz", - "integrity": "sha512-r15s9/s+ub/d4bxNXqIUmwp6imVSdTorIRaxoecYjqTVLZ8RuoXr/4EDGwIBo6Waxn7f2gnURX9zuhAfCwaF6Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^13.0.5", - "@sinonjs/samsam": "^8.0.1", - "diff": "^7.0.0", - "nise": "^6.1.1", - "supports-color": "^7.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "node_modules/sinon/node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -2857,19 +2398,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/taffydb": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.7.3.tgz", @@ -2925,16 +2453,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -3184,13 +2702,6 @@ "node": ">=0.10.0" } }, - "node_modules/workerpool": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-10.0.3.tgz", - "integrity": "sha512-6z2Iis68Wqth93/G/wJP9u+R3O+d2XTlgWChGCwuT1qLbBsOYueGRZuJ++v3mtDP5KjYdy+WzvWC+VWETSVXJA==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/xmlcreate": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", diff --git a/package.json b/package.json index 113a440..83aed58 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "lib" ], "scripts": { - "test": "mocha --reporter list", + "test": "node --test --test-reporter=spec --test-timeout=5000 \"test/*.js\"", "lint": "eslint .", "generate-types": "node scripts/generate-types.mts", "build": "jsdoc --configure ./docs/jsdoc.json", @@ -39,14 +39,11 @@ "@athombv/jsdoc-template": "^1.7.0", "@eslint/js": "^10.0.1", "@types/node": "^25.0.10", - "@types/sinon": "^17.0.3", "eslint": "^10.13.0", "globals": "^17.13.0", "jsdoc": "^4.0.5", "jsdoc-ts-utils": "^6.0.0", - "mocha": "^12.0.3", "prettier": "^3.9.10", - "sinon": "^19.0.2", "typescript": "^5.9.3" }, "dependencies": { diff --git a/test/attributeReport.js b/test/attributeReport.js index a595a89..79a86e7 100644 --- a/test/attributeReport.js +++ b/test/attributeReport.js @@ -1,12 +1,13 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const OnOffCluster = require('../lib/clusters/onOff'); const { createMockNode } = require('./util'); const { ZCLStandardHeader } = require('../lib/zclFrames'); describe('Attribute report', function () { - it('should emit the transaction sequence number with the attribute value', function (done) { + it('should emit the transaction sequence number with the attribute value', function (t, done) { const node = createMockNode({ endpoints: [ { diff --git a/test/boundClusterAttributeStatus.js b/test/boundClusterAttributeStatus.js index 338f7ce..e02703d 100644 --- a/test/boundClusterAttributeStatus.js +++ b/test/boundClusterAttributeStatus.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const BoundCluster = require('../lib/BoundCluster'); const BasicCluster = require('../lib/clusters/basic'); diff --git a/test/colorControl.js b/test/colorControl.js index 967b46d..c84caa4 100644 --- a/test/colorControl.js +++ b/test/colorControl.js @@ -1,15 +1,13 @@ 'use strict'; -const assert = require('assert'); -const sinon = require('sinon'); +const { describe, it, afterEach, mock } = require('node:test'); +const assert = require('node:assert'); const BoundCluster = require('../lib/BoundCluster'); const ColorControlCluster = require('../lib/clusters/colorControl'); const Endpoint = require('../lib/Endpoint'); const { createMockNode } = require('./util'); -const sandbox = sinon.createSandbox(); - // Sends colorLoopSet on a fresh node and returns the parsed outgoing frame, so tests can assert // the payload bytes against the ZCL spec instead of round-tripping through our own definition async function sendColorLoopSet(args) { @@ -32,15 +30,15 @@ async function sendColorLoopSet(args) { })(), ); - const sendFrameSpy = sandbox.spy(node, 'sendFrame'); + const sendFrameSpy = mock.method(node, 'sendFrame'); await node.endpoints[1].clusters.colorControl.colorLoopSet(args); - return Endpoint.parseFrame(sendFrameSpy.getCall(0).args[2]); + return Endpoint.parseFrame(sendFrameSpy.mock.calls[0].arguments[2]); } describe('Color Control', function () { afterEach(function () { - sandbox.restore(); + mock.restoreAll(); }); it('should receive moveToColor', async function () { diff --git a/test/defaultResponse.js b/test/defaultResponse.js index 91cf82d..ea8474e 100644 --- a/test/defaultResponse.js +++ b/test/defaultResponse.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const { Endpoint, BoundCluster } = require('..'); const DoorLockCluster = require('../lib/clusters/doorLock'); diff --git a/test/doorLock.js b/test/doorLock.js index a3c5b65..842233c 100644 --- a/test/doorLock.js +++ b/test/doorLock.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const BoundCluster = require('../lib/BoundCluster'); const DoorLockCluster = require('../lib/clusters/doorLock'); const { createMockNode } = require('./util'); @@ -127,7 +128,7 @@ describe('Door Lock', function () { // Server-to-client notifications require manual frame construction // since they are sent BY the device TO the controller - it('should receive operationEventNotification', function (done) { + it('should receive operationEventNotification', function (t, done) { const node = createMockNode({ loopback: true, endpoints: [ @@ -159,7 +160,7 @@ describe('Door Lock', function () { node.handleFrame(1, DoorLockCluster.ID, frame.toBuffer(), {}); }); - it('should receive programmingEventNotification', function (done) { + it('should receive programmingEventNotification', function (t, done) { const node = createMockNode({ loopback: true, endpoints: [ diff --git a/test/endpointStandalone.js b/test/endpointStandalone.js index 0593f3f..ae3d6fe 100644 --- a/test/endpointStandalone.js +++ b/test/endpointStandalone.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const { Endpoint, BoundCluster } = require('..'); diff --git a/test/iasZone.js b/test/iasZone.js index 514e5d9..4fad182 100644 --- a/test/iasZone.js +++ b/test/iasZone.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const BoundCluster = require('../lib/BoundCluster'); const IASZoneCluster = require('../lib/clusters/iasZone'); const { createMockNode, MOCK_DEVICES } = require('./util'); @@ -8,7 +9,7 @@ const { ZCLStandardHeader } = require('../lib/zclFrames'); describe('IAS Zone', function () { // Server-to-client notification - requires manual frame - it('should receive onZoneEnrollRequest', function (done) { + it('should receive onZoneEnrollRequest', function (t, done) { const node = createMockNode({ loopback: true, endpoints: [ @@ -66,7 +67,7 @@ describe('IAS Zone', function () { }); // Server-to-client notification - requires manual frame - it('should receive zoneStatusChangeNotification', function (done) { + it('should receive zoneStatusChangeNotification', function (t, done) { const node = createMockNode({ loopback: true, endpoints: [ diff --git a/test/levelControl.js b/test/levelControl.js index cc55762..a1f6ade 100644 --- a/test/levelControl.js +++ b/test/levelControl.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const BoundCluster = require('../lib/BoundCluster'); const LevelControlCluster = require('../lib/clusters/levelControl'); const { createMockNode } = require('./util'); diff --git a/test/onOff.js b/test/onOff.js index 803d15e..ba8bd77 100644 --- a/test/onOff.js +++ b/test/onOff.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const BoundCluster = require('../lib/BoundCluster'); const OnOffCluster = require('../lib/clusters/onOff'); const { createMockNode } = require('./util'); diff --git a/test/ota.js b/test/ota.js index 74eafa3..cb96e97 100644 --- a/test/ota.js +++ b/test/ota.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const BoundCluster = require('../lib/BoundCluster'); const Node = require('../lib/Node'); const OTACluster = require('../lib/clusters/ota'); diff --git a/test/testClusterResponseTimeout.js b/test/testClusterResponseTimeout.js index 2e20ac7..add1098 100644 --- a/test/testClusterResponseTimeout.js +++ b/test/testClusterResponseTimeout.js @@ -1,7 +1,7 @@ 'use strict'; -const assert = require('assert'); -const sinon = require('sinon'); +const { describe, it, beforeEach, afterEach, mock } = require('node:test'); +const assert = require('node:assert'); const Cluster = require('../lib/Cluster'); const Node = require('../lib/Node'); @@ -11,8 +11,6 @@ const OnOffCluster = require('../lib/clusters/onOff'); const AlarmsCluster = require('../lib/clusters/alarms'); const IasZoneCluster = require('../lib/clusters/iasZone'); -const sandbox = sinon.createSandbox(); - describe('Cluster Response Timeout', function () { let receivingNode; let sendingNode; @@ -46,12 +44,14 @@ describe('Cluster Response Timeout', function () { }); afterEach(function () { - sandbox.restore(); + mock.restoreAll(); + mock.timers.reset(); }); it('should only start timeout after frame is sent', async function () { + mock.timers.enable({ apis: ['setTimeout'] }); const originalSendFrame = sendingNode.endpoints[1].sendFrame.bind(sendingNode.endpoints[1]); - sandbox.stub(sendingNode.endpoints[1], 'sendFrame').callsFake(async (...args) => { + mock.method(sendingNode.endpoints[1], 'sendFrame', async (...args) => { // Delay sendFrame by 150ms (longer than timeout) await new Promise((resolve) => setTimeout(resolve, 150)); return originalSendFrame(...args); @@ -68,11 +68,15 @@ describe('Cluster Response Timeout', function () { // Start the command with 100ms timeout, if timeout starts immediately it will fire before // sendFrame completes and cause the command to fail - await sendingNode.endpoints[1].clusters.onOff.toggle(undefined, { timeout: 100 }); + const toggled = sendingNode.endpoints[1].clusters.onOff.toggle(undefined, { timeout: 100 }); + // Let the stubbed sendFrame reach its fake timer before advancing past the 100ms timeout. + await Promise.resolve(); + mock.timers.tick(150); + await toggled; }); it('should clear timeout when response is received', async function () { - const clearTimeoutSpy = sandbox.spy(global, 'clearTimeout'); + const clearTimeoutSpy = mock.method(global, 'clearTimeout'); receivingNode.endpoints[1].bind( 'onOff', @@ -87,11 +91,14 @@ describe('Cluster Response Timeout', function () { await sendingNode.endpoints[1].clusters.onOff.toggle(undefined, { timeout: 5000 }); // Should have called clearTimeout to prevent timeout firing - assert(clearTimeoutSpy.called, 'clearTimeout should be called when response received'); + assert( + clearTimeoutSpy.mock.callCount() > 0, + 'clearTimeout should be called when response received', + ); }); it('should not wait for response when waitForResponse is false', async function () { - const sendingNodeSendFrameSpy = sandbox.spy(sendingNode, 'sendFrame'); + const sendingNodeSendFrameSpy = mock.method(sendingNode, 'sendFrame'); receivingNode.endpoints[1].bind( 'onOff', @@ -109,7 +116,7 @@ describe('Cluster Response Timeout', function () { assert.strictEqual(result, undefined); // Only one frame should be sent (the command itself) - assert.strictEqual(sendingNodeSendFrameSpy.callCount, 1); + assert.strictEqual(sendingNodeSendFrameSpy.mock.callCount(), 1); }); it('should not resolve with a device-initiated frame that reuses the pending TSN', async function () { diff --git a/test/testConfigureReportingRecordsStruct.js b/test/testConfigureReportingRecordsStruct.js index 588c64c..9c0d519 100644 --- a/test/testConfigureReportingRecordsStruct.js +++ b/test/testConfigureReportingRecordsStruct.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const { ZCLConfigureReportingRecords } = require('../lib/zclFrames'); diff --git a/test/testMfgSpecificCommands.js b/test/testMfgSpecificCommands.js index 35a3830..8bf9f29 100644 --- a/test/testMfgSpecificCommands.js +++ b/test/testMfgSpecificCommands.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const Cluster = require('../lib/Cluster'); const { ZCLDataTypes } = require('../lib/zclTypes'); const BoundCluster = require('../lib/BoundCluster'); @@ -108,7 +109,7 @@ class IkeaBoundCluster extends BoundCluster { } describe('manufacturer specific commands', function () { - it('should execute custom commands', function (done) { + it('should execute custom commands', function (t, done) { let commandsExecuted = 0; const ikeaSceneStepCommand = { mode: 'up', @@ -150,7 +151,7 @@ describe('manufacturer specific commands', function () { describe('global commands', function () { describe('readAttributes', function () { - it('should set correct manufacturerId', function (done) { + it('should set correct manufacturerId', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -162,7 +163,7 @@ describe('manufacturer specific commands', function () { ); node.endpoints[1].clusters['scenes'].readAttributes(['sceneCount']); }); - it('should throw when different manufacturerIds are found', function (done) { + it('should throw when different manufacturerIds are found', function (t, done) { node.endpoints[1].clusters['scenes'] .readAttributes(['sceneCount', 'currentScene']) .catch((err) => { @@ -170,7 +171,7 @@ describe('manufacturer specific commands', function () { done(); }); }); - it('should set correct manufacturerId for multiple attributes', function (done) { + it('should set correct manufacturerId for multiple attributes', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -183,7 +184,7 @@ describe('manufacturer specific commands', function () { node.endpoints[1].clusters['scenes'].readAttributes(['currentScene', 'currentGroup']); }); - it('should not set manufacturerId when zcl attribute is present', function (done) { + it('should not set manufacturerId when zcl attribute is present', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -199,7 +200,7 @@ describe('manufacturer specific commands', function () { }); describe('writeAttributes', function () { - it('should set correct manufacturerId', function (done) { + it('should set correct manufacturerId', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -211,7 +212,7 @@ describe('manufacturer specific commands', function () { ); node.endpoints[1].clusters['scenes'].writeAttributes({ sceneCount: 1 }); }); - it('should throw when different manufacturerIds are found', function (done) { + it('should throw when different manufacturerIds are found', function (t, done) { node.endpoints[1].clusters['scenes'] .writeAttributes({ sceneCount: 1, currentScene: 2 }) .catch((err) => { @@ -219,7 +220,7 @@ describe('manufacturer specific commands', function () { done(); }); }); - it('should set correct manufacturerId for multiple attributes', function (done) { + it('should set correct manufacturerId for multiple attributes', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -232,7 +233,7 @@ describe('manufacturer specific commands', function () { node.endpoints[1].clusters['scenes'].writeAttributes({ currentScene: 1, currentGroup: 2 }); }); - it('should not set manufacturerId when zcl attribute is present', function (done) { + it('should not set manufacturerId when zcl attribute is present', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -248,7 +249,7 @@ describe('manufacturer specific commands', function () { }); describe('configureReporting', function () { - it('should set correct manufacturerId', function (done) { + it('should set correct manufacturerId', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -262,7 +263,7 @@ describe('manufacturer specific commands', function () { sceneCount: { minInterval: 0, maxInterval: 300, minChange: 1 }, }); }); - it('should throw when different manufacturerIds are found', function (done) { + it('should throw when different manufacturerIds are found', function (t, done) { node.endpoints[1].clusters['scenes'] .configureReporting({ sceneCount: { minInterval: 0, maxInterval: 300, minChange: 1 }, @@ -273,7 +274,7 @@ describe('manufacturer specific commands', function () { done(); }); }); - it('should set correct manufacturerId for multiple attributes', function (done) { + it('should set correct manufacturerId for multiple attributes', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -288,7 +289,7 @@ describe('manufacturer specific commands', function () { currentGroup: { minInterval: 0, maxInterval: 300, minChange: 1 }, }); }); - it('should not set manufacturerId when zcl attribute is present', function (done) { + it('should not set manufacturerId when zcl attribute is present', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -307,7 +308,7 @@ describe('manufacturer specific commands', function () { }); describe('readReportingConfiguration', function () { - it('should set correct manufacturerId', function (done) { + it('should set correct manufacturerId', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -319,7 +320,7 @@ describe('manufacturer specific commands', function () { ); node.endpoints[1].clusters['scenes'].readReportingConfiguration(['sceneCount']); }); - it('should throw when different manufacturerIds are found', function (done) { + it('should throw when different manufacturerIds are found', function (t, done) { node.endpoints[1].clusters['scenes'] .readReportingConfiguration(['sceneCount', 'currentScene']) .catch((err) => { @@ -327,7 +328,7 @@ describe('manufacturer specific commands', function () { done(); }); }); - it('should set correct manufacturerId for multiple attributes', function (done) { + it('should set correct manufacturerId for multiple attributes', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ @@ -342,7 +343,7 @@ describe('manufacturer specific commands', function () { 'currentScene', ]); }); - it('should not set manufacturerId when zcl attribute is present', function (done) { + it('should not set manufacturerId when zcl attribute is present', function (t, done) { node.endpoints[1].bind( 'scenes', new IkeaBoundCluster({ diff --git a/test/testNode.js b/test/testNode.js index f1ad27c..d0a9738 100644 --- a/test/testNode.js +++ b/test/testNode.js @@ -1,7 +1,7 @@ 'use strict'; -const assert = require('assert'); -const sinon = require('sinon'); +const { describe, it, before, afterEach, mock } = require('node:test'); +const assert = require('node:assert'); const { createMockNode, createConnectedNodePair } = require('./util'); let { debug } = require('./util'); @@ -14,8 +14,6 @@ const OnOffCluster = require('../lib/clusters/onOff'); debug = debug.extend('test-node'); -const sandbox = sinon.createSandbox(); - describe('Node', function () { let mockNode; let receivingNode; @@ -59,7 +57,7 @@ describe('Node', function () { receivingNode.endpoints[1].getLogId = receivingNodeLog; }); afterEach(function () { - sandbox.restore(); + mock.restoreAll(); }); it('should fail for unbound cluster', async function () { try { @@ -255,20 +253,20 @@ describe('Node', function () { })(), ); - const sendingNodeSendFrameSpy = sandbox.spy(sendingNode, 'sendFrame'); - const receivingNodeSendFrameSpy = sandbox.spy(receivingNode, 'sendFrame'); + const sendingNodeSendFrameSpy = mock.method(sendingNode, 'sendFrame'); + const receivingNodeSendFrameSpy = mock.method(receivingNode, 'sendFrame'); await sendingNode.endpoints[1].clusters['basic'].readAttributes(['modelId'], { disableDefaultResponse: true, }); // 1 frame sent by sending node, the read attributes command - assert.equal(sendingNodeSendFrameSpy.callCount, 1); + assert.equal(sendingNodeSendFrameSpy.mock.callCount(), 1); // 1 frame sent by receiving node, the read attributes response - assert.equal(receivingNodeSendFrameSpy.callCount, 1); + assert.equal(receivingNodeSendFrameSpy.mock.callCount(), 1); // Parse the received frame - const frame = Endpoint.parseFrame(receivingNodeSendFrameSpy.getCall(0).args[2]); + const frame = Endpoint.parseFrame(receivingNodeSendFrameSpy.mock.calls[0].arguments[2]); // 0x01 is read attributes response assert.equal(frame.cmdId, 1); @@ -284,17 +282,17 @@ describe('Node', function () { })(), ); - const sendingNodeSendFrameSpy = sandbox.spy(sendingNode, 'sendFrame'); - const receivingNodeSendFrameSpy = sandbox.spy(receivingNode, 'sendFrame'); + const sendingNodeSendFrameSpy = mock.method(sendingNode, 'sendFrame'); + const receivingNodeSendFrameSpy = mock.method(receivingNode, 'sendFrame'); await sendingNode.endpoints[1].clusters['onOff'].toggle(); // 1 frame sent by sending node, the toggle command - assert.equal(sendingNodeSendFrameSpy.callCount, 1); + assert.equal(sendingNodeSendFrameSpy.mock.callCount(), 1); // 1 frame sent by receiving node, the toggle command default response - assert.equal(receivingNodeSendFrameSpy.callCount, 1); + assert.equal(receivingNodeSendFrameSpy.mock.callCount(), 1); // Parse the received frame - const frame = Endpoint.parseFrame(receivingNodeSendFrameSpy.getCall(0).args[2]); + const frame = Endpoint.parseFrame(receivingNodeSendFrameSpy.mock.calls[0].arguments[2]); // 0x01 is read attributes response assert.equal(frame.cmdId, 11); @@ -303,7 +301,7 @@ describe('Node', function () { it('should respond with default response error when frame could not be handled', async function () { receivingNode.endpoints[1].bind('onOff', new (class extends BoundCluster {})()); - const sendingNodeHandleFrameSpy = sandbox.spy(sendingNode, 'handleFrame'); + const sendingNodeHandleFrameSpy = mock.method(sendingNode, 'handleFrame'); // Execute toggle and expect UNSUP_COMMAND as the command is not implemented (ZCL R8 2.5.12.2) await assert.rejects(sendingNode.endpoints[1].clusters['onOff'].toggle(), { @@ -311,16 +309,16 @@ describe('Node', function () { }); // 1 frame received by sending node, the default response error - assert.equal(sendingNodeHandleFrameSpy.callCount, 1); + assert.equal(sendingNodeHandleFrameSpy.mock.callCount(), 1); // Parse the received frame - const frame = Endpoint.parseFrame(sendingNodeHandleFrameSpy.getCall(0).args[2]); + const frame = Endpoint.parseFrame(sendingNodeHandleFrameSpy.mock.calls[0].arguments[2]); assert.equal(frame.cmdId, 0x0b); assert.equal(frame.data[1], 0x81); // UNSUP_COMMAND }); it('should not respond to a group cast frame', async function () { - const toggledSpy = sandbox.spy(); + const toggledSpy = mock.fn(); receivingNode.endpoints[1].bind( 'onOff', new (class extends BoundCluster { @@ -330,17 +328,17 @@ describe('Node', function () { })(), ); - const receivingNodeSendFrameSpy = sandbox.spy(receivingNode, 'sendFrame'); + const receivingNodeSendFrameSpy = mock.method(receivingNode, 'sendFrame'); // Send toggle command to onOff cluster with group id await receivingNode.handleFrame(1, 6, Buffer.from([0x01, 0x01, 0x02]), { groupId: 0x01 }); - assert.equal(toggledSpy.callCount, 1); + assert.equal(toggledSpy.mock.callCount(), 1); // 0 frames sent by receiving node, no response to group commands - assert.equal(receivingNodeSendFrameSpy.callCount, 0); + assert.equal(receivingNodeSendFrameSpy.mock.callCount(), 0); }); it('should not send a default response to a broadcast frame', async function () { - const toggledSpy = sandbox.spy(); + const toggledSpy = mock.fn(); receivingNode.endpoints[1].bind( 'onOff', new (class extends BoundCluster { @@ -350,22 +348,22 @@ describe('Node', function () { })(), ); - const receivingNodeSendFrameSpy = sandbox.spy(receivingNode, 'sendFrame'); + const receivingNodeSendFrameSpy = mock.method(receivingNode, 'sendFrame'); const meta = { broadcast: true, dstEndpoint: 1 }; await receivingNode.handleFrame(1, 6, Buffer.from([0x01, 0x01, 0x02]), meta); - assert.equal(toggledSpy.callCount, 1); - assert.equal(receivingNodeSendFrameSpy.callCount, 0); + assert.equal(toggledSpy.mock.callCount(), 1); + assert.equal(receivingNodeSendFrameSpy.mock.callCount(), 0); }); it('should not send a default response error to a broadcast frame', async function () { receivingNode.endpoints[1].bind('onOff', new (class extends BoundCluster {})()); - const receivingNodeSendFrameSpy = sandbox.spy(receivingNode, 'sendFrame'); + const receivingNodeSendFrameSpy = mock.method(receivingNode, 'sendFrame'); // toggle is not implemented, so this would otherwise get a default response error await receivingNode.handleFrame(1, 6, Buffer.from([0x01, 0x01, 0x02]), { broadcast: true }); - assert.equal(receivingNodeSendFrameSpy.callCount, 0); + assert.equal(receivingNodeSendFrameSpy.mock.callCount(), 0); }); it('should send a default response to a unicast frame for the broadcast endpoint', async function () { @@ -376,10 +374,10 @@ describe('Node', function () { })(), ); - const receivingNodeSendFrameSpy = sandbox.spy(receivingNode, 'sendFrame'); + const receivingNodeSendFrameSpy = mock.method(receivingNode, 'sendFrame'); await receivingNode.handleFrame(1, 6, Buffer.from([0x01, 0x01, 0x02]), { dstEndpoint: 0xff }); - assert.equal(receivingNodeSendFrameSpy.callCount, 1); + assert.equal(receivingNodeSendFrameSpy.mock.callCount(), 1); }); it('should write attributes', async function () { diff --git a/test/testTrxSequenceNumber.js b/test/testTrxSequenceNumber.js index cdee59b..0fc29ca 100644 --- a/test/testTrxSequenceNumber.js +++ b/test/testTrxSequenceNumber.js @@ -1,15 +1,13 @@ 'use strict'; -const assert = require('assert'); -const sinon = require('sinon'); +const { describe, it, beforeEach, afterEach, mock } = require('node:test'); +const assert = require('node:assert'); const Node = require('../lib/Node'); const BoundCluster = require('../lib/BoundCluster'); const BasicCluster = require('../lib/clusters/basic'); const OnOffCluster = require('../lib/clusters/onOff'); -const sandbox = sinon.createSandbox(); - describe('Transaction sequence number', function () { let receivingNode; let sendingNode; @@ -46,7 +44,7 @@ describe('Transaction sequence number', function () { }); afterEach(function () { - sandbox.restore(); + mock.restoreAll(); }); it('should share one counter across endpoints and clusters', async function () { @@ -60,7 +58,7 @@ describe('Transaction sequence number', function () { }); it('should start at a random value and wrap around', function () { - sandbox.stub(Math, 'random').returns(0.999); + mock.method(Math, 'random', () => 0.999); const node = new Node({ sendFrame: () => {}, endpointDescriptors: [] }); assert.strictEqual(node.nextSeqNr(), 0); diff --git a/test/testZCLError.js b/test/testZCLError.js index 8b00be3..42a36a1 100644 --- a/test/testZCLError.js +++ b/test/testZCLError.js @@ -1,6 +1,7 @@ 'use strict'; -const assert = require('assert'); +const { describe, it } = require('node:test'); +const assert = require('node:assert'); const BoundCluster = require('../lib/BoundCluster'); const Node = require('../lib/Node'); const OnOffCluster = require('../lib/clusters/onOff');