diff --git a/package-lock.json b/package-lock.json index aa8e3a55155..d4f20a3cac4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6841,6 +6841,448 @@ "node": ">=10" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", @@ -13827,19 +14269,6 @@ "node": ">=6" } }, - "node_modules/ansi-gray": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-wrap": "0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ansi-html-community": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", @@ -13876,16 +14305,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ansi-wrap": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", - "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ansis": { "version": "3.17.0", "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", @@ -13895,13 +14314,6 @@ "node": ">=14" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -14199,26 +14611,6 @@ "node": ">= 0.4" } }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", @@ -14285,16 +14677,6 @@ "node": ">=12.0.0" } }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", @@ -18505,6 +18887,48 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -19444,22 +19868,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fancy-log": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -21330,37 +21738,6 @@ "node": ">= 0.10" } }, - "node_modules/gulp-gzip": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/gulp-gzip/-/gulp-gzip-1.4.2.tgz", - "integrity": "sha512-ZIxfkUwk2XmZPTT9pPHrHUQlZMyp9nPhg2sfoeN27mBGpi7OaHnOD+WCN41NXjfJQ69lV1nQ9LLm1hYxx4h3UQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^1.0.1", - "bytes": "^3.0.0", - "fancy-log": "^1.3.2", - "plugin-error": "^1.0.0", - "stream-to-array": "^2.3.0", - "through2": "^2.0.3" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/gulp-gzip/node_modules/ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-wrap": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/gulp-header": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-2.0.12.tgz", @@ -30563,16 +30940,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, - "node_modules/parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/parse-numeric-range": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", @@ -31005,62 +31372,6 @@ "node": ">=16.0.0" } }, - "node_modules/plugin-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", - "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/plugin-error/node_modules/ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-wrap": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/plugin-error/node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/plugin-error/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/portfinder": { "version": "1.0.38", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.38.tgz", @@ -35815,16 +36126,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stream-to-array": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz", - "integrity": "sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.1.0" - } - }, "node_modules/streamx": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", @@ -36597,16 +36898,6 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "license": "MIT" }, - "node_modules/time-stamp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/timers-ext": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", @@ -39802,12 +40093,12 @@ "async-done": "^2.0.0", "chai": "^6.2.2", "concurrently": "^10.0.4", + "esbuild": "^0.28.2", "glob": "^13.0.6", "globals": "^17.7.0", "google-closure-compiler": "^20260803.0.0", "gulp": "^5.0.1", "gulp-concat": "^2.6.1", - "gulp-gzip": "^1.4.2", "gulp-header": "^2.0.12", "gulp-rename": "^2.1.0", "gulp-replace": "^1.1.4", diff --git a/packages/blockly/gulpfile.mjs b/packages/blockly/gulpfile.mjs index b84c7caa042..e240eec1483 100644 --- a/packages/blockly/gulpfile.mjs +++ b/packages/blockly/gulpfile.mjs @@ -38,11 +38,7 @@ import { pack, typings, } from './scripts/gulpfiles/package_tasks.mjs'; -import { - generators, - interactiveMocha, - test, -} from './scripts/gulpfiles/test_tasks.mjs'; +import {generators, test} from './scripts/gulpfiles/test_tasks.mjs'; const clean = parallel(cleanBuildDir, cleanReleaseDir); @@ -75,7 +71,6 @@ export { clean, test, generators as testGenerators, - interactiveMocha, buildAdvancedCompilationTest, docs, typings, diff --git a/packages/blockly/package.json b/packages/blockly/package.json index 98ebc5a1c25..df7eb2ee9b9 100644 --- a/packages/blockly/package.json +++ b/packages/blockly/package.json @@ -21,16 +21,22 @@ }, "imports": { "#core/*": "./build/src/core/*", + "#blocks/*": "./build/src/blocks/*", "#generators/*": "./build/src/generators/*" }, "publishConfig": { "directory": "dist" }, "nx": { - "implicitDependencies": ["!@blockly/block-test"], + "implicitDependencies": [ + "!@blockly/block-test" + ], "targets": { "build": { - "dependsOn": ["package", "^build"] + "dependsOn": [ + "package", + "^build" + ] }, "test": { "dependsOn": [ @@ -42,10 +48,13 @@ "projects": "@blockly/dev-tools", "target": "build" } - ] + ] }, "docs": { - "dependsOn": ["package", "^build"] + "dependsOn": [ + "package", + "^build" + ] } } }, @@ -73,7 +82,7 @@ "test:browser": "npx mocha --config tests/browser/.mocharc.js", "test:mocha:node": "npx mocha --config tests/mocha/.mocharc.node.cjs \"tests/mocha/**/*_test.js\"", "test:generators": "gulp testGenerators", - "test:mocha:interactive": "npm run build && concurrently -n tsc,server \"tsc --watch --preserveWatchOutput --outDir \"build/src\" --declarationDir \"build/declarations\"\" \"gulp interactiveMocha\"", + "test:mocha:interactive": "npm run build && node scripts/prepare_mocha_bundle.mjs && concurrently -n tsc,esbuild,python3 \"tsc --watch --preserveWatchOutput --outDir \"build/src\" --declarationDir \"build/declarations\"\" \"esbuild build/tests/bundle-entry.js --bundle --sourcemap --alias:blockly/core=./build/src/core/blockly.js --alias:blockly/blocks=./build/src/blocks/blocks.js --alias:blockly=./build/src/core/blockly.js --outfile=build/tests/mocha-bundle.js --servedir=. --serve=127.0.0.1:8080 --watch=forever --log-level=warning\" \"python3 -m webbrowser 'http://localhost:8080/tests/mocha/index.html'\"", "test:compile:advanced": "gulp buildAdvancedCompilationTest --debug", "updateGithubPages": "npm ci && gulp updateGithubPages --upstream", "updateGithubPages:staging": "npm ci && gulp updateGithubPages --use-local" @@ -138,12 +147,12 @@ "async-done": "^2.0.0", "chai": "^6.2.2", "concurrently": "^10.0.4", + "esbuild": "^0.28.2", "glob": "^13.0.6", "globals": "^17.7.0", "google-closure-compiler": "^20260803.0.0", "gulp": "^5.0.1", "gulp-concat": "^2.6.1", - "gulp-gzip": "^1.4.2", "gulp-header": "^2.0.12", "gulp-rename": "^2.1.0", "gulp-replace": "^1.1.4", diff --git a/packages/blockly/scripts/gulpfiles/test_tasks.mjs b/packages/blockly/scripts/gulpfiles/test_tasks.mjs index 53af411477a..cef09f693b5 100644 --- a/packages/blockly/scripts/gulpfiles/test_tasks.mjs +++ b/packages/blockly/scripts/gulpfiles/test_tasks.mjs @@ -12,15 +12,11 @@ import asyncDone from 'async-done'; import {spawnSync} from 'child_process'; import * as fs from 'fs'; -import {globSync} from 'glob'; import * as gulp from 'gulp'; -import gzip from 'gulp-gzip'; import * as path from 'path'; import {rimraf} from 'rimraf'; -import {RELEASE_DIR, TEST_TSC_OUTPUT_DIR} from './config.mjs'; - -import {runMochaTestsInBrowser} from '../../tests/mocha/webdriver.js'; +import {TEST_TSC_OUTPUT_DIR} from './config.mjs'; const OUTPUT_DIR = 'build/generators'; const GOLDEN_DIR = 'tests/generators/golden'; @@ -168,146 +164,13 @@ function renamings() { } /** - * Helper method for gzipping file. - * @param {string} file Target file. - * @return {Promise} Asynchronous result. - */ -function gzipFile(file) { - return new Promise((resolve) => { - const name = path.posix.join(RELEASE_DIR, file); - - const stream = gulp.src(name) - .pipe(gzip()) - .pipe(gulp.dest(RELEASE_DIR)); - - stream.on('end', () => { - resolve(); - }); - }); -} - -/** - * Helper method for comparing file size. - * @param {string} file Target file. - * @param {number} expected Expected size. - * @return {number} 0: success / 1: failed. - */ -function compareSize(file, expected) { - const name = path.posix.join(RELEASE_DIR, file); - const compare = Math.floor(expected * 1.1); - const stat = fs.statSync(name); - const size = stat.size; - - if (!compare) { - const message = `Failed: Previous size of ${name} is undefined.`; - console.log(`${BOLD_RED}${message}${ANSI_RESET}`); - return 1; - } - - if (size > compare) { - const message = `Failed: ` + - `Size of ${name} has grown more than 10%. ${size} vs ${expected}`; - console.log(`${BOLD_RED}${message}${ANSI_RESET}`); - return 1; - } - - const message = - `Size of ${name} at ${size} compared to previous ${expected}`; - console.log(`${BOLD_GREEN}${message}${ANSI_RESET}`); - return 0; -} - -/** - * Helper method for zipping the compressed files. - * @return {Promise} Asynchronous result. - */ -function zippingFiles() { - // GZip them for additional size comparisons (keep originals, force - // overwite previously-gzipped copies). - console.log('Zipping the compressed files'); - const gzip1 = gzipFile('blockly_compressed.js'); - const gzip2 = gzipFile('blocks_compressed.js'); - return Promise.all([gzip1, gzip2]); -} - -/** - * Check the sizes of built files for unexpected growth. - * @return {Promise} Asynchronous result. - */ -async function metadata() { - return runTestTask('metadata', async () => { - // Zipping the compressed files. - await zippingFiles(); - // Read expected size from script. - const contents = fs.readFileSync('tests/scripts/check_metadata.sh') - .toString(); - const pattern = /^readonly (?[A-Z_]+)=(?\d+)$/gm; - const matches = contents.matchAll(pattern); - const expected = {}; - for (const match of matches) { - expected[match.groups.key] = match.groups.value; - } - - // Check the sizes of the files. - let failed = 0; - failed += compareSize('blockly_compressed.js', - expected.BLOCKLY_SIZE_EXPECTED); - failed += compareSize('blocks_compressed.js', - expected.BLOCKS_SIZE_EXPECTED); - failed += compareSize('blockly_compressed.js.gz', - expected.BLOCKLY_GZ_SIZE_EXPECTED); - failed += compareSize('blocks_compressed.js.gz', - expected.BLOCKS_GZ_SIZE_EXPECTED); - if (failed > 0) { - throw new Error('Unexpected growth was detected.'); - } - }); -} - -/** - * Generates tests/mocha/test-modules.generated.mjs, - * the list of tests imported by the browser harness (tests/mocha/index.html). - * Keeping it generated from a glob means new *_test.js files are picked up - * automatically and the browser and Node runners stay in sync. - * @return {Promise} Asynchronous result. - */ -function generateMochaIndex() { - return runTestTask('generateMochaIndex', async () => { - const files = globSync('**/*_test.js', {cwd: 'tests/mocha'}).sort(); - - const body = files.map((f) => `import './${f}';`).join('\n') + '\n'; - fs.writeFileSync('tests/mocha/test-modules.generated.mjs', body); - }); -} - -/** - * Run Mocha tests inside a browser. + * Run Mocha tests under Node. * @return {Promise} Asynchronous result. */ function mocha() { - // Run in a subprocess so webdriverio is not loaded inside gulp's asyncDone - // domain (which has been observed to exit the process on CI after ~2s). - return runTestCommand('mocha', 'node tests/mocha/webdriver.js'); + return runTestCommand('mocha', 'npm run test:mocha:node'); } -/** - * Run Mocha tests inside a browser and keep the browser open upon completion. - * @return {Promise} Asynchronous result. - */ -export const interactiveMocha = gulp.series( - generateMochaIndex, - function interactiveMochaRun() { - return runTestTask('interactiveMocha', () => { - return runMochaTestsInBrowser(false).then((result) => { - if (result) { - throw new Error('Mocha tests failed'); - } - console.log('Mocha tests passed'); - }); - }); - }, -); - /** * Helper method for comparison file. * @param {string} file1 First target file. @@ -429,7 +292,6 @@ const tasks = [ // Build must run before the remaining tasks build, renamings, - generateMochaIndex, mocha, generators, typeDefinitions, diff --git a/packages/blockly/scripts/prepare_mocha_bundle.mjs b/packages/blockly/scripts/prepare_mocha_bundle.mjs new file mode 100644 index 00000000000..7cc375db9cf --- /dev/null +++ b/packages/blockly/scripts/prepare_mocha_bundle.mjs @@ -0,0 +1,43 @@ +/** + * @license + * Copyright 2026 Raspberry Pi Foundation + * SPDX-License-Identifier: Apache-2.0 + */ + +import {globSync} from 'glob'; +import * as fs from 'node:fs/promises'; +import {createRequire} from 'node:module'; +import * as path from 'node:path'; + +const require = createRequire(import.meta.url); + +const TEST_DIR = 'tests/mocha'; +const OUT_DIR = 'build/tests'; +const ENTRY_POINT = path.posix.join(OUT_DIR, 'bundle-entry.js'); + +/** + * Writes out a file that loads the in-browser Mocha test bootstrap code and + * the tests themselves, and acts as an entrypoint for esbuild to enumerate and + * bundle the various scripts that make up the test suite. + */ +async function writeEntryPoint() { + const tests = globSync('**/*_test.{js,ts}', {cwd: TEST_DIR}).sort(); + const dir = path.posix.relative(path.posix.dirname(ENTRY_POINT), TEST_DIR); + + await fs.writeFile( + ENTRY_POINT, + [ + `import '${dir}/browser-setup.js';`, + ...tests.map((file) => `import '${dir}/${file}';`), + ].join('\n'), + ); +} + +await fs.mkdir(OUT_DIR, {recursive: true}); +// Resolve and copy Mocha's prebuilt browser distribution. +await fs.copyFile( + require.resolve('mocha/mocha.js'), + path.join(OUT_DIR, 'mocha.js'), +); +// Write out the esbuild entrypoint that imports the test files. +await writeEntryPoint(); diff --git a/packages/blockly/tests/mocha/.mocharc.js b/packages/blockly/tests/mocha/.mocharc.js deleted file mode 100644 index 42378c7fa9d..00000000000 --- a/packages/blockly/tests/mocha/.mocharc.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -module.exports = { - ui: 'tdd', - reporter: 'landing', -}; diff --git a/packages/blockly/tests/mocha/.mocharc.node.cjs b/packages/blockly/tests/mocha/.mocharc.node.cjs index ee49831778b..decf5d7dbee 100644 --- a/packages/blockly/tests/mocha/.mocharc.node.cjs +++ b/packages/blockly/tests/mocha/.mocharc.node.cjs @@ -14,7 +14,8 @@ module.exports = { ui: 'tdd', - reporter: 'spec', + 'node-option': ['enable-source-maps'], + reporter: './tests/mocha/quiet_reporter.cjs', parallel: true, require: ['./tests/mocha/node-setup.mjs'], timeout: 10000, diff --git a/packages/blockly/tests/mocha/browser-setup.js b/packages/blockly/tests/mocha/browser-setup.js new file mode 100644 index 00000000000..36dca422f9a --- /dev/null +++ b/packages/blockly/tests/mocha/browser-setup.js @@ -0,0 +1,40 @@ +/** + * @license + * Copyright 2026 Raspberry Pi Foundation + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Browser harness for the Blockly unit suite. + * + * This is the browser counterpart to node-setup.mjs: it loads Blockly, exposes + * the globals the tests expect, installs the shared DOM fixtures and configures + * Mocha. It is bundled, along with every test module, into + * build/tests/mocha-bundle.js, which is loaded by the test page. + */ + +// Prevent Prettier from reordering imports to load blocks before Blockly. +// organize-imports-ignore +import * as Blockly from '#core/blockly.js'; +import '#blocks/blocks.js'; +import {javascriptGenerator} from '#generators/javascript.js'; +import {config as chaiConfig} from 'chai'; +import sinon from 'sinon'; +import 'mocha/mocha.css'; +import {installFixtures} from './test_helpers/dom_fixtures.js'; +import '@blockly/block-test'; + +chaiConfig.showDiff = false; + +globalThis.Blockly = Blockly; +globalThis.javascriptGenerator = javascriptGenerator; +globalThis.sinon = sinon; + +// The focusable trees, toolbox definitions and #blocklyDiv the tests expect. +// Shared with the Node harness so both run against identical markup. +installFixtures(); + +mocha.setup({ + ui: 'tdd', + failZero: true, +}); diff --git a/packages/blockly/tests/mocha/dropdowndiv_test.js b/packages/blockly/tests/mocha/dropdowndiv_test.js index 0dd2f8af83f..cb6e119bc4d 100644 --- a/packages/blockly/tests/mocha/dropdowndiv_test.js +++ b/packages/blockly/tests/mocha/dropdowndiv_test.js @@ -177,10 +177,8 @@ suite('DropDownDiv', function () { const block = this.setUpBlockWithField(); const field = Array.from(block.getFields())[0]; - const errorMsgRegex = /Cannot read properties of null.+?/; - assert.throws( - () => Blockly.DropDownDiv.show(field, false, 50, 60, 70, 80, false), - errorMsgRegex, + assert.throws(() => + Blockly.DropDownDiv.show(field, false, 50, 60, 70, 80, false), ); }); diff --git a/packages/blockly/tests/mocha/index.html b/packages/blockly/tests/mocha/index.html index bc77544301c..3e790312bb2 100644 --- a/packages/blockly/tests/mocha/index.html +++ b/packages/blockly/tests/mocha/index.html @@ -3,23 +3,7 @@ Mocha Tests for Blockly - - - +
- - - + + + - - diff --git a/packages/blockly/tests/mocha/quiet_reporter.cjs b/packages/blockly/tests/mocha/quiet_reporter.cjs new file mode 100644 index 00000000000..39650065da5 --- /dev/null +++ b/packages/blockly/tests/mocha/quiet_reporter.cjs @@ -0,0 +1,21 @@ +/** + * @license + * Copyright 2026 Raspberry Pi Foundation + * SPDX-License-Identifier: Apache-2.0 + */ + +'use strict'; + +const {reporters, Runner} = require('mocha'); +/** + * Mocha reporter that prints only the run totals, plus the details of any + * failures. + */ +class Quiet extends reporters.Base { + constructor(runner, options) { + super(runner, options); + runner.once(Runner.constants.EVENT_RUN_END, () => this.epilogue()); + } +} + +module.exports = Quiet; diff --git a/packages/blockly/tests/mocha/tsconfig.json b/packages/blockly/tests/mocha/tsconfig.json new file mode 100644 index 00000000000..a85bf44f30d --- /dev/null +++ b/packages/blockly/tests/mocha/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "include": ["**/*", "../../build/declarations/core/any_aliases.d.ts"], + "compilerOptions": { + "checkJs": false, + "noEmit": true + } +} diff --git a/packages/blockly/tests/mocha/webdriver.js b/packages/blockly/tests/mocha/webdriver.js deleted file mode 100644 index 99d3271ca75..00000000000 --- a/packages/blockly/tests/mocha/webdriver.js +++ /dev/null @@ -1,268 +0,0 @@ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Node.js script to run Mocha tests in Chrome, via webdriver. - */ -const webdriverio = require('webdriverio'); -const fs = require('fs'); -const path = require('path'); -const {posixPath} = require('../../scripts/helpers'); -const { - getWebdriverOptions, - runBrowserTestMain, -} = require('../scripts/webdriver_helpers.js'); - -/** @const {number} Max time to wait for the browser test page to finish. */ -const PAGE_TIMEOUT_MS = 120000; - -/** @const {number} Max time to wait for browser.deleteSession(). */ -const DELETE_SESSION_TIMEOUT_MS = 15000; - -/** - * Ensure browser test imports that use ../../node_modules/* continue to work - * when npm hoists dependencies to the repository root node_modules dir - */ -function ensureWorkspaceNodeModulesLinks() { - const workspaceNodeModules = path.resolve(__dirname, '../../node_modules'); - const rootNodeModules = path.resolve(__dirname, '../../../../node_modules'); - const packages = ['mocha', 'sinon', 'chai', '@blockly/dev-tools']; - - for (const pkg of packages) { - const workspacePkgPath = path.join(workspaceNodeModules, pkg); - const rootPkgPath = path.join(rootNodeModules, pkg); - - if (fs.existsSync(workspacePkgPath) || !fs.existsSync(rootPkgPath)) { - continue; - } - - fs.mkdirSync(path.dirname(workspacePkgPath), {recursive: true}); - fs.symlinkSync(rootPkgPath, workspacePkgPath, 'dir'); - } -} - -/** - * Run a promise with a timeout. - * @param {!Promise} promise The promise to run. - * @param {number} timeoutMs Timeout in milliseconds. - * @param {string} label Description for timeout errors. - * @return {!Promise} - */ -function withTimeout(promise, timeoutMs, label) { - return Promise.race([ - promise, - new Promise((_, reject) => { - setTimeout(() => { - reject(new Error(label + ' timed out after ' + timeoutMs + 'ms')); - }, timeoutMs); - }), - ]); -} - -/** - * Close the browser session, with a timeout so CI cannot hang forever. - * @param {?Object} browser The webdriverio browser instance. - */ -async function closeBrowserSession(browser) { - if (!browser) { - return; - } - try { - await withTimeout( - browser.deleteSession(), - DELETE_SESSION_TIMEOUT_MS, - 'browser.deleteSession()', - ); - } catch (e) { - console.warn('Failed to close browser session:', e.message); - } -} - -/** - * Enable focus emulation via CDP. Wrapped in a timeout because this call - * has been observed to hang intermittently on CI. - * @param {!Object} browser The webdriverio browser instance. - */ -async function enableFocusEmulation(browser) { - const timeoutMs = 10000; - try { - await withTimeout( - (async () => { - const puppeteer = await browser.getPuppeteer(); - await browser.call(async () => { - const page = (await puppeteer.pages())[0]; - const session = await page.createCDPSession(); - await session.send('Emulation.setFocusEmulationEnabled', { - enabled: true, - }); - }); - })(), - timeoutMs, - 'Focus emulation setup', - ); - } catch (e) { - console.warn('Focus emulation setup failed (continuing anyway):', - e.message); - } -} - -/** - * Print browser-side load diagnostics when tests fail to start or finish. - * @param {!Object} browser The webdriverio browser instance. - */ -async function printBrowserLoadDiagnostics(browser) { - if (!browser) { - return; - } - try { - await withTimeout((async () => { - console.log('============Blockly Mocha Load Diagnostics================'); - const loadStatus = await browser.$('#loadStatus') - .getAttribute('data-status'); - console.log('Load status:', loadStatus ?? 'unknown'); - const loadErrors = await browser.execute(() => { - return window.__blocklyTestLoadState?.errors ?? []; - }); - if (loadErrors.length) { - console.log('Captured load errors:'); - for (const error of loadErrors) { - console.log(' ' + error); - } - } - const failureMessagesEls = await browser.$$('#failureMessages p'); - for (const el of failureMessagesEls) { - const messageHtml = await el.getHTML(); - console.log(messageHtml.replace(/<\/?p>/g, '')); - } - if (loadStatus === 'pending' || loadStatus === 'imports-complete') { - console.log( - 'Blockly or test modules may not have loaded completely.'); - } - console.log('=========================================================='); - })(), 10000, 'Load diagnostics'); - } catch (diagErr) { - console.warn('Could not collect browser diagnostics:', diagErr.message); - } -} - -/** - * Wait until Mocha reports results or the page reports a load failure. - * @param {!Object} browser The webdriverio browser instance. - */ -async function waitForTestCompletion(browser) { - await browser.waitUntil(async() => { - const failureCount = await browser.$('#failureCount') - .getAttribute('tests_failed'); - if (failureCount !== 'unset') { - return true; - } - const loadStatus = await browser.$('#loadStatus') - .getAttribute('data-status'); - return loadStatus === 'failed'; - }, { - timeout: PAGE_TIMEOUT_MS, - timeoutMsg: 'Timed out waiting for Mocha tests to finish. Blockly may ' + - 'have failed to load; see load diagnostics below.', - }); -} - -/** - * Runs the Mocha tests in this directory in Chrome. It uses webdriverio to - * launch Chrome and load index.html. Outputs a summary of the test results - * to the console. - * - * @param {boolean} exitOnCompletetion True if the browser should automatically - * quit after tests have finished running. - * @return {number} 0 on success, 1 on failure. - */ -async function runMochaTestsInBrowser(exitOnCompletion = true) { - // Gulp may pass a done callback as the first argument. - if (typeof exitOnCompletion === 'function') { - exitOnCompletion = true; - } - return runMochaTestsInBrowserImpl(exitOnCompletion); -} - -/** - * @param {boolean} exitOnCompletion True if the browser should quit after tests. - * @return {number} 0 on success, 1 on failure. - */ -async function runMochaTestsInBrowserImpl(exitOnCompletion) { - ensureWorkspaceNodeModulesLinks(); - - const options = getWebdriverOptions(); - - const url = 'file://' + posixPath(__dirname) + '/index.html'; - console.log('Starting webdriverio...'); - let browser; - let numOfFailure = '1'; - let pageLoaded = false; - let testsCompleted = false; - try { - browser = await webdriverio.remote(options); - console.log('Loading URL: ' + url); - await browser.url(url); - pageLoaded = true; - - // Focus emulation via CDP has hung on CI; skip there. - if (!process.env.CI) { - await enableFocusEmulation(browser); - } - - await waitForTestCompletion(browser); - testsCompleted = true; - - const elem = await browser.$('#failureCount'); - numOfFailure = await elem.getAttribute('tests_failed'); - - if (numOfFailure > 0) { - console.log('============Blockly Mocha Test Failures================'); - const failureMessagesEls = await browser.$$('#failureMessages p'); - if (!failureMessagesEls.length) { - console.log('There is at least one test failure, but no messages ' + - 'reported. Mocha may be failing because no tests are being run.'); - } - for (const el of failureMessagesEls) { - const messageHtml = await el.getHTML(); - console.log(messageHtml.replace(/<\/?p>/g, '')); - } - } - } catch (e) { - await printBrowserLoadDiagnostics(browser); - throw e; - } finally { - if (exitOnCompletion) { - await closeBrowserSession(browser); - } - } - - if (!pageLoaded) { - throw new Error( - 'Mocha browser tests did not start: Chrome failed to load the test ' + - 'page. Check chromedriver and Chrome setup.', - ); - } - if (!testsCompleted) { - throw new Error( - 'Mocha browser tests did not finish: the test page never reported ' + - 'results.', - ); - } - - console.log('============Blockly Mocha Test Summary================='); - console.log(numOfFailure + ' tests failed'); - console.log('============Blockly Mocha Test Summary================='); - if (parseInt(numOfFailure) !== 0) { - return 1; - } - return 0; -} - -if (require.main === module) { - runBrowserTestMain(() => runMochaTestsInBrowser()); -} - -module.exports = {runMochaTestsInBrowser}; diff --git a/packages/blockly/tsconfig.json b/packages/blockly/tsconfig.json index 840d73a82c6..659c02029cd 100644 --- a/packages/blockly/tsconfig.json +++ b/packages/blockly/tsconfig.json @@ -5,6 +5,8 @@ // normally they are ignored as source files "allowJs": true, + "incremental": true, + // Generate d.ts files and sourcemaps. "declaration": true, // Generate declaration maps used for api-extractor