From 50930013bd2c1b601565970fe1ad1d5ba0819ae4 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Feb 2025 18:05:14 +0100 Subject: [PATCH 01/15] Install Vitest --- package.json | 4 +- yarn.lock | 535 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 537 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8d778902..605b9d1e 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,10 @@ "description": "Webpack Encore is a simpler way to integrate Webpack into your application", "main": "index.js", "scripts": { - "test": "yarn run test:main && yarn run test:persistent-cache", + "test": "yarn run vitest && yarn run test:main && yarn run test:persistent-cache", "test:main": "mocha --reporter spec test --recursive --ignore test/persistent-cache/*", "test:persistent-cache": "node run-persistent-tests", + "test:vitest": "vitest", "lint": "eslint lib test index.js .eslintrc.js --report-unused-disable-directives --max-warnings=0", "travis:lint": "yarn run lint" }, @@ -93,6 +94,7 @@ "svelte-loader": "^3.1.0", "ts-loader": "^9.0.0", "typescript": "^5.0.0", + "vitest": "^3.0.6", "vue": "^3.2.14", "vue-loader": "^17.0.0", "webpack": "^5.72", diff --git a/yarn.lock b/yarn.lock index 7361ce58..79aaca81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1055,6 +1055,131 @@ esquery "^1.6.0" jsdoc-type-pratt-parser "~4.1.0" +"@esbuild/aix-ppc64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz#38848d3e25afe842a7943643cbcd387cc6e13461" + integrity sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== + +"@esbuild/android-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz#f592957ae8b5643129fa889c79e69cd8669bb894" + integrity sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== + +"@esbuild/android-arm@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz#72d8a2063aa630308af486a7e5cbcd1e134335b3" + integrity sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== + +"@esbuild/android-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz#9a7713504d5f04792f33be9c197a882b2d88febb" + integrity sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== + +"@esbuild/darwin-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz#02ae04ad8ebffd6e2ea096181b3366816b2b5936" + integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== + +"@esbuild/darwin-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz#9ec312bc29c60e1b6cecadc82bd504d8adaa19e9" + integrity sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== + +"@esbuild/freebsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz#5e82f44cb4906d6aebf24497d6a068cfc152fa00" + integrity sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== + +"@esbuild/freebsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz#3fb1ce92f276168b75074b4e51aa0d8141ecce7f" + integrity sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== + +"@esbuild/linux-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz#856b632d79eb80aec0864381efd29de8fd0b1f43" + integrity sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== + +"@esbuild/linux-arm@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz#c846b4694dc5a75d1444f52257ccc5659021b736" + integrity sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== + +"@esbuild/linux-ia32@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz#f8a16615a78826ccbb6566fab9a9606cfd4a37d5" + integrity sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== + +"@esbuild/linux-loong64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz#1c451538c765bf14913512c76ed8a351e18b09fc" + integrity sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== + +"@esbuild/linux-mips64el@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz#0846edeefbc3d8d50645c51869cc64401d9239cb" + integrity sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== + +"@esbuild/linux-ppc64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz#8e3fc54505671d193337a36dfd4c1a23b8a41412" + integrity sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== + +"@esbuild/linux-riscv64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz#6a1e92096d5e68f7bb10a0d64bb5b6d1daf9a694" + integrity sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== + +"@esbuild/linux-s390x@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz#ab18e56e66f7a3c49cb97d337cd0a6fea28a8577" + integrity sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== + +"@esbuild/linux-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz#8140c9b40da634d380b0b29c837a0b4267aff38f" + integrity sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q== + +"@esbuild/netbsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz#65f19161432bafb3981f5f20a7ff45abb2e708e6" + integrity sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== + +"@esbuild/netbsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz#7a3a97d77abfd11765a72f1c6f9b18f5396bcc40" + integrity sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw== + +"@esbuild/openbsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz#58b00238dd8f123bfff68d3acc53a6ee369af89f" + integrity sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== + +"@esbuild/openbsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz#0ac843fda0feb85a93e288842936c21a00a8a205" + integrity sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== + +"@esbuild/sunos-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz#8b7aa895e07828d36c422a4404cc2ecf27fb15c6" + integrity sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== + +"@esbuild/win32-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz#c023afb647cabf0c3ed13f0eddfc4f1d61c66a85" + integrity sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== + +"@esbuild/win32-ia32@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz#96c356132d2dda990098c8b8b951209c3cd743c2" + integrity sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== + +"@esbuild/win32-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b" + integrity sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg== + "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -1257,6 +1382,101 @@ unbzip2-stream "^1.4.3" yargs "^17.7.2" +"@rollup/rollup-android-arm-eabi@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz#731df27dfdb77189547bcef96ada7bf166bbb2fb" + integrity sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw== + +"@rollup/rollup-android-arm64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz#4bea6db78e1f6927405df7fe0faf2f5095e01343" + integrity sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q== + +"@rollup/rollup-darwin-arm64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz#a7aab77d44be3c44a20f946e10160f84e5450e7f" + integrity sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q== + +"@rollup/rollup-darwin-x64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz#c572c024b57ee8ddd1b0851703ace9eb6cc0dd82" + integrity sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw== + +"@rollup/rollup-freebsd-arm64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz#cf74f8113b5a83098a5c026c165742277cbfb88b" + integrity sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA== + +"@rollup/rollup-freebsd-x64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz#39561f3a2f201a4ad6a01425b1ff5928154ecd7c" + integrity sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q== + +"@rollup/rollup-linux-arm-gnueabihf@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz#980d6061e373bfdaeb67925c46d2f8f9b3de537f" + integrity sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g== + +"@rollup/rollup-linux-arm-musleabihf@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz#f91a90f30dc00d5a64ac2d9bbedc829cd3cfaa78" + integrity sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA== + +"@rollup/rollup-linux-arm64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz#fac700fa5c38bc13a0d5d34463133093da4c92a0" + integrity sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A== + +"@rollup/rollup-linux-arm64-musl@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz#f50ecccf8c78841ff6df1706bc4782d7f62bf9c3" + integrity sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q== + +"@rollup/rollup-linux-loongarch64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz#5869dc0b28242da6553e2b52af41374f4038cd6e" + integrity sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ== + +"@rollup/rollup-linux-powerpc64le-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz#5cdd9f851ce1bea33d6844a69f9574de335f20b1" + integrity sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw== + +"@rollup/rollup-linux-riscv64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz#ef5dc37f4388f5253f0def43e1440ec012af204d" + integrity sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw== + +"@rollup/rollup-linux-s390x-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz#7dbc3ccbcbcfb3e65be74538dfb6e8dd16178fde" + integrity sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA== + +"@rollup/rollup-linux-x64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz#5783fc0adcab7dc069692056e8ca8d83709855ce" + integrity sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA== + +"@rollup/rollup-linux-x64-musl@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz#00b6c29b298197a384e3c659910b47943003a678" + integrity sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ== + +"@rollup/rollup-win32-arm64-msvc@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz#cbfee01f1fe73791c35191a05397838520ca3cdd" + integrity sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ== + +"@rollup/rollup-win32-ia32-msvc@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz#95cdbdff48fe6c948abcf6a1d500b2bd5ce33f62" + integrity sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w== + +"@rollup/rollup-win32-x64-msvc@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz#4cdb2cfae69cdb7b1a3cc58778e820408075e928" + integrity sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g== + "@rtsao/scc@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" @@ -1374,6 +1594,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/estree@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": version "4.19.5" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz#218064e321126fcf9048d1ca25dd2465da55d9c6" @@ -1536,6 +1761,65 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@vitest/expect@3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.0.6.tgz#30993c0841203d2243826ee04fb25463eb86e20d" + integrity sha512-zBduHf/ja7/QRX4HdP1DSq5XrPgdN+jzLOwaTq/0qZjYfgETNFCKf9nOAp2j3hmom3oTbczuUzrzg9Hafh7hNg== + dependencies: + "@vitest/spy" "3.0.6" + "@vitest/utils" "3.0.6" + chai "^5.2.0" + tinyrainbow "^2.0.0" + +"@vitest/mocker@3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.0.6.tgz#28287cfdc06a8b0318344e5c240d774e43014cd0" + integrity sha512-KPztr4/tn7qDGZfqlSPQoF2VgJcKxnDNhmfR3VgZ6Fy1bO8T9Fc1stUiTXtqz0yG24VpD00pZP5f8EOFknjNuQ== + dependencies: + "@vitest/spy" "3.0.6" + estree-walker "^3.0.3" + magic-string "^0.30.17" + +"@vitest/pretty-format@3.0.6", "@vitest/pretty-format@^3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.0.6.tgz#a828569818b666a6e955c9af8129e6b0d2968ee6" + integrity sha512-Zyctv3dbNL+67qtHfRnUE/k8qxduOamRfAL1BurEIQSyOEFffoMvx2pnDSSbKAAVxY0Ej2J/GH2dQKI0W2JyVg== + dependencies: + tinyrainbow "^2.0.0" + +"@vitest/runner@3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.0.6.tgz#a07b54674b1a495424f2ea959a28a6096c17c33b" + integrity sha512-JopP4m/jGoaG1+CBqubV/5VMbi7L+NQCJTu1J1Pf6YaUbk7bZtaq5CX7p+8sY64Sjn1UQ1XJparHfcvTTdu9cA== + dependencies: + "@vitest/utils" "3.0.6" + pathe "^2.0.3" + +"@vitest/snapshot@3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.0.6.tgz#e962319e487b2e8da7ad39322b5e0b39ea639d7a" + integrity sha512-qKSmxNQwT60kNwwJHMVwavvZsMGXWmngD023OHSgn873pV0lylK7dwBTfYP7e4URy5NiBCHHiQGA9DHkYkqRqg== + dependencies: + "@vitest/pretty-format" "3.0.6" + magic-string "^0.30.17" + pathe "^2.0.3" + +"@vitest/spy@3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.0.6.tgz#3a50ec0ab11e8f729cdaa938a6d271b12547cea1" + integrity sha512-HfOGx/bXtjy24fDlTOpgiAEJbRfFxoX3zIGagCqACkFKKZ/TTOE6gYMKXlqecvxEndKFuNHcHqP081ggZ2yM0Q== + dependencies: + tinyspy "^3.0.2" + +"@vitest/utils@3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.0.6.tgz#be4246ab46a076db1e49b9e0479bdd9a7f65782f" + integrity sha512-18ktZpf4GQFTbf9jK543uspU03Q2qya7ZGya5yiZ0Gx0nnnalBvd5ZBislbl2EhLjM8A8rt4OilqKG7QwcGkvQ== + dependencies: + "@vitest/pretty-format" "3.0.6" + loupe "^3.1.3" + tinyrainbow "^2.0.0" + "@vue/babel-helper-vue-jsx-merge-props@^1.0.0", "@vue/babel-helper-vue-jsx-merge-props@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz#8d53a1e21347db8edbe54d339902583176de09f2" @@ -2090,6 +2374,11 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + ast-types@^0.13.4: version "0.13.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" @@ -2358,6 +2647,11 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" @@ -2430,6 +2724,17 @@ chai@^4.2.0: pathval "^1.1.1" type-detect "^4.1.0" +chai@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.2.0.tgz#1358ee106763624114addf84ab02697e411c9c05" + integrity sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" + chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -2454,6 +2759,11 @@ check-error@^1.0.3: dependencies: get-func-name "^2.0.2" +check-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc" + integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw== + "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3, chokidar@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" @@ -2903,6 +3213,13 @@ debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + decamelize@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" @@ -2915,6 +3232,11 @@ deep-eql@^4.1.3: dependencies: type-detect "^4.0.0" +deep-eql@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" + integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -3243,6 +3565,11 @@ es-module-lexer@^1.2.1, es-module-lexer@^1.5.3: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== +es-module-lexer@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.6.0.tgz#da49f587fd9e68ee2404fe4e256c0c7d3a81be21" + integrity sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ== + es-object-atoms@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" @@ -3280,6 +3607,37 @@ es6-promise@^4.1.0: resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== +esbuild@^0.24.2: + version "0.24.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.2.tgz#b5b55bee7de017bff5fb8a4e3e44f2ebe2c3567d" + integrity sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA== + optionalDependencies: + "@esbuild/aix-ppc64" "0.24.2" + "@esbuild/android-arm" "0.24.2" + "@esbuild/android-arm64" "0.24.2" + "@esbuild/android-x64" "0.24.2" + "@esbuild/darwin-arm64" "0.24.2" + "@esbuild/darwin-x64" "0.24.2" + "@esbuild/freebsd-arm64" "0.24.2" + "@esbuild/freebsd-x64" "0.24.2" + "@esbuild/linux-arm" "0.24.2" + "@esbuild/linux-arm64" "0.24.2" + "@esbuild/linux-ia32" "0.24.2" + "@esbuild/linux-loong64" "0.24.2" + "@esbuild/linux-mips64el" "0.24.2" + "@esbuild/linux-ppc64" "0.24.2" + "@esbuild/linux-riscv64" "0.24.2" + "@esbuild/linux-s390x" "0.24.2" + "@esbuild/linux-x64" "0.24.2" + "@esbuild/netbsd-arm64" "0.24.2" + "@esbuild/netbsd-x64" "0.24.2" + "@esbuild/openbsd-arm64" "0.24.2" + "@esbuild/openbsd-x64" "0.24.2" + "@esbuild/sunos-x64" "0.24.2" + "@esbuild/win32-arm64" "0.24.2" + "@esbuild/win32-ia32" "0.24.2" + "@esbuild/win32-x64" "0.24.2" + escalade@^3.1.1, escalade@^3.1.2: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -3559,6 +3917,11 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +expect-type@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.1.0.tgz#a146e414250d13dfc49eafcfd1344a4060fa4c75" + integrity sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA== + express@^4.19.2: version "4.21.1" resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281" @@ -3840,7 +4203,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@~2.3.2: +fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -4869,6 +5232,11 @@ loupe@^2.3.6: dependencies: get-func-name "^2.0.1" +loupe@^3.1.0, loupe@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.1.3.tgz#042a8f7986d77f3d0f98ef7990a2b2fef18b0fd2" + integrity sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -4888,6 +5256,13 @@ magic-string@^0.30.11, magic-string@^0.30.4: dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" +magic-string@^0.30.17: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -5076,6 +5451,11 @@ nanoid@^3.3.7: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== +nanoid@^3.3.8: + version "3.3.8" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -5416,11 +5796,21 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + pathval@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== +pathval@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" + integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -5440,6 +5830,11 @@ picocolors@^1.0.0, picocolors@^1.0.1, picocolors@^1.1.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -5736,6 +6131,15 @@ postcss@^8.2.14, postcss@^8.3.0, postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4. picocolors "^1.0.1" source-map-js "^1.2.0" +postcss@^8.5.2: + version "8.5.3" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" + integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + preact-compat@^3.17.0: version "3.19.0" resolved "https://registry.yarnpkg.com/preact-compat/-/preact-compat-3.19.0.tgz#a71457b6a3bf051690a4411603bc2085aa061c2f" @@ -6123,6 +6527,34 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +rollup@^4.30.1: + version "4.34.8" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.8.tgz#e859c1a51d899aba9bcf451d4eed1d11fb8e2a6e" + integrity sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.34.8" + "@rollup/rollup-android-arm64" "4.34.8" + "@rollup/rollup-darwin-arm64" "4.34.8" + "@rollup/rollup-darwin-x64" "4.34.8" + "@rollup/rollup-freebsd-arm64" "4.34.8" + "@rollup/rollup-freebsd-x64" "4.34.8" + "@rollup/rollup-linux-arm-gnueabihf" "4.34.8" + "@rollup/rollup-linux-arm-musleabihf" "4.34.8" + "@rollup/rollup-linux-arm64-gnu" "4.34.8" + "@rollup/rollup-linux-arm64-musl" "4.34.8" + "@rollup/rollup-linux-loongarch64-gnu" "4.34.8" + "@rollup/rollup-linux-powerpc64le-gnu" "4.34.8" + "@rollup/rollup-linux-riscv64-gnu" "4.34.8" + "@rollup/rollup-linux-s390x-gnu" "4.34.8" + "@rollup/rollup-linux-x64-gnu" "4.34.8" + "@rollup/rollup-linux-x64-musl" "4.34.8" + "@rollup/rollup-win32-arm64-msvc" "4.34.8" + "@rollup/rollup-win32-ia32-msvc" "4.34.8" + "@rollup/rollup-win32-x64-msvc" "4.34.8" + fsevents "~2.3.2" + run-applescript@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" @@ -6389,6 +6821,11 @@ sift@*: resolved "https://registry.yarnpkg.com/sift/-/sift-17.1.3.tgz#9d2000d4d41586880b0079b5183d839c7a142bf7" integrity sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ== +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + sinon@^14.0.0: version "14.0.2" resolved "https://registry.yarnpkg.com/sinon/-/sinon-14.0.2.tgz#585a81a3c7b22cf950762ac4e7c28eb8b151c46f" @@ -6442,6 +6879,11 @@ socks@^2.8.3: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -6506,6 +6948,11 @@ sprintf-js@^1.1.3: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + stackframe@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" @@ -6526,6 +6973,11 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +std-env@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" + integrity sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w== + streamx@^2.15.0, streamx@^2.18.0: version "2.20.0" resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.20.0.tgz#5f3608483499a9346852122b26042f964ceec931" @@ -6801,6 +7253,31 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" + integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== + +tinypool@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.2.tgz#706193cc532f4c100f66aa00b01c42173d9051b2" + integrity sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA== + +tinyrainbow@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294" + integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw== + +tinyspy@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-3.0.2.tgz#86dd3cf3d737b15adcf17d7887c84a75201df20a" + integrity sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q== + tmp@^0.2.1: version "0.2.3" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae" @@ -7056,6 +7533,54 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +vite-node@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.0.6.tgz#a68c06c08e95c9a83f21993eabb17e0398804b1b" + integrity sha512-s51RzrTkXKJrhNbUzQRsarjmAae7VmMPAsRT7lppVpIg6mK3zGthP9Hgz0YQQKuNcF+Ii7DfYk3Fxz40jRmePw== + dependencies: + cac "^6.7.14" + debug "^4.4.0" + es-module-lexer "^1.6.0" + pathe "^2.0.3" + vite "^5.0.0 || ^6.0.0" + +"vite@^5.0.0 || ^6.0.0": + version "6.1.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.1.1.tgz#c1f221749298357b9230782a04483e60ad83c8db" + integrity sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA== + dependencies: + esbuild "^0.24.2" + postcss "^8.5.2" + rollup "^4.30.1" + optionalDependencies: + fsevents "~2.3.3" + +vitest@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.0.6.tgz#2fd1571a3cc1fa1648e29af73ff2dc85872f3a69" + integrity sha512-/iL1Sc5VeDZKPDe58oGK4HUFLhw6b5XdY1MYawjuSaDA4sEfYlY9HnS6aCEG26fX+MgUi7MwlduTBHHAI/OvMA== + dependencies: + "@vitest/expect" "3.0.6" + "@vitest/mocker" "3.0.6" + "@vitest/pretty-format" "^3.0.6" + "@vitest/runner" "3.0.6" + "@vitest/snapshot" "3.0.6" + "@vitest/spy" "3.0.6" + "@vitest/utils" "3.0.6" + chai "^5.2.0" + debug "^4.4.0" + expect-type "^1.1.0" + magic-string "^0.30.17" + pathe "^2.0.3" + std-env "^3.8.0" + tinybench "^2.9.0" + tinyexec "^0.3.2" + tinypool "^1.0.2" + tinyrainbow "^2.0.0" + vite "^5.0.0 || ^6.0.0" + vite-node "3.0.6" + why-is-node-running "^2.3.0" + vue-loader@^17.0.0: version "17.4.2" resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.4.2.tgz#f87f0d8adfcbbe8623de9eba1979d41ba223c6da" @@ -7257,6 +7782,14 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + wildcard@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" From bd526920e5e9a76706a3ecb041036f3354261a7c Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Feb 2025 18:08:44 +0100 Subject: [PATCH 02/15] Migrate test/utils/*.js test files to Vitest --- .../{get-file-extension.js => get-file-extension.test.js} | 2 +- test/utils/{get-vue-version.js => get-vue-version.test.js} | 6 +++--- test/utils/{package-up.js => package-up.test.js} | 2 +- test/utils/{regexp-escaper.js => regexp-escaper.test.js} | 2 +- test/utils/string-escaper.js | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) rename test/utils/{get-file-extension.js => get-file-extension.test.js} (97%) rename test/utils/{get-vue-version.js => get-vue-version.test.js} (95%) rename test/utils/{package-up.js => package-up.test.js} (97%) rename test/utils/{regexp-escaper.js => regexp-escaper.test.js} (95%) diff --git a/test/utils/get-file-extension.js b/test/utils/get-file-extension.test.js similarity index 97% rename from test/utils/get-file-extension.js rename to test/utils/get-file-extension.test.js index 3a57b6b5..6c47a26b 100644 --- a/test/utils/get-file-extension.js +++ b/test/utils/get-file-extension.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const getFileExtension = require('../../lib/utils/get-file-extension'); describe('get-file-extension', () => { diff --git a/test/utils/get-vue-version.js b/test/utils/get-vue-version.test.js similarity index 95% rename from test/utils/get-vue-version.js rename to test/utils/get-vue-version.test.js index 190c2b13..b6960a86 100644 --- a/test/utils/get-vue-version.js +++ b/test/utils/get-vue-version.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; const getVueVersion = require('../../lib/utils/get-vue-version'); const sinon = require('sinon'); const packageHelper = require('../../lib/package-helper'); @@ -27,10 +27,10 @@ const createWebpackConfig = function() { describe('get-vue-version', () => { let getPackageVersionStub = null; - before(() => { + beforeEach(() => { getPackageVersionStub = sinon.stub(packageHelper, 'getPackageVersion'); }); - after(() => { + afterEach(() => { packageHelper.getPackageVersion.restore(); }); diff --git a/test/utils/package-up.js b/test/utils/package-up.test.js similarity index 97% rename from test/utils/package-up.js rename to test/utils/package-up.test.js index 06d96a84..60951ce2 100644 --- a/test/utils/package-up.js +++ b/test/utils/package-up.test.js @@ -9,8 +9,8 @@ 'use strict'; +import { describe, it, expect } from 'vitest'; const { resolve: resolvePath } = require('path'); -const expect = require('chai').expect; const packageUp = require('../../lib/utils/package-up'); describe('package-up', () => { diff --git a/test/utils/regexp-escaper.js b/test/utils/regexp-escaper.test.js similarity index 95% rename from test/utils/regexp-escaper.js rename to test/utils/regexp-escaper.test.js index f143b87a..0343847e 100644 --- a/test/utils/regexp-escaper.js +++ b/test/utils/regexp-escaper.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const regexpEscaper = require('../../lib/utils/regexp-escaper'); describe('regexp-escaper', () => { diff --git a/test/utils/string-escaper.js b/test/utils/string-escaper.js index dcdae517..e0e0e3ba 100644 --- a/test/utils/string-escaper.js +++ b/test/utils/string-escaper.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const stringEscaper = require('../../lib/utils/string-escaper'); function expectEvaledStringToEqual(str, expectedStr) { From fccf9d199fd6b4f6a27a9ac92352e1fce03e0dc7 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Feb 2025 18:13:23 +0100 Subject: [PATCH 03/15] Migrate test/plugins/*.js test files to Vitest --- test/plugins/{define.js => define.test.js} | 2 +- test/plugins/{forked-ts-types.js => forked-ts-types.test.js} | 2 +- test/plugins/{friendly-errors.js => friendly-errors.test.js} | 2 +- test/plugins/{manifest.js => manifest.test.js} | 2 +- test/plugins/{mini-css-extract.js => mini-css-extract.test.js} | 2 +- test/plugins/{notifier.js => notifier.test.js} | 2 +- test/plugins/{terser.js => terser.test.js} | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename test/plugins/{define.js => define.test.js} (98%) rename test/plugins/{forked-ts-types.js => forked-ts-types.test.js} (98%) rename test/plugins/{friendly-errors.js => friendly-errors.test.js} (98%) rename test/plugins/{manifest.js => manifest.test.js} (98%) rename test/plugins/{mini-css-extract.js => mini-css-extract.test.js} (98%) rename test/plugins/{notifier.js => notifier.test.js} (98%) rename test/plugins/{terser.js => terser.test.js} (97%) diff --git a/test/plugins/define.js b/test/plugins/define.test.js similarity index 98% rename from test/plugins/define.js rename to test/plugins/define.test.js index 65406555..a0491454 100644 --- a/test/plugins/define.js +++ b/test/plugins/define.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const webpack = require('webpack'); const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); diff --git a/test/plugins/forked-ts-types.js b/test/plugins/forked-ts-types.test.js similarity index 98% rename from test/plugins/forked-ts-types.js rename to test/plugins/forked-ts-types.test.js index 4155b204..b99d5962 100644 --- a/test/plugins/forked-ts-types.js +++ b/test/plugins/forked-ts-types.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const tsLoader = require('../../lib/loaders/typescript'); diff --git a/test/plugins/friendly-errors.js b/test/plugins/friendly-errors.test.js similarity index 98% rename from test/plugins/friendly-errors.js rename to test/plugins/friendly-errors.test.js index e3e26459..2dc12347 100644 --- a/test/plugins/friendly-errors.js +++ b/test/plugins/friendly-errors.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const FriendlyErrorsWebpackPlugin = require('@nuxt/friendly-errors-webpack-plugin'); const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); diff --git a/test/plugins/manifest.js b/test/plugins/manifest.test.js similarity index 98% rename from test/plugins/manifest.js rename to test/plugins/manifest.test.js index 89a3405e..2fa69460 100644 --- a/test/plugins/manifest.js +++ b/test/plugins/manifest.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const { WebpackManifestPlugin } = require('../../lib/webpack-manifest-plugin'); const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); diff --git a/test/plugins/mini-css-extract.js b/test/plugins/mini-css-extract.test.js similarity index 98% rename from test/plugins/mini-css-extract.js rename to test/plugins/mini-css-extract.test.js index a500bea1..57d16791 100644 --- a/test/plugins/mini-css-extract.js +++ b/test/plugins/mini-css-extract.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); diff --git a/test/plugins/notifier.js b/test/plugins/notifier.test.js similarity index 98% rename from test/plugins/notifier.js rename to test/plugins/notifier.test.js index fc465e9b..d1842cf9 100644 --- a/test/plugins/notifier.js +++ b/test/plugins/notifier.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackNotifier = require('webpack-notifier'); const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); diff --git a/test/plugins/terser.js b/test/plugins/terser.test.js similarity index 97% rename from test/plugins/terser.js rename to test/plugins/terser.test.js index 5ab8b6f6..40ea0bb5 100644 --- a/test/plugins/terser.js +++ b/test/plugins/terser.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const TerserPlugin = require('terser-webpack-plugin'); const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); From 36025b11104c9eee1e483f5d85c5b0a756437128 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Feb 2025 18:14:55 +0100 Subject: [PATCH 04/15] Migrate test/loaders/*.js test files to Vitest --- test/loaders/{babel.js => babel.test.js} | 2 +- test/loaders/{css-extract.js => css-extract.test.js} | 2 +- test/loaders/{css.js => css.test.js} | 2 +- test/loaders/{handlebars.js => handlebars.test.js} | 2 +- test/loaders/{less.js => less.test.js} | 2 +- test/loaders/{sass.js => sass.test.js} | 2 +- test/loaders/{stylus.js => stylus.test.js} | 2 +- test/loaders/{typescript.js => typescript.test.js} | 2 +- test/loaders/{vue.js => vue.test.js} | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) rename test/loaders/{babel.js => babel.test.js} (99%) rename test/loaders/{css-extract.js => css-extract.test.js} (97%) rename test/loaders/{css.js => css.test.js} (98%) rename test/loaders/{handlebars.js => handlebars.test.js} (97%) rename test/loaders/{less.js => less.test.js} (98%) rename test/loaders/{sass.js => sass.test.js} (99%) rename test/loaders/{stylus.js => stylus.test.js} (98%) rename test/loaders/{typescript.js => typescript.test.js} (97%) rename test/loaders/{vue.js => vue.test.js} (97%) diff --git a/test/loaders/babel.js b/test/loaders/babel.test.js similarity index 99% rename from test/loaders/babel.js rename to test/loaders/babel.test.js index a21b47b5..6ed1447b 100644 --- a/test/loaders/babel.js +++ b/test/loaders/babel.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const babelLoader = require('../../lib/loaders/babel'); diff --git a/test/loaders/css-extract.js b/test/loaders/css-extract.test.js similarity index 97% rename from test/loaders/css-extract.js rename to test/loaders/css-extract.test.js index 91e98470..16293dbb 100644 --- a/test/loaders/css-extract.js +++ b/test/loaders/css-extract.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const cssExtractLoader = require('../../lib/loaders/css-extract'); diff --git a/test/loaders/css.js b/test/loaders/css.test.js similarity index 98% rename from test/loaders/css.js rename to test/loaders/css.test.js index d7dc1ee8..6f6a3180 100644 --- a/test/loaders/css.js +++ b/test/loaders/css.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const cssLoader = require('../../lib/loaders/css'); diff --git a/test/loaders/handlebars.js b/test/loaders/handlebars.test.js similarity index 97% rename from test/loaders/handlebars.js rename to test/loaders/handlebars.test.js index 09ddb5d4..3441da72 100644 --- a/test/loaders/handlebars.js +++ b/test/loaders/handlebars.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const handlebarsLoader = require('../../lib/loaders/handlebars'); diff --git a/test/loaders/less.js b/test/loaders/less.test.js similarity index 98% rename from test/loaders/less.js rename to test/loaders/less.test.js index 43faace1..483711cb 100644 --- a/test/loaders/less.js +++ b/test/loaders/less.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const lessLoader = require('../../lib/loaders/less'); diff --git a/test/loaders/sass.js b/test/loaders/sass.test.js similarity index 99% rename from test/loaders/sass.js rename to test/loaders/sass.test.js index 0e611c11..dcf6b522 100644 --- a/test/loaders/sass.js +++ b/test/loaders/sass.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const sassLoader = require('../../lib/loaders/sass'); diff --git a/test/loaders/stylus.js b/test/loaders/stylus.test.js similarity index 98% rename from test/loaders/stylus.js rename to test/loaders/stylus.test.js index a4da4339..1e76566a 100644 --- a/test/loaders/stylus.js +++ b/test/loaders/stylus.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const stylusLoader = require('../../lib/loaders/stylus'); diff --git a/test/loaders/typescript.js b/test/loaders/typescript.test.js similarity index 97% rename from test/loaders/typescript.js rename to test/loaders/typescript.test.js index 2485d22a..af394dcc 100644 --- a/test/loaders/typescript.js +++ b/test/loaders/typescript.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const tsLoader = require('../../lib/loaders/typescript'); diff --git a/test/loaders/vue.js b/test/loaders/vue.test.js similarity index 97% rename from test/loaders/vue.js rename to test/loaders/vue.test.js index 9b1325ae..c341a39a 100644 --- a/test/loaders/vue.js +++ b/test/loaders/vue.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const vueLoader = require('../../lib/loaders/vue'); From ee6d4267813991c864842a8492a59ddb0c4b800e Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Feb 2025 18:17:08 +0100 Subject: [PATCH 05/15] Migrate test/friendly-errors/*.js test files to Vitest --- .../{missing-css-file.js => missing-css-file.test.js} | 2 +- .../formatters/{missing-loader.js => missing-loader.test.js} | 2 +- .../{missing-css-file.js => missing-css-file.test.js} | 2 +- .../transformers/{missing-loader.js => missing-loader.test.js} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename test/friendly-errors/formatters/{missing-css-file.js => missing-css-file.test.js} (97%) rename test/friendly-errors/formatters/{missing-loader.js => missing-loader.test.js} (98%) rename test/friendly-errors/transformers/{missing-css-file.js => missing-css-file.test.js} (97%) rename test/friendly-errors/transformers/{missing-loader.js => missing-loader.test.js} (99%) diff --git a/test/friendly-errors/formatters/missing-css-file.js b/test/friendly-errors/formatters/missing-css-file.test.js similarity index 97% rename from test/friendly-errors/formatters/missing-css-file.js rename to test/friendly-errors/formatters/missing-css-file.test.js index 67b17688..5a04c29e 100644 --- a/test/friendly-errors/formatters/missing-css-file.js +++ b/test/friendly-errors/formatters/missing-css-file.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const formatter = require('../../../lib/friendly-errors/formatters/missing-css-file'); describe('formatters/missing-css-file', () => { diff --git a/test/friendly-errors/formatters/missing-loader.js b/test/friendly-errors/formatters/missing-loader.test.js similarity index 98% rename from test/friendly-errors/formatters/missing-loader.js rename to test/friendly-errors/formatters/missing-loader.test.js index 94dfb1cc..bc37f902 100644 --- a/test/friendly-errors/formatters/missing-loader.js +++ b/test/friendly-errors/formatters/missing-loader.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const formatter = require('../../../lib/friendly-errors/formatters/missing-loader'); describe('formatters/missing-loader', () => { diff --git a/test/friendly-errors/transformers/missing-css-file.js b/test/friendly-errors/transformers/missing-css-file.test.js similarity index 97% rename from test/friendly-errors/transformers/missing-css-file.js rename to test/friendly-errors/transformers/missing-css-file.test.js index 0b6a4dfa..66a358bf 100644 --- a/test/friendly-errors/transformers/missing-css-file.js +++ b/test/friendly-errors/transformers/missing-css-file.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const transform = require('../../../lib/friendly-errors/transformers/missing-css-file'); describe('transform/missing-css-file', () => { diff --git a/test/friendly-errors/transformers/missing-loader.js b/test/friendly-errors/transformers/missing-loader.test.js similarity index 99% rename from test/friendly-errors/transformers/missing-loader.js rename to test/friendly-errors/transformers/missing-loader.test.js index 4edd83f3..cd4a770a 100644 --- a/test/friendly-errors/transformers/missing-loader.js +++ b/test/friendly-errors/transformers/missing-loader.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const transformFactory = require('../../../lib/friendly-errors/transformers/missing-loader'); const RuntimeConfig = require('../../../lib/config/RuntimeConfig'); const WebpackConfig = require('../../../lib/WebpackConfig'); From f3a31d2594a784dd2c403ea715f47861e5671a65 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Feb 2025 21:35:00 +0100 Subject: [PATCH 06/15] Migrate test/config/*.js test files to Vitest --- test/config/{parse-runtime.js => parse-runtime.test.js} | 6 +----- test/config/{path-util.js => path-util.test.js} | 2 +- test/config/{validator.js => validator.test.js} | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) rename test/config/{parse-runtime.js => parse-runtime.test.js} (98%) rename test/config/{path-util.js => path-util.test.js} (99%) rename test/config/{validator.js => validator.test.js} (98%) diff --git a/test/config/parse-runtime.js b/test/config/parse-runtime.test.js similarity index 98% rename from test/config/parse-runtime.js rename to test/config/parse-runtime.test.js index e2a66872..db0215f4 100644 --- a/test/config/parse-runtime.js +++ b/test/config/parse-runtime.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const parseArgv = require('../../lib/config/parse-runtime'); const testSetup = require('../helpers/setup'); const fs = require('fs-extra'); @@ -30,10 +30,6 @@ function createTestDirectory() { } describe('parse-runtime', () => { - beforeEach(() => { - testSetup.emptyTmpDir(); - }); - it('Basic usage', () => { const testDir = createTestDirectory(); const config = parseArgv(createArgv(['foobar', '--bar', '--help']), testDir); diff --git a/test/config/path-util.js b/test/config/path-util.test.js similarity index 99% rename from test/config/path-util.js rename to test/config/path-util.test.js index 4256f373..97e1218b 100644 --- a/test/config/path-util.js +++ b/test/config/path-util.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const pathUtil = require('../../lib/config/path-util'); diff --git a/test/config/validator.js b/test/config/validator.test.js similarity index 98% rename from test/config/validator.js rename to test/config/validator.test.js index cab05984..4cd0c568 100644 --- a/test/config/validator.js +++ b/test/config/validator.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const validator = require('../../lib/config/validator'); From fcb9be0ed76c9cc0bcb480cf9da90ac66e35d011 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Feb 2025 21:35:36 +0100 Subject: [PATCH 07/15] Configure Vitest to exclude package.json from rerun triggers --- vitest.config.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 vitest.config.js diff --git a/vitest.config.js b/vitest.config.js new file mode 100644 index 00000000..20c12026 --- /dev/null +++ b/vitest.config.js @@ -0,0 +1,10 @@ +// vitest.config.js +import {defineConfig} from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['test/**/*.test.js'], + // Re-define "forceRerunTriggers" to exclude `package.json` and `test_tmp/**/package.json` to prevent infinite watch loop + forceRerunTriggers: ['./vitest.config.js'], + }, +}); From 30697556e6d780e1d9a8f47286d227002f5c0faa Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 23 Feb 2025 21:43:50 +0100 Subject: [PATCH 08/15] Migrate test/persistent-cache/* test files to Vitest --- package.json | 3 +-- run-persistent-tests.js | 18 ------------------ .../{functional.js => functional.test.js} | 7 ++----- 3 files changed, 3 insertions(+), 25 deletions(-) delete mode 100755 run-persistent-tests.js rename test/persistent-cache/{functional.js => functional.test.js} (94%) diff --git a/package.json b/package.json index 605b9d1e..22683a0e 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,8 @@ "description": "Webpack Encore is a simpler way to integrate Webpack into your application", "main": "index.js", "scripts": { - "test": "yarn run vitest && yarn run test:main && yarn run test:persistent-cache", + "test": "yarn run vitest && yarn run test:main", "test:main": "mocha --reporter spec test --recursive --ignore test/persistent-cache/*", - "test:persistent-cache": "node run-persistent-tests", "test:vitest": "vitest", "lint": "eslint lib test index.js .eslintrc.js --report-unused-disable-directives --max-warnings=0", "travis:lint": "yarn run lint" diff --git a/run-persistent-tests.js b/run-persistent-tests.js deleted file mode 100755 index ba6a9f22..00000000 --- a/run-persistent-tests.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * This file is part of the Symfony Webpack Encore package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -'use strict'; - -const { execSync } = require('child_process'); -const { emptyTmpDir } = require('./test/helpers/setup'); - -emptyTmpDir(); -for (let i = 0; i < 2; i++) { - execSync('mocha --reporter spec test/persistent-cache --recursive', { stdio: 'inherit' }); -} diff --git a/test/persistent-cache/functional.js b/test/persistent-cache/functional.test.js similarity index 94% rename from test/persistent-cache/functional.js rename to test/persistent-cache/functional.test.js index 9fd209a2..2266bd2c 100644 --- a/test/persistent-cache/functional.js +++ b/test/persistent-cache/functional.test.js @@ -9,7 +9,7 @@ 'use strict'; -const chai = require('chai'); +import { describe, it, expect, chai } from 'vitest'; chai.use(require('chai-fs')); chai.use(require('chai-subset')); const path = require('path'); @@ -33,10 +33,7 @@ function createWebpackConfig(outputDirName = '', testName, command, argv = {}) { return webpackConfig; } -describe('Functional persistent cache tests using webpack', function() { - // being functional tests, these can take quite long - this.timeout(10000); - +describe('Functional persistent cache tests using webpack', { repeats: 2, timeout: 10000 }, function() { describe('Basic scenarios.', () => { it('Persistent caching does not cause problems', (done) => { const config = createWebpackConfig('www/build', 'basic_cache', 'dev'); From 6d07e3a597bae0d3829ce77eb09b5cd83b0a5e7a Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Mon, 24 Feb 2025 08:33:20 +0100 Subject: [PATCH 09/15] Migrate test/bin/*.js test files to Vitest --- package.json | 1 + test/bin/{encore.js => encore.test.js} | 197 ++++++++++++------------- yarn.lock | 5 + 3 files changed, 98 insertions(+), 105 deletions(-) rename test/bin/{encore.js => encore.test.js} (60%) diff --git a/package.json b/package.json index 22683a0e..2fe9b426 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "file-loader": "^6.0.0", "fork-ts-checker-webpack-plugin": "^7.0.0 || ^8.0.0 || ^9.0.0", "fs-extra": "^10.0.0", + "get-port": "^7.1.0", "handlebars": "^4.7.7", "handlebars-loader": "^1.7.0", "http-server": "^14.1.0", diff --git a/test/bin/encore.js b/test/bin/encore.test.js similarity index 60% rename from test/bin/encore.js rename to test/bin/encore.test.js index ba57590d..4a8e7a28 100644 --- a/test/bin/encore.js +++ b/test/bin/encore.test.js @@ -9,22 +9,19 @@ 'use strict'; -const chai = require('chai'); +import { describe, it, expect, beforeEach, afterEach, chai } from 'vitest'; +import getPort from "get-port"; chai.use(require('chai-fs')); -const expect = chai.expect; const path = require('path'); const testSetup = require('../helpers/setup'); const fs = require('fs-extra'); +const { promisify } = require('util'); const { exec, execSync, spawn } = require('child_process'); - +const execAsync = promisify(exec); const projectDir = path.resolve(__dirname, '../', '../'); -describe('bin/encore.js', function() { - // being functional tests, these can take quite long - this.timeout(10000); - - it('Basic smoke test', (done) => { - testSetup.emptyTmpDir(); +describe('bin/encore.js', { sequential: true, timeout: 10000 }, function() { + it('Basic smoke test', async () => { const testDir = testSetup.createTestAppDir(); fs.writeFileSync( @@ -52,23 +49,19 @@ module.exports = Encore.getWebpackConfig(); ); const binPath = path.resolve(__dirname, '../', '../', 'bin', 'encore.js'); - exec(`node ${binPath} dev --context=${testDir}`, { cwd: testDir }, (err, stdout, stderr) => { - if (err) { - throw new Error(`Error executing encore: ${err} ${stderr} ${stdout}`); - } + try { + const { stdout, stderr } = await execAsync(`node ${binPath} dev --context=${testDir}`, { cwd: testDir }); expect(stdout).to.contain('Compiled successfully'); - expect(stdout).not.to.contain('Hash: '); expect(stdout).not.to.contain('Version: '); expect(stdout).not.to.contain('Time: '); - - done(); - }); + } catch (err) { + throw new Error(`Error executing encore: ${err} ${err.stderr} ${err.stdout}`); + } }); - it('Smoke test using the --json option', (done) => { - testSetup.emptyTmpDir(); + it('Smoke test using the --json option', async () => { const testDir = testSetup.createTestAppDir(); fs.writeFileSync( @@ -87,10 +80,8 @@ module.exports = Encore.getWebpackConfig(); ); const binPath = path.resolve(__dirname, '../', '../', 'bin', 'encore.js'); - exec(`node ${binPath} dev --json --context=${testDir}`, { cwd: testDir }, (err, stdout, stderr) => { - if (err) { - throw new Error(`Error executing encore: ${err} ${stderr} ${stdout}`); - } + try { + const { stdout, stderr } = await execAsync(`node ${binPath} dev --json --context=${testDir}`, { cwd: testDir }); let parsedOutput = null; try { @@ -101,21 +92,18 @@ module.exports = Encore.getWebpackConfig(); expect(parsedOutput).to.be.an('object'); expect(parsedOutput.modules).to.be.an('array'); - // We expect 4 modules there: // - webpack/runtime/chunk loaded // - webpack/runtime/jsonp chunk loading // - webpack/runtime/hasOwnProperty shorthand // - ./js/no_require.js expect(parsedOutput.modules.length).to.equal(4); - - - done(); - }); + } catch (err) { + throw new Error(`Error executing encore: ${err} ${err.stderr} ${err.stdout}`); + } }); - it('Smoke test using the --profile option', (done) => { - testSetup.emptyTmpDir(); + it('Smoke test using the --profile option', async () => { const testDir = testSetup.createTestAppDir(); fs.writeFileSync( @@ -134,22 +122,19 @@ module.exports = Encore.getWebpackConfig(); ); const binPath = path.resolve(__dirname, '../', '../', 'bin', 'encore.js'); - exec(`node ${binPath} dev --profile --context=${testDir}`, { cwd: testDir }, (err, stdout, stderr) => { - if (err) { - throw new Error(`Error executing encore: ${err} ${stderr} ${stdout}`); - } + try { + const { stdout, stderr } = await execAsync(`node ${binPath} dev --profile --context=${testDir}`, { cwd: testDir }); expect(stdout).to.contain('resolving: '); expect(stdout).to.contain('restoring: '); expect(stdout).to.contain('integration: '); expect(stdout).to.contain('building: '); - - done(); - }); + } catch (err) { + throw new Error(`Error executing encore: ${err} ${err.stderr} ${err.stdout}`); + } }); - it('Smoke test using the --keep-public-path option', (done) => { - testSetup.emptyTmpDir(); + it('Smoke test using the --keep-public-path option', async () => { const testDir = testSetup.createTestAppDir(); fs.writeFileSync( @@ -168,17 +153,14 @@ module.exports = Encore.getWebpackConfig(); ); const binPath = path.resolve(__dirname, '../', '../', 'bin', 'encore.js'); - exec(`node ${binPath} dev --keep-public-path --context=${testDir}`, { cwd: testDir }, (err, stdout, stderr) => { - if (err) { - throw new Error(`Error executing encore: ${err} ${stderr} ${stdout}`); - } - - done(); - }); + try { + await execAsync(`node ${binPath} dev --keep-public-path --context=${testDir}`, { cwd: testDir }); + } catch (err) { + throw new Error(`Error executing encore: ${err} ${err.stderr} ${err.stdout}`); + } }); - it('Display an error when calling an unknown method', (done) => { - testSetup.emptyTmpDir(); + it('Display an error when calling an unknown method', async () => { const testDir = testSetup.createTestAppDir(); fs.writeFileSync( @@ -207,17 +189,18 @@ module.exports = Encore.getWebpackConfig(); ); const binPath = path.resolve(__dirname, '../', '../', 'bin', 'encore.js'); - exec(`node ${binPath} dev --context=${testDir}`, { cwd: testDir }, (err, stdout, stderr) => { + try { + await execAsync(`node ${binPath} dev --context=${testDir}`, { cwd: testDir }); + throw new Error('Expected command to fail.'); + } catch (err) { expect(err).not.to.be.null; - expect(stdout).to.contain('is not a recognized property'); - expect(stdout).to.contain('or method'); - expect(stdout).to.contain('Did you mean'); - done(); - }); + expect(err.stdout).to.contain('is not a recognized property'); + expect(err.stdout).to.contain('or method'); + expect(err.stdout).to.contain('Did you mean'); + } }); - it('Run the webpack-dev-server successfully', (done) => { - testSetup.emptyTmpDir(); + it('Run the webpack-dev-server successfully', {timeout: 15000}, async () => { const testDir = testSetup.createTestAppDir(); fs.writeFileSync( @@ -246,57 +229,58 @@ module.exports = Encore.getWebpackConfig(); const binPath = path.resolve(__dirname, '../', '../', 'bin', 'encore.js'); const abortController = new AbortController(); - const node = spawn('node', [binPath, 'dev-server', `--context=${testDir}`], { - cwd: testDir, - env: Object.assign({}, process.env, { NO_COLOR: 'true' }), - signal: abortController.signal - }); - - let stdout = ''; - let stderr = ''; - - node.stdout.on('data', (data) => { - stdout += data.toString(); - }); - - node.stderr.on('data', (data) => { - stderr += data.toString(); - }); - - node.on('error', (error) => { - if (error.name !== 'AbortError') { - throw new Error('Error executing encore', { cause: error }); - } + const port = await getPort(); + await new Promise(async (resolve, reject) => { + const node = spawn('node', [binPath, 'dev-server', `--context=${testDir}`, `--port=${port}`], { + cwd: testDir, + env: Object.assign({}, process.env, { NO_COLOR: 'true', FORCE_COLOR: 'false' }), + signal: abortController.signal + }); + + let stdout = ''; + let stderr = ''; + + node.stdout.on('data', (data) => { + stdout += data.toString(); + }); + + node.stderr.on('data', (data) => { + stderr += data.toString(); + }); + + node.on('error', (error) => { + if (error.name !== 'AbortError') { + reject(new Error('Error executing encore', { cause: error })); + } - expect(stdout).to.contain('Running webpack-dev-server ...'); - expect(stdout).to.contain('Compiled successfully in'); - expect(stdout).to.contain('webpack compiled successfully'); + expect(stdout).to.contain('Running webpack-dev-server ...'); + expect(stdout).to.contain('Compiled successfully in'); + expect(stdout).to.contain('webpack compiled successfully'); - expect(stderr).to.contain('[webpack-dev-server] Project is running at:'); - expect(stderr).to.contain('[webpack-dev-server] Loopback: http://localhost:8080/'); - expect(stderr).to.contain('[webpack-dev-server] Content not from webpack is served from'); + expect(stderr).to.contain('[webpack-dev-server] Project is running at:'); + expect(stderr).to.contain(`[webpack-dev-server] Loopback: http://localhost:${port}/`); + expect(stderr).to.contain('[webpack-dev-server] Content not from webpack is served from'); - done(); - }); + resolve(); + }); - setTimeout(() => { + await new Promise(r => setTimeout(r, 9000)); abortController.abort(); - }, 5000); + }) }); describe('Without webpack-dev-server installed', () => { - before(() => { + beforeEach(() => { execSync('yarn remove webpack-dev-server --dev', { cwd: projectDir }); }); - after(() => { + afterEach(() => { // Re-install webpack-dev-server and ensure the project is in a clean state - execSync('git checkout package.json', { cwd: projectDir }); + execSync('git checkout package.json yarn.lock', { cwd: projectDir }); execSync('yarn install', { cwd: projectDir }); }); - it('Throw an error when trying to use the webpack-dev-server if not installed', done => { - testSetup.emptyTmpDir(); + it('Throw an error when trying to use the webpack-dev-server if not installed', async () => { const testDir = testSetup.createTestAppDir(); fs.writeFileSync( @@ -324,23 +308,26 @@ module.exports = Encore.getWebpackConfig(); ); const binPath = path.resolve(projectDir, 'bin', 'encore.js'); - exec( - `node ${binPath} dev-server --context=${testDir}`, - { + try { + await execAsync(`node ${binPath} dev-server --context=${testDir}`, { cwd: testDir, env: Object.assign({}, process.env, { NO_COLOR: 'true' }) - }, - (err, stdout, stderr) => { - expect(stdout).to.contain('Install webpack-dev-server to use the webpack Development Server'); - expect(stdout).to.contain('npm install webpack-dev-server --save-dev'); - expect(stderr).to.equal(''); + }); - expect(stdout).not.to.contain('Running webpack-dev-server ...'); - expect(stdout).not.to.contain('Compiled successfully in'); - expect(stdout).not.to.contain('webpack compiled successfully'); + throw new Error('Expected command to fail.'); + } catch (err) { + if (!'stdout' in err || !'stderr' in err) { + throw new Error(`Error executing encore: ${err}`); + } - done(); - }); + expect(err.stdout).to.contain('Install webpack-dev-server to use the webpack Development Server'); + expect(err.stdout).to.contain('npm install webpack-dev-server --save-dev'); + expect(err.stderr).to.equal(''); + + expect(err.stdout).not.to.contain('Running webpack-dev-server ...'); + expect(err.stdout).not.to.contain('Compiled successfully in'); + expect(err.stdout).not.to.contain('webpack compiled successfully'); + } }); }); }); diff --git a/yarn.lock b/yarn.lock index 79aaca81..6bfe2c05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4254,6 +4254,11 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-port@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-7.1.0.tgz#d5a500ebfc7aa705294ec2b83cc38c5d0e364fec" + integrity sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw== + get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" From 65c76aaea9223cc43cea91c5e9a7f69602154f30 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Tue, 25 Feb 2025 00:22:26 +0100 Subject: [PATCH 10/15] Migrate test/* test files to Vitest --- ...WebpackConfig.js => WebpackConfig.test.js} | 2 +- test/bin/encore.test.js | 28 +- ...-generator.js => config-generator.test.js} | 24 +- test/functional.js | 3160 ---------------- test/functional.test.js | 3310 +++++++++++++++++ test/{index.js => index.test.js} | 2 +- test/{logger.js => logger.test.js} | 2 +- ...ckage-helper.js => package-helper.test.js} | 4 +- 8 files changed, 3338 insertions(+), 3194 deletions(-) rename test/{WebpackConfig.js => WebpackConfig.test.js} (99%) rename test/{config-generator.js => config-generator.test.js} (98%) delete mode 100644 test/functional.js create mode 100644 test/functional.test.js rename test/{index.js => index.test.js} (99%) rename test/{logger.js => logger.test.js} (95%) rename test/{package-helper.js => package-helper.test.js} (99%) diff --git a/test/WebpackConfig.js b/test/WebpackConfig.test.js similarity index 99% rename from test/WebpackConfig.js rename to test/WebpackConfig.test.js index 61817296..ae9b0818 100644 --- a/test/WebpackConfig.js +++ b/test/WebpackConfig.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; const WebpackConfig = require('../lib/WebpackConfig'); const RuntimeConfig = require('../lib/config/RuntimeConfig'); const path = require('path'); diff --git a/test/bin/encore.test.js b/test/bin/encore.test.js index 4a8e7a28..05c27fe7 100644 --- a/test/bin/encore.test.js +++ b/test/bin/encore.test.js @@ -308,26 +308,18 @@ module.exports = Encore.getWebpackConfig(); ); const binPath = path.resolve(projectDir, 'bin', 'encore.js'); - try { - await execAsync(`node ${binPath} dev-server --context=${testDir}`, { - cwd: testDir, - env: Object.assign({}, process.env, { NO_COLOR: 'true' }) - }); - - throw new Error('Expected command to fail.'); - } catch (err) { - if (!'stdout' in err || !'stderr' in err) { - throw new Error(`Error executing encore: ${err}`); - } + const { stdout, stderr } = await execAsync(`node ${binPath} dev-server --context=${testDir}`, { + cwd: testDir, + env: Object.assign({}, process.env, { NO_COLOR: 'true' }) + }); - expect(err.stdout).to.contain('Install webpack-dev-server to use the webpack Development Server'); - expect(err.stdout).to.contain('npm install webpack-dev-server --save-dev'); - expect(err.stderr).to.equal(''); + expect(stdout).to.contain('Install webpack-dev-server to use the webpack Development Server'); + expect(stdout).to.contain('npm install webpack-dev-server --save-dev'); + expect(stderr).to.equal(''); - expect(err.stdout).not.to.contain('Running webpack-dev-server ...'); - expect(err.stdout).not.to.contain('Compiled successfully in'); - expect(err.stdout).not.to.contain('webpack compiled successfully'); - } + expect(stdout).not.to.contain('Running webpack-dev-server ...'); + expect(stdout).not.to.contain('Compiled successfully in'); + expect(stdout).not.to.contain('webpack compiled successfully'); }); }); }); diff --git a/test/config-generator.js b/test/config-generator.test.js similarity index 98% rename from test/config-generator.js rename to test/config-generator.test.js index 920fd0ea..cf72a98c 100644 --- a/test/config-generator.js +++ b/test/config-generator.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect, beforeEach, beforeAll, afterAll } from 'vitest'; const WebpackConfig = require('../lib/WebpackConfig'); const RuntimeConfig = require('../lib/config/RuntimeConfig'); const configGenerator = require('../lib/config-generator'); @@ -965,23 +965,25 @@ describe('The config-generator function', () => { }); describe('Test shouldSplitEntryChunks', () => { - const config = createConfig(); - config.outputPath = '/tmp/public/build'; - config.setPublicPath('/build/'); - config.splitEntryChunks(); - - const actualConfig = configGenerator(config); - expect(actualConfig.optimization.splitChunks.chunks).to.equal('all'); - expect(actualConfig.optimization.splitChunks.name).to.be.undefined; + it('with defaults', () => { + const config = createConfig(); + config.outputPath = '/tmp/public/build'; + config.setPublicPath('/build/'); + config.splitEntryChunks(); + + const actualConfig = configGenerator(config); + expect(actualConfig.optimization.splitChunks.chunks).to.equal('all'); + expect(actualConfig.optimization.splitChunks.name).to.be.undefined; + }) }); describe('Test shouldUseSingleRuntimeChunk', () => { - before(() => { + beforeAll(() => { logger.reset(); logger.quiet(); }); - after(() => { + afterAll(() => { logger.quiet(false); }); diff --git a/test/functional.js b/test/functional.js deleted file mode 100644 index 725d8270..00000000 --- a/test/functional.js +++ /dev/null @@ -1,3160 +0,0 @@ -/* - * This file is part of the Symfony Webpack Encore package. - * - * (c) Fabien Potencier - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -'use strict'; - -const chai = require('chai'); -chai.use(require('chai-fs')); -chai.use(require('chai-subset')); -const expect = chai.expect; -const path = require('path'); -const testSetup = require('./helpers/setup'); -const fs = require('fs-extra'); -const getVueVersion = require('../lib/utils/get-vue-version'); -const packageHelper = require('../lib/package-helper'); -const semver = require('semver'); -const puppeteer = require('puppeteer'); - -function createWebpackConfig(outputDirName = '', command, argv = {}) { - const webpackConfig = testSetup.createWebpackConfig( - testSetup.createTestAppDir(), - outputDirName, - command, - argv - ); - - webpackConfig.enableSingleRuntimeChunk(); - - return webpackConfig; -} - -function convertToManifestPath(assetSrc, webpackConfig) { - const manifestData = JSON.parse(readOutputFileContents('manifest.json', webpackConfig)); - - if (typeof manifestData[assetSrc] === 'undefined') { - throw new Error(`Path ${assetSrc} not found in manifest!`); - } - - return manifestData[assetSrc]; -} - -function readOutputFileContents(filename, config) { - const fullPath = path.join(config.outputPath, filename); - - if (!fs.existsSync(fullPath)) { - throw new Error(`Output file "${filename}" does not exist.`); - } - - return fs.readFileSync(fullPath, 'utf8'); -} - -function getEntrypointData(config, entryName) { - const entrypointsData = JSON.parse(readOutputFileContents('entrypoints.json', config)); - - if (typeof entrypointsData.entrypoints[entryName] === 'undefined') { - throw new Error(`The entry ${entryName} was not found!`); - } - - return entrypointsData.entrypoints[entryName]; -} - -function getIntegrityData(config) { - const entrypointsData = JSON.parse(readOutputFileContents('entrypoints.json', config)); - if (typeof entrypointsData.integrity === 'undefined') { - throw new Error('The entrypoints.json file does not contain an integrity object!'); - } - - return entrypointsData.integrity; -} - -describe('Functional tests using webpack', function() { - /** @type {import('puppeteer').Browser} */ - let browser; - - // being functional tests, these can take quite long - this.timeout(10000); - - before(function(done) { - puppeteer.launch().then(_browser => { - browser = _browser; - done(); - }); - }); - - after(function(done) { - browser.close().then(done); - }); - - describe('Basic scenarios.', () => { - - it('Builds a few simple entries file + manifest.json', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', './js/no_require'); - config.addStyleEntry('font', './css/roboto_font.css'); - config.addStyleEntry('bg', './css/another_bg_image.css'); - config.setPublicPath('/build'); - - testSetup.runWebpack(config, (webpackAssert) => { - // should have a main.js file - // should have a manifest.json with public/main.js - - expect(config.outputPath).to.be.a.directory().with.deep.files([ - 'runtime.js', - 'main.js', - 'font.css', - 'bg.css', - 'fonts/Roboto.e1dcc0db.woff2', - 'images/symfony_logo.91beba37.png', - 'manifest.json', - 'entrypoints.json' - ]); - - // check that main.js has the correct contents - webpackAssert.assertOutputFileContains( - 'main.js', - 'i am the no_require.js file' - ); - // check that main.js has the webpack bootstrap - webpackAssert.assertOutputFileContains( - 'runtime.js', - '__webpack_require__' - ); - webpackAssert.assertManifestPath( - 'build/main.js', - '/build/main.js' - ); - webpackAssert.assertManifestPath( - 'build/font.css', - '/build/font.css' - ); - webpackAssert.assertManifestPath( - 'build/fonts/Roboto.woff2', - '/build/fonts/Roboto.e1dcc0db.woff2' - ); - webpackAssert.assertManifestPath( - 'build/images/symfony_logo.png', - '/build/images/symfony_logo.91beba37.png' - ); - - webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { - entrypoints: { - main: { - js: ['/build/runtime.js', '/build/main.js'] - }, - font: { - js: ['/build/runtime.js'], - css: ['/build/font.css'] - }, - bg: { - js: ['/build/runtime.js'], - css: ['/build/bg.css'] - } - } - }); - - done(); - }); - }); - - it('Check manifest.json with node_module includes', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', './js/import_node_modules_image'); - config.setPublicPath('/build'); - - testSetup.runWebpack(config, (webpackAssert) => { - // should have a main.js file - // should have a manifest.json with public/main.js - - webpackAssert.assertOutputJsonFileMatches('manifest.json', { - 'build/main.js': '/build/main.js', - 'build/runtime.js': '/build/runtime.js', - 'build/images/symfony_logo.png': '/build/images/symfony_logo.91beba37.png', - 'build/images/logo.png': '/build/images/logo.cb197657.png', - }); - - done(); - }); - }); - - it('Use "all" splitChunks & look at entrypoints.json', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); - config.addEntry('other', ['./css/roboto_font.css', 'vue']); - config.setPublicPath('/build'); - config.configureSplitChunks((splitChunks) => { - splitChunks.chunks = 'all'; - splitChunks.minSize = 0; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { - entrypoints: { - main: { - js: [ - '/build/runtime.js', - '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', - '/build/css_roboto_font_css.js', - '/build/main.js' - ], - css: ['/build/css_roboto_font_css.css'] - }, - other: { - js: [ - '/build/runtime.js', - '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', - '/build/css_roboto_font_css.js', - '/build/other.js' - ], - css: ['/build/css_roboto_font_css.css'] - } - } - }); - - done(); - }); - }); - - it('Disable the runtime chunk', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', './js/no_require'); - config.disableSingleRuntimeChunk(); - config.setPublicPath('/build'); - - testSetup.runWebpack(config, (webpackAssert) => { - // no runtime.js - expect(config.outputPath).to.be.a.directory().with.deep.files([ - 'main.js', - 'manifest.json', - 'entrypoints.json' - ]); - - done(); - }); - }); - - it('setPublicPath with CDN loads assets from the CDN', (done) => { - const config = createWebpackConfig('public/assets', 'dev'); - config.addEntry('main', './js/code_splitting'); - config.addStyleEntry('font', './css/roboto_font.css'); - config.addStyleEntry('bg', './css/background_image.scss'); - config.setPublicPath('http://127.0.0.1:8090/assets'); - config.enableSassLoader(); - config.setManifestKeyPrefix('assets'); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'js_no_require_js-css_h1_style_css.css', - 'js_no_require_js-css_h1_style_css.js', - 'main.js', - 'runtime.js', - 'font.css', - 'bg.css', - 'manifest.json', - 'entrypoints.json' - ]); - - // check that the publicPath is set correctly - webpackAssert.assertOutputFileContains( - 'runtime.js', - '__webpack_require__.p = "http://127.0.0.1:8090/assets/";' - ); - - webpackAssert.assertOutputFileContains( - 'bg.css', - 'http://127.0.0.1:8090/assets/images/symfony_logo.91beba37.png' - ); - webpackAssert.assertOutputFileContains( - 'font.css', - 'http://127.0.0.1:8090/assets/fonts/Roboto.e1dcc0db.woff2' - ); - // manifest file has CDN in value - webpackAssert.assertManifestPath( - 'assets/main.js', - 'http://127.0.0.1:8090/assets/main.js' - ); - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'public'), - [ - // purposely load this NOT from the CDN - 'assets/runtime.js', - 'assets/main.js' - ], - ({ loadedResources }) => { - webpackAssert.assertResourcesLoadedCorrectly(loadedResources, [ - 'js_no_require_js-css_h1_style_css.css', - 'js_no_require_js-css_h1_style_css.js', - // guarantee that we assert that main.js is loaded from the - // main server, as it's simply a script tag to main.js on the page - // we did this to check that the internally-loaded assets - // use the CDN, even if the entry point does not - 'http://127.0.0.1:8080/assets/runtime.js', - 'http://127.0.0.1:8080/assets/main.js' - ]); - - done(); - } - ); - }); - }); - - it('The devServer config loads successfully', (done) => { - const config = createWebpackConfig('public/assets', 'dev-server', { - port: '8090', - host: '127.0.0.1', - }); - config.addEntry('main', './js/code_splitting'); - config.addStyleEntry('font', './css/roboto_font.css'); - config.addStyleEntry('bg', './css/background_image.scss'); - config.setPublicPath('/assets'); - config.enableSassLoader(); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that the publicPath is set correctly - webpackAssert.assertOutputFileContains( - 'runtime.js', - '__webpack_require__.p = "http://127.0.0.1:8090/assets/";' - ); - - webpackAssert.assertOutputFileContains( - 'bg.css', - 'http://127.0.0.1:8090/assets/images/symfony_logo.91beba37.png' - ); - // manifest file has CDN in value - webpackAssert.assertManifestPath( - 'assets/main.js', - 'http://127.0.0.1:8090/assets/main.js' - ); - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'public'), - [ - convertToManifestPath('assets/runtime.js', config), - convertToManifestPath('assets/main.js', config) - ], - ({ loadedResources }) => { - webpackAssert.assertResourcesLoadedCorrectly(loadedResources, [ - 'runtime.js', - 'main.js', - 'js_no_require_js-css_h1_style_css.css', - 'js_no_require_js-css_h1_style_css.js', - ]); - - done(); - } - ); - }); - }); - - it('Deploying to a subdirectory is no problem', (done) => { - const config = createWebpackConfig('subdirectory/build', 'dev'); - config.addEntry('main', './js/code_splitting'); - config.setPublicPath('/subdirectory/build'); - config.setManifestKeyPrefix('build'); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertManifestPath( - 'build/main.js', - '/subdirectory/build/main.js' - ); - - testSetup.requestTestPage( - browser, - // the webroot will not include the /subdirectory/build part - path.join(config.getContext(), ''), - [ - convertToManifestPath('build/runtime.js', config), - convertToManifestPath('build/main.js', config) - ], - ({ loadedResources }) => { - webpackAssert.assertResourcesLoadedCorrectly(loadedResources, [ - 'http://127.0.0.1:8080/subdirectory/build/runtime.js', - 'http://127.0.0.1:8080/subdirectory/build/main.js', - 'http://127.0.0.1:8080/subdirectory/build/js_no_require_js-css_h1_style_css.js', - 'http://127.0.0.1:8080/subdirectory/build/js_no_require_js-css_h1_style_css.css', - ]); - - done(); - } - ); - }); - }); - - it('Empty manifestKeyPrefix is allowed', (done) => { - const config = createWebpackConfig('build', 'dev'); - config.addEntry('main', './js/code_splitting'); - config.setPublicPath('/build'); - config.setManifestKeyPrefix(''); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertManifestPath( - 'main.js', - '/build/main.js' - ); - - done(); - }); - }); - - it('.mjs files are supported natively', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', './js/hello_world'); - config.setPublicPath('/build'); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that main.js has the correct contents - webpackAssert.assertOutputFileContains( - 'main.js', - 'Hello World!' - ); - - done(); - }); - }); - - describe('addStyleEntry .js files are removed', () => { - it('Without versioning', (done) => { - const config = createWebpackConfig('web', 'dev'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/'); - config.addStyleEntry('styles', './css/h1_style.css'); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - // public.js should not exist - .with.files(['main.js', 'styles.css', 'manifest.json', 'entrypoints.json', 'runtime.js']); - - webpackAssert.assertOutputFileContains( - 'styles.css', - 'font-size: 50px;' - ); - webpackAssert.assertManifestPathDoesNotExist( - 'styles.js' - ); - webpackAssert.assertManifestPath( - 'styles.css', - '/styles.css' - ); - - done(); - }); - }); - - it('With default versioning', (done) => { - const config = createWebpackConfig('web', 'dev'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/'); - config.addStyleEntry('styles', './css/h1_style.css'); - config.enableVersioning(true); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertDirectoryContents([ - 'main.[hash:8].js', - 'styles.[hash:8].css', - 'manifest.json', - 'entrypoints.json', - 'runtime.[hash:8].js', - ]); - - webpackAssert.assertOutputFileContains( - 'styles.[hash:8].css', - 'font-size: 50px;' - ); - webpackAssert.assertManifestPathDoesNotExist( - 'styles.js' - ); - webpackAssert.assertManifestPath( - 'styles.css', - '/styles.[hash:8].css' - ); - - done(); - }); - }); - - it('With query string versioning', (done) => { - const config = createWebpackConfig('web', 'dev'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/'); - config.addStyleEntry('styles', './css/h1_style.css'); - config.enableVersioning(true); - config.configureFilenames({ - js: '[name].js?[contenthash:16]', - css: '[name].css?[contenthash:16]' - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files(['main.js', 'styles.css', 'manifest.json', 'entrypoints.json', 'runtime.js']); - - webpackAssert.assertOutputFileContains( - 'styles.css', - 'font-size: 50px;' - ); - webpackAssert.assertManifestPathDoesNotExist( - 'styles.js' - ); - webpackAssert.assertManifestPath( - 'styles.css', - '/styles.css?[hash:16]' - ); - - done(); - }); - }); - - it('With source maps in production mode', (done) => { - const config = createWebpackConfig('web', 'production'); - config.addEntry('main', './js/arrow_function'); - config.setPublicPath('/'); - config.addStyleEntry('styles', './css/h1_style.css'); - config.enableSourceMaps(true); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'main.js', - 'main.js.map', - 'styles.css', - 'styles.css.map', - 'manifest.json', - 'entrypoints.json', - 'runtime.js', - 'runtime.js.map', - // no styles.js - // no styles.js.map - ]); - - webpackAssert.assertManifestPathDoesNotExist( - 'styles.js' - ); - - webpackAssert.assertManifestPathDoesNotExist( - 'styles.js.map' - ); - - done(); - }); - }); - }); - - it('enableVersioning applies to js, css & manifest', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', './js/code_splitting'); - config.setPublicPath('/build'); - config.addStyleEntry('h1', './css/h1_style.css'); - config.addStyleEntry('bg', './css/another_bg_image.css'); - config.enableSassLoader(); - config.enableVersioning(true); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertDirectoryContents([ - 'js_no_require_js-css_h1_style_css.[hash:8].js', // chunks are also versioned - 'js_no_require_js-css_h1_style_css.[hash:8].css', - 'main.[hash:8].js', - 'h1.[hash:8].css', - 'bg.[hash:8].css', - 'manifest.json', - 'entrypoints.json', - 'runtime.[hash:8].js', - ]); - - expect(path.join(config.outputPath, 'images')).to.be.a.directory() - .with.files([ - 'symfony_logo.91beba37.png' - ]); - - webpackAssert.assertOutputFileContains( - 'bg.[hash:8].css', - '/build/images/symfony_logo.91beba37.png' - ); - - done(); - }); - }); - - it('font and image files are copied correctly', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addStyleEntry('bg', './css/background_image.scss'); - config.addStyleEntry('font', './css/roboto_font.css'); - config.enableSassLoader(options => { - // Use sass-loader instead of node-sass - options.implementation = require('sass'); - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'bg.css', - 'font.css', - 'manifest.json', - 'entrypoints.json', - 'runtime.js', - ]); - - expect(path.join(config.outputPath, 'images')).to.be.a.directory() - .with.files([ - 'symfony_logo.91beba37.png' - ]); - - expect(path.join(config.outputPath, 'fonts')).to.be.a.directory() - .with.files([ - 'Roboto.e1dcc0db.woff2' - ]); - - webpackAssert.assertOutputFileContains( - 'bg.css', - '/build/images/symfony_logo.91beba37.png' - ); - - webpackAssert.assertOutputFileContains( - 'font.css', - '/build/fonts/Roboto.e1dcc0db.woff2' - ); - - done(); - }); - }); - - it('two fonts or images with the same filename should not output a single file', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addStyleEntry('styles', './css/same_filename.css'); - config.enableSassLoader(); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'styles.css', - 'manifest.json', - 'entrypoints.json', - 'runtime.js', - ]); - - expect(path.join(config.outputPath, 'images')).to.be.a.directory() - .with.files([ - 'symfony_logo.91beba37.png', - 'symfony_logo.f880ba14.png' - ]); - - expect(path.join(config.outputPath, 'fonts')).to.be.a.directory() - .with.files([ - 'Roboto.e1dcc0db.woff2', - 'Roboto.2779fd7b.woff2' - ]); - - webpackAssert.assertOutputFileContains( - 'styles.css', - '/build/images/symfony_logo.91beba37.png' - ); - - webpackAssert.assertOutputFileContains( - 'styles.css', - '/build/images/symfony_logo.f880ba14.png' - ); - - webpackAssert.assertOutputFileContains( - 'styles.css', - '/build/fonts/Roboto.e1dcc0db.woff2' - ); - - webpackAssert.assertOutputFileContains( - 'styles.css', - '/build/fonts/Roboto.2779fd7b.woff2' - ); - - done(); - }); - }); - - it('enableSourceMaps() adds to .js, css & scss', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', './js/no_require'); - config.addStyleEntry('bg', './css/background_image.scss'); - config.addStyleEntry('font', './css/roboto_font.css'); - config.enableSassLoader(); - config.enableSourceMaps(); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertOutputFileHasSourcemap( - 'main.js' - ); - webpackAssert.assertOutputFileHasSourcemap( - 'bg.css' - ); - webpackAssert.assertOutputFileHasSourcemap( - 'font.css' - ); - - done(); - }); - }); - - it('Without enableSourceMaps(), there are no sourcemaps', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', './js/no_require'); - config.addStyleEntry('bg', './css/background_image.scss'); - config.addStyleEntry('font', './css/roboto_font.css'); - config.enableSassLoader(); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertOutputFileDoesNotHaveSourcemap( - 'main.js' - ); - webpackAssert.assertOutputFileDoesNotHaveSourcemap( - 'bg.css' - ); - webpackAssert.assertOutputFileDoesNotHaveSourcemap( - 'font.css' - ); - - done(); - }); - }); - - it('Without enableSourceMaps(), there are no sourcemaps in production', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.setPublicPath('/build'); - config.addEntry('main', './js/no_require'); - config.addStyleEntry('bg', './css/background_image.scss'); - config.addStyleEntry('font', './css/roboto_font.css'); - config.enableSassLoader(); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertOutputFileDoesNotHaveSourcemap( - 'main.js' - ); - webpackAssert.assertOutputFileDoesNotHaveSourcemap( - 'font.css' - ); - webpackAssert.assertOutputFileDoesNotHaveSourcemap( - 'bg.css' - ); - - done(); - }); - }); - - it('Code splitting a scss file works', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - // loads sass_features.scss via require.ensure - config.addEntry('main', './js/code_split_load_scss'); - config.enableSassLoader(); - - testSetup.runWebpack(config, (webpackAssert) => { - // make sure sass is parsed - webpackAssert.assertOutputFileContains( - 'css_sass_features_scss.css', - 'color: #333' - ); - // and imported files are loaded correctly - webpackAssert.assertOutputFileContains( - 'css_sass_features_scss.css', - 'background: top left' - ); - - done(); - }); - }); - - describe('addCacheGroup()', () => { - it('addCacheGroup() to extract a vendor into its own chunk', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.enableVueLoader(); - config.enablePreactPreset(); - config.enableSassLoader(); - config.enableLessLoader(); - config.addEntry('page1', `./vuejs/main_v${getVueVersion(config)}`); - config.addEntry('page2', './preact/main'); - - // Move Vue.js code into its own chunk - config.addCacheGroup('vuejs', { - test: /[\\/]node_modules[\\/]@vue[\\/]/ - }); - - testSetup.runWebpack(config, (webpackAssert) => { - // Vue.js code should be present in common.js but not in page1.js/page2.js - webpackAssert.assertOutputFileContains( - 'vuejs.js', - '/***/ "../../node_modules/@vue/' - ); - - webpackAssert.assertOutputFileDoesNotContain( - 'page1.js', - '/***/ "../../node_modules/@vue/' - ); - - webpackAssert.assertOutputFileDoesNotContain( - 'page2.js', - '/***/ "../../node_modules/@vue/' - ); - - // Preact code should be present in page2.js only - webpackAssert.assertOutputFileDoesNotContain( - 'vuejs.js', - '/***/ "../../node_modules/preact/' - ); - - webpackAssert.assertOutputFileDoesNotContain( - 'page1.js', - '/***/ "../../node_modules/preact/' - ); - - webpackAssert.assertOutputFileContains( - 'page2.js', - '/***/ "../../node_modules/preact/' - ); - - // Check if each entrypoint is associated to the right chunks - webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { - entrypoints: { - page1: { - js: ['/build/runtime.js', '/build/vuejs.js', '/build/page1.js'], - css: ['/build/page1.css'] - }, - page2: { - js: ['/build/runtime.js', '/build/page2.js'] - } - } - }); - - // Check if Vue.js code is still executed properly - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/page1.js', - 'build/vuejs.js', - ], - async({ page }) => { - const bodyText = await page.evaluate(() => document.querySelector('#app').textContent); - expect(bodyText).to.contains('Welcome to Your Vue.js App'); - - done(); - } - ); - }); - }); - - it('addCacheGroup() with node_modules', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.enableVueLoader(); - config.enablePreactPreset(); - config.enableSassLoader(); - config.enableLessLoader(); - config.addEntry('page1', `./vuejs/main_v${getVueVersion(config)}`); - config.addEntry('page2', './preact/main'); - - // Move both vue.js and preact code into their own chunk - config.addCacheGroup('common', { - node_modules: [ - '@vue', - 'preact' - ] - }); - - testSetup.runWebpack(config, (webpackAssert) => { - // Vue.js code should be present in common.js but not in page1.js/page2.js - webpackAssert.assertOutputFileContains( - 'common.js', - '/***/ "../../node_modules/@vue/' - ); - - webpackAssert.assertOutputFileDoesNotContain( - 'page1.js', - '/***/ "../../node_modules/@vue/' - ); - - webpackAssert.assertOutputFileDoesNotContain( - 'page2.js', - '/***/ "../../node_modules/@vue/' - ); - - // Preact code should be present in common.js but not in page1.js/page2.js - webpackAssert.assertOutputFileContains( - 'common.js', - '/***/ "../../node_modules/preact/' - ); - - webpackAssert.assertOutputFileDoesNotContain( - 'page1.js', - '/***/ "../../node_modules/preact/' - ); - - webpackAssert.assertOutputFileDoesNotContain( - 'page2.js', - '/***/ "../../node_modules/preact/' - ); - - // Check if each entrypoint is associated to the right chunks - webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { - entrypoints: { - page1: { - js: ['/build/runtime.js', '/build/common.js', '/build/page1.js'], - css: ['/build/page1.css'] - }, - page2: { - js: ['/build/runtime.js', '/build/common.js', '/build/page2.js'] - } - } - }); - - // Check if Preact code is still executed properly - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/page2.js', - 'build/common.js', - ], - async({ page }) => { - expect(await page.evaluate(() => document.querySelector('#app').textContent)).to.contains('This is a React component!'); - done(); - } - ); - }); - }); - - it('addCacheGroup() with versioning enabled', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.enableVersioning(); - config.enableVueLoader(); - config.enablePreactPreset(); - config.enableSassLoader(); - config.enableLessLoader(); - config.addEntry('page1', `./vuejs/main_v${getVueVersion(config)}`); - config.addEntry('page2', './preact/main'); - - // Move Vue.js code into its own chunk - config.addCacheGroup('vuejs', { - test: getVueVersion(config) === 2 ? - /[\\/]node_modules[\\/]vue[\\/]/ : - /[\\/]node_modules[\\/]@vue[\\/]/ - }); - - testSetup.runWebpack(config, (webpackAssert) => { - // Check if Vue.js code is still executed properly - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - convertToManifestPath('build/runtime.js', config), - convertToManifestPath('build/page1.js', config), - convertToManifestPath('build/vuejs.js', config), - ], - async({ page }) => { - const bodyText = await page.evaluate(() => document.querySelector('#app').textContent); - expect(bodyText).to.contains('Welcome to Your Vue.js App'); - - done(); - } - ); - }); - }); - - it('addCacheGroup() with source maps enabled', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.enableSourceMaps(); - config.enableVueLoader(); - config.enablePreactPreset(); - config.enableSassLoader(); - config.enableLessLoader(); - config.addEntry('page1', `./vuejs/main_v${getVueVersion(config)}`); - config.addEntry('page2', './preact/main'); - - // Move Vue.js code into its own chunk - config.addCacheGroup('vuejs', { - test: getVueVersion(config) === 2 ? - /[\\/]node_modules[\\/]vue[\\/]/ : - /[\\/]node_modules[\\/]@vue[\\/]/ - }); - - testSetup.runWebpack(config, (webpackAssert) => { - // Check if Vue.js code is still executed properly - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/page1.js', - 'build/vuejs.js', - ], - async({ page }) => { - const bodyText = await page.evaluate(() => document.querySelector('#app').textContent); - expect(bodyText).to.contains('Welcome to Your Vue.js App'); - - done(); - } - ); - }); - }); - }); - - it('in production mode, code is uglified', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.setPublicPath('/build'); - config.addEntry('main', ['./js/no_require']); - config.addEntry('styles', './css/h1_style.css'); - - testSetup.runWebpack(config, (webpackAssert) => { - // the comment should not live in the file - webpackAssert.assertOutputFileDoesNotContain( - 'main.js', - '// comments in no_require.js' - ); - // check for any webpack-added comments - webpackAssert.assertOutputFileDoesNotContain( - 'main.js', - '/*!' - ); - // extra spaces should not live in the CSS file - webpackAssert.assertOutputFileDoesNotContain( - 'styles.css', - ' font-size: 50px;' - ); - - done(); - }); - }); - - it('PostCSS works when enabled', (done) => { - const appDir = testSetup.createTestAppDir(); - - fs.writeFileSync( - path.join(appDir, 'postcss.config.js'), - ` -module.exports = { - plugins: [ - require('autoprefixer')() - ] -} - ` - ); - - const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); - config.enableSingleRuntimeChunk(); - config.setPublicPath('/build'); - // load a file that @import's another file, so that we can - // test that @import resources are parsed through postcss - config.addStyleEntry('styles', ['./css/imports_autoprefixer.css']); - config.addStyleEntry('postcss', './css/postcss_extension.postcss'); - config.enablePostCssLoader(); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that the autoprefixer did its work! - webpackAssert.assertOutputFileContains( - 'styles.css', - '-webkit-backdrop-filter' - ); - - // check that the .postcss file was also processed - // correctly (it also @import the autoprefixer_test.css - // file) - webpackAssert.assertOutputFileContains( - 'postcss.css', - '-webkit-backdrop-filter' - ); - - done(); - }); - }); - - it('less processes when enabled', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addStyleEntry('styles', ['./css/h2_styles.less']); - config.enableLessLoader(); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that less did its work! - webpackAssert.assertOutputFileContains( - 'styles.css', - // less logic inside will resolve to tis - 'color: #fe33ac;' - ); - - done(); - }); - }); - - it('stylus processes when enabled', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addStyleEntry('styles', ['./css/h2_styles.styl']); - config.enableStylusLoader(); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that stylus did its work! - webpackAssert.assertOutputFileContains( - 'styles.css', - // stylus logic inside will resolve to tis - 'color: #9e9399;' - ); - - done(); - }); - }); - - it('Babel is executed on .js files', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', './js/class-syntax'); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that babel transformed the class - webpackAssert.assertOutputFileDoesNotContain( - 'main.js', - 'class A {}' - ); - - done(); - }); - }); - - it('Babel can be configured via .babelrc', (done) => { - // create the .babelrc file first, so we see it - const appDir = testSetup.createTestAppDir(); - - fs.writeFileSync( - path.join(appDir, '.babelrc'), - ` -{ - "presets": [ - ["@babel/preset-env", { - "targets": { - "chrome": 52 - } - }] - ] -} -` - ); - - const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); - config.enableSingleRuntimeChunk(); - config.setPublicPath('/build'); - config.addEntry('main', './js/class-syntax'); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that babel transformed classes - webpackAssert.assertOutputFileContains( - 'main.js', - // chrome 45 supports class, so it's not transpiled - 'class A {}' - ); - - done(); - }); - }); - - it('Babel can be configured via package.json browserlist', (done) => { - const cwd = process.cwd(); - after(() => { - process.chdir(cwd); - }); - - const appDir = testSetup.createTestAppDir(); - /* - * Most of the time, we don't explicitly use chdir - * in order to set the cwd() to the test directory. - * That's because, in theory, you should be able to - * run Encore from other directories, give you set - * the context. However, in this case, babel/presest-env - * uses process.cwd() to find the configPath, instead of the - * context. So, in this case, we *must* set the cwd() - * to be the temp test directory. - */ - process.chdir(appDir); - - // create the package.json file first, so we see it - fs.writeFileSync( - path.join(appDir, 'package.json'), - ` -{ - "browserslist": "Chrome 52" -} -` - ); - - const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); - config.enableSingleRuntimeChunk(); - config.setPublicPath('/build'); - config.addEntry('main', './js/class-syntax'); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that babel did not transform classes - webpackAssert.assertOutputFileContains( - 'main.js', - // chrome 45 supports class, so it's not transpiled - 'class A {}' - ); - - done(); - }); - }); - - it('Babel adds polyfills correctly', (done) => { - const cwd = process.cwd(); - after(() => { - process.chdir(cwd); - }); - - const appDir = testSetup.createTestAppDir(); - process.chdir(appDir); - - fs.writeFileSync( - path.join(appDir, 'package.json'), - - // The test case uses Array.flat which - // isn't supported by IE11 - '{"browserslist": "IE 11"}' - ); - - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('commonjs', './js/import_polyfills_commonjs.js'); - config.addEntry('ecmascript', './js/import_polyfills_ecmascript.js'); - config.configureBabel(null, { - useBuiltIns: 'usage', - corejs: 3, - }); - - testSetup.runWebpack(config, async(webpackAssert) => { - for (const scriptName of ['commonjs.js', 'ecmascript.js']) { - // Check that the polyfills are included correctly - // in both files. - webpackAssert.assertOutputFileContains( - scriptName, - 'Array.prototype.flat' - ); - - // Test that the generated scripts work fine - await testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - `build/${scriptName}`, - ], - async({ page }) => { - expect(await page.evaluate(() => document.body.textContent)).to.contains('[1,2,3,4]'); - } - ); - } - - done(); - }); - }); - - it('Babel does not force transforms if they are not needed', (done) => { - const cwd = process.cwd(); - after(() => { - process.chdir(cwd); - }); - - const appDir = testSetup.createTestAppDir(); - process.chdir(appDir); - - fs.writeFileSync( - path.join(appDir, 'package.json'), - - // Chrome 55 supports async and arrow functions - '{"browserslist": "Chrome 55"}' - ); - - const config = createWebpackConfig('www/build', 'prod'); - config.setPublicPath('/build'); - config.addEntry('async', './js/async_function.js'); - config.configureBabel(null, { - useBuiltIns: 'usage', - corejs: 3, - }); - - testSetup.runWebpack(config, async(webpackAssert) => { - webpackAssert.assertOutputFileContains( - 'async.js', - 'async function(){console.log("foo")}().then((()=>{console.log("bar")}))' - ); - - done(); - }); - }); - - it('When enabled, react JSX is transformed!', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', './js/CoolReactComponent.jsx'); - config.enableReactPreset(); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that babel transformed the JSX - webpackAssert.assertOutputFileContains( - 'main.js', - 'React.createElement' - ); - - done(); - }); - }); - - it('When enabled, preact JSX is transformed without preact-compat!', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', './js/CoolReactComponent.jsx'); - config.enablePreactPreset(); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that babel transformed the JSX - webpackAssert.assertOutputFileContains( - 'main.js', - 'var hiGuys = h(' - ); - - done(); - }); - }); - - it('When enabled, svelte is transformed', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', './js/hello_world.svelte'); - config.enableSvelte(); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that babel transformed the svelte files - webpackAssert.assertOutputFileContains( - 'main.js', - 'SvelteComponent' - ); - - done(); - }); - }); - - it('When enabled, preact JSX is transformed with preact-compat!', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', './js/CoolReactComponent.jsx'); - config.enablePreactPreset({ preactCompat: true }); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that babel transformed the JSX - webpackAssert.assertOutputFileContains( - 'main.js', - 'React.createElement' - ); - - done(); - }); - }); - - it('When configured, TypeScript is compiled!', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', ['./js/index.ts']); - const testCallback = () => {}; - config.enableTypeScriptLoader(testCallback); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that ts-loader transformed the ts file - webpackAssert.assertOutputFileContains( - 'main.js', - 'document.getElementById(\'app\').innerHTML =' - ); - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/main.js' - ], - async({ page }) => { - // assert that the ts module rendered - const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); - expect(h1Text).to.contains('Welcome to Your TypeScript App'); - - done(); - } - ); - }); - }); - - it('TypeScript is compiled and type checking is done in a separate process!', (done) => { - this.timeout(10000); - setTimeout(done, 9000); - - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', ['./js/render.ts', './js/index.ts']); - config.enableTypeScriptLoader(); - // test should fail if `config.typescript.configFile` is not set up properly - config.enableForkedTypeScriptTypesChecking((config) => { - - }); - - expect(function() { - testSetup.runWebpack(config, (webpackAssert) => { - done(); - }); - // Cannot find the "/path/to/tsconfig.json" file - }).to.throw('Cannot find the'); - }); - - it('TypeScript can be compiled by Babel', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', ['./js/render.ts', './js/index.ts']); - config.enableBabelTypeScriptPreset(); - - testSetup.runWebpack(config, (webpackAssert) => { - // check that babel-loader transformed the ts file - webpackAssert.assertOutputFileContains( - 'main.js', - 'document.getElementById(\'app\').innerHTML =', - ); - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/main.js', - ], - async({ page }) => { - // assert that the ts module rendered - const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); - expect(h1Text).to.contains('Welcome to Your TypeScript App'); - - done(); - }, - ); - }); - }); - - it('When configured, Handlebars is compiled', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', ['./js/handlebars.js']); - const testCallback = () => {}; - config.enableHandlebarsLoader(testCallback); - - testSetup.runWebpack(config, () => { - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/main.js' - ], - async({ page }) => { - const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); - expect(h1Text).to.contains('Welcome to Your Handlebars App'); - - done(); - } - ); - }); - }); - - it('The output directory is cleaned between builds', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', './js/no_require'); - config.cleanupOutputBeforeBuild(); - testSetup.touchFileInOutputDir('file.txt', config); - testSetup.touchFileInOutputDir('deeper/other.txt', config); - - testSetup.runWebpack(config, (webpackAssert) => { - // make sure the file was cleaned up! - webpackAssert.assertOutputFileDoesNotExist( - 'file.txt' - ); - webpackAssert.assertOutputFileDoesNotExist( - 'deeper/other.txt' - ); - - done(); - }); - }); - - it('Vue.js is compiled correctly', (done) => { - const appDir = testSetup.createTestAppDir(); - - fs.writeFileSync( - path.join(appDir, 'postcss.config.js'), - ` -module.exports = { - plugins: [ - require('autoprefixer')() - ] -} ` - ); - - const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); - config.enableSingleRuntimeChunk(); - config.setPublicPath('/build'); - config.addEntry('main', `./vuejs/main_v${getVueVersion(config)}`); - config.enableVueLoader(); - config.enableSassLoader(); - config.enableLessLoader(); - config.configureBabel(function(config) { - config.presets = [ - ['@babel/preset-env', { - 'targets': { - 'chrome': 52 - } - }] - ]; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory().with.deep.files([ - 'main.js', - 'main.css', - 'images/logo.26bd867d.png', - 'manifest.json', - 'entrypoints.json', - 'runtime.js', - ]); - - // test that our custom babel config is used - webpackAssert.assertOutputFileContains( - 'main.js', - 'class TestClassSyntax' - ); - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/main.js' - ], - async({ page }) => { - // assert that the vue.js app rendered - const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); - expect(h1Text).to.contains('Welcome to Your Vue.js App'); - - // make sure the styles are not inlined - const styleElementsCount = await page.evaluate(() => document.querySelectorAll('style').length); - expect(styleElementsCount).to.equal(0); - - done(); - } - ); - }); - }); - - it('Vue.js is compiled correctly using TypeScript', (done) => { - const appDir = testSetup.createTestAppDir(); - - fs.writeFileSync( - path.join(appDir, 'postcss.config.js'), - ` -module.exports = { - plugins: [ - require('autoprefixer')() - ] -} ` - ); - - const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); - config.enableSingleRuntimeChunk(); - config.setPublicPath('/build'); - config.addEntry('main', `./vuejs${getVueVersion(config)}-typescript/main`); - config.enableVueLoader(); - config.enableSassLoader(); - config.enableLessLoader(); - config.enableTypeScriptLoader(); - config.configureBabel(function(config) { - config.presets = [ - ['@babel/preset-env', { - 'targets': { - 'chrome': 52 - } - }] - ]; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory().with.deep.files([ - 'main.js', - 'main.css', - 'images/logo.26bd867d.png', - 'manifest.json', - 'entrypoints.json', - 'runtime.js', - ]); - - // test that our custom babel config is used - webpackAssert.assertOutputFileContains( - 'main.js', - 'class TestClassSyntax' - ); - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/main.js' - ], - async({ page }) => { - // assert that the vue.js app rendered - const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); - expect(h1Text).to.contains('Welcome to Your Vue.js App'); - - // make sure the styles are not inlined - const styleElementsCount = await page.evaluate(() => document.querySelectorAll('style').length); - expect(styleElementsCount).to.equal(0); - - done(); - } - ); - }); - }); - - it('Vue.js supports CSS/Sass/Less/Stylus/PostCSS modules', (done) => { - const appDir = testSetup.createTestAppDir(); - const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); - config.enableSingleRuntimeChunk(); - config.setPublicPath('/build'); - config.addEntry('main', `./vuejs-css-modules/main_v${getVueVersion(config)}`); - config.enableVueLoader(); - config.enableSassLoader(); - config.enableLessLoader(); - config.enableStylusLoader(); - config.configureCssLoader(options => { - // Until https://github.com/vuejs/vue-loader/pull/1909 is merged, - // Vue users should configure the css-loader modules - // to keep the previous default behavior from css-loader v6 - if (options.modules) { - options.modules.namedExport = false; - options.modules.exportLocalsConvention = 'as-is'; - } - - // Remove hashes from local ident names - // since they are not always the same. - if (options.modules) { - options.modules.localIdentName = '[local]_foo'; - } - }); - - // Enable the PostCSS loader so we can use `lang="postcss"` - config.enablePostCssLoader(); - fs.writeFileSync( - path.join(appDir, 'postcss.config.js'), - ` -module.exports = { - plugins: [ - require('autoprefixer')() - ] -} ` - ); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory().with.deep.files([ - 'main.js', - 'main.css', - 'manifest.json', - 'entrypoints.json', - 'runtime.js', - ]); - - const expectClassDeclaration = (className) => { - webpackAssert.assertOutputFileContains( - 'main.css', - `.${className} {` - ); - }; - - expectClassDeclaration('red'); // Standard CSS - expectClassDeclaration('large'); // Standard SCSS - expectClassDeclaration('justified'); // Standard Less - expectClassDeclaration('lowercase'); // Standard Stylus - expectClassDeclaration('block'); // Standard PostCSS - - expectClassDeclaration('italic_foo'); // CSS Module - expectClassDeclaration('bold_foo'); // SCSS Module - expectClassDeclaration('underline_foo'); // Less Module - expectClassDeclaration('rtl_foo'); // Stylus Module - expectClassDeclaration('hidden_foo'); // PostCSS Module - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/main.js' - ], - async({ page }) => { - const divClassArray = await page.evaluate(() => Array.from(document.body.querySelector('#app > div').classList.values())); - - expect(divClassArray.includes('red')).to.be.true; // Standard CSS - expect(divClassArray.includes('large')).to.be.true; // Standard SCSS - expect(divClassArray.includes('justified')).to.be.true; // Standard Less - expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus - expect(divClassArray.includes('block')).to.be.true; // Standard PostCSS - - expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module - expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module - expect(divClassArray.includes('underline_foo')).to.be.true; // Less module - expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module - expect(divClassArray.includes('hidden_foo')).to.be.true; // PostCSS module - - done(); - } - ); - }); - }); - - it('React supports CSS/Sass/Less/Stylus modules', (done) => { - const appDir = testSetup.createTestAppDir(); - const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); - config.enableSingleRuntimeChunk(); - config.setPublicPath('/build'); - config.addEntry('main', './react-css-modules/main.js'); - config.enableReactPreset(); - config.enableSassLoader(); - config.enableLessLoader(); - config.enableStylusLoader(); - config.configureCssLoader(options => { - // Remove hashes from local ident names - // since they are not always the same. - if (options.modules) { - options.modules.localIdentName = '[local]_foo'; - } - }); - - // Enable the PostCSS loader so we can use `lang="postcss"` - config.enablePostCssLoader(); - fs.writeFileSync( - path.join(appDir, 'postcss.config.js'), - ` -module.exports = { - plugins: [ - require('autoprefixer')() - ] -} ` - ); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory().with.deep.files([ - 'main.js', - 'main.css', - 'manifest.json', - 'entrypoints.json', - 'runtime.js', - ]); - - const expectClassDeclaration = (className) => { - webpackAssert.assertOutputFileContains( - 'main.css', - `.${className} {` - ); - }; - - expectClassDeclaration('red'); // Standard CSS - expectClassDeclaration('large'); // Standard SCSS - expectClassDeclaration('justified'); // Standard Less - expectClassDeclaration('lowercase'); // Standard Stylus - - expectClassDeclaration('italic_foo'); // CSS Module - expectClassDeclaration('bold_foo'); // SCSS Module - expectClassDeclaration('underline_foo'); // Less Module - expectClassDeclaration('rtl_foo'); // Stylus Module - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/main.js' - ], - async({ page }) => { - const divClassArray = await page.evaluate(() => Array.from(document.body.querySelector('#app > div').classList.values())); - - expect(divClassArray.includes('red')).to.be.true; // Standard CSS - expect(divClassArray.includes('large')).to.be.true; // Standard SCSS - expect(divClassArray.includes('justified')).to.be.true; // Standard Less - expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus - - expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module - expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module - expect(divClassArray.includes('underline_foo')).to.be.true; // Less module - expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module - - done(); - } - ); - }); - }); - - it('Preact supports CSS/Sass/Less/Stylus modules', (done) => { - const appDir = testSetup.createTestAppDir(); - const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); - config.enableSingleRuntimeChunk(); - config.setPublicPath('/build'); - config.addEntry('main', './preact-css-modules/main.js'); - config.enablePreactPreset(); - config.enableSassLoader(); - config.enableLessLoader(); - config.enableStylusLoader(); - config.configureCssLoader(options => { - // Remove hashes from local ident names - // since they are not always the same. - if (options.modules) { - options.modules.localIdentName = '[local]_foo'; - } - }); - - // Enable the PostCSS loader so we can use `lang="postcss"` - config.enablePostCssLoader(); - fs.writeFileSync( - path.join(appDir, 'postcss.config.js'), - ` -module.exports = { - plugins: [ - require('autoprefixer')() - ] -} ` - ); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory().with.deep.files([ - 'main.js', - 'main.css', - 'manifest.json', - 'entrypoints.json', - 'runtime.js', - ]); - - const expectClassDeclaration = (className) => { - webpackAssert.assertOutputFileContains( - 'main.css', - `.${className} {` - ); - }; - - expectClassDeclaration('red'); // Standard CSS - expectClassDeclaration('large'); // Standard SCSS - expectClassDeclaration('justified'); // Standard Less - expectClassDeclaration('lowercase'); // Standard Stylus - - expectClassDeclaration('italic_foo'); // CSS Module - expectClassDeclaration('bold_foo'); // SCSS Module - expectClassDeclaration('underline_foo'); // Less Module - expectClassDeclaration('rtl_foo'); // Stylus Module - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/main.js' - ], - async({ page }) => { - const divClassArray = await page.evaluate(() => Array.from(document.body.querySelector('#app > div').classList.values())); - - expect(divClassArray.includes('red')).to.be.true; // Standard CSS - expect(divClassArray.includes('large')).to.be.true; // Standard SCSS - expect(divClassArray.includes('justified')).to.be.true; // Standard Less - expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus - - expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module - expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module - expect(divClassArray.includes('underline_foo')).to.be.true; // Less module - expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module - - done(); - } - ); - }); - }); - - it('Vue.js error when using non-activated loaders', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', `./vuejs/main_v${getVueVersion(config)}`); - config.enableVueLoader(); - - testSetup.runWebpack(config, (webpackAssert, stats, output) => { - expect(output).to.contain('To load LESS files'); - expect(output).to.contain('To load Sass files'); - - done(); - }, true); - }); - - it('Vue.js is compiled correctly with JSX support', function(done) { - const appDir = testSetup.createTestAppDir(); - - fs.writeFileSync( - path.join(appDir, 'postcss.config.js'), - ` -module.exports = { - plugins: [ - require('autoprefixer')() - ] -} ` - ); - - const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); - - config.enableSingleRuntimeChunk(); - config.setPublicPath('/build'); - config.addEntry('main', `./vuejs-jsx/main_v${getVueVersion(config)}`); - config.enableVueLoader(() => {}, { - useJsx: true, - version: getVueVersion(config), - }); - config.enableSassLoader(); - config.enableLessLoader(); - config.configureBabel(function(config) { - // throw new Error(JSON.stringify(config)); - expect(config.presets[0][0]).to.equal(require.resolve('@babel/preset-env')); - config.presets[0][1].targets = { - chrome: 109 - }; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory().with.deep.files([ - 'main.js', - 'main.css', - 'images/logo.26bd867d.png', - 'manifest.json', - 'entrypoints.json', - 'runtime.js', - ]); - - // test that our custom babel config is used - webpackAssert.assertOutputFileContains( - 'main.js', - 'class TestClassSyntax' - ); - - // test that global styles are working correctly - webpackAssert.assertOutputFileContains( - 'main.css', - '#app {' - ); - - // test that CSS Modules (for scoped styles) is used - webpackAssert.assertOutputFileContains( - 'main.css', - '.h1_' // `.h1` is transformed to `.h1_[a-zA-Z0-9]` - ); - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/main.js' - ], - async({ page }) => { - // assert that the vue.js app rendered - const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); - expect(h1Text).to.contains('Welcome to Your Vue.js App'); - - // make sure the styles are not inlined - const styleElementsCount = await page.evaluate(() => document.querySelectorAll('style').length); - expect(styleElementsCount).to.equal(0); - - done(); - } - ); - }); - }); - - it('configureImageRule() allows configuring maxSize for inlining', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.setPublicPath('/build'); - config.addStyleEntry('url-loader', './css/url-loader.css'); - // set a size so that they do NOT inline - config.configureImageRule({ type: 'asset', maxSize: 102400 }); - config.configureFontRule({ type: 'asset', maxSize: 102400 }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'url-loader.css', - 'manifest.json', - 'entrypoints.json', - 'runtime.js' - ]); - - webpackAssert.assertOutputFileContains( - 'url-loader.css', - 'url(data:font/woff2;base64,' - ); - - webpackAssert.assertOutputFileContains( - 'url-loader.css', - 'url(data:image/png;base64,' - ); - - done(); - }); - }); - - it('Code splitting with dynamic import', (done) => { - const config = createWebpackConfig('www/build', 'dev'); - config.setPublicPath('/build'); - config.addEntry('main', './js/code_splitting_dynamic_import'); - - testSetup.runWebpack(config, (webpackAssert) => { - // check for the code-split file - webpackAssert.assertOutputFileContains( - 'js_print_to_app_export_js.js', - 'document.getElementById(\'app\').innerHTML =' - ); - - testSetup.requestTestPage( - browser, - path.join(config.getContext(), 'www'), - [ - 'build/runtime.js', - 'build/main.js' - ], - async({ page }) => { - // assert the async module was loaded and works - expect(await page.evaluate(() => document.querySelector('#app').textContent)).to.contains('Welcome to Encore!'); - - done(); - } - ); - }); - }); - - it('Symfony - Stimulus standard app is built correctly', function(done) { - const appDir = testSetup.createTestAppDir(); - - const version = packageHelper.getPackageVersion('@symfony/stimulus-bridge'); - if (!semver.satisfies(version, '^3.0.0')) { - // we support the old version, but it's not tested - this.skip(); - - return; - } - - const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); - config.enableSingleRuntimeChunk(); - config.setPublicPath('/build'); - config.addEntry('main', './stimulus/assets/app.js'); - config.enableStimulusBridge(__dirname + '/../fixtures/stimulus/assets/controllers.json'); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory().with.deep.files([ - 'main.js', - 'main.css', - 'manifest.json', - 'node_modules_symfony_mock-module_dist_controller_js.js', - 'entrypoints.json', - 'runtime.js', - ]); - - // test controllers and style are shipped - webpackAssert.assertOutputFileContains('main.js', 'app-controller'); - webpackAssert.assertOutputFileContains('node_modules_symfony_mock-module_dist_controller_js.js', 'mock-module-controller'); - webpackAssert.assertOutputFileContains('main.css', 'body {}'); - - done(); - }); - }); - - describe('copyFiles() allows to copy files and folders', () => { - it('Single file copy', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.copyFiles({ - from: './images', - pattern: /symfony_logo\.png/, - includeSubdirectories: false - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json', - 'symfony_logo.png' - ]); - - webpackAssert.assertManifestPath( - 'build/main.js', - '/build/main.js' - ); - - webpackAssert.assertManifestPath( - 'build/symfony_logo.png', - '/build/symfony_logo.png' - ); - - done(); - }); - }); - - it('Folder copy without subdirectories', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.copyFiles({ - from: './images', - includeSubdirectories: false - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json', - 'symfony_logo.png', - 'symfony_logo_alt.png', - ]); - - webpackAssert.assertManifestPath( - 'build/main.js', - '/build/main.js' - ); - - webpackAssert.assertManifestPath( - 'build/symfony_logo.png', - '/build/symfony_logo.png' - ); - - webpackAssert.assertManifestPath( - 'build/symfony_logo_alt.png', - '/build/symfony_logo_alt.png' - ); - - done(); - }); - }); - - it('Multiple copies', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.copyFiles([{ - from: './images', - to: 'assets/[path][name].[ext]', - includeSubdirectories: false - }, { - from: './fonts', - to: 'assets/[path][name].[ext]', - includeSubdirectories: false - }]); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json' - ]); - - expect(path.join(config.outputPath, 'assets')).to.be.a.directory() - .with.files([ - 'symfony_logo.png', - 'symfony_logo_alt.png', - 'Roboto.woff2', - ]); - - webpackAssert.assertManifestPath( - 'build/main.js', - '/build/main.js' - ); - - webpackAssert.assertManifestPath( - 'build/assets/symfony_logo.png', - '/build/assets/symfony_logo.png' - ); - - webpackAssert.assertManifestPath( - 'build/assets/symfony_logo_alt.png', - '/build/assets/symfony_logo_alt.png' - ); - - webpackAssert.assertManifestPath( - 'build/assets/Roboto.woff2', - '/build/assets/Roboto.woff2' - ); - - done(); - }); - }); - - it('Copy folder and subdirectories with versioning enabled to the specified location', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.copyFiles({ - from: './images', - to: 'images/[path][name].[hash:8].[ext]', - includeSubdirectories: true - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json', - ]); - - expect(path.join(config.outputPath, 'images')).to.be.a.directory() - .with.files([ - 'symfony_logo.91beba37.png', - 'symfony_logo_alt.f880ba14.png', - ]); - - expect(path.join(config.outputPath, 'images', 'same_filename')).to.be.a.directory() - .with.files([ - 'symfony_logo.f880ba14.png', - ]); - - webpackAssert.assertManifestPath( - 'build/main.js', - '/build/main.js' - ); - - webpackAssert.assertManifestPath( - 'build/images/symfony_logo.png', - '/build/images/symfony_logo.91beba37.png' - ); - - webpackAssert.assertManifestPath( - 'build/images/symfony_logo_alt.png', - '/build/images/symfony_logo_alt.f880ba14.png' - ); - - webpackAssert.assertManifestPath( - 'build/images/same_filename/symfony_logo.png', - '/build/images/same_filename/symfony_logo.f880ba14.png' - ); - - done(); - }); - }); - - it('Filter files using the given pattern', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.copyFiles({ - from: './images', - pattern: /_alt/, - includeSubdirectories: false - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json', - 'symfony_logo_alt.png', - ]); - - webpackAssert.assertManifestPath( - 'build/main.js', - '/build/main.js' - ); - - webpackAssert.assertManifestPath( - 'build/symfony_logo_alt.png', - '/build/symfony_logo_alt.png' - ); - - webpackAssert.assertManifestPathDoesNotExist( - 'build/symfony_logo.png' - ); - - done(); - }); - }); - - it('Copy with versioning enabled', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.enableVersioning(true); - config.copyFiles([{ - from: './images', - includeSubdirectories: false - }, { - from: './fonts', - to: 'assets/[path][name].[ext]', - includeSubdirectories: false - }]); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertDirectoryContents([ - 'entrypoints.json', - 'runtime.[hash:8].js', - 'main.[hash:8].js', - 'manifest.json', - 'symfony_logo.[hash:8].png', - 'symfony_logo_alt.[hash:8].png', - ]); - - webpackAssert.assertManifestPath( - 'build/main.js', - '/build/main.[hash:8].js' - ); - - expect(path.join(config.outputPath, 'assets')).to.be.a.directory() - .with.files([ - 'Roboto.woff2', - ]); - - webpackAssert.assertManifestPath( - 'build/symfony_logo.png', - '/build/symfony_logo.91beba37.png' - ); - - webpackAssert.assertManifestPath( - 'build/symfony_logo_alt.png', - '/build/symfony_logo_alt.f880ba14.png' - ); - - webpackAssert.assertManifestPath( - 'build/assets/Roboto.woff2', - '/build/assets/Roboto.woff2' - ); - - done(); - }); - }); - - it('Do not try to copy files from an invalid path', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.copyFiles([{ - from: './images', - to: 'assets/[path][name].[ext]', - includeSubdirectories: false - }, { - from: './foo', - to: 'assets/[path][name].[ext]', - includeSubdirectories: false - }, { - from: './fonts', - to: 'assets/[path][name].[ext]', - includeSubdirectories: false - }, { - from: './images/symfony_logo.png', - includeSubdirectories: true - }]); - - testSetup.runWebpack(config, (webpackAssert, stats, stdout) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json' - ]); - - expect(stdout).to.contain('should be set to an existing directory but "./foo" does not seem to exist'); - expect(stdout).to.contain('should be set to an existing directory but "./images/symfony_logo.png" seems to be a file'); - - done(); - }); - }); - - it('Copy with a custom context', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.copyFiles({ - from: './images', - to: '[path][name].[hash:8].[ext]', - includeSubdirectories: true, - context: './', - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json', - ]); - - expect(path.join(config.outputPath, 'images')).to.be.a.directory() - .with.files([ - 'symfony_logo.91beba37.png', - 'symfony_logo_alt.f880ba14.png', - ]); - - expect(path.join(config.outputPath, 'images', 'same_filename')).to.be.a.directory() - .with.files([ - 'symfony_logo.f880ba14.png', - ]); - - webpackAssert.assertManifestPath( - 'build/main.js', - '/build/main.js' - ); - - webpackAssert.assertManifestPath( - 'build/images/symfony_logo.png', - '/build/images/symfony_logo.91beba37.png' - ); - - webpackAssert.assertManifestPath( - 'build/images/symfony_logo_alt.png', - '/build/images/symfony_logo_alt.f880ba14.png' - ); - - webpackAssert.assertManifestPath( - 'build/images/same_filename/symfony_logo.png', - '/build/images/same_filename/symfony_logo.f880ba14.png' - ); - - done(); - }); - }); - - it('Copy files without processing them', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.copyFiles({ from: './copy' }); - - // By default the css-minimizer-webpack-plugin will - // run on ALL emitted CSS files, which includes the ones - // handled by `Encore.copyFiles()`. - // We disable it for this test since our CSS file will - // not be valid and can't be handled by this plugin. - config.configureCssMinimizerPlugin(options => { - options.include = /^$/; - }); - - // By default the terser-webpack-plugin will run on - // ALL emitted JS files, which includes the ones - // handled by `Encore.copyFiles()`. - // We disable it for this test since our JS file will - // not be valid and can't be handled by this plugin. - config.configureTerserPlugin(options => { - options.include = /^$/; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json', - 'foo.css', - 'foo.js', - 'foo.json', - 'foo.png', - ]); - - for (const file of ['foo.css', 'foo.js', 'foo.json', 'foo.png']) { - webpackAssert.assertOutputFileContains( - file, - 'This is an invalid content to check that the file is still copied' - ); - } - - done(); - }); - }); - - it('Do not copy files excluded by a RegExp', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - - // foo.css and foo.js should match this rule - // and be versioned - config.copyFiles({ - from: './copy', - to: './[path][name]-[hash:8].[ext]', - pattern: /\.(css|js)$/, - }); - - // foo.css and foo.js should *not* match this rule - config.copyFiles({ - from: './copy', - to: './[path][name].[ext]', - pattern: /\.(?!(css|js)$)([^.]+$)/ - }); - - // By default the css-minimizer-webpack-plugin will - // run on ALL emitted CSS files, which includes the ones - // handled by `Encore.copyFiles()`. - // We disable it for this test since our CSS file will - // not be valid and can't be handled by this plugin. - config.configureCssMinimizerPlugin(options => { - options.include = /^$/; - }); - - // By default the terser-webpack-plugin will run on - // ALL emitted JS files, which includes the ones - // handled by `Encore.copyFiles()`. - // We disable it for this test since our JS file will - // not be valid and can't be handled by this plugin. - config.configureTerserPlugin(options => { - options.include = /^$/; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertDirectoryContents([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json', - - // 1st rule - 'foo-[hash:8].css', - 'foo-[hash:8].js', - - // 2nd rule - 'foo.json', - 'foo.png', - ]); - - done(); - }); - }); - - it('Can use the "[N]" placeholder', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.copyFiles({ - from: './images', - pattern: /(symfony)_(logo)\.png/, - to: '[path][2]_[1].[ext]', - includeSubdirectories: false - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json', - 'logo_symfony.png' - ]); - - webpackAssert.assertManifestPath( - 'build/main.js', - '/build/main.js' - ); - - webpackAssert.assertManifestPath( - 'build/symfony_logo.png', - '/build/logo_symfony.png' - ); - - done(); - }); - }); - - it('Does not prevent from setting the map option of the manifest plugin', (done) => { - const config = createWebpackConfig('www/build', 'production'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.copyFiles({ - from: './images', - pattern: /symfony_logo\.png/, - includeSubdirectories: false - }); - - config.configureManifestPlugin(options => { - options.map = (file) => { - return Object.assign({}, file, { - name: `${file.name}.test`, - }); - }; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'entrypoints.json', - 'runtime.js', - 'main.js', - 'manifest.json', - 'symfony_logo.png' - ]); - - webpackAssert.assertManifestPath( - 'build/main.js.test', - '/build/main.js' - ); - - webpackAssert.assertManifestPath( - 'build/symfony_logo.png.test', - '/build/symfony_logo.png' - ); - - done(); - }); - }); - }); - - describe('entrypoints.json & splitChunks()', () => { - it('Use "all" splitChunks & look at entrypoints.json', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); - config.addEntry('other', ['./css/roboto_font.css', 'vue']); - config.setPublicPath('/build'); - // enable versioning to make sure entrypoints.json is not affected - config.splitEntryChunks(); - config.configureSplitChunks((splitChunks) => { - splitChunks.minSize = 0; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { - entrypoints: { - main: { - js: [ - '/build/runtime.js', - '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', - '/build/css_roboto_font_css.js', - '/build/main.js' - ], - css: ['/build/css_roboto_font_css.css'] - }, - other: { - js: [ - '/build/runtime.js', - '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', - '/build/css_roboto_font_css.js', - '/build/other.js' - ], - css: ['/build/css_roboto_font_css.css'] - } - } - }); - - // make split chunks are correct in manifest - webpackAssert.assertManifestKeyExists('build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js'); - - done(); - }); - }); - - it('Custom public path does affect entrypoints.json but does not affect manifest.json', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); - config.addEntry('other', ['./css/roboto_font.css', 'vue']); - config.setPublicPath('http://localhost:8080/build'); - config.setManifestKeyPrefix('custom_prefix'); - config.configureSplitChunks((splitChunks) => { - splitChunks.chunks = 'all'; - splitChunks.minSize = 0; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { - entrypoints: { - main: { - js: [ - 'http://localhost:8080/build/runtime.js', - 'http://localhost:8080/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', - 'http://localhost:8080/build/css_roboto_font_css.js', - 'http://localhost:8080/build/main.js' - ], - css: ['http://localhost:8080/build/css_roboto_font_css.css'] - }, - other: { - js: [ - 'http://localhost:8080/build/runtime.js', - 'http://localhost:8080/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', - 'http://localhost:8080/build/css_roboto_font_css.js', - 'http://localhost:8080/build/other.js' - ], - css: ['http://localhost:8080/build/css_roboto_font_css.css'] - } - } - }); - - // make split chunks are correct in manifest - webpackAssert.assertManifestKeyExists('custom_prefix/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js'); - - done(); - }); - }); - - it('Subdirectory public path affects entrypoints.json but does not affect manifest.json', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); - config.addEntry('other', ['./css/roboto_font.css', 'vue']); - config.setPublicPath('/subdirectory/build'); - config.setManifestKeyPrefix('custom_prefix'); - config.configureSplitChunks((splitChunks) => { - splitChunks.chunks = 'all'; - splitChunks.minSize = 0; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { - entrypoints: { - main: { - js: [ - '/subdirectory/build/runtime.js', - '/subdirectory/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', - '/subdirectory/build/css_roboto_font_css.js', - '/subdirectory/build/main.js' - ], - css: ['/subdirectory/build/css_roboto_font_css.css'] - }, - other: { - js: [ - '/subdirectory/build/runtime.js', - '/subdirectory/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', - '/subdirectory/build/css_roboto_font_css.js', - '/subdirectory/build/other.js' - ], - css: ['/subdirectory/build/css_roboto_font_css.css'] - } - } - }); - - // make split chunks are correct in manifest - webpackAssert.assertManifestKeyExists('custom_prefix/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js'); - - done(); - }); - }); - - it('Use splitChunks in production mode', (done) => { - const config = createWebpackConfig('web/build', 'production'); - config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); - config.addEntry('other', ['./css/roboto_font.css', 'vue']); - config.setPublicPath('/build'); - config.splitEntryChunks(); - config.configureSplitChunks((splitChunks) => { - splitChunks.minSize = 0; - }); - - testSetup.runWebpack(config, () => { - // in production, we hash the chunk names to avoid exposing any extra details - const entrypointsData = JSON.parse(readOutputFileContents('entrypoints.json', config)); - const mainJsFiles = entrypointsData.entrypoints.main.js; - expect(mainJsFiles).to.have.length(4); - expect(mainJsFiles[0]).equals('/build/runtime.js'); - // keys 1 and 2 are "split files" with an integer name that sometimes changes - expect(mainJsFiles[3]).equals('/build/main.js'); - - expect(entrypointsData.entrypoints.main.css[0]).matches(/\/build\/(\d)+\.css/); - - // make split chunks are correct in manifest - const manifestData = JSON.parse(readOutputFileContents('manifest.json', config)); - mainJsFiles.forEach((file) => { - // file.substring(1) => /build/main.js -> build/main.js - expect(Object.keys(manifestData)).includes(file.substring(1)); - }); - - done(); - }); - }); - - it('Use splitEntryChunks() with code splitting', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', ['./js/code_splitting', 'vue']); - config.addEntry('other', ['./js/no_require', 'vue']); - config.setPublicPath('/build'); - config.splitEntryChunks(); - config.configureSplitChunks((splitChunks) => { - splitChunks.minSize = 0; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { - entrypoints: { - main: { - js: ['/build/runtime.js', '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', '/build/main.js'] - }, - other: { - js: ['/build/runtime.js', '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', '/build/js_no_require_js.js', '/build/other.js'] - } - } - }); - - // make split chunks are correct in manifest - webpackAssert.assertManifestKeyExists('build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js'); - webpackAssert.assertManifestKeyExists('build/js_no_require_js.js'); - - done(); - }); - }); - - it('Make sure chunkIds do not change between builds', (done) => { - // https://github.com/symfony/webpack-encore/issues/461 - const createSimilarConfig = function(includeExtraEntry) { - const config = createWebpackConfig('web/build', 'production'); - config.addEntry('main1', './js/code_splitting'); - if (includeExtraEntry) { - config.addEntry('main2', './js/eslint'); - } - config.addEntry('main3', './js/no_require'); - config.setPublicPath('/build'); - - return config; - }; - - const configA = createSimilarConfig(false); - const configB = createSimilarConfig(true); - - testSetup.runWebpack(configA, () => { - testSetup.runWebpack(configB, () => { - const main3Contents = readOutputFileContents('main3.js', configA); - const finalMain3Contents = readOutputFileContents('main3.js', configB); - - if (finalMain3Contents !== main3Contents) { - throw new Error(`Contents after first compile do not match after second compile: \n\n ${main3Contents} \n\n versus \n\n ${finalMain3Contents} \n`); - } - - done(); - }); - }); - }); - - it('Do not change contents or filenames when more modules require the same split contents', (done) => { - const createSimilarConfig = function(includeExtraEntry) { - const config = createWebpackConfig('web/build', 'production'); - config.addEntry('main1', ['./js/code_splitting', 'preact']); - config.addEntry('main3', ['./js/no_require', 'preact']); - if (includeExtraEntry) { - config.addEntry('main4', ['./js/eslint', 'preact']); - } - config.setPublicPath('/build'); - config.splitEntryChunks(); - config.configureSplitChunks((splitChunks) => { - // will include preact, but prevent any other splitting - splitChunks.minSize = 10000; - }); - - return config; - }; - - const getSplitVendorJsPath = function(config) { - const entrypointData = getEntrypointData(config, 'main3'); - - const splitFiles = entrypointData.js.filter(filename => { - return filename !== '/build/runtime.js' && filename !== '/build/main3.js'; - }); - - // sanity check - if (splitFiles.length !== 1) { - throw new Error(`Unexpected number (${splitFiles.length}) of split files for main3 entry`); - } - - return splitFiles[0]; - }; - - const configA = createSimilarConfig(false); - const configB = createSimilarConfig(true); - - testSetup.runWebpack(configA, () => { - testSetup.runWebpack(configB, () => { - const vendorPath = getSplitVendorJsPath(configA); - const finalVendorPath = getSplitVendorJsPath(configB); - - // make sure that the filename of the split vendor file didn't change, - // even though an additional entry is now sharing its contents - if (finalVendorPath !== vendorPath) { - throw new Error(`Vendor filename changed! Before ${vendorPath} and after ${finalVendorPath}.`); - } - - // make sure that, internally, the split chunk name did not change, - // which would cause the contents of main3 to suddenly change - const main3Contents = readOutputFileContents('main3.js', configA); - const finalMain3Contents = readOutputFileContents('main3.js', configB); - - if (finalMain3Contents !== main3Contents) { - throw new Error(`Contents after first compile do not match after second compile: \n\n ${main3Contents} \n\n versus \n\n ${finalMain3Contents} \n`); - } - - done(); - }); - }); - }); - }); - - describe('Package entrypoint imports', () => { - it('Import via "sass" package property', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - - config.setPublicPath('/build'); - config.addAliases({ - lib: path.resolve('./lib') - }); - config.enableSassLoader(); - config.addStyleEntry('sass', './css/sass_package_import.scss'); - - testSetup.runWebpack(config, () => { - // A successful compile is all that is needed to pass this test. - // If this test fails then the import in the above sass file - // is not loading the package's sass file. - done(); - }); - }); - - it('Import via "style" package property', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - - config.setPublicPath('/build'); - config.addAliases({ - lib: path.resolve('./lib') - }); - config.addStyleEntry('style', './css/style_package_import.css'); - - testSetup.runWebpack(config, () => { - // A successful compile is all that is needed to pass this test. - // If this test fails then the import in the above css file - // is not loading the package's style file. - done(); - }); - }); - }); - - describe('CSS extraction', () => { - it('With CSS extraction enabled', (done) => { - const config = createWebpackConfig('build', 'dev'); - config.setPublicPath('/build'); - config.disableSingleRuntimeChunk(); - config.addEntry('main', './js/css_import'); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'manifest.json', - 'entrypoints.json', - 'main.js', - 'main.css', - ]); - - webpackAssert.assertOutputFileContains( - 'main.css', - 'font-size: 50px;' - ); - - done(); - }); - }); - - it('With CSS extraction disabled', (done) => { - const config = createWebpackConfig('build', 'dev'); - config.setPublicPath('/build'); - config.disableSingleRuntimeChunk(); - config.addEntry('main', './js/css_import'); - config.disableCssExtraction(); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'manifest.json', - 'entrypoints.json', - 'main.js' - ]); - - webpackAssert.assertOutputFileContains( - 'main.js', - 'font-size: 50px;' - ); - - done(); - }); - }); - - it('With CSS extraction disabled and with options callback of the StyleLoader', (done) => { - const config = createWebpackConfig('build', 'dev'); - config.setPublicPath('/build'); - config.disableSingleRuntimeChunk(); - config.addEntry('main', './js/css_import'); - config.disableCssExtraction(); - config.configureStyleLoader((options) => { - options.attributes = { id: 'TESTING_ATTRIBUTES' }; - }); - - testSetup.runWebpack(config, (webpackAssert) => { - expect(config.outputPath).to.be.a.directory() - .with.files([ - 'manifest.json', - 'entrypoints.json', - 'main.js' - ]); - - webpackAssert.assertOutputFileContains( - 'main.js', - 'TESTING_ATTRIBUTES' - ); - - done(); - }); - }); - }); - - describe('enableIntegrityHashes() adds hashes to the entrypoints.json file', () => { - it('Using default algorithm', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); - config.addEntry('other', ['./css/roboto_font.css', 'vue']); - config.setPublicPath('/build'); - config.configureSplitChunks((splitChunks) => { - splitChunks.chunks = 'all'; - splitChunks.minSize = 0; - }); - config.enableIntegrityHashes(); - - testSetup.runWebpack(config, () => { - const integrityData = getIntegrityData(config); - const expectedFilesWithHashes = [ - '/build/runtime.js', - '/build/main.js', - '/build/css_roboto_font_css.js', - '/build/css_roboto_font_css.css', - '/build/other.js', - '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', - ]; - - expectedFilesWithHashes.forEach((file) => { - expect(integrityData[file]).to.contain('sha384-'); - expect(integrityData[file]).to.have.length(71); - }); - - done(); - }); - }); - - it('Using another algorithm and a different public path', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); - config.addEntry('other', ['./css/roboto_font.css', 'vue']); - config.setPublicPath('http://localhost:8090/assets'); - config.setManifestKeyPrefix('assets'); - config.configureSplitChunks((splitChunks) => { - splitChunks.chunks = 'all'; - splitChunks.minSize = 0; - }); - config.enableIntegrityHashes(true, 'sha256'); - - testSetup.runWebpack(config, () => { - const integrityData = getIntegrityData(config); - const expectedFilesWithHashes = [ - 'http://localhost:8090/assets/runtime.js', - 'http://localhost:8090/assets/main.js', - 'http://localhost:8090/assets/css_roboto_font_css.js', - 'http://localhost:8090/assets/css_roboto_font_css.css', - 'http://localhost:8090/assets/other.js', - ]; - - expectedFilesWithHashes.forEach((file) => { - expect(integrityData[file]).to.contain('sha256-'); - expect(integrityData[file]).to.have.length(51); - }); - - done(); - }); - }); - - it('Using multiple algorithms', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); - config.addEntry('other', ['./css/roboto_font.css', 'vue']); - config.setPublicPath('/build'); - config.configureSplitChunks((splitChunks) => { - splitChunks.chunks = 'all'; - splitChunks.minSize = 0; - }); - config.enableIntegrityHashes(true, ['sha256', 'sha512']); - - testSetup.runWebpack(config, () => { - const integrityData = getIntegrityData(config); - const expectedFilesWithHashes = [ - '/build/runtime.js', - '/build/main.js', - '/build/css_roboto_font_css.js', - '/build/css_roboto_font_css.css', - '/build/other.js', - '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', - ]; - - expectedFilesWithHashes.forEach((file) => { - expect(integrityData[file]).to.contain('sha256-'); - expect(integrityData[file]).to.contain('sha512-'); - expect(integrityData[file]).to.have.length(147); - }); - - done(); - }); - }); - - it('With query string versioning', (done) => { - const config = createWebpackConfig('web/build', 'dev'); - config.addEntry('main', './js/no_require'); - config.setPublicPath('/build'); - config.addStyleEntry('styles', './css/h1_style.css'); - config.enableVersioning(true); - config.configureFilenames({ - js: '[name].js?v=[contenthash:16]', - css: '[name].css?v=[contenthash:16]' - }); - config.enableIntegrityHashes(); - - testSetup.runWebpack(config, (webpackAssert) => { - const integrityData = getIntegrityData(config); - const expectedFilesWithHashes = Object.keys(integrityData).filter(file => { - if (!/\?v=[a-z0-9]{16}$/.test(file)) { - return false; - } - return file.startsWith('/build/runtime.js?v=') - || file.startsWith('/build/main.js?v=') - || file.startsWith('/build/styles.css?v='); - }); - - expectedFilesWithHashes.forEach((file) => { - expect(integrityData[file]).to.contain('sha384-'); - expect(integrityData[file]).to.have.length(71); - }); - - done(); - }); - }); - }); - }); -}); diff --git a/test/functional.test.js b/test/functional.test.js new file mode 100644 index 00000000..233965c6 --- /dev/null +++ b/test/functional.test.js @@ -0,0 +1,3310 @@ +/* + * This file is part of the Symfony Webpack Encore package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +'use strict'; + +import { describe, it, expect, beforeAll, afterAll, chai } from 'vitest'; +chai.use(require('chai-fs')); +chai.use(require('chai-subset')); +const path = require('path'); +const testSetup = require('./helpers/setup'); +const fs = require('fs-extra'); +const getVueVersion = require('../lib/utils/get-vue-version'); +const packageHelper = require('../lib/package-helper'); +const semver = require('semver'); +const puppeteer = require('puppeteer'); + +function createWebpackConfig(outputDirName = '', command, argv = {}) { + const webpackConfig = testSetup.createWebpackConfig( + testSetup.createTestAppDir(), + outputDirName, + command, + argv + ); + + webpackConfig.enableSingleRuntimeChunk(); + + return webpackConfig; +} + +function convertToManifestPath(assetSrc, webpackConfig) { + const manifestData = JSON.parse(readOutputFileContents('manifest.json', webpackConfig)); + + if (typeof manifestData[assetSrc] === 'undefined') { + throw new Error(`Path ${assetSrc} not found in manifest!`); + } + + return manifestData[assetSrc]; +} + +function readOutputFileContents(filename, config) { + const fullPath = path.join(config.outputPath, filename); + + if (!fs.existsSync(fullPath)) { + throw new Error(`Output file "${filename}" does not exist.`); + } + + return fs.readFileSync(fullPath, 'utf8'); +} + +function getEntrypointData(config, entryName) { + const entrypointsData = JSON.parse(readOutputFileContents('entrypoints.json', config)); + + if (typeof entrypointsData.entrypoints[entryName] === 'undefined') { + throw new Error(`The entry ${entryName} was not found!`); + } + + return entrypointsData.entrypoints[entryName]; +} + +function getIntegrityData(config) { + const entrypointsData = JSON.parse(readOutputFileContents('entrypoints.json', config)); + if (typeof entrypointsData.integrity === 'undefined') { + throw new Error('The entrypoints.json file does not contain an integrity object!'); + } + + return entrypointsData.integrity; +} + +describe('Functional tests using webpack', { timeout: 10000}, function() { + /** @type {import('puppeteer').Browser} */ + let browser; + + beforeAll(async () => { + browser = await puppeteer.launch() + + return async() => { + await browser.close(); + } + }); + + describe('Basic scenarios.', () => { + + it('Builds a few simple entries file + manifest.json', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', './js/no_require'); + config.addStyleEntry('font', './css/roboto_font.css'); + config.addStyleEntry('bg', './css/another_bg_image.css'); + config.setPublicPath('/build'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // should have a main.js file + // should have a manifest.json with public/main.js + + expect(config.outputPath).to.be.a.directory().with.deep.files([ + 'runtime.js', + 'main.js', + 'font.css', + 'bg.css', + 'fonts/Roboto.e1dcc0db.woff2', + 'images/symfony_logo.91beba37.png', + 'manifest.json', + 'entrypoints.json' + ]); + + // check that main.js has the correct contents + webpackAssert.assertOutputFileContains( + 'main.js', + 'i am the no_require.js file' + ); + // check that main.js has the webpack bootstrap + webpackAssert.assertOutputFileContains( + 'runtime.js', + '__webpack_require__' + ); + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.js' + ); + webpackAssert.assertManifestPath( + 'build/font.css', + '/build/font.css' + ); + webpackAssert.assertManifestPath( + 'build/fonts/Roboto.woff2', + '/build/fonts/Roboto.e1dcc0db.woff2' + ); + webpackAssert.assertManifestPath( + 'build/images/symfony_logo.png', + '/build/images/symfony_logo.91beba37.png' + ); + + webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { + entrypoints: { + main: { + js: ['/build/runtime.js', '/build/main.js'] + }, + font: { + js: ['/build/runtime.js'], + css: ['/build/font.css'] + }, + bg: { + js: ['/build/runtime.js'], + css: ['/build/bg.css'] + } + } + }); + + resolve(); + }); + }); + }); + + it('Check manifest.json with node_module includes', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', './js/import_node_modules_image'); + config.setPublicPath('/build'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // should have a main.js file + // should have a manifest.json with public/main.js + + webpackAssert.assertOutputJsonFileMatches('manifest.json', { + 'build/main.js': '/build/main.js', + 'build/runtime.js': '/build/runtime.js', + 'build/images/symfony_logo.png': '/build/images/symfony_logo.91beba37.png', + 'build/images/logo.png': '/build/images/logo.cb197657.png', + }); + + resolve(); + }); + }); + }); + + it('Use "all" splitChunks & look at entrypoints.json', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); + config.addEntry('other', ['./css/roboto_font.css', 'vue']); + config.setPublicPath('/build'); + config.configureSplitChunks((splitChunks) => { + splitChunks.chunks = 'all'; + splitChunks.minSize = 0; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { + entrypoints: { + main: { + js: [ + '/build/runtime.js', + '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', + '/build/css_roboto_font_css.js', + '/build/main.js' + ], + css: ['/build/css_roboto_font_css.css'] + }, + other: { + js: [ + '/build/runtime.js', + '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', + '/build/css_roboto_font_css.js', + '/build/other.js' + ], + css: ['/build/css_roboto_font_css.css'] + } + } + }); + + resolve(); + }); + }); + }); + + it('Disable the runtime chunk', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', './js/no_require'); + config.disableSingleRuntimeChunk(); + config.setPublicPath('/build'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // no runtime.js + expect(config.outputPath).to.be.a.directory().with.deep.files([ + 'main.js', + 'manifest.json', + 'entrypoints.json' + ]); + + resolve(); + }); + }); + }); + + it('setPublicPath with CDN loads assets from the CDN', async () => { + const config = createWebpackConfig('public/assets', 'dev'); + config.addEntry('main', './js/code_splitting'); + config.addStyleEntry('font', './css/roboto_font.css'); + config.addStyleEntry('bg', './css/background_image.scss'); + config.setPublicPath('http://127.0.0.1:8090/assets'); + config.enableSassLoader(); + config.setManifestKeyPrefix('assets'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'js_no_require_js-css_h1_style_css.css', + 'js_no_require_js-css_h1_style_css.js', + 'main.js', + 'runtime.js', + 'font.css', + 'bg.css', + 'manifest.json', + 'entrypoints.json' + ]); + + // check that the publicPath is set correctly + webpackAssert.assertOutputFileContains( + 'runtime.js', + '__webpack_require__.p = "http://127.0.0.1:8090/assets/";' + ); + + webpackAssert.assertOutputFileContains( + 'bg.css', + 'http://127.0.0.1:8090/assets/images/symfony_logo.91beba37.png' + ); + webpackAssert.assertOutputFileContains( + 'font.css', + 'http://127.0.0.1:8090/assets/fonts/Roboto.e1dcc0db.woff2' + ); + // manifest file has CDN in value + webpackAssert.assertManifestPath( + 'assets/main.js', + 'http://127.0.0.1:8090/assets/main.js' + ); + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'public'), + [ + // purposely load this NOT from the CDN + 'assets/runtime.js', + 'assets/main.js' + ], + ({ loadedResources }) => { + webpackAssert.assertResourcesLoadedCorrectly(loadedResources, [ + 'js_no_require_js-css_h1_style_css.css', + 'js_no_require_js-css_h1_style_css.js', + // guarantee that we assert that main.js is loaded from the + // main server, as it's simply a script tag to main.js on the page + // we did this to check that the internally-loaded assets + // use the CDN, even if the entry point does not + 'http://127.0.0.1:8080/assets/runtime.js', + 'http://127.0.0.1:8080/assets/main.js' + ]); + + resolve(); + } + ); + }); + }); + }); + + it('The devServer config loads successfully', async () => { + const config = createWebpackConfig('public/assets', 'dev-server', { + port: '8090', + host: '127.0.0.1', + }); + config.addEntry('main', './js/code_splitting'); + config.addStyleEntry('font', './css/roboto_font.css'); + config.addStyleEntry('bg', './css/background_image.scss'); + config.setPublicPath('/assets'); + config.enableSassLoader(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that the publicPath is set correctly + webpackAssert.assertOutputFileContains( + 'runtime.js', + '__webpack_require__.p = "http://127.0.0.1:8090/assets/";' + ); + + webpackAssert.assertOutputFileContains( + 'bg.css', + 'http://127.0.0.1:8090/assets/images/symfony_logo.91beba37.png' + ); + // manifest file has CDN in value + webpackAssert.assertManifestPath( + 'assets/main.js', + 'http://127.0.0.1:8090/assets/main.js' + ); + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'public'), + [ + convertToManifestPath('assets/runtime.js', config), + convertToManifestPath('assets/main.js', config) + ], + ({ loadedResources }) => { + webpackAssert.assertResourcesLoadedCorrectly(loadedResources, [ + 'runtime.js', + 'main.js', + 'js_no_require_js-css_h1_style_css.css', + 'js_no_require_js-css_h1_style_css.js', + ]); + + resolve(); + } + ); + }); + }); + }); + + it('Deploying to a subdirectory is no problem', async () => { + const config = createWebpackConfig('subdirectory/build', 'dev'); + config.addEntry('main', './js/code_splitting'); + config.setPublicPath('/subdirectory/build'); + config.setManifestKeyPrefix('build'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertManifestPath( + 'build/main.js', + '/subdirectory/build/main.js' + ); + + testSetup.requestTestPage( + browser, + // the webroot will not include the /subdirectory/build part + path.join(config.getContext(), ''), + [ + convertToManifestPath('build/runtime.js', config), + convertToManifestPath('build/main.js', config) + ], + ({ loadedResources }) => { + webpackAssert.assertResourcesLoadedCorrectly(loadedResources, [ + 'http://127.0.0.1:8080/subdirectory/build/runtime.js', + 'http://127.0.0.1:8080/subdirectory/build/main.js', + 'http://127.0.0.1:8080/subdirectory/build/js_no_require_js-css_h1_style_css.js', + 'http://127.0.0.1:8080/subdirectory/build/js_no_require_js-css_h1_style_css.css', + ]); + + resolve(); + } + ); + }); + }); + }); + + it('Empty manifestKeyPrefix is allowed', async () => { + const config = createWebpackConfig('build', 'dev'); + config.addEntry('main', './js/code_splitting'); + config.setPublicPath('/build'); + config.setManifestKeyPrefix(''); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertManifestPath( + 'main.js', + '/build/main.js' + ); + + resolve(); + }); + }); + }); + + it('.mjs files are supported natively', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', './js/hello_world'); + config.setPublicPath('/build'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that main.js has the correct contents + webpackAssert.assertOutputFileContains( + 'main.js', + 'Hello World!' + ); + + resolve(); + }); + }); + }); + + describe('addStyleEntry .js files are removed', () => { + it('Without versioning', async () => { + const config = createWebpackConfig('web', 'dev'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/'); + config.addStyleEntry('styles', './css/h1_style.css'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + // public.js should not exist + .with.files(['main.js', 'styles.css', 'manifest.json', 'entrypoints.json', 'runtime.js']); + + webpackAssert.assertOutputFileContains( + 'styles.css', + 'font-size: 50px;' + ); + webpackAssert.assertManifestPathDoesNotExist( + 'styles.js' + ); + webpackAssert.assertManifestPath( + 'styles.css', + '/styles.css' + ); + + resolve(); + }); + }); + }); + + it('With default versioning', async () => { + const config = createWebpackConfig('web', 'dev'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/'); + config.addStyleEntry('styles', './css/h1_style.css'); + config.enableVersioning(true); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertDirectoryContents([ + 'main.[hash:8].js', + 'styles.[hash:8].css', + 'manifest.json', + 'entrypoints.json', + 'runtime.[hash:8].js', + ]); + + webpackAssert.assertOutputFileContains( + 'styles.[hash:8].css', + 'font-size: 50px;' + ); + webpackAssert.assertManifestPathDoesNotExist( + 'styles.js' + ); + webpackAssert.assertManifestPath( + 'styles.css', + '/styles.[hash:8].css' + ); + + resolve(); + }); + }); + }); + + it('With query string versioning', async () => { + const config = createWebpackConfig('web', 'dev'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/'); + config.addStyleEntry('styles', './css/h1_style.css'); + config.enableVersioning(true); + config.configureFilenames({ + js: '[name].js?[contenthash:16]', + css: '[name].css?[contenthash:16]' + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files(['main.js', 'styles.css', 'manifest.json', 'entrypoints.json', 'runtime.js']); + + webpackAssert.assertOutputFileContains( + 'styles.css', + 'font-size: 50px;' + ); + webpackAssert.assertManifestPathDoesNotExist( + 'styles.js' + ); + webpackAssert.assertManifestPath( + 'styles.css', + '/styles.css?[hash:16]' + ); + + resolve(); + }); + }); + }); + + it('With source maps in production mode', async () => { + const config = createWebpackConfig('web', 'production'); + config.addEntry('main', './js/arrow_function'); + config.setPublicPath('/'); + config.addStyleEntry('styles', './css/h1_style.css'); + config.enableSourceMaps(true); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'main.js', + 'main.js.map', + 'styles.css', + 'styles.css.map', + 'manifest.json', + 'entrypoints.json', + 'runtime.js', + 'runtime.js.map', + // no styles.js + // no styles.js.map + ]); + + webpackAssert.assertManifestPathDoesNotExist( + 'styles.js' + ); + + webpackAssert.assertManifestPathDoesNotExist( + 'styles.js.map' + ); + + resolve(); + }); + }); + }); + }); + + it('enableVersioning applies to js, css & manifest', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', './js/code_splitting'); + config.setPublicPath('/build'); + config.addStyleEntry('h1', './css/h1_style.css'); + config.addStyleEntry('bg', './css/another_bg_image.css'); + config.enableSassLoader(); + config.enableVersioning(true); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertDirectoryContents([ + 'js_no_require_js-css_h1_style_css.[hash:8].js', // chunks are also versioned + 'js_no_require_js-css_h1_style_css.[hash:8].css', + 'main.[hash:8].js', + 'h1.[hash:8].css', + 'bg.[hash:8].css', + 'manifest.json', + 'entrypoints.json', + 'runtime.[hash:8].js', + ]); + + expect(path.join(config.outputPath, 'images')).to.be.a.directory() + .with.files([ + 'symfony_logo.91beba37.png' + ]); + + webpackAssert.assertOutputFileContains( + 'bg.[hash:8].css', + '/build/images/symfony_logo.91beba37.png' + ); + + resolve(); + }); + }); + }); + + it('font and image files are copied correctly', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addStyleEntry('bg', './css/background_image.scss'); + config.addStyleEntry('font', './css/roboto_font.css'); + config.enableSassLoader(options => { + // Use sass-loader instead of node-sass + options.implementation = require('sass'); + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'bg.css', + 'font.css', + 'manifest.json', + 'entrypoints.json', + 'runtime.js', + ]); + + expect(path.join(config.outputPath, 'images')).to.be.a.directory() + .with.files([ + 'symfony_logo.91beba37.png' + ]); + + expect(path.join(config.outputPath, 'fonts')).to.be.a.directory() + .with.files([ + 'Roboto.e1dcc0db.woff2' + ]); + + webpackAssert.assertOutputFileContains( + 'bg.css', + '/build/images/symfony_logo.91beba37.png' + ); + + webpackAssert.assertOutputFileContains( + 'font.css', + '/build/fonts/Roboto.e1dcc0db.woff2' + ); + + resolve(); + }); + }); + }); + + it('two fonts or images with the same filename should not output a single file', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addStyleEntry('styles', './css/same_filename.css'); + config.enableSassLoader(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'styles.css', + 'manifest.json', + 'entrypoints.json', + 'runtime.js', + ]); + + expect(path.join(config.outputPath, 'images')).to.be.a.directory() + .with.files([ + 'symfony_logo.91beba37.png', + 'symfony_logo.f880ba14.png' + ]); + + expect(path.join(config.outputPath, 'fonts')).to.be.a.directory() + .with.files([ + 'Roboto.e1dcc0db.woff2', + 'Roboto.2779fd7b.woff2' + ]); + + webpackAssert.assertOutputFileContains( + 'styles.css', + '/build/images/symfony_logo.91beba37.png' + ); + + webpackAssert.assertOutputFileContains( + 'styles.css', + '/build/images/symfony_logo.f880ba14.png' + ); + + webpackAssert.assertOutputFileContains( + 'styles.css', + '/build/fonts/Roboto.e1dcc0db.woff2' + ); + + webpackAssert.assertOutputFileContains( + 'styles.css', + '/build/fonts/Roboto.2779fd7b.woff2' + ); + + resolve(); + }); + }); + }); + + it('enableSourceMaps() adds to .js, css & scss', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', './js/no_require'); + config.addStyleEntry('bg', './css/background_image.scss'); + config.addStyleEntry('font', './css/roboto_font.css'); + config.enableSassLoader(); + config.enableSourceMaps(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertOutputFileHasSourcemap( + 'main.js' + ); + webpackAssert.assertOutputFileHasSourcemap( + 'bg.css' + ); + webpackAssert.assertOutputFileHasSourcemap( + 'font.css' + ); + + resolve(); + }); + }); + }); + + it('Without enableSourceMaps(), there are no sourcemaps', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', './js/no_require'); + config.addStyleEntry('bg', './css/background_image.scss'); + config.addStyleEntry('font', './css/roboto_font.css'); + config.enableSassLoader(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertOutputFileDoesNotHaveSourcemap( + 'main.js' + ); + webpackAssert.assertOutputFileDoesNotHaveSourcemap( + 'bg.css' + ); + webpackAssert.assertOutputFileDoesNotHaveSourcemap( + 'font.css' + ); + + resolve(); + }); + }); + }); + + it('Without enableSourceMaps(), there are no sourcemaps in production', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.setPublicPath('/build'); + config.addEntry('main', './js/no_require'); + config.addStyleEntry('bg', './css/background_image.scss'); + config.addStyleEntry('font', './css/roboto_font.css'); + config.enableSassLoader(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertOutputFileDoesNotHaveSourcemap( + 'main.js' + ); + webpackAssert.assertOutputFileDoesNotHaveSourcemap( + 'font.css' + ); + webpackAssert.assertOutputFileDoesNotHaveSourcemap( + 'bg.css' + ); + + resolve(); + }); + }); + }); + + it('Code splitting a scss file works', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + // loads sass_features.scss via require.ensure + config.addEntry('main', './js/code_split_load_scss'); + config.enableSassLoader(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // make sure sass is parsed + webpackAssert.assertOutputFileContains( + 'css_sass_features_scss.css', + 'color: #333' + ); + // and imported files are loaded correctly + webpackAssert.assertOutputFileContains( + 'css_sass_features_scss.css', + 'background: top left' + ); + + resolve(); + }); + }); + }); + + describe('addCacheGroup()', () => { + it('addCacheGroup() to extract a vendor into its own chunk', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.enableVueLoader(); + config.enablePreactPreset(); + config.enableSassLoader(); + config.enableLessLoader(); + config.addEntry('page1', `./vuejs/main_v${getVueVersion(config)}`); + config.addEntry('page2', './preact/main'); + + // Move Vue.js code into its own chunk + config.addCacheGroup('vuejs', { + test: /[\\/]node_modules[\\/]@vue[\\/]/ + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // Vue.js code should be present in common.js but not in page1.js/page2.js + webpackAssert.assertOutputFileContains( + 'vuejs.js', + '/***/ "../../node_modules/@vue/' + ); + + webpackAssert.assertOutputFileDoesNotContain( + 'page1.js', + '/***/ "../../node_modules/@vue/' + ); + + webpackAssert.assertOutputFileDoesNotContain( + 'page2.js', + '/***/ "../../node_modules/@vue/' + ); + + // Preact code should be present in page2.js only + webpackAssert.assertOutputFileDoesNotContain( + 'vuejs.js', + '/***/ "../../node_modules/preact/' + ); + + webpackAssert.assertOutputFileDoesNotContain( + 'page1.js', + '/***/ "../../node_modules/preact/' + ); + + webpackAssert.assertOutputFileContains( + 'page2.js', + '/***/ "../../node_modules/preact/' + ); + + // Check if each entrypoint is associated to the right chunks + webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { + entrypoints: { + page1: { + js: ['/build/runtime.js', '/build/vuejs.js', '/build/page1.js'], + css: ['/build/page1.css'] + }, + page2: { + js: ['/build/runtime.js', '/build/page2.js'] + } + } + }); + + // Check if Vue.js code is still executed properly + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/page1.js', + 'build/vuejs.js', + ], + async({ page }) => { + const bodyText = await page.evaluate(() => document.querySelector('#app').textContent); + expect(bodyText).to.contains('Welcome to Your Vue.js App'); + + resolve(); + } + ); + }); + }); + }); + + it('addCacheGroup() with node_modules', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.enableVueLoader(); + config.enablePreactPreset(); + config.enableSassLoader(); + config.enableLessLoader(); + config.addEntry('page1', `./vuejs/main_v${getVueVersion(config)}`); + config.addEntry('page2', './preact/main'); + + // Move both vue.js and preact code into their own chunk + config.addCacheGroup('common', { + node_modules: [ + '@vue', + 'preact' + ] + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // Vue.js code should be present in common.js but not in page1.js/page2.js + webpackAssert.assertOutputFileContains( + 'common.js', + '/***/ "../../node_modules/@vue/' + ); + + webpackAssert.assertOutputFileDoesNotContain( + 'page1.js', + '/***/ "../../node_modules/@vue/' + ); + + webpackAssert.assertOutputFileDoesNotContain( + 'page2.js', + '/***/ "../../node_modules/@vue/' + ); + + // Preact code should be present in common.js but not in page1.js/page2.js + webpackAssert.assertOutputFileContains( + 'common.js', + '/***/ "../../node_modules/preact/' + ); + + webpackAssert.assertOutputFileDoesNotContain( + 'page1.js', + '/***/ "../../node_modules/preact/' + ); + + webpackAssert.assertOutputFileDoesNotContain( + 'page2.js', + '/***/ "../../node_modules/preact/' + ); + + // Check if each entrypoint is associated to the right chunks + webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { + entrypoints: { + page1: { + js: ['/build/runtime.js', '/build/common.js', '/build/page1.js'], + css: ['/build/page1.css'] + }, + page2: { + js: ['/build/runtime.js', '/build/common.js', '/build/page2.js'] + } + } + }); + + // Check if Preact code is still executed properly + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/page2.js', + 'build/common.js', + ], + async({ page }) => { + expect(await page.evaluate(() => document.querySelector('#app').textContent)).to.contains('This is a React component!'); + resolve(); + } + ); + }); + }); + }); + + it('addCacheGroup() with versioning enabled', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.enableVersioning(); + config.enableVueLoader(); + config.enablePreactPreset(); + config.enableSassLoader(); + config.enableLessLoader(); + config.addEntry('page1', `./vuejs/main_v${getVueVersion(config)}`); + config.addEntry('page2', './preact/main'); + + // Move Vue.js code into its own chunk + config.addCacheGroup('vuejs', { + test: getVueVersion(config) === 2 ? + /[\\/]node_modules[\\/]vue[\\/]/ : + /[\\/]node_modules[\\/]@vue[\\/]/ + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // Check if Vue.js code is still executed properly + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + convertToManifestPath('build/runtime.js', config), + convertToManifestPath('build/page1.js', config), + convertToManifestPath('build/vuejs.js', config), + ], + async({ page }) => { + const bodyText = await page.evaluate(() => document.querySelector('#app').textContent); + expect(bodyText).to.contains('Welcome to Your Vue.js App'); + + resolve(); + } + ); + }); + }); + }); + + it('addCacheGroup() with source maps enabled', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.enableSourceMaps(); + config.enableVueLoader(); + config.enablePreactPreset(); + config.enableSassLoader(); + config.enableLessLoader(); + config.addEntry('page1', `./vuejs/main_v${getVueVersion(config)}`); + config.addEntry('page2', './preact/main'); + + // Move Vue.js code into its own chunk + config.addCacheGroup('vuejs', { + test: getVueVersion(config) === 2 ? + /[\\/]node_modules[\\/]vue[\\/]/ : + /[\\/]node_modules[\\/]@vue[\\/]/ + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // Check if Vue.js code is still executed properly + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/page1.js', + 'build/vuejs.js', + ], + async({ page }) => { + const bodyText = await page.evaluate(() => document.querySelector('#app').textContent); + expect(bodyText).to.contains('Welcome to Your Vue.js App'); + + resolve(); + } + ); + }); + }); + }); + }); + + it('in production mode, code is uglified', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.setPublicPath('/build'); + config.addEntry('main', ['./js/no_require']); + config.addEntry('styles', './css/h1_style.css'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // the comment should not live in the file + webpackAssert.assertOutputFileDoesNotContain( + 'main.js', + '// comments in no_require.js' + ); + // check for any webpack-added comments + webpackAssert.assertOutputFileDoesNotContain( + 'main.js', + '/*!' + ); + // extra spaces should not live in the CSS file + webpackAssert.assertOutputFileDoesNotContain( + 'styles.css', + ' font-size: 50px;' + ); + + resolve(); + }); + }); + }); + + it('PostCSS works when enabled', async () => { + const appDir = testSetup.createTestAppDir(); + + fs.writeFileSync( + path.join(appDir, 'postcss.config.js'), + ` +module.exports = { + plugins: [ + require('autoprefixer')() + ] +} + ` + ); + + const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); + config.enableSingleRuntimeChunk(); + config.setPublicPath('/build'); + // load a file that @import's another file, so that we can + // test that @import resources are parsed through postcss + config.addStyleEntry('styles', ['./css/imports_autoprefixer.css']); + config.addStyleEntry('postcss', './css/postcss_extension.postcss'); + config.enablePostCssLoader(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that the autoprefixer did its work! + webpackAssert.assertOutputFileContains( + 'styles.css', + '-webkit-backdrop-filter' + ); + + // check that the .postcss file was also processed + // correctly (it also @import the autoprefixer_test.css + // file) + webpackAssert.assertOutputFileContains( + 'postcss.css', + '-webkit-backdrop-filter' + ); + + resolve(); + }); + }); + }); + + it('less processes when enabled', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addStyleEntry('styles', ['./css/h2_styles.less']); + config.enableLessLoader(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that less did its work! + webpackAssert.assertOutputFileContains( + 'styles.css', + // less logic inside will resolve to tis + 'color: #fe33ac;' + ); + + resolve(); + }); + }); + }); + + it('stylus processes when enabled', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addStyleEntry('styles', ['./css/h2_styles.styl']); + config.enableStylusLoader(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that stylus did its work! + webpackAssert.assertOutputFileContains( + 'styles.css', + // stylus logic inside will resolve to tis + 'color: #9e9399;' + ); + + resolve(); + }); + }); + }); + + it('Babel is executed on .js files', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', './js/class-syntax'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that babel transformed the class + webpackAssert.assertOutputFileDoesNotContain( + 'main.js', + 'class A {}' + ); + + resolve(); + }); + }); + }); + + it('Babel can be configured via .babelrc', async () => { + // create the .babelrc file first, so we see it + const appDir = testSetup.createTestAppDir(); + + fs.writeFileSync( + path.join(appDir, '.babelrc'), + ` +{ + "presets": [ + ["@babel/preset-env", { + "targets": { + "chrome": 52 + } + }] + ] +} +` + ); + + const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); + config.enableSingleRuntimeChunk(); + config.setPublicPath('/build'); + config.addEntry('main', './js/class-syntax'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that babel transformed classes + webpackAssert.assertOutputFileContains( + 'main.js', + // chrome 45 supports class, so it's not transpiled + 'class A {}' + ); + + resolve(); + }); + }); + }); + + it('Babel can be configured via package.json browserlist', async () => { + const cwd = process.cwd(); + afterAll(() => { + process.chdir(cwd); + }); + + const appDir = testSetup.createTestAppDir(); + /* + * Most of the time, we don't explicitly use chdir + * in order to set the cwd() to the test directory. + * That's because, in theory, you should be able to + * run Encore from other directories, give you set + * the context. However, in this case, babel/presest-env + * uses process.cwd() to find the configPath, instead of the + * context. So, in this case, we *must* set the cwd() + * to be the temp test directory. + */ + process.chdir(appDir); + + // create the package.json file first, so we see it + fs.writeFileSync( + path.join(appDir, 'package.json'), + ` +{ + "browserslist": "Chrome 52" +} +` + ); + + const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); + config.enableSingleRuntimeChunk(); + config.setPublicPath('/build'); + config.addEntry('main', './js/class-syntax'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that babel did not transform classes + webpackAssert.assertOutputFileContains( + 'main.js', + // chrome 45 supports class, so it's not transpiled + 'class A {}' + ); + + resolve(); + }); + }); + }); + + it('Babel adds polyfills correctly', async () => { + const cwd = process.cwd(); + afterAll(() => { + process.chdir(cwd); + }); + + const appDir = testSetup.createTestAppDir(); + process.chdir(appDir); + + fs.writeFileSync( + path.join(appDir, 'package.json'), + + // The test case uses Array.flat which + // isn't supported by IE11 + '{"browserslist": "IE 11"}' + ); + + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('commonjs', './js/import_polyfills_commonjs.js'); + config.addEntry('ecmascript', './js/import_polyfills_ecmascript.js'); + config.configureBabel(null, { + useBuiltIns: 'usage', + corejs: 3, + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, async(webpackAssert) => { + for (const scriptName of ['commonjs.js', 'ecmascript.js']) { + // Check that the polyfills are included correctly + // in both files. + webpackAssert.assertOutputFileContains( + scriptName, + 'Array.prototype.flat' + ); + + // Test that the generated scripts work fine + await testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + `build/${scriptName}`, + ], + async({ page }) => { + expect(await page.evaluate(() => document.body.textContent)).to.contains('[1,2,3,4]'); + } + ); + } + + resolve(); + }); + }); + }); + + it('Babel does not force transforms if they are not needed', async () => { + const cwd = process.cwd(); + afterAll(() => { + process.chdir(cwd); + }); + + const appDir = testSetup.createTestAppDir(); + process.chdir(appDir); + + fs.writeFileSync( + path.join(appDir, 'package.json'), + + // Chrome 55 supports async and arrow functions + '{"browserslist": "Chrome 55"}' + ); + + const config = createWebpackConfig('www/build', 'prod'); + config.setPublicPath('/build'); + config.addEntry('async', './js/async_function.js'); + config.configureBabel(null, { + useBuiltIns: 'usage', + corejs: 3, + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, async(webpackAssert) => { + webpackAssert.assertOutputFileContains( + 'async.js', + 'async function(){console.log("foo")}().then((()=>{console.log("bar")}))' + ); + + resolve(); + }); + }); + }); + + it('When enabled, react JSX is transformed!', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', './js/CoolReactComponent.jsx'); + config.enableReactPreset(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that babel transformed the JSX + webpackAssert.assertOutputFileContains( + 'main.js', + 'React.createElement' + ); + + resolve(); + }); + }); + }); + + it('When enabled, preact JSX is transformed without preact-compat!', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', './js/CoolReactComponent.jsx'); + config.enablePreactPreset(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that babel transformed the JSX + webpackAssert.assertOutputFileContains( + 'main.js', + 'var hiGuys = h(' + ); + + resolve(); + }); + }); + }); + + it('When enabled, svelte is transformed', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', './js/hello_world.svelte'); + config.enableSvelte(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that babel transformed the svelte files + webpackAssert.assertOutputFileContains( + 'main.js', + 'SvelteComponent' + ); + + resolve(); + }); + }); + }); + + it('When enabled, preact JSX is transformed with preact-compat!', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', './js/CoolReactComponent.jsx'); + config.enablePreactPreset({ preactCompat: true }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that babel transformed the JSX + webpackAssert.assertOutputFileContains( + 'main.js', + 'React.createElement' + ); + + resolve(); + }); + }); + }); + + it('When configured, TypeScript is compiled!', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', ['./js/index.ts']); + const testCallback = () => {}; + config.enableTypeScriptLoader(testCallback); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that ts-loader transformed the ts file + webpackAssert.assertOutputFileContains( + 'main.js', + 'document.getElementById(\'app\').innerHTML =' + ); + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/main.js' + ], + async({ page }) => { + // assert that the ts module rendered + const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); + expect(h1Text).to.contains('Welcome to Your TypeScript App'); + + resolve(); + } + ); + }); + }); + }); + + it('TypeScript is compiled and type checking is done in a separate process!', {timeout: 9000}, async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', ['./js/render.ts', './js/index.ts']); + config.enableTypeScriptLoader(); + // test should fail if `config.typescript.configFile` is not set up properly + config.enableForkedTypeScriptTypesChecking((config) => { + + }); + + await expect(() => { + return new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + resolve(); + }); + }); + // Cannot find the "/path/to/tsconfig.json" file + }).rejects.toThrow('Cannot find the'); + }); + + it('TypeScript can be compiled by Babel', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', ['./js/render.ts', './js/index.ts']); + config.enableBabelTypeScriptPreset(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check that babel-loader transformed the ts file + webpackAssert.assertOutputFileContains( + 'main.js', + 'document.getElementById(\'app\').innerHTML =', + ); + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/main.js', + ], + async({ page }) => { + // assert that the ts module rendered + const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); + expect(h1Text).to.contains('Welcome to Your TypeScript App'); + + resolve(); + }, + ); + }); + }); + }); + + it('When configured, Handlebars is compiled', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', ['./js/handlebars.js']); + const testCallback = () => {}; + config.enableHandlebarsLoader(testCallback); + + await new Promise(resolve => { + testSetup.runWebpack(config, () => { + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/main.js' + ], + async({ page }) => { + const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); + expect(h1Text).to.contains('Welcome to Your Handlebars App'); + + resolve(); + } + ); + }); + }); + }); + + it('The output directory is cleaned between builds', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', './js/no_require'); + config.cleanupOutputBeforeBuild(); + testSetup.touchFileInOutputDir('file.txt', config); + testSetup.touchFileInOutputDir('deeper/other.txt', config); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // make sure the file was cleaned up! + webpackAssert.assertOutputFileDoesNotExist( + 'file.txt' + ); + webpackAssert.assertOutputFileDoesNotExist( + 'deeper/other.txt' + ); + + resolve(); + }); + }); + }); + + it('Vue.js is compiled correctly', async () => { + const appDir = testSetup.createTestAppDir(); + + fs.writeFileSync( + path.join(appDir, 'postcss.config.js'), + ` +module.exports = { + plugins: [ + require('autoprefixer')() + ] +} ` + ); + + const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); + config.enableSingleRuntimeChunk(); + config.setPublicPath('/build'); + config.addEntry('main', `./vuejs/main_v${getVueVersion(config)}`); + config.enableVueLoader(); + config.enableSassLoader(); + config.enableLessLoader(); + config.configureBabel(function(config) { + config.presets = [ + ['@babel/preset-env', { + 'targets': { + 'chrome': 52 + } + }] + ]; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory().with.deep.files([ + 'main.js', + 'main.css', + 'images/logo.26bd867d.png', + 'manifest.json', + 'entrypoints.json', + 'runtime.js', + ]); + + // test that our custom babel config is used + webpackAssert.assertOutputFileContains( + 'main.js', + 'class TestClassSyntax' + ); + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/main.js' + ], + async({ page }) => { + // assert that the vue.js app rendered + const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); + expect(h1Text).to.contains('Welcome to Your Vue.js App'); + + // make sure the styles are not inlined + const styleElementsCount = await page.evaluate(() => document.querySelectorAll('style').length); + expect(styleElementsCount).to.equal(0); + + resolve(); + } + ); + }); + }); + }); + + it('Vue.js is compiled correctly using TypeScript', async () => { + const appDir = testSetup.createTestAppDir(); + + fs.writeFileSync( + path.join(appDir, 'postcss.config.js'), + ` +module.exports = { + plugins: [ + require('autoprefixer')() + ] +} ` + ); + + const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); + config.enableSingleRuntimeChunk(); + config.setPublicPath('/build'); + config.addEntry('main', `./vuejs${getVueVersion(config)}-typescript/main`); + config.enableVueLoader(); + config.enableSassLoader(); + config.enableLessLoader(); + config.enableTypeScriptLoader(); + config.configureBabel(function(config) { + config.presets = [ + ['@babel/preset-env', { + 'targets': { + 'chrome': 52 + } + }] + ]; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory().with.deep.files([ + 'main.js', + 'main.css', + 'images/logo.26bd867d.png', + 'manifest.json', + 'entrypoints.json', + 'runtime.js', + ]); + + // test that our custom babel config is used + webpackAssert.assertOutputFileContains( + 'main.js', + 'class TestClassSyntax' + ); + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/main.js' + ], + async({ page }) => { + // assert that the vue.js app rendered + const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); + expect(h1Text).to.contains('Welcome to Your Vue.js App'); + + // make sure the styles are not inlined + const styleElementsCount = await page.evaluate(() => document.querySelectorAll('style').length); + expect(styleElementsCount).to.equal(0); + + resolve(); + } + ); + }); + }); + }); + + it('Vue.js supports CSS/Sass/Less/Stylus/PostCSS modules', async () => { + const appDir = testSetup.createTestAppDir(); + const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); + config.enableSingleRuntimeChunk(); + config.setPublicPath('/build'); + config.addEntry('main', `./vuejs-css-modules/main_v${getVueVersion(config)}`); + config.enableVueLoader(); + config.enableSassLoader(); + config.enableLessLoader(); + config.enableStylusLoader(); + config.configureCssLoader(options => { + // Until https://github.com/vuejs/vue-loader/pull/1909 is merged, + // Vue users should configure the css-loader modules + // to keep the previous default behavior from css-loader v6 + if (options.modules) { + options.modules.namedExport = false; + options.modules.exportLocalsConvention = 'as-is'; + } + + // Remove hashes from local ident names + // since they are not always the same. + if (options.modules) { + options.modules.localIdentName = '[local]_foo'; + } + }); + + // Enable the PostCSS loader so we can use `lang="postcss"` + config.enablePostCssLoader(); + fs.writeFileSync( + path.join(appDir, 'postcss.config.js'), + ` +module.exports = { + plugins: [ + require('autoprefixer')() + ] +} ` + ); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory().with.deep.files([ + 'main.js', + 'main.css', + 'manifest.json', + 'entrypoints.json', + 'runtime.js', + ]); + + const expectClassDeclaration = (className) => { + webpackAssert.assertOutputFileContains( + 'main.css', + `.${className} {` + ); + }; + + expectClassDeclaration('red'); // Standard CSS + expectClassDeclaration('large'); // Standard SCSS + expectClassDeclaration('justified'); // Standard Less + expectClassDeclaration('lowercase'); // Standard Stylus + expectClassDeclaration('block'); // Standard PostCSS + + expectClassDeclaration('italic_foo'); // CSS Module + expectClassDeclaration('bold_foo'); // SCSS Module + expectClassDeclaration('underline_foo'); // Less Module + expectClassDeclaration('rtl_foo'); // Stylus Module + expectClassDeclaration('hidden_foo'); // PostCSS Module + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/main.js' + ], + async({ page }) => { + const divClassArray = await page.evaluate(() => Array.from(document.body.querySelector('#app > div').classList.values())); + + expect(divClassArray.includes('red')).to.be.true; // Standard CSS + expect(divClassArray.includes('large')).to.be.true; // Standard SCSS + expect(divClassArray.includes('justified')).to.be.true; // Standard Less + expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus + expect(divClassArray.includes('block')).to.be.true; // Standard PostCSS + + expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module + expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module + expect(divClassArray.includes('underline_foo')).to.be.true; // Less module + expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module + expect(divClassArray.includes('hidden_foo')).to.be.true; // PostCSS module + + resolve(); + } + ); + }); + }); + }); + + it('React supports CSS/Sass/Less/Stylus modules', async () => { + const appDir = testSetup.createTestAppDir(); + const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); + config.enableSingleRuntimeChunk(); + config.setPublicPath('/build'); + config.addEntry('main', './react-css-modules/main.js'); + config.enableReactPreset(); + config.enableSassLoader(); + config.enableLessLoader(); + config.enableStylusLoader(); + config.configureCssLoader(options => { + // Remove hashes from local ident names + // since they are not always the same. + if (options.modules) { + options.modules.localIdentName = '[local]_foo'; + } + }); + + // Enable the PostCSS loader so we can use `lang="postcss"` + config.enablePostCssLoader(); + fs.writeFileSync( + path.join(appDir, 'postcss.config.js'), + ` +module.exports = { + plugins: [ + require('autoprefixer')() + ] +} ` + ); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory().with.deep.files([ + 'main.js', + 'main.css', + 'manifest.json', + 'entrypoints.json', + 'runtime.js', + ]); + + const expectClassDeclaration = (className) => { + webpackAssert.assertOutputFileContains( + 'main.css', + `.${className} {` + ); + }; + + expectClassDeclaration('red'); // Standard CSS + expectClassDeclaration('large'); // Standard SCSS + expectClassDeclaration('justified'); // Standard Less + expectClassDeclaration('lowercase'); // Standard Stylus + + expectClassDeclaration('italic_foo'); // CSS Module + expectClassDeclaration('bold_foo'); // SCSS Module + expectClassDeclaration('underline_foo'); // Less Module + expectClassDeclaration('rtl_foo'); // Stylus Module + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/main.js' + ], + async({ page }) => { + const divClassArray = await page.evaluate(() => Array.from(document.body.querySelector('#app > div').classList.values())); + + expect(divClassArray.includes('red')).to.be.true; // Standard CSS + expect(divClassArray.includes('large')).to.be.true; // Standard SCSS + expect(divClassArray.includes('justified')).to.be.true; // Standard Less + expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus + + expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module + expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module + expect(divClassArray.includes('underline_foo')).to.be.true; // Less module + expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module + + resolve(); + } + ); + }); + }); + }); + + it('Preact supports CSS/Sass/Less/Stylus modules', async () => { + const appDir = testSetup.createTestAppDir(); + const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); + config.enableSingleRuntimeChunk(); + config.setPublicPath('/build'); + config.addEntry('main', './preact-css-modules/main.js'); + config.enablePreactPreset(); + config.enableSassLoader(); + config.enableLessLoader(); + config.enableStylusLoader(); + config.configureCssLoader(options => { + // Remove hashes from local ident names + // since they are not always the same. + if (options.modules) { + options.modules.localIdentName = '[local]_foo'; + } + }); + + // Enable the PostCSS loader so we can use `lang="postcss"` + config.enablePostCssLoader(); + fs.writeFileSync( + path.join(appDir, 'postcss.config.js'), + ` +module.exports = { + plugins: [ + require('autoprefixer')() + ] +} ` + ); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory().with.deep.files([ + 'main.js', + 'main.css', + 'manifest.json', + 'entrypoints.json', + 'runtime.js', + ]); + + const expectClassDeclaration = (className) => { + webpackAssert.assertOutputFileContains( + 'main.css', + `.${className} {` + ); + }; + + expectClassDeclaration('red'); // Standard CSS + expectClassDeclaration('large'); // Standard SCSS + expectClassDeclaration('justified'); // Standard Less + expectClassDeclaration('lowercase'); // Standard Stylus + + expectClassDeclaration('italic_foo'); // CSS Module + expectClassDeclaration('bold_foo'); // SCSS Module + expectClassDeclaration('underline_foo'); // Less Module + expectClassDeclaration('rtl_foo'); // Stylus Module + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/main.js' + ], + async({ page }) => { + const divClassArray = await page.evaluate(() => Array.from(document.body.querySelector('#app > div').classList.values())); + + expect(divClassArray.includes('red')).to.be.true; // Standard CSS + expect(divClassArray.includes('large')).to.be.true; // Standard SCSS + expect(divClassArray.includes('justified')).to.be.true; // Standard Less + expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus + + expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module + expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module + expect(divClassArray.includes('underline_foo')).to.be.true; // Less module + expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module + + resolve(); + } + ); + }); + }); + }); + + it('Vue.js error when using non-activated loaders', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', `./vuejs/main_v${getVueVersion(config)}`); + config.enableVueLoader(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert, stats, output) => { + expect(output).to.contain('To load LESS files'); + expect(output).to.contain('To load Sass files'); + + resolve(); + }, true); + }); + }); + + it('Vue.js is compiled correctly with JSX support', async () => { + const appDir = testSetup.createTestAppDir(); + + fs.writeFileSync( + path.join(appDir, 'postcss.config.js'), + ` +module.exports = { + plugins: [ + require('autoprefixer')() + ] +} ` + ); + + const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); + + config.enableSingleRuntimeChunk(); + config.setPublicPath('/build'); + config.addEntry('main', `./vuejs-jsx/main_v${getVueVersion(config)}`); + config.enableVueLoader(() => {}, { + useJsx: true, + version: getVueVersion(config), + }); + config.enableSassLoader(); + config.enableLessLoader(); + config.configureBabel(function(config) { + // throw new Error(JSON.stringify(config)); + expect(config.presets[0][0]).to.equal(require.resolve('@babel/preset-env')); + config.presets[0][1].targets = { + chrome: 109 + }; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory().with.deep.files([ + 'main.js', + 'main.css', + 'images/logo.26bd867d.png', + 'manifest.json', + 'entrypoints.json', + 'runtime.js', + ]); + + // test that our custom babel config is used + webpackAssert.assertOutputFileContains( + 'main.js', + 'class TestClassSyntax' + ); + + // test that global styles are working correctly + webpackAssert.assertOutputFileContains( + 'main.css', + '#app {' + ); + + // test that CSS Modules (for scoped styles) is used + webpackAssert.assertOutputFileContains( + 'main.css', + '.h1_' // `.h1` is transformed to `.h1_[a-zA-Z0-9]` + ); + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/main.js' + ], + async({ page }) => { + // assert that the vue.js app rendered + const h1Text = await page.evaluate(() => document.querySelector('#app h1').textContent); + expect(h1Text).to.contains('Welcome to Your Vue.js App'); + + // make sure the styles are not inlined + const styleElementsCount = await page.evaluate(() => document.querySelectorAll('style').length); + expect(styleElementsCount).to.equal(0); + + resolve(); + } + ); + }); + }); + }); + + it('configureImageRule() allows configuring maxSize for inlining', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.setPublicPath('/build'); + config.addStyleEntry('url-loader', './css/url-loader.css'); + // set a size so that they do NOT inline + config.configureImageRule({ type: 'asset', maxSize: 102400 }); + config.configureFontRule({ type: 'asset', maxSize: 102400 }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'url-loader.css', + 'manifest.json', + 'entrypoints.json', + 'runtime.js' + ]); + + webpackAssert.assertOutputFileContains( + 'url-loader.css', + 'url(data:font/woff2;base64,' + ); + + webpackAssert.assertOutputFileContains( + 'url-loader.css', + 'url(data:image/png;base64,' + ); + + resolve(); + }); + }); + }); + + it('Code splitting with dynamic import', async () => { + const config = createWebpackConfig('www/build', 'dev'); + config.setPublicPath('/build'); + config.addEntry('main', './js/code_splitting_dynamic_import'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // check for the code-split file + webpackAssert.assertOutputFileContains( + 'js_print_to_app_export_js.js', + 'document.getElementById(\'app\').innerHTML =' + ); + + testSetup.requestTestPage( + browser, + path.join(config.getContext(), 'www'), + [ + 'build/runtime.js', + 'build/main.js' + ], + async({ page }) => { + // assert the async module was loaded and works + expect(await page.evaluate(() => document.querySelector('#app').textContent)).to.contains('Welcome to Encore!'); + + resolve(); + } + ); + }); + }); + }); + + it('Symfony - Stimulus standard app is built correctly', async function({ skip }) { + const appDir = testSetup.createTestAppDir(); + + const version = packageHelper.getPackageVersion('@symfony/stimulus-bridge'); + if (!semver.satisfies(version, '^3.0.0')) { + // we support the old version, but it's not tested + skip(); + + return; + } + + const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev'); + config.enableSingleRuntimeChunk(); + config.setPublicPath('/build'); + config.addEntry('main', './stimulus/assets/app.js'); + config.enableStimulusBridge(__dirname + '/../fixtures/stimulus/assets/controllers.json'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory().with.deep.files([ + 'main.js', + 'main.css', + 'manifest.json', + 'node_modules_symfony_mock-module_dist_controller_js.js', + 'entrypoints.json', + 'runtime.js', + ]); + + // test controllers and style are shipped + webpackAssert.assertOutputFileContains('main.js', 'app-controller'); + webpackAssert.assertOutputFileContains('node_modules_symfony_mock-module_dist_controller_js.js', 'mock-module-controller'); + webpackAssert.assertOutputFileContains('main.css', 'body {}'); + + resolve(); + }); + }); + }); + + describe('copyFiles() allows to copy files and folders', () => { + it('Single file copy', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.copyFiles({ + from: './images', + pattern: /symfony_logo\.png/, + includeSubdirectories: false + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json', + 'symfony_logo.png' + ]); + + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.js' + ); + + webpackAssert.assertManifestPath( + 'build/symfony_logo.png', + '/build/symfony_logo.png' + ); + + resolve(); + }); + }); + }); + + it('Folder copy without subdirectories', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.copyFiles({ + from: './images', + includeSubdirectories: false + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json', + 'symfony_logo.png', + 'symfony_logo_alt.png', + ]); + + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.js' + ); + + webpackAssert.assertManifestPath( + 'build/symfony_logo.png', + '/build/symfony_logo.png' + ); + + webpackAssert.assertManifestPath( + 'build/symfony_logo_alt.png', + '/build/symfony_logo_alt.png' + ); + + resolve(); + }); + }); + }); + + it('Multiple copies', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.copyFiles([{ + from: './images', + to: 'assets/[path][name].[ext]', + includeSubdirectories: false + }, { + from: './fonts', + to: 'assets/[path][name].[ext]', + includeSubdirectories: false + }]); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json' + ]); + + expect(path.join(config.outputPath, 'assets')).to.be.a.directory() + .with.files([ + 'symfony_logo.png', + 'symfony_logo_alt.png', + 'Roboto.woff2', + ]); + + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.js' + ); + + webpackAssert.assertManifestPath( + 'build/assets/symfony_logo.png', + '/build/assets/symfony_logo.png' + ); + + webpackAssert.assertManifestPath( + 'build/assets/symfony_logo_alt.png', + '/build/assets/symfony_logo_alt.png' + ); + + webpackAssert.assertManifestPath( + 'build/assets/Roboto.woff2', + '/build/assets/Roboto.woff2' + ); + + resolve(); + }); + }); + }); + + it('Copy folder and subdirectories with versioning enabled to the specified location', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.copyFiles({ + from: './images', + to: 'images/[path][name].[hash:8].[ext]', + includeSubdirectories: true + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json', + ]); + + expect(path.join(config.outputPath, 'images')).to.be.a.directory() + .with.files([ + 'symfony_logo.91beba37.png', + 'symfony_logo_alt.f880ba14.png', + ]); + + expect(path.join(config.outputPath, 'images', 'same_filename')).to.be.a.directory() + .with.files([ + 'symfony_logo.f880ba14.png', + ]); + + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.js' + ); + + webpackAssert.assertManifestPath( + 'build/images/symfony_logo.png', + '/build/images/symfony_logo.91beba37.png' + ); + + webpackAssert.assertManifestPath( + 'build/images/symfony_logo_alt.png', + '/build/images/symfony_logo_alt.f880ba14.png' + ); + + webpackAssert.assertManifestPath( + 'build/images/same_filename/symfony_logo.png', + '/build/images/same_filename/symfony_logo.f880ba14.png' + ); + + resolve(); + }); + }); + }); + + it('Filter files using the given pattern', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.copyFiles({ + from: './images', + pattern: /_alt/, + includeSubdirectories: false + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json', + 'symfony_logo_alt.png', + ]); + + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.js' + ); + + webpackAssert.assertManifestPath( + 'build/symfony_logo_alt.png', + '/build/symfony_logo_alt.png' + ); + + webpackAssert.assertManifestPathDoesNotExist( + 'build/symfony_logo.png' + ); + + resolve(); + }); + }); + }); + + it('Copy with versioning enabled', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.enableVersioning(true); + config.copyFiles([{ + from: './images', + includeSubdirectories: false + }, { + from: './fonts', + to: 'assets/[path][name].[ext]', + includeSubdirectories: false + }]); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertDirectoryContents([ + 'entrypoints.json', + 'runtime.[hash:8].js', + 'main.[hash:8].js', + 'manifest.json', + 'symfony_logo.[hash:8].png', + 'symfony_logo_alt.[hash:8].png', + ]); + + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.[hash:8].js' + ); + + expect(path.join(config.outputPath, 'assets')).to.be.a.directory() + .with.files([ + 'Roboto.woff2', + ]); + + webpackAssert.assertManifestPath( + 'build/symfony_logo.png', + '/build/symfony_logo.91beba37.png' + ); + + webpackAssert.assertManifestPath( + 'build/symfony_logo_alt.png', + '/build/symfony_logo_alt.f880ba14.png' + ); + + webpackAssert.assertManifestPath( + 'build/assets/Roboto.woff2', + '/build/assets/Roboto.woff2' + ); + + resolve(); + }); + }); + }); + + it('Do not try to copy files from an invalid path', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.copyFiles([{ + from: './images', + to: 'assets/[path][name].[ext]', + includeSubdirectories: false + }, { + from: './foo', + to: 'assets/[path][name].[ext]', + includeSubdirectories: false + }, { + from: './fonts', + to: 'assets/[path][name].[ext]', + includeSubdirectories: false + }, { + from: './images/symfony_logo.png', + includeSubdirectories: true + }]); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert, stats, stdout) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json' + ]); + + expect(stdout).to.contain('should be set to an existing directory but "./foo" does not seem to exist'); + expect(stdout).to.contain('should be set to an existing directory but "./images/symfony_logo.png" seems to be a file'); + + resolve(); + }); + }); + }); + + it('Copy with a custom context', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.copyFiles({ + from: './images', + to: '[path][name].[hash:8].[ext]', + includeSubdirectories: true, + context: './', + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json', + ]); + + expect(path.join(config.outputPath, 'images')).to.be.a.directory() + .with.files([ + 'symfony_logo.91beba37.png', + 'symfony_logo_alt.f880ba14.png', + ]); + + expect(path.join(config.outputPath, 'images', 'same_filename')).to.be.a.directory() + .with.files([ + 'symfony_logo.f880ba14.png', + ]); + + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.js' + ); + + webpackAssert.assertManifestPath( + 'build/images/symfony_logo.png', + '/build/images/symfony_logo.91beba37.png' + ); + + webpackAssert.assertManifestPath( + 'build/images/symfony_logo_alt.png', + '/build/images/symfony_logo_alt.f880ba14.png' + ); + + webpackAssert.assertManifestPath( + 'build/images/same_filename/symfony_logo.png', + '/build/images/same_filename/symfony_logo.f880ba14.png' + ); + + resolve(); + }); + }); + }); + + it('Copy files without processing them', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.copyFiles({ from: './copy' }); + + // By default the css-minimizer-webpack-plugin will + // run on ALL emitted CSS files, which includes the ones + // handled by `Encore.copyFiles()`. + // We disable it for this test since our CSS file will + // not be valid and can't be handled by this plugin. + config.configureCssMinimizerPlugin(options => { + options.include = /^$/; + }); + + // By default the terser-webpack-plugin will run on + // ALL emitted JS files, which includes the ones + // handled by `Encore.copyFiles()`. + // We disable it for this test since our JS file will + // not be valid and can't be handled by this plugin. + config.configureTerserPlugin(options => { + options.include = /^$/; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json', + 'foo.css', + 'foo.js', + 'foo.json', + 'foo.png', + ]); + + for (const file of ['foo.css', 'foo.js', 'foo.json', 'foo.png']) { + webpackAssert.assertOutputFileContains( + file, + 'This is an invalid content to check that the file is still copied' + ); + } + + resolve(); + }); + }); + }); + + it('Do not copy files excluded by a RegExp', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + + // foo.css and foo.js should match this rule + // and be versioned + config.copyFiles({ + from: './copy', + to: './[path][name]-[hash:8].[ext]', + pattern: /\.(css|js)$/, + }); + + // foo.css and foo.js should *not* match this rule + config.copyFiles({ + from: './copy', + to: './[path][name].[ext]', + pattern: /\.(?!(css|js)$)([^.]+$)/ + }); + + // By default the css-minimizer-webpack-plugin will + // run on ALL emitted CSS files, which includes the ones + // handled by `Encore.copyFiles()`. + // We disable it for this test since our CSS file will + // not be valid and can't be handled by this plugin. + config.configureCssMinimizerPlugin(options => { + options.include = /^$/; + }); + + // By default the terser-webpack-plugin will run on + // ALL emitted JS files, which includes the ones + // handled by `Encore.copyFiles()`. + // We disable it for this test since our JS file will + // not be valid and can't be handled by this plugin. + config.configureTerserPlugin(options => { + options.include = /^$/; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertDirectoryContents([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json', + + // 1st rule + 'foo-[hash:8].css', + 'foo-[hash:8].js', + + // 2nd rule + 'foo.json', + 'foo.png', + ]); + + resolve(); + }); + }); + }); + + it('Can use the "[N]" placeholder', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.copyFiles({ + from: './images', + pattern: /(symfony)_(logo)\.png/, + to: '[path][2]_[1].[ext]', + includeSubdirectories: false + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json', + 'logo_symfony.png' + ]); + + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.js' + ); + + webpackAssert.assertManifestPath( + 'build/symfony_logo.png', + '/build/logo_symfony.png' + ); + + resolve(); + }); + }); + }); + + it('Does not prevent from setting the map option of the manifest plugin', async () => { + const config = createWebpackConfig('www/build', 'production'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.copyFiles({ + from: './images', + pattern: /symfony_logo\.png/, + includeSubdirectories: false + }); + + config.configureManifestPlugin(options => { + options.map = (file) => { + return Object.assign({}, file, { + name: `${file.name}.test`, + }); + }; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'entrypoints.json', + 'runtime.js', + 'main.js', + 'manifest.json', + 'symfony_logo.png' + ]); + + webpackAssert.assertManifestPath( + 'build/main.js.test', + '/build/main.js' + ); + + webpackAssert.assertManifestPath( + 'build/symfony_logo.png.test', + '/build/symfony_logo.png' + ); + + resolve(); + }); + }); + }); + }); + + describe('entrypoints.json & splitChunks()', () => { + it('Use "all" splitChunks & look at entrypoints.json', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); + config.addEntry('other', ['./css/roboto_font.css', 'vue']); + config.setPublicPath('/build'); + // enable versioning to make sure entrypoints.json is not affected + config.splitEntryChunks(); + config.configureSplitChunks((splitChunks) => { + splitChunks.minSize = 0; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { + entrypoints: { + main: { + js: [ + '/build/runtime.js', + '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', + '/build/css_roboto_font_css.js', + '/build/main.js' + ], + css: ['/build/css_roboto_font_css.css'] + }, + other: { + js: [ + '/build/runtime.js', + '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', + '/build/css_roboto_font_css.js', + '/build/other.js' + ], + css: ['/build/css_roboto_font_css.css'] + } + } + }); + + // make split chunks are correct in manifest + webpackAssert.assertManifestKeyExists('build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js'); + + resolve(); + }); + }); + }); + + it('Custom public path does affect entrypoints.json but does not affect manifest.json', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); + config.addEntry('other', ['./css/roboto_font.css', 'vue']); + config.setPublicPath('http://localhost:8080/build'); + config.setManifestKeyPrefix('custom_prefix'); + config.configureSplitChunks((splitChunks) => { + splitChunks.chunks = 'all'; + splitChunks.minSize = 0; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { + entrypoints: { + main: { + js: [ + 'http://localhost:8080/build/runtime.js', + 'http://localhost:8080/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', + 'http://localhost:8080/build/css_roboto_font_css.js', + 'http://localhost:8080/build/main.js' + ], + css: ['http://localhost:8080/build/css_roboto_font_css.css'] + }, + other: { + js: [ + 'http://localhost:8080/build/runtime.js', + 'http://localhost:8080/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', + 'http://localhost:8080/build/css_roboto_font_css.js', + 'http://localhost:8080/build/other.js' + ], + css: ['http://localhost:8080/build/css_roboto_font_css.css'] + } + } + }); + + // make split chunks are correct in manifest + webpackAssert.assertManifestKeyExists('custom_prefix/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js'); + + resolve(); + }); + }); + }); + + it('Subdirectory public path affects entrypoints.json but does not affect manifest.json', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); + config.addEntry('other', ['./css/roboto_font.css', 'vue']); + config.setPublicPath('/subdirectory/build'); + config.setManifestKeyPrefix('custom_prefix'); + config.configureSplitChunks((splitChunks) => { + splitChunks.chunks = 'all'; + splitChunks.minSize = 0; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { + entrypoints: { + main: { + js: [ + '/subdirectory/build/runtime.js', + '/subdirectory/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', + '/subdirectory/build/css_roboto_font_css.js', + '/subdirectory/build/main.js' + ], + css: ['/subdirectory/build/css_roboto_font_css.css'] + }, + other: { + js: [ + '/subdirectory/build/runtime.js', + '/subdirectory/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', + '/subdirectory/build/css_roboto_font_css.js', + '/subdirectory/build/other.js' + ], + css: ['/subdirectory/build/css_roboto_font_css.css'] + } + } + }); + + // make split chunks are correct in manifest + webpackAssert.assertManifestKeyExists('custom_prefix/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js'); + + resolve(); + }); + }); + }); + + it('Use splitChunks in production mode', async () => { + const config = createWebpackConfig('web/build', 'production'); + config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); + config.addEntry('other', ['./css/roboto_font.css', 'vue']); + config.setPublicPath('/build'); + config.splitEntryChunks(); + config.configureSplitChunks((splitChunks) => { + splitChunks.minSize = 0; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, () => { + // in production, we hash the chunk names to avoid exposing any extra details + const entrypointsData = JSON.parse(readOutputFileContents('entrypoints.json', config)); + const mainJsFiles = entrypointsData.entrypoints.main.js; + expect(mainJsFiles).to.have.length(4); + expect(mainJsFiles[0]).equals('/build/runtime.js'); + // keys 1 and 2 are "split files" with an integer name that sometimes changes + expect(mainJsFiles[3]).equals('/build/main.js'); + + expect(entrypointsData.entrypoints.main.css[0]).matches(/\/build\/(\d)+\.css/); + + // make split chunks are correct in manifest + const manifestData = JSON.parse(readOutputFileContents('manifest.json', config)); + mainJsFiles.forEach((file) => { + // file.substring(1) => /build/main.js -> build/main.js + expect(Object.keys(manifestData)).includes(file.substring(1)); + }); + + resolve(); + }); + }); + }); + + it('Use splitEntryChunks() with code splitting', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', ['./js/code_splitting', 'vue']); + config.addEntry('other', ['./js/no_require', 'vue']); + config.setPublicPath('/build'); + config.splitEntryChunks(); + config.configureSplitChunks((splitChunks) => { + splitChunks.minSize = 0; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertOutputJsonFileMatches('entrypoints.json', { + entrypoints: { + main: { + js: ['/build/runtime.js', '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', '/build/main.js'] + }, + other: { + js: ['/build/runtime.js', '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', '/build/js_no_require_js.js', '/build/other.js'] + } + } + }); + + // make split chunks are correct in manifest + webpackAssert.assertManifestKeyExists('build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js'); + webpackAssert.assertManifestKeyExists('build/js_no_require_js.js'); + + resolve(); + }); + }); + }); + + it('Make sure chunkIds do not change between builds', async () => { + // https://github.com/symfony/webpack-encore/issues/461 + const createSimilarConfig = function(includeExtraEntry) { + const config = createWebpackConfig('web/build', 'production'); + config.addEntry('main1', './js/code_splitting'); + if (includeExtraEntry) { + config.addEntry('main2', './js/eslint'); + } + config.addEntry('main3', './js/no_require'); + config.setPublicPath('/build'); + + return config; + }; + + const configA = createSimilarConfig(false); + const configB = createSimilarConfig(true); + + await new Promise(resolve => { + testSetup.runWebpack(configA, () => { + testSetup.runWebpack(configB, () => { + const main3Contents = readOutputFileContents('main3.js', configA); + const finalMain3Contents = readOutputFileContents('main3.js', configB); + + if (finalMain3Contents !== main3Contents) { + throw new Error(`Contents after first compile do not match after second compile: \n\n ${main3Contents} \n\n versus \n\n ${finalMain3Contents} \n`); + } + + resolve(); + }); + }); + }); + }); + + it('Do not change contents or filenames when more modules require the same split contents', async () => { + const createSimilarConfig = function(includeExtraEntry) { + const config = createWebpackConfig('web/build', 'production'); + config.addEntry('main1', ['./js/code_splitting', 'preact']); + config.addEntry('main3', ['./js/no_require', 'preact']); + if (includeExtraEntry) { + config.addEntry('main4', ['./js/eslint', 'preact']); + } + config.setPublicPath('/build'); + config.splitEntryChunks(); + config.configureSplitChunks((splitChunks) => { + // will include preact, but prevent any other splitting + splitChunks.minSize = 10000; + }); + + return config; + }; + + const getSplitVendorJsPath = function(config) { + const entrypointData = getEntrypointData(config, 'main3'); + + const splitFiles = entrypointData.js.filter(filename => { + return filename !== '/build/runtime.js' && filename !== '/build/main3.js'; + }); + + // sanity check + if (splitFiles.length !== 1) { + throw new Error(`Unexpected number (${splitFiles.length}) of split files for main3 entry`); + } + + return splitFiles[0]; + }; + + const configA = createSimilarConfig(false); + const configB = createSimilarConfig(true); + + await new Promise(resolve => { + testSetup.runWebpack(configA, () => { + testSetup.runWebpack(configB, () => { + const vendorPath = getSplitVendorJsPath(configA); + const finalVendorPath = getSplitVendorJsPath(configB); + + // make sure that the filename of the split vendor file didn't change, + // even though an additional entry is now sharing its contents + if (finalVendorPath !== vendorPath) { + throw new Error(`Vendor filename changed! Before ${vendorPath} and after ${finalVendorPath}.`); + } + + // make sure that, internally, the split chunk name did not change, + // which would cause the contents of main3 to suddenly change + const main3Contents = readOutputFileContents('main3.js', configA); + const finalMain3Contents = readOutputFileContents('main3.js', configB); + + if (finalMain3Contents !== main3Contents) { + throw new Error(`Contents after first compile do not match after second compile: \n\n ${main3Contents} \n\n versus \n\n ${finalMain3Contents} \n`); + } + + resolve(); + }); + }); + }); + }); + }); + + describe('Package entrypoint imports', () => { + it('Import via "sass" package property', async () => { + const config = createWebpackConfig('web/build', 'dev'); + + config.setPublicPath('/build'); + config.addAliases({ + lib: path.resolve('./lib') + }); + config.enableSassLoader(); + config.addStyleEntry('sass', './css/sass_package_import.scss'); + + await new Promise(resolve => { + testSetup.runWebpack(config, () => { + // A successful compile is all that is needed to pass this test. + // If this test fails then the import in the above sass file + // is not loading the package's sass file. + resolve(); + }); + }); + }); + + it('Import via "style" package property', async () => { + const config = createWebpackConfig('web/build', 'dev'); + + config.setPublicPath('/build'); + config.addAliases({ + lib: path.resolve('./lib') + }); + config.addStyleEntry('style', './css/style_package_import.css'); + + await new Promise(resolve => { + testSetup.runWebpack(config, () => { + // A successful compile is all that is needed to pass this test. + // If this test fails then the import in the above css file + // is not loading the package's style file. + resolve(); + }); + }); + }); + }); + + describe('CSS extraction', () => { + it('With CSS extraction enabled', async () => { + const config = createWebpackConfig('build', 'dev'); + config.setPublicPath('/build'); + config.disableSingleRuntimeChunk(); + config.addEntry('main', './js/css_import'); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'manifest.json', + 'entrypoints.json', + 'main.js', + 'main.css', + ]); + + webpackAssert.assertOutputFileContains( + 'main.css', + 'font-size: 50px;' + ); + + resolve(); + }); + }); + }); + + it('With CSS extraction disabled', async () => { + const config = createWebpackConfig('build', 'dev'); + config.setPublicPath('/build'); + config.disableSingleRuntimeChunk(); + config.addEntry('main', './js/css_import'); + config.disableCssExtraction(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'manifest.json', + 'entrypoints.json', + 'main.js' + ]); + + webpackAssert.assertOutputFileContains( + 'main.js', + 'font-size: 50px;' + ); + + resolve(); + }); + }); + }); + + it('With CSS extraction disabled and with options callback of the StyleLoader', async () => { + const config = createWebpackConfig('build', 'dev'); + config.setPublicPath('/build'); + config.disableSingleRuntimeChunk(); + config.addEntry('main', './js/css_import'); + config.disableCssExtraction(); + config.configureStyleLoader((options) => { + options.attributes = { id: 'TESTING_ATTRIBUTES' }; + }); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + expect(config.outputPath).to.be.a.directory() + .with.files([ + 'manifest.json', + 'entrypoints.json', + 'main.js' + ]); + + webpackAssert.assertOutputFileContains( + 'main.js', + 'TESTING_ATTRIBUTES' + ); + + resolve(); + }); + }); + }); + }); + + describe('enableIntegrityHashes() adds hashes to the entrypoints.json file', () => { + it('Using default algorithm', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); + config.addEntry('other', ['./css/roboto_font.css', 'vue']); + config.setPublicPath('/build'); + config.configureSplitChunks((splitChunks) => { + splitChunks.chunks = 'all'; + splitChunks.minSize = 0; + }); + config.enableIntegrityHashes(); + + await new Promise(resolve => { + testSetup.runWebpack(config, () => { + const integrityData = getIntegrityData(config); + const expectedFilesWithHashes = [ + '/build/runtime.js', + '/build/main.js', + '/build/css_roboto_font_css.js', + '/build/css_roboto_font_css.css', + '/build/other.js', + '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', + ]; + + expectedFilesWithHashes.forEach((file) => { + expect(integrityData[file]).to.contain('sha384-'); + expect(integrityData[file]).to.have.length(71); + }); + + resolve(); + }); + }); + }); + + it('Using another algorithm and a different public path', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); + config.addEntry('other', ['./css/roboto_font.css', 'vue']); + config.setPublicPath('http://localhost:8090/assets'); + config.setManifestKeyPrefix('assets'); + config.configureSplitChunks((splitChunks) => { + splitChunks.chunks = 'all'; + splitChunks.minSize = 0; + }); + config.enableIntegrityHashes(true, 'sha256'); + + await new Promise(resolve => { + testSetup.runWebpack(config, () => { + const integrityData = getIntegrityData(config); + const expectedFilesWithHashes = [ + 'http://localhost:8090/assets/runtime.js', + 'http://localhost:8090/assets/main.js', + 'http://localhost:8090/assets/css_roboto_font_css.js', + 'http://localhost:8090/assets/css_roboto_font_css.css', + 'http://localhost:8090/assets/other.js', + ]; + + expectedFilesWithHashes.forEach((file) => { + expect(integrityData[file]).to.contain('sha256-'); + expect(integrityData[file]).to.have.length(51); + }); + + resolve(); + }); + }); + }); + + it('Using multiple algorithms', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', ['./css/roboto_font.css', './js/no_require', 'vue']); + config.addEntry('other', ['./css/roboto_font.css', 'vue']); + config.setPublicPath('/build'); + config.configureSplitChunks((splitChunks) => { + splitChunks.chunks = 'all'; + splitChunks.minSize = 0; + }); + config.enableIntegrityHashes(true, ['sha256', 'sha512']); + + await new Promise(resolve => { + testSetup.runWebpack(config, () => { + const integrityData = getIntegrityData(config); + const expectedFilesWithHashes = [ + '/build/runtime.js', + '/build/main.js', + '/build/css_roboto_font_css.js', + '/build/css_roboto_font_css.css', + '/build/other.js', + '/build/vendors-node_modules_vue_dist_vue_runtime_esm-bundler_js.js', + ]; + + expectedFilesWithHashes.forEach((file) => { + expect(integrityData[file]).to.contain('sha256-'); + expect(integrityData[file]).to.contain('sha512-'); + expect(integrityData[file]).to.have.length(147); + }); + + resolve(); + }); + }); + }); + + it('With query string versioning', async () => { + const config = createWebpackConfig('web/build', 'dev'); + config.addEntry('main', './js/no_require'); + config.setPublicPath('/build'); + config.addStyleEntry('styles', './css/h1_style.css'); + config.enableVersioning(true); + config.configureFilenames({ + js: '[name].js?v=[contenthash:16]', + css: '[name].css?v=[contenthash:16]' + }); + config.enableIntegrityHashes(); + + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + const integrityData = getIntegrityData(config); + const expectedFilesWithHashes = Object.keys(integrityData).filter(file => { + if (!/\?v=[a-z0-9]{16}$/.test(file)) { + return false; + } + return file.startsWith('/build/runtime.js?v=') + || file.startsWith('/build/main.js?v=') + || file.startsWith('/build/styles.css?v='); + }); + + expectedFilesWithHashes.forEach((file) => { + expect(integrityData[file]).to.contain('sha384-'); + expect(integrityData[file]).to.have.length(71); + }); + + resolve(); + }); + }); + }); + }); + }); +}); diff --git a/test/index.js b/test/index.test.js similarity index 99% rename from test/index.js rename to test/index.test.js index 2b559beb..308041f0 100644 --- a/test/index.js +++ b/test/index.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect, beforeEach } from 'vitest'; const sinon = require('sinon'); const api = require('../index'); const path = require('path'); diff --git a/test/logger.js b/test/logger.test.js similarity index 95% rename from test/logger.js rename to test/logger.test.js index 20146c01..458bd842 100644 --- a/test/logger.js +++ b/test/logger.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; require('../lib/context').runtimeConfig = {}; const logger = require('../lib/logger'); diff --git a/test/package-helper.js b/test/package-helper.test.js similarity index 99% rename from test/package-helper.js rename to test/package-helper.test.js index 97ca5534..9f169507 100644 --- a/test/package-helper.js +++ b/test/package-helper.test.js @@ -9,7 +9,7 @@ 'use strict'; -const expect = require('chai').expect; +import { describe, it, expect, afterAll } from 'vitest'; const packageHelper = require('../lib/package-helper'); const path = require('path'); const process = require('process'); @@ -20,7 +20,7 @@ describe('package-helper', () => { const baseCwd = process.cwd(); describe('recommended install command is based on the existing lock files', () => { - after(() => { + afterAll(() => { process.chdir(baseCwd); }); From 76952aaa166d19915ca58153615216fe4fdc449c Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Tue, 25 Feb 2025 09:04:22 +0100 Subject: [PATCH 11/15] Remove "sinon", migrate to vi.spyOn() or vi.fn() --- package.json | 1 - test/index.test.js | 11 +++---- test/loaders/less.test.js | 31 +++++++++---------- test/loaders/sass.test.js | 49 +++++++++++++++--------------- test/loaders/stylus.test.js | 31 +++++++++---------- test/utils/get-vue-version.test.js | 17 +++++------ 6 files changed, 67 insertions(+), 73 deletions(-) diff --git a/package.json b/package.json index 2fe9b426..a07a67f3 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,6 @@ "react-dom": "^18.0.0", "sass": "^1.17.0", "sass-loader": "^16.0.1", - "sinon": "^14.0.0", "strip-ansi": "^6.0.0", "stylus": "^0.63.0", "stylus-loader": "^7.0.0 || ^8.1.0", diff --git a/test/index.test.js b/test/index.test.js index 308041f0..4f75d954 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -9,8 +9,7 @@ 'use strict'; -import { describe, it, expect, beforeEach } from 'vitest'; -const sinon = require('sinon'); +import { describe, it, expect, beforeEach, vi } from 'vitest'; const api = require('../index'); const path = require('path'); @@ -469,14 +468,14 @@ describe('Public API', () => { it('should call or not callbacks depending of the conditions', () => { api.configureRuntimeEnvironment('dev', {}, false); - const spy = sinon.spy(); + const spy = vi.fn(); api .when((Encore) => Encore.isDev(), (Encore) => spy('is dev')) .when((Encore) => Encore.isProduction(), (Encore) => spy('is production')) .when(true, (Encore) => spy('true')); - expect(spy.calledWith('is dev'), 'callback for "is dev" should be called').to.be.true; - expect(spy.calledWith('is production'), 'callback for "is production" should NOT be called').to.be.false; - expect(spy.calledWith('true'), 'callback for "true" should be called').to.be.true; + expect(spy, 'callback for "is dev" should be called').toHaveBeenCalledWith('is dev'); + expect(spy, 'callback for "is production" should NOT be called').not.toHaveBeenCalledWith('is production'); + expect(spy, 'callback for "true" should be called').toHaveBeenCalledWith('true'); }); }); diff --git a/test/loaders/less.test.js b/test/loaders/less.test.js index 483711cb..ed9e39a0 100644 --- a/test/loaders/less.test.js +++ b/test/loaders/less.test.js @@ -9,12 +9,11 @@ 'use strict'; -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, vi } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const lessLoader = require('../../lib/loaders/less'); const cssLoader = require('../../lib/loaders/css'); -const sinon = require('sinon'); function createConfig() { const runtimeConfig = new RuntimeConfig(); @@ -30,15 +29,15 @@ describe('loaders/less', () => { config.enableSourceMaps(true); // make the cssLoader return nothing - const cssLoaderStub = sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); const actualLoaders = lessLoader.getLoaders(config); expect(actualLoaders).to.have.lengthOf(1); expect(actualLoaders[0].options.sourceMap).to.be.true; - expect(cssLoaderStub.getCall(0).args[1]).to.be.false; + expect(cssLoaderStub.mock.calls[0][1]).to.be.false; - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with options callback', () => { @@ -46,8 +45,8 @@ describe('loaders/less', () => { config.enableSourceMaps(true); // make the cssLoader return nothing - sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); config.enableLessLoader(function(lessOptions) { lessOptions.custom_option = 'foo'; @@ -60,7 +59,7 @@ describe('loaders/less', () => { custom_option: 'foo', other_option: true }); - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with a callback that returns an object', () => { @@ -68,8 +67,8 @@ describe('loaders/less', () => { config.enableSourceMaps(true); // make the cssLoader return nothing - sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); config.enableLessLoader(function(lessOptions) { lessOptions.custom_option = 'foo'; @@ -80,7 +79,7 @@ describe('loaders/less', () => { const actualLoaders = lessLoader.getLoaders(config); expect(actualLoaders[0].options).to.deep.equals({ foo: true }); - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with CSS modules enabled', () => { @@ -88,14 +87,14 @@ describe('loaders/less', () => { config.enableSourceMaps(true); // make the cssLoader return nothing - const cssLoaderStub = sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); const actualLoaders = lessLoader.getLoaders(config, true); expect(actualLoaders).to.have.lengthOf(1); expect(actualLoaders[0].options.sourceMap).to.be.true; - expect(cssLoaderStub.getCall(0).args[1]).to.be.true; + expect(cssLoaderStub.mock.calls[0][1]).to.be.true; - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); }); diff --git a/test/loaders/sass.test.js b/test/loaders/sass.test.js index dcf6b522..f2c0ea3e 100644 --- a/test/loaders/sass.test.js +++ b/test/loaders/sass.test.js @@ -9,12 +9,11 @@ 'use strict'; -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, vi } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const sassLoader = require('../../lib/loaders/sass'); const cssLoader = require('../../lib/loaders/css'); -const sinon = require('sinon'); function createConfig() { const runtimeConfig = new RuntimeConfig(); @@ -30,8 +29,8 @@ describe('loaders/sass', () => { config.enableSourceMaps(true); // make the cssLoader return nothing - const cssLoaderStub = sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); const actualLoaders = sassLoader.getLoaders(config); expect(actualLoaders).to.have.lengthOf(2); @@ -40,9 +39,9 @@ describe('loaders/sass', () => { expect(actualLoaders[1].loader).to.contain('sass-loader'); expect(actualLoaders[1].options.sourceMap).to.be.true; - expect(cssLoaderStub.getCall(0).args[1]).to.be.false; + expect(cssLoaderStub.mock.calls[0][1]).to.be.false; - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with resolve-url-loader but not sourcemaps', () => { @@ -50,8 +49,8 @@ describe('loaders/sass', () => { config.enableSourceMaps(false); // make the cssLoader return nothing - sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); const actualLoaders = sassLoader.getLoaders(config); expect(actualLoaders).to.have.lengthOf(2); @@ -62,7 +61,7 @@ describe('loaders/sass', () => { // sourcemaps always enabled when resolve-url-loader is enabled expect(actualLoaders[1].options.sourceMap).to.be.true; - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with resolve-url-loader options', () => { @@ -74,15 +73,15 @@ describe('loaders/sass', () => { }); // make the cssLoader return nothing - sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); const actualLoaders = sassLoader.getLoaders(config); expect(actualLoaders).to.have.lengthOf(2); expect(actualLoaders[0].loader).to.contain('resolve-url-loader'); expect(actualLoaders[0].options.removeCR).to.be.true; - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() without resolve-url-loader', () => { @@ -93,23 +92,23 @@ describe('loaders/sass', () => { config.enableSourceMaps(false); // make the cssLoader return nothing - sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); const actualLoaders = sassLoader.getLoaders(config); expect(actualLoaders).to.have.lengthOf(1); expect(actualLoaders[0].loader).to.contain('sass-loader'); expect(actualLoaders[0].options.sourceMap).to.be.false; - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with options callback', () => { const config = createConfig(); // make the cssLoader return nothing - sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); config.enableSassLoader(function(options) { options.sassOptions.custom_option = 'baz'; @@ -126,15 +125,15 @@ describe('loaders/sass', () => { other_option: true } }); - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with a callback that returns an object', () => { const config = createConfig(); // make the cssLoader return nothing - sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); config.enableSassLoader(function(options) { options.custom_option = 'baz'; @@ -147,7 +146,7 @@ describe('loaders/sass', () => { const actualLoaders = sassLoader.getLoaders(config); expect(actualLoaders[1].options).to.deep.equals({ foo: true }); - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with CSS modules enabled', () => { @@ -155,8 +154,8 @@ describe('loaders/sass', () => { config.enableSourceMaps(true); // make the cssLoader return nothing - const cssLoaderStub = sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); const actualLoaders = sassLoader.getLoaders(config, true); expect(actualLoaders).to.have.lengthOf(2); @@ -165,8 +164,8 @@ describe('loaders/sass', () => { expect(actualLoaders[1].loader).to.contain('sass-loader'); expect(actualLoaders[1].options.sourceMap).to.be.true; - expect(cssLoaderStub.getCall(0).args[1]).to.be.true; + expect(cssLoaderStub.mock.calls[0][1]).to.be.true; - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); }); diff --git a/test/loaders/stylus.test.js b/test/loaders/stylus.test.js index 1e76566a..f4d19ba9 100644 --- a/test/loaders/stylus.test.js +++ b/test/loaders/stylus.test.js @@ -9,12 +9,11 @@ 'use strict'; -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, vi } from 'vitest'; const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); const stylusLoader = require('../../lib/loaders/stylus'); const cssLoader = require('../../lib/loaders/css'); -const sinon = require('sinon'); function createConfig() { const runtimeConfig = new RuntimeConfig(); @@ -30,15 +29,15 @@ describe('loaders/stylus', () => { config.enableSourceMaps(true); // make the cssLoader return nothing - const cssLoaderStub = sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); const actualLoaders = stylusLoader.getLoaders(config); expect(actualLoaders).to.have.lengthOf(1); expect(actualLoaders[0].options.sourceMap).to.be.true; - expect(cssLoaderStub.getCall(0).args[1]).to.be.false; + expect(cssLoaderStub.mock.calls[0][1]).to.be.false; - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with options callback', () => { @@ -46,8 +45,8 @@ describe('loaders/stylus', () => { config.enableSourceMaps(true); // make the cssLoader return nothing - sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); config.enableStylusLoader(function(stylusOptions) { stylusOptions.custom_option = 'foo'; @@ -60,7 +59,7 @@ describe('loaders/stylus', () => { custom_option: 'foo', other_option: true }); - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with a callback that returns an object', () => { @@ -68,8 +67,8 @@ describe('loaders/stylus', () => { config.enableSourceMaps(true); // make the cssLoader return nothing - sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); config.enableStylusLoader(function(stylusOptions) { stylusOptions.custom_option = 'foo'; @@ -80,7 +79,7 @@ describe('loaders/stylus', () => { const actualLoaders = stylusLoader.getLoaders(config); expect(actualLoaders[0].options).to.deep.equals({ foo: true }); - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); it('getLoaders() with CSS modules enabled', () => { @@ -88,14 +87,14 @@ describe('loaders/stylus', () => { config.enableSourceMaps(true); // make the cssLoader return nothing - const cssLoaderStub = sinon.stub(cssLoader, 'getLoaders') - .callsFake(() => []); + const cssLoaderStub = vi.spyOn(cssLoader, 'getLoaders') + .mockImplementation(() => []); const actualLoaders = stylusLoader.getLoaders(config, true); expect(actualLoaders).to.have.lengthOf(1); expect(actualLoaders[0].options.sourceMap).to.be.true; - expect(cssLoaderStub.getCall(0).args[1]).to.be.true; + expect(cssLoaderStub.mock.calls[0][1]).to.be.true; - cssLoader.getLoaders.restore(); + cssLoaderStub.mockRestore(); }); }); diff --git a/test/utils/get-vue-version.test.js b/test/utils/get-vue-version.test.js index b6960a86..49eb1cd9 100644 --- a/test/utils/get-vue-version.test.js +++ b/test/utils/get-vue-version.test.js @@ -9,9 +9,8 @@ 'use strict'; -import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; const getVueVersion = require('../../lib/utils/get-vue-version'); -const sinon = require('sinon'); const packageHelper = require('../../lib/package-helper'); const WebpackConfig = require('../../lib/WebpackConfig'); const RuntimeConfig = require('../../lib/config/RuntimeConfig'); @@ -28,10 +27,10 @@ const createWebpackConfig = function() { describe('get-vue-version', () => { let getPackageVersionStub = null; beforeEach(() => { - getPackageVersionStub = sinon.stub(packageHelper, 'getPackageVersion'); + getPackageVersionStub = vi.spyOn(packageHelper, 'getPackageVersion'); }); afterEach(() => { - packageHelper.getPackageVersion.restore(); + getPackageVersionStub.mockRestore(); }); it('returns the value configured in Webpack.config.js', () => { @@ -44,7 +43,7 @@ describe('get-vue-version', () => { it('returns the default recommended version when vue is not installed', () => { const config = createWebpackConfig(); getPackageVersionStub - .callsFake(() => null); + .mockImplementation(() => null); expect(getVueVersion(config)).to.equal(3); }); @@ -52,7 +51,7 @@ describe('get-vue-version', () => { it('throw an error when Vue 2 is installed', () => { const config = createWebpackConfig(); getPackageVersionStub - .callsFake(() => '2.2.4'); + .mockImplementation(() => '2.2.4'); expect(() => getVueVersion(config)).to.throw('vue version 2 is not supported.'); }); @@ -60,7 +59,7 @@ describe('get-vue-version', () => { it('returns 3 when Vue 3 beta is installed', () => { const config = createWebpackConfig(); getPackageVersionStub - .callsFake(() => '3.0.0-beta.9'); + .mockImplementation(() => '3.0.0-beta.9'); expect(getVueVersion(config)).to.equal(3); }); @@ -68,7 +67,7 @@ describe('get-vue-version', () => { it('returns 3 when Vue 3 is installed', () => { const config = createWebpackConfig(); getPackageVersionStub - .callsFake(() => '3.0.0'); + .mockImplementation(() => '3.0.0'); expect(getVueVersion(config)).to.equal(3); }); @@ -76,7 +75,7 @@ describe('get-vue-version', () => { it('returns 3 when a version is too new', () => { const config = createWebpackConfig(); getPackageVersionStub - .callsFake(() => '4.0.0'); // unsupported version + .mockImplementation(() => '4.0.0'); // unsupported version expect(getVueVersion(config)).to.equal(3); }); From a5e1a97be9cca81919a0228052621d16eb8e37f1 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Tue, 25 Feb 2025 09:06:11 +0100 Subject: [PATCH 12/15] Remove mocha --- package.json | 5 +- test/.eslintrc.js | 1 - yarn.lock | 233 +--------------------------------------------- 3 files changed, 6 insertions(+), 233 deletions(-) diff --git a/package.json b/package.json index a07a67f3..aaad07e1 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,7 @@ "description": "Webpack Encore is a simpler way to integrate Webpack into your application", "main": "index.js", "scripts": { - "test": "yarn run vitest && yarn run test:main", - "test:main": "mocha --reporter spec test --recursive --ignore test/persistent-cache/*", - "test:vitest": "vitest", + "test": "vitest", "lint": "eslint lib test index.js .eslintrc.js --report-unused-disable-directives --max-warnings=0", "travis:lint": "yarn run lint" }, @@ -76,7 +74,6 @@ "http-server": "^14.1.0", "less": "^4.0.0", "less-loader": "^11.0.0 || ^12.2.0", - "mocha": "^10.0.0", "postcss": "^8.3.0", "postcss-loader": "^7.0.0 || ^8.1.0", "preact": "^10.5.0", diff --git a/test/.eslintrc.js b/test/.eslintrc.js index 8f825a5e..444af3cb 100644 --- a/test/.eslintrc.js +++ b/test/.eslintrc.js @@ -10,6 +10,5 @@ module.exports = { "env": { "node": true, - "mocha": true } }; diff --git a/yarn.lock b/yarn.lock index 6bfe2c05..1c6c2059 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1487,55 +1487,6 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== -"@sinonjs/commons@^1.7.0": - version "1.8.6" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" - integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/commons@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" - integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== - dependencies: - type-detect "4.0.8" - -"@sinonjs/commons@^3.0.0", "@sinonjs/commons@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" - integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^11.2.2": - version "11.3.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-11.3.1.tgz#51d6e8d83ca261ff02c0ab0e68e9db23d5cd5999" - integrity sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA== - dependencies: - "@sinonjs/commons" "^3.0.1" - -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== - dependencies: - "@sinonjs/commons" "^1.7.0" - -"@sinonjs/samsam@^7.0.1": - version "7.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-7.0.1.tgz#5b5fa31c554636f78308439d220986b9523fc51f" - integrity sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw== - dependencies: - "@sinonjs/commons" "^2.0.0" - lodash.get "^4.4.2" - type-detect "^4.0.8" - -"@sinonjs/text-encoding@^0.7.2": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz#282046f03e886e352b2d5f5da5eb755e01457f3f" - integrity sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA== - "@symfony/mock-module@file:fixtures/stimulus/mock-module": version "1.0.0" @@ -2236,11 +2187,6 @@ ajv@^8.0.0, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" -ansi-colors@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" - integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== - ansi-html-community@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" @@ -2583,13 +2529,6 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -2597,11 +2536,6 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -browser-stdout@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - browserslist@^4.0.0, browserslist@^4.21.10, browserslist@^4.23.1, browserslist@^4.23.3: version "4.23.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" @@ -2678,7 +2612,7 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0, camelcase@^6.3.0: +camelcase@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -2798,15 +2732,6 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - cliui@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" @@ -3199,7 +3124,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.6: version "4.3.6" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== @@ -3220,11 +3145,6 @@ debug@^4.4.0: dependencies: ms "^2.1.3" -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - deep-eql@^4.1.3: version "4.1.4" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.4.tgz#d0d3912865911bb8fac5afb4e3acfa6a28dc72b7" @@ -3322,11 +3242,6 @@ devtools-protocol@0.0.1330662: resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1330662.tgz#400fe703c2820d6b2d9ebdd1785934310152373e" integrity sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw== -diff@^5.0.0, diff@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" - integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== - dns-packet@^5.2.2: version "5.6.1" resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" @@ -4321,17 +4236,6 @@ glob@^7.1.3, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -4836,11 +4740,6 @@ is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - is-plain-obj@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" @@ -4896,11 +4795,6 @@ is-typed-array@^1.1.13: dependencies: which-typed-array "^1.1.14" -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -5061,11 +4955,6 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -just-extend@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-6.2.0.tgz#b816abfb3d67ee860482e7401564672558163947" - integrity sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw== - keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -5185,11 +5074,6 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== -lodash.get@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== - lodash.kebabcase@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" @@ -5215,14 +5099,6 @@ lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -5378,13 +5254,6 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1, minimatch@^5.1.6: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -5402,32 +5271,6 @@ mkdirp@^0.5.6: dependencies: minimist "^1.2.6" -mocha@^10.0.0: - version "10.7.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" - integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A== - dependencies: - ansi-colors "^4.1.3" - browser-stdout "^1.3.1" - chokidar "^3.5.3" - debug "^4.3.5" - diff "^5.2.0" - escape-string-regexp "^4.0.0" - find-up "^5.0.0" - glob "^8.1.0" - he "^1.2.0" - js-yaml "^4.1.0" - log-symbols "^4.1.0" - minimatch "^5.1.6" - ms "^2.1.3" - serialize-javascript "^6.0.2" - strip-json-comments "^3.1.1" - supports-color "^8.1.1" - workerpool "^6.5.1" - yargs "^16.2.0" - yargs-parser "^20.2.9" - yargs-unparser "^2.0.0" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -5489,17 +5332,6 @@ netmask@^2.0.2: resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== -nise@^5.1.2: - version "5.1.9" - resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.9.tgz#0cb73b5e4499d738231a473cd89bd8afbb618139" - integrity sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww== - dependencies: - "@sinonjs/commons" "^3.0.0" - "@sinonjs/fake-timers" "^11.2.2" - "@sinonjs/text-encoding" "^0.7.2" - just-extend "^6.2.0" - path-to-regexp "^6.2.1" - node-abort-controller@^3.0.1: version "3.1.1" resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" @@ -5791,11 +5623,6 @@ path-to-regexp@0.1.10: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== -path-to-regexp@^6.2.1: - version "6.2.2" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.2.tgz#324377a83e5049cbecadc5554d6a63a9a4866b36" - integrity sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw== - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -6831,18 +6658,6 @@ siginfo@^2.0.0: resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== -sinon@^14.0.0: - version "14.0.2" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-14.0.2.tgz#585a81a3c7b22cf950762ac4e7c28eb8b151c46f" - integrity sha512-PDpV0ZI3ZCS3pEqx0vpNp6kzPhHrLx72wA0G+ZLaaJjLIYeE0n8INlgaohKuGy7hP0as5tbUd23QWu5U233t+w== - dependencies: - "@sinonjs/commons" "^2.0.0" - "@sinonjs/fake-timers" "^9.1.2" - "@sinonjs/samsam" "^7.0.1" - diff "^5.0.0" - nise "^5.1.2" - supports-color "^7.2.0" - slashes@^3.0.12: version "3.0.12" resolved "https://registry.yarnpkg.com/slashes/-/slashes-3.0.12.tgz#3d664c877ad542dc1509eaf2c50f38d483a6435a" @@ -7101,14 +6916,14 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0, supports-color@^7.2.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -supports-color@^8.0.0, supports-color@^8.1.1: +supports-color@^8.0.0: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -7343,12 +7158,7 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-detect@^4.0.0, type-detect@^4.0.8, type-detect@^4.1.0: +type-detect@^4.0.0, type-detect@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== @@ -7810,11 +7620,6 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -workerpool@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" - integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -7844,39 +7649,11 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yargs-parser@^20.2.2, yargs-parser@^20.2.9: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - yargs-parser@^21.0.0, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs-unparser@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" From 5e3aff90e7f9061b9818c9110cbd472f2db73728 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Tue, 25 Feb 2025 22:35:33 +0100 Subject: [PATCH 13/15] Remove "done" usages from test/persistent-cache/* tests files --- test/helpers/assert.js | 12 ++--- test/helpers/setup.js | 2 +- test/persistent-cache/functional.test.js | 62 +++++++++++++----------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/test/helpers/assert.js b/test/helpers/assert.js index e4465e7d..1b103415 100644 --- a/test/helpers/assert.js +++ b/test/helpers/assert.js @@ -9,11 +9,11 @@ 'use strict'; -const path = require('path'); -const fs = require('fs'); -const chai = require('chai'); -const expect = chai.expect; -const regexEscaper = require('../../lib/utils/regexp-escaper'); +import { expect } from 'vitest' +import path from "path"; +import fs from "fs"; + +import regexEscaper from "../../lib/utils/regexp-escaper.js"; const loadManifest = function(webpackConfig) { return JSON.parse( @@ -275,6 +275,6 @@ class Assert { } } -module.exports = function(webpackConfig) { +export default function(webpackConfig) { return new Assert(webpackConfig); }; diff --git a/test/helpers/setup.js b/test/helpers/setup.js index 17cee802..b43833b4 100644 --- a/test/helpers/setup.js +++ b/test/helpers/setup.js @@ -17,7 +17,7 @@ const fs = require('fs-extra'); const httpServer = require('http-server'); const configGenerator = require('../../lib/config-generator'); const validator = require('../../lib/config/validator'); -const assertUtil = require('./assert'); +const assertUtil = require('./assert').default; const tmpDir = path.join(__dirname, '../', '../', 'test_tmp'); const testFixturesDir = path.join(__dirname, '../', '../', 'fixtures'); diff --git a/test/persistent-cache/functional.test.js b/test/persistent-cache/functional.test.js index 2266bd2c..b7b4fd8e 100644 --- a/test/persistent-cache/functional.test.js +++ b/test/persistent-cache/functional.test.js @@ -9,7 +9,7 @@ 'use strict'; -import { describe, it, expect, chai } from 'vitest'; +import { describe, it, chai } from 'vitest'; chai.use(require('chai-fs')); chai.use(require('chai-subset')); const path = require('path'); @@ -35,25 +35,27 @@ function createWebpackConfig(outputDirName = '', testName, command, argv = {}) { describe('Functional persistent cache tests using webpack', { repeats: 2, timeout: 10000 }, function() { describe('Basic scenarios.', () => { - it('Persistent caching does not cause problems', (done) => { + it('Persistent caching does not cause problems', async () => { const config = createWebpackConfig('www/build', 'basic_cache', 'dev'); config.setPublicPath('/build'); config.addEntry('main', './js/code_splitting'); - testSetup.runWebpack(config, (webpackAssert) => { - // sanity check - webpackAssert.assertManifestPath( - 'build/main.js', - '/build/main.js', - ); + return new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + // sanity check + webpackAssert.assertManifestPath( + 'build/main.js', + '/build/main.js', + ); - done(); - }); + resolve(); + }); + }) }); }); describe('copyFiles() allows to copy files and folders', () => { - it('Persistent caching does not cause problems', (done) => { + it('Persistent caching does not cause problems', async () => { const config = createWebpackConfig('www/build', 'copy_files_cache', 'production'); config.addEntry('main', './js/no_require'); config.setPublicPath('/build'); @@ -63,27 +65,29 @@ describe('Functional persistent cache tests using webpack', { repeats: 2, timeou includeSubdirectories: false, }]); - testSetup.runWebpack(config, (webpackAssert) => { - webpackAssert.assertDirectoryContents([ - 'entrypoints.json', - 'runtime.[hash:8].js', - 'main.[hash:8].js', - 'manifest.json', - 'symfony_logo.[hash:8].png', - 'symfony_logo_alt.[hash:8].png', - ]); + await new Promise(resolve => { + testSetup.runWebpack(config, (webpackAssert) => { + webpackAssert.assertDirectoryContents([ + 'entrypoints.json', + 'runtime.[hash:8].js', + 'main.[hash:8].js', + 'manifest.json', + 'symfony_logo.[hash:8].png', + 'symfony_logo_alt.[hash:8].png', + ]); - webpackAssert.assertManifestPath( - 'build/symfony_logo.png', - '/build/symfony_logo.91beba37.png', - ); + webpackAssert.assertManifestPath( + 'build/symfony_logo.png', + '/build/symfony_logo.91beba37.png', + ); - webpackAssert.assertManifestPath( - 'build/symfony_logo_alt.png', - '/build/symfony_logo_alt.f880ba14.png', - ); + webpackAssert.assertManifestPath( + 'build/symfony_logo_alt.png', + '/build/symfony_logo_alt.f880ba14.png', + ); - done(); + resolve(); + }); }); }); }); From 63dfa7a3c8946431c921f9b2238252f48e641b69 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 2 Mar 2025 22:57:03 +0100 Subject: [PATCH 14/15] debug --- test/helpers/assert.js | 14 ++++++++------ test/helpers/setup.js | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/test/helpers/assert.js b/test/helpers/assert.js index 1b103415..ff855a71 100644 --- a/test/helpers/assert.js +++ b/test/helpers/assert.js @@ -9,11 +9,13 @@ 'use strict'; -import { expect } from 'vitest' -import path from "path"; -import fs from "fs"; - -import regexEscaper from "../../lib/utils/regexp-escaper.js"; +const path = require('path'); +const fs = require('fs'); +const chai = require('chai'); +const expect = chai.expect; +const regexEscaper = require('../../lib/utils/regexp-escaper'); +chai.use(require('chai-fs')); +chai.use(require('chai-subset')); const loadManifest = function(webpackConfig) { return JSON.parse( @@ -275,6 +277,6 @@ class Assert { } } -export default function(webpackConfig) { +module.exports = function(webpackConfig) { return new Assert(webpackConfig); }; diff --git a/test/helpers/setup.js b/test/helpers/setup.js index b43833b4..17cee802 100644 --- a/test/helpers/setup.js +++ b/test/helpers/setup.js @@ -17,7 +17,7 @@ const fs = require('fs-extra'); const httpServer = require('http-server'); const configGenerator = require('../../lib/config-generator'); const validator = require('../../lib/config/validator'); -const assertUtil = require('./assert').default; +const assertUtil = require('./assert'); const tmpDir = path.join(__dirname, '../', '../', 'test_tmp'); const testFixturesDir = path.join(__dirname, '../', '../', 'fixtures'); From 5096ff9c917df9810f057d49a3eafcd0adbf6136 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Sun, 2 Mar 2025 23:09:21 +0100 Subject: [PATCH 15/15] debug --- test/functional.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/functional.test.js b/test/functional.test.js index 233965c6..227ee100 100644 --- a/test/functional.test.js +++ b/test/functional.test.js @@ -77,7 +77,9 @@ describe('Functional tests using webpack', { timeout: 10000}, function() { let browser; beforeAll(async () => { - browser = await puppeteer.launch() + browser = await puppeteer.launch({ + args: ['--no-sandbox'], + }) return async() => { await browser.close();