diff --git a/package-lock.json b/package-lock.json index dd7b8fff94a..24549985e2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "playcanvas", - "version": "1.69.0-dev", + "version": "1.70.0-dev", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "playcanvas", - "version": "1.69.0-dev", + "version": "1.70.0-dev", "license": "MIT", "dependencies": { "@types/webxr": "^0.5.7", @@ -29,9 +29,11 @@ "chai": "^4.3.10", "eslint": "^8.52.0", "fflate": "^0.8.1", + "global-jsdom": "^24.0.0", "jsdoc": "^4.0.2", "jsdoc-tsimport-plugin": "^1.0.5", "jsdoc-typeof-plugin": "^1.0.0", + "jsdom": "^24.0.0", "karma": "^6.4.2", "karma-chrome-launcher": "^3.2.0", "karma-mocha": "2.0.1", @@ -2346,6 +2348,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2598,6 +2612,12 @@ "node": "*" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -3237,6 +3257,18 @@ "node": ">=0.1.90" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -3421,12 +3453,37 @@ "node": ">= 8" } }, + "node_modules/cssstyle": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz", + "integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==", + "dev": true, + "dependencies": { + "rrweb-cssom": "^0.6.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", "dev": true }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/date-format": { "version": "4.0.14", "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", @@ -3465,6 +3522,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, "node_modules/deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -3541,6 +3604,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4511,6 +4583,20 @@ "node": ">=8.0.0" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -4683,6 +4769,18 @@ "node": ">=10.13.0" } }, + "node_modules/global-jsdom": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/global-jsdom/-/global-jsdom-24.0.0.tgz", + "integrity": "sha512-CARBUWkqZ3O9VOc2PIVE5kQpdQeJh9eF9kQ7zSeNtmqx5vAFDKMr9XnDt1epVMMrz1s9uK/yFCa4HLwpa6TcPA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "jsdom": ">=24 <25" + } + }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -4842,6 +4940,18 @@ "he": "bin/he" } }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -4887,6 +4997,32 @@ "node": ">=8.0.0" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -5203,6 +5339,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -5516,6 +5658,67 @@ "node": ">=8" } }, + "node_modules/jsdom": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.0.0.tgz", + "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==", + "dev": true, + "dependencies": { + "cssstyle": "^4.0.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.7", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.16.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -6551,6 +6754,12 @@ "node": ">=8" } }, + "node_modules/nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", + "dev": true + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6763,6 +6972,30 @@ "node": ">=6" } }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -6866,6 +7099,12 @@ "node": ">= 0.8.0" } }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, "node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -6896,6 +7135,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7290,6 +7535,12 @@ "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", "dev": true }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", + "dev": true + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7371,6 +7622,18 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -7920,6 +8183,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, "node_modules/terser": { "version": "5.22.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz", @@ -8000,6 +8269,60 @@ "node": ">=0.6" } }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tr46/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -8387,6 +8710,16 @@ "node": ">=6" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -8440,6 +8773,73 @@ "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dev": true, + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8609,6 +9009,21 @@ "node": ">= 6" } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "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 588cd6ef54e..8bce340c765 100644 --- a/package.json +++ b/package.json @@ -123,9 +123,11 @@ "chai": "^4.3.10", "eslint": "^8.52.0", "fflate": "^0.8.1", + "global-jsdom": "^24.0.0", "jsdoc": "^4.0.2", "jsdoc-tsimport-plugin": "^1.0.5", "jsdoc-typeof-plugin": "^1.0.0", + "jsdom": "^24.0.0", "karma": "^6.4.2", "karma-chrome-launcher": "^3.2.0", "karma-mocha": "2.0.1", diff --git a/test/fixtures.mjs b/test/fixtures.mjs index 049db52d3c9..dea35e4c36d 100644 --- a/test/fixtures.mjs +++ b/test/fixtures.mjs @@ -2,6 +2,8 @@ import handler from 'serve-handler'; import http from 'http'; import XMLHttpRequest from 'xhr2'; +import 'global-jsdom/register'; // eslint-disable-line import/no-unresolved,import/extensions + let server; export const mochaGlobalSetup = () => { diff --git a/test/platform/input/keyboard.test.mjs b/test/platform/input/keyboard.test.mjs new file mode 100644 index 00000000000..cfb6a0c00ff --- /dev/null +++ b/test/platform/input/keyboard.test.mjs @@ -0,0 +1,139 @@ +import { Keyboard } from '../../../src/platform/input/keyboard.js'; +import { EVENT_KEYDOWN, EVENT_KEYUP, KEY_UP } from '../../../src/platform/input/constants.js'; + +import { expect } from 'chai'; + +describe('Keyboard', function () { + + /** @type { Keyboard } */ + let keyboard; + + beforeEach(function () { + keyboard = new Keyboard(); + keyboard.attach(window); + }); + + afterEach(function () { + keyboard.detach(); + }); + + describe('#constructor', function () { + + it('should create a new instance', function () { + expect(keyboard).to.be.an.instanceOf(Keyboard); + }); + + }); + + describe('#isPressed', function () { + + it('should return false for a key that is not pressed', function () { + expect(keyboard.isPressed(KEY_UP)).to.be.false; + }); + + it('should return true for a key that is pressed', function () { + const keyDownEvent = new KeyboardEvent('keydown', { + keyCode: 38 // Up arrow + }); + window.dispatchEvent(keyDownEvent); + + expect(keyboard.isPressed(KEY_UP)).to.be.true; + + keyboard.update(); + + expect(keyboard.isPressed(KEY_UP)).to.be.true; + + const keyUpEvent = new KeyboardEvent('keyup', { + keyCode: 38 // Up arrow + }); + window.dispatchEvent(keyUpEvent); + + expect(keyboard.isPressed(KEY_UP)).to.be.false; + }); + + }); + + describe('#on', function () { + + it('should handle keydown events', function (done) { + keyboard.on(EVENT_KEYDOWN, function (event) { + expect(event.key).to.equal(KEY_UP); + expect(event.element).to.equal(window); + expect(event.event).to.be.an.instanceOf(KeyboardEvent); + + done(); + }); + + const keyDownEvent = new KeyboardEvent('keydown', { + keyCode: 38 // Up arrow + }); + window.dispatchEvent(keyDownEvent); + }); + + it('should handle keyup events', function (done) { + keyboard.on(EVENT_KEYUP, function (event) { + expect(event.key).to.equal(KEY_UP); + expect(event.element).to.equal(window); + expect(event.event).to.be.an.instanceOf(KeyboardEvent); + + done(); + }); + + const keyUpEvent = new KeyboardEvent('keyup', { + keyCode: 38 // Up arrow + }); + window.dispatchEvent(keyUpEvent); + }); + + }); + + describe('#wasPressed', function () { + + it('should return false for a key that was not pressed', function () { + expect(keyboard.wasPressed(KEY_UP)).to.be.false; + }); + + it('should return true for a key that was pressed since the last update', function () { + const keyDownEvent = new KeyboardEvent('keydown', { + keyCode: 38 // Up arrow + }); + window.dispatchEvent(keyDownEvent); + + expect(keyboard.wasPressed(KEY_UP)).to.be.true; + + keyboard.update(); + + expect(keyboard.wasPressed(KEY_UP)).to.be.false; + }); + + }); + + describe('#wasReleased', function () { + + it('should return false for a key that was not released', function () { + expect(keyboard.wasReleased(KEY_UP)).to.be.false; + }); + + it('should return true for a key that was released since the last update', function () { + const keyDownEvent = new KeyboardEvent('keydown', { + keyCode: 38 // Up arrow + }); + window.dispatchEvent(keyDownEvent); + + keyboard.update(); + + const keyUpEvent = new KeyboardEvent('keyup', { + keyCode: 38 // Up arrow + }); + window.dispatchEvent(keyUpEvent); + + expect(keyboard.wasReleased(KEY_UP)).to.be.true; + + keyboard.update(); + + expect(keyboard.wasReleased(KEY_UP)).to.be.false; + }); + + }); + +}); diff --git a/test/platform/input/mouse.test.mjs b/test/platform/input/mouse.test.mjs new file mode 100644 index 00000000000..9d0eb29f058 --- /dev/null +++ b/test/platform/input/mouse.test.mjs @@ -0,0 +1,140 @@ +import { Mouse } from '../../../src/platform/input/mouse.js'; +import { + EVENT_MOUSEDOWN, EVENT_MOUSEUP, + MOUSEBUTTON_LEFT, MOUSEBUTTON_MIDDLE, MOUSEBUTTON_RIGHT +} from '../../../src/platform/input/constants.js'; + +import { expect } from 'chai'; + +const buttons = [MOUSEBUTTON_LEFT, MOUSEBUTTON_MIDDLE, MOUSEBUTTON_RIGHT]; + +// Mock the _getTargetCoords method, otherwise it returns null +Mouse.prototype._getTargetCoords = function (event) { + return { x: 0, y: 0 }; +}; + +describe('Mouse', function () { + + /** @type { Mouse } */ + let mouse; + + beforeEach(function () { + mouse = new Mouse(document.body); + }); + + afterEach(function () { + mouse.detach(); + }); + + describe('#constructor', function () { + + it('should create a new instance', function () { + expect(mouse).to.be.an.instanceOf(Mouse); + }); + + }); + + describe('#isPressed', function () { + + it('should return false for all buttons by default', function () { + for (const button of buttons) { + expect(mouse.isPressed(button)).to.be.false; + } + }); + + it('should return true for a mouse button that is pressed', function () { + for (const button of buttons) { + const mouseDownEvent = new MouseEvent('mousedown', { button }); + window.dispatchEvent(mouseDownEvent); + + expect(mouse.isPressed(button)).to.be.true; + + const mouseUpEvent = new MouseEvent('mouseup', { button }); + window.dispatchEvent(mouseUpEvent); + + expect(mouse.isPressed(button)).to.be.false; + } + }); + + }); + + describe('#on', function () { + + it('should handle mousedown events', function (done) { + mouse.on(EVENT_MOUSEDOWN, function (event) { + expect(event.button).to.equal(MOUSEBUTTON_LEFT); + expect(event.event).to.be.an.instanceOf(MouseEvent); + + done(); + }); + + const mouseDownEvent = new MouseEvent('mousedown', { button: 0 }); + window.dispatchEvent(mouseDownEvent); + }); + + it('should handle mouseup events', function (done) { + mouse.on(EVENT_MOUSEUP, function (event) { + expect(event.button).to.equal(MOUSEBUTTON_LEFT); + expect(event.event).to.be.an.instanceOf(MouseEvent); + + done(); + }); + + const mouseUpEvent = new MouseEvent('mouseup', { button: 0 }); + window.dispatchEvent(mouseUpEvent); + }); + + }); + + describe('#wasPressed', function () { + + it('should return false for all buttons by default', function () { + for (const button of buttons) { + expect(mouse.wasPressed(button)).to.be.false; + } + }); + + it('should return true for a mouse button that was pressed', function () { + for (const button of buttons) { + const mouseDownEvent = new MouseEvent('mousedown', { button }); + window.dispatchEvent(mouseDownEvent); + + expect(mouse.wasPressed(button)).to.be.true; + + mouse.update(); + + expect(mouse.wasPressed(button)).to.be.false; + } + }); + + }); + + describe('#wasReleased', function () { + + it('should return false for all buttons by default', function () { + for (const button of buttons) { + expect(mouse.wasReleased(button)).to.be.false; + } + }); + + it('should return true for a mouse button that was released', function () { + for (const button of buttons) { + const mouseDownEvent = new MouseEvent('mousedown', { button }); + window.dispatchEvent(mouseDownEvent); + + mouse.update(); + + const mouseUpEvent = new MouseEvent('mouseup', { button }); + window.dispatchEvent(mouseUpEvent); + + expect(mouse.wasReleased(button)).to.be.true; + + mouse.update(); + + expect(mouse.wasReleased(button)).to.be.false; + } + }); + + }); + +}); diff --git a/tests/platform/input/simulate_event.js b/tests/platform/input/simulate_event.js deleted file mode 100644 index f6110ce303c..00000000000 --- a/tests/platform/input/simulate_event.js +++ /dev/null @@ -1,84 +0,0 @@ -function simulate(element, eventName) { - var defaults = extend({}, defaultOptions); - var options = extend(defaults, arguments[2] || {}); - var oEvent, eventType = null; - - for (var name in eventMatchers) { - if (eventMatchers[name].test(eventName)) { eventType = name; break; } - } - - if (!eventType) { - throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported'); - } - - if (document.createEvent) { - oEvent = document.createEvent(eventType); - if (eventType == 'HTMLEvents') { - oEvent.initEvent(eventName, options.bubbles, options.cancelable); - } - else if (eventType === 'MouseEvents') { - oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView, options.detail, options.pointerX, options.pointerY, options.pointerX, options.pointerY, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element); - } else if (eventType === 'KeyboardEvent') { - defaults = extend({}, defaultKeyboardOptions); - options = extend(defaults, arguments[2] || {}); - if (oEvent.initKeyEvent) { - oEvent.initKeyEvent(eventName, options.bubbles, options.cancelable, document.defaultView, options.ctrl, options.shift, options.alt, options.meta, options.keyCode, options.charCode); - } else { - oEvent = document.createEvent("Events"); - // initKeyboardEvent doesn't work property in Chrome, fudge it using plain event - oEvent.initEvent(eventName, options.bubbles, options.cancelable); - oEvent.keyCode = options.keyCode; - oEvent.which = options.keyCode; - } - } - - element.dispatchEvent(oEvent); - } else { - options.clientX = options.pointerX; - options.clientY = options.pointerY; - var evt = document.createEventObject(); - oEvent = extend(evt, options); - element.fireEvent('on' + eventName, oEvent); - } - return element; -} - -function extend(destination, source) { - for (var property in source) - destination[property] = source[property]; - return destination; -} - -var eventMatchers = { - 'HTMLEvents': /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/, - 'MouseEvents': /^(?:click|dblclick|mouse(?:down|up|over|move|out|wheel))$/, - 'KeyboardEvent': /^(?:keydown|keypress|keyup)$/ -}; - -var defaultOptions = { - pointerX: 16, - pointerY: 16, - detail: 0, - button: 0, - ctrlKey: false, - altKey: false, - shiftKey: false, - metaKey: false, - bubbles: true, - cancelable: true -}; - -var defaultKeyboardOptions = { - bubbles: true, - cancelable: true, - charCode: 0, - keyCode: 0, - location: 0, - modifiers: '', - repeat: false, - locale: '', - ctrl: false, - alt: false, - shift: false, - meta: false -}; diff --git a/tests/platform/input/test_input.js b/tests/platform/input/test_input.js deleted file mode 100644 index 2a449d88094..00000000000 --- a/tests/platform/input/test_input.js +++ /dev/null @@ -1,6 +0,0 @@ -describe('pc.input', function () { - it("Namespace exists", function () { - expect(pc.input).to.exist; - }); -}); - diff --git a/tests/platform/input/test_keyboard.js b/tests/platform/input/test_keyboard.js deleted file mode 100644 index 023e8b812fc..00000000000 --- a/tests/platform/input/test_keyboard.js +++ /dev/null @@ -1,218 +0,0 @@ - -describe('pc.keyboard', function () { - var k; - - beforeEach(function () { - k = new pc.Keyboard(); - k.attach(document.body); - }); - - afterEach(function () { - k.detach(); - delete k; - }); - - function pressAndRelease(keyCode) { - simulate(document.body, 'keydown', { - keyCode: keyCode - }); - simulate(document.body, 'keypress', { - keyCode: keyCode - }); - simulate(document.body, 'keyup', { - keyCode: keyCode - }); - } - - function press(keyCode) { - simulate(document.body, 'keydown', { - keyCode: keyCode - }); - - simulate(document.body, 'keypress', { - keyCode: keyCode - }); - } - - function pressSpecialChar(keyCode) { - simulate(document.body, 'keydown', { - keyCode: keyCode - }); - } - - function pressAndHold(keyCode) { - press(keyCode); - press(keyCode); - } - - function release(keyCode) { - simulate(document.body, 'keyup', { - keyCode: keyCode - }); - } - - it("Object Exists", function () { - ok(pc.Keyboard); - }); - - it("Keydown A", function () { - k.on(pc.EVENT_KEYDOWN, function (event) { - equal(event.key, pc.KEY_A); - equal(event.element, document.body); - ok(event.event); - }); - - simulate(document.body, 'keydown', { - keyCode: pc.KEY_A - }); - }); - - it("Keydown Left arrow", function () { - k.on(pc.EVENT_KEYDOWN, function (event) { - equal(event.key, pc.KEY_LEFT); - equal(event.element, document.body); - ok(event.event); - }); - - simulate(document.body, 'keydown', { - keyCode: pc.KEY_LEFT - }); - }); - - it("Keydown F1", function () { - k.on(pc.EVENT_KEYDOWN, function (event) { - equal(event.key, pc.KEY_F1); - equal(event.element, document.body); - ok(event.event); - }); - - simulate(document.body, 'keydown', { - keyCode: pc.KEY_F1 - }); - }); - - it("Keyup A", function () { - k.on(pc.EVENT_KEYUP, function (event) { - equal(event.key, pc.KEY_A); - equal(event.element, document.body); - ok(event.event); - }); - - simulate(document.body, 'keyup', { - keyCode: pc.KEY_A - }); - }); - - it("Keyup Left arrow", function () { - k.on(pc.EVENT_KEYUP, function (event) { - equal(event.key, pc.KEY_LEFT); - equal(event.element, document.body); - ok(event.event); - }); - - simulate(document.body, 'keyup', { - keyCode: pc.KEY_LEFT - }); - }); - - it("Keyup F1", function () { - k.on(pc.EVENT_KEYUP, function (event) { - equal(event.key, pc.KEY_F1); - equal(event.element, document.body); - ok(event.event); - }); - - simulate(document.body, 'keyup', { - keyCode: pc.KEY_F1 - }); - }); - - it("isPressed", function () { - press(pc.KEY_A); - ok(k.isPressed(pc.KEY_A)); - k.update(); - ok(k.isPressed(pc.KEY_A)); - }); - - it("isPressed: released", function () { - pressAndRelease(pc.KEY_A); - equal(k.isPressed(pc.KEY_A), false); - }); - - it("isPressed: hold", function () { - pressAndHold(pc.KEY_A); - equal(k.isPressed(pc.KEY_A), true); - }); - - it("wasPressed", function () { - press(pc.KEY_A); - equal(k.wasPressed(pc.KEY_A), true); - k.update(); - equal(k.wasPressed(pc.KEY_A), false); - }); - - it("wasReleased", function () { - press(pc.KEY_A); - equal(k.wasReleased(pc.KEY_A), false); - k.update(); - release(pc.KEY_A); - equal(k.wasReleased(pc.KEY_A), true); - }); - - // it("toKeyIdentifier: output is uppercase", function () { - // var k = new pc.Keyboard(document.body); - // var id = k.toKeyIdentifier(pc.KEY_N); - // equal(id, "U+004E"); - // }); - - // it("toKeyIdentifier: Upper and lowercase", function () { - // var k = new pc.Keyboard(document.body), - // lower = pc.string.ASCII_LOWERCASE, - // upper = pc.string.ASCII_UPPERCASE, - // index, - // id; - - // for(index = 0; index < lower.length; index++) { - // id = k.toKeyIdentifier(lower.charCodeAt(index)); - // equal(id.length, 6); - // equal(String.fromCharCode(parseInt(id.slice(2), 16)), lower[index]); - - // id = k.toKeyIdentifier(upper.charCodeAt(index)); - // equal(id.length, 6); - // equal(String.fromCharCode(parseInt(id.slice(2), 16)), upper[index]); - // } - - // }); - - // it("toKeyIdentifier: Special Keys", function () { - // var codes = { - // "Tab": 9, - // "Shift": 16, - // "Control": 17, - // "Alt": 18, - // "Escape": 27, - - // "Left": 37, - // "Up": 38, - // "Right": 39, - // "Down": 40, - - // "Delete": 46 - // }, - // code, - // id, - // k = new pc.Keyboard(document.body); - - // for(code in codes) { - // id = k.toKeyIdentifier(codes[code]); - // equal(id, code); - // } - - - // }); - - -}); - - - diff --git a/tests/platform/input/test_mouse.js b/tests/platform/input/test_mouse.js deleted file mode 100644 index 4947c14bf8e..00000000000 --- a/tests/platform/input/test_mouse.js +++ /dev/null @@ -1,136 +0,0 @@ -describe('pc.mouse', function () { - var m; - - beforeEach(function () { - this.prevDocumentElementStyle = document.documentElement.style; - this.prevBodyStyle = document.body.style; - - document.documentElement.style = "height: 100%;"; - document.body.style = "height: 100%;"; - - m = new pc.Mouse(); - m.attach(document.body); - }); - - afterEach( function () { - document.documentElement.style = this.prevDocumentElementStyle; - document.body.style = this.prevBodyStyle; - - m.detach(document.body); - }); - - it("Object exists", function () { - ok(pc.Mouse); - }); - - it("mousedown: middlebutton", function () { - m.on(pc.EVENT_MOUSEDOWN, function (event) { - equal(event.x, 8); - equal(event.y, 8); - equal(event.dx, 8); - equal(event.dy, 8); - equal(event.button, pc.MOUSEBUTTON_MIDDLE); - equal(event.buttons[pc.MOUSEBUTTON_LEFT], false); - equal(event.buttons[pc.MOUSEBUTTON_MIDDLE], true); - equal(event.buttons[pc.MOUSEBUTTON_RIGHT], false); - equal(event.element, document.body); - ok(event.event); - }); - - simulate(document.body, 'mousedown', { - button: pc.MOUSEBUTTON_MIDDLE - }); - }); - - it("mouseup: middlebutton", function () { - m.on(pc.EVENT_MOUSEUP, function (event) { - equal(event.x, 8); - equal(event.y, 8); - equal(event.dx, 8); - equal(event.dy, 8); - equal(event.button, pc.MOUSEBUTTON_MIDDLE); - equal(event.buttons[pc.MOUSEBUTTON_LEFT], false); - equal(event.buttons[pc.MOUSEBUTTON_MIDDLE], false); - equal(event.buttons[pc.MOUSEBUTTON_RIGHT], false); - equal(event.element, document.body); - ok(event.event); - }); - - simulate(document.body, 'mouseup', { - button: pc.MOUSEBUTTON_MIDDLE - }); - }); - - it("mousemove", function () { - // move before event bound - simulate(document.body, 'mousemove', { - pointerX: 16, - pointerY: 16 - }); - - m.on(pc.EVENT_MOUSEMOVE, function (event) { - equal(event.x, 24); - equal(event.y, 24); - equal(event.dx, 16); - equal(event.dy, 16); - equal(event.button, pc.MOUSEBUTTON_NONE); - equal(event.buttons[pc.MOUSEBUTTON_LEFT], false); - equal(event.buttons[pc.MOUSEBUTTON_MIDDLE], false); - equal(event.buttons[pc.MOUSEBUTTON_RIGHT], false); - equal(event.element, document.body); - ok(event.event); - }); - - simulate(document.body, 'mousemove', { - pointerX: 32, - pointerY: 32 - }); - }); - - it("mousewheel: fires", function () { - m.on(pc.EVENT_MOUSEWHEEL, function (event) { - equal(event.x, 8); - equal(event.y, 8); - equal(event.dx, 8); - equal(event.dy, 8); - equal(event.wheel, -120); - equal(event.button, pc.MOUSEBUTTON_NONE); - equal(event.buttons[pc.MOUSEBUTTON_LEFT], false); - equal(event.buttons[pc.MOUSEBUTTON_MIDDLE], false); - equal(event.buttons[pc.MOUSEBUTTON_RIGHT], false); - ok(event.event); - equal(event.element, document.body); - }); - - simulate(document.body, 'mousewheel', { - detail: 120 - }); - }); - - it("isPressed", function () { - m.update(); - simulate(document.body, 'mousedown'); - equal(m.isPressed(pc.MOUSEBUTTON_LEFT), true); - m.update(); - equal(m.isPressed(pc.MOUSEBUTTON_LEFT), true); - }); - - it("wasPressed", function () { - m.update(); - simulate(document.body, 'mousedown'); - equal(m.wasPressed(pc.MOUSEBUTTON_LEFT), true); - m.update(); - equal(m.wasPressed(pc.MOUSEBUTTON_LEFT), false); - }); - - it("wasReleased", function () { - m.update(); - simulate(document.body, 'mousedown'); - equal(m.wasReleased(pc.MOUSEBUTTON_LEFT), false); - m.update(); - simulate(document.body, 'mouseup'); - equal(m.wasReleased(pc.MOUSEBUTTON_LEFT), true); - }); - -}); -