diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index acc0d093b9..cf36a20979 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x] + node-version: [16.x, 18.x, 20.x] steps: - uses: actions/checkout@v3 @@ -33,7 +33,7 @@ jobs: node-version: 16.x - run: npm i - run: npm run build - - run: npx uvu test win32 + - run: node ./test/win32.test.js timeout-minutes: 1 env: FORCE_COLOR: 3 diff --git a/package-lock.json b/package-lock.json index 6b8d3715b4..e2f7fb4340 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,8 +34,7 @@ "madge": "^6.0.0", "prettier": "^2.8.8", "tsd": "^0.28.1", - "typescript": "^5.0.4", - "uvu": "^0.5.6" + "typescript": "^5.0.4" }, "engines": { "node": ">= 16.0.0" @@ -106,9 +105,9 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -170,9 +169,9 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -208,9 +207,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -1800,15 +1799,6 @@ "node": ">=4.2.0" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/detective-amd": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-4.0.1.tgz", @@ -1955,15 +1945,6 @@ "node": ">=4.2.0" } }, - "node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/diff-match-patch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", @@ -3237,15 +3218,6 @@ "node": ">=0.10.0" } }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -3629,9 +3601,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -3880,15 +3852,6 @@ "node": "*" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4912,9 +4875,9 @@ } }, "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -5178,18 +5141,6 @@ "tslib": "^2.1.0" } }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dev": true, - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -5224,9 +5175,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5955,24 +5906,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/uvu": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", - "dev": true, - "dependencies": { - "dequal": "^2.0.0", - "diff": "^5.0.0", - "kleur": "^4.0.3", - "sade": "^1.7.3" - }, - "bin": { - "uvu": "bin.js" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -6052,9 +5985,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6237,9 +6170,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -6288,9 +6221,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, "yallist": { @@ -6319,9 +6252,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -7486,12 +7419,6 @@ } } }, - "dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true - }, "detective-amd": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-4.0.1.tgz", @@ -7602,12 +7529,6 @@ } } }, - "diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "dev": true - }, "diff-match-patch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", @@ -8520,12 +8441,6 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, - "kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "dev": true - }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -8793,9 +8708,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -8977,12 +8892,6 @@ } } }, - "mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -9639,9 +9548,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "type-fest": { @@ -9879,15 +9788,6 @@ "tslib": "^2.1.0" } }, - "sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dev": true, - "requires": { - "mri": "^1.1.0" - } - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -9918,9 +9818,9 @@ } }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -10456,18 +10356,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "uvu": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", - "dev": true, - "requires": { - "dequal": "^2.0.0", - "diff": "^5.0.0", - "kleur": "^4.0.3", - "sade": "^1.7.3" - } - }, "v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -10529,9 +10417,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index 70aa99e2b6..9f57e3196a 100644 --- a/package.json +++ b/package.json @@ -43,9 +43,9 @@ "fmt:check": "prettier --check .", "build": "tsc --project tsconfig.prod.json", "build:check": "tsc", - "test": "npm run build && uvu test -i fixtures", + "test": "npm run build && node ./test/all.test.js", "test:types": "tsd", - "coverage": "c8 --check-coverage npm test -- -i package", + "coverage": "c8 --check-coverage npm test", "mutation": "stryker run", "circular": "madge --circular src/*", "version": "cat package.json | fx .version" @@ -73,8 +73,7 @@ "madge": "^6.0.0", "prettier": "^2.8.8", "tsd": "^0.28.1", - "typescript": "^5.0.4", - "uvu": "^0.5.6" + "typescript": "^5.0.4" }, "publishConfig": { "registry": "https://wombat-dressing-room.appspot.com" diff --git a/test/all.test.js b/test/all.test.js new file mode 100644 index 0000000000..cd9fdf5475 --- /dev/null +++ b/test/all.test.js @@ -0,0 +1,24 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import './cli.test.js' +import './core.test.js' +import './deps.test.js' +import './experimental.test.js' +import './extra.test.js' +import './global.test.js' +import './goods.test.js' +import './package.test.js' +import './win32.test.js' +import './util.test.js' diff --git a/test/cli.test.js b/test/cli.test.js index 60eb09fa8a..f4d3c58f1f 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -12,195 +12,195 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import assert from 'node:assert' +import { test, describe, before, beforeEach } from 'node:test' import '../build/globals.js' -const test = suite('cli') - -$.verbose = false - -// Helps detect unresolved ProcessPromise. -let promiseResolved = false -process.on('exit', () => { - if (!promiseResolved) { - console.error('Error: ProcessPromise never resolved.') - process.exitCode = 1 - } -}) - -test('promise resolved', async () => { - await $`echo` - promiseResolved = true -}) - -test('prints version', async () => { - assert.match((await $`node build/cli.js -v`).toString(), /\d+.\d+.\d+/) -}) - -test('prints help', async () => { - let p = $`node build/cli.js -h` - p.stdin.end() - let help = await p - assert.match(help.stdout, 'zx') -}) - -test('zx prints usage', async () => { - let p = $`node build/cli.js` - p.stdin.end() - let out = await p - assert.match(out.stdout, 'A tool for writing better scripts') -}) - -test('starts repl with --repl', async () => { - let p = $`node build/cli.js --repl` - p.stdin.write('await $`echo f"o"o`\n') - p.stdin.write('"b"+"ar"\n') - p.stdin.end() - let out = await p - assert.match(out.stdout, 'foo') - assert.match(out.stdout, 'bar') -}) - -test('starts repl with verbosity off', async () => { - let p = $`node build/cli.js --repl` - p.stdin.write('"verbose" + " is " + $.verbose\n') - p.stdin.end() - let out = await p - assert.match(out.stdout, 'verbose is false') -}) - -test('supports `--experimental` flag', async () => { - let out = await $`echo 'echo("test")' | node build/cli.js --experimental` - assert.match(out.stdout, 'test') -}) - -test('supports `--quiet` flag', async () => { - let p = await $`node build/cli.js test/fixtures/markdown.md` - assert.ok(!p.stderr.includes('ignore'), 'ignore was printed') - assert.ok(p.stderr.includes('hello'), 'no hello') - assert.ok(p.stdout.includes('world'), 'no world') -}) - -test('supports `--shell` flag ', async () => { - let shell = $.shell - let p = - await $`node build/cli.js --shell=${shell} <<< '$\`echo \${$.shell}\`'` - assert.ok(p.stderr.includes(shell)) -}) - -test('supports `--prefix` flag ', async () => { - let prefix = 'set -e;' - let p = - await $`node build/cli.js --prefix=${prefix} <<< '$\`echo \${$.prefix}\`'` - assert.ok(p.stderr.includes(prefix)) -}) - -test('scripts from https', async () => { - $`cat ${path.resolve('test/fixtures/echo.http')} | nc -l 8080` - let out = await $`node build/cli.js http://127.0.0.1:8080/echo.mjs` - assert.match(out.stderr, 'test') -}) - -test('scripts from https not ok', async () => { - $`echo $'HTTP/1.1 500\n\n' | nc -l 8081` - let out = await $`node build/cli.js http://127.0.0.1:8081`.nothrow() - assert.match(out.stderr, "Error: Can't get") -}) - -test('scripts with no extension', async () => { - await $`node build/cli.js test/fixtures/no-extension` - assert.ok( - /Test file to verify no-extension didn't overwrite similarly name .mjs file./.test( - (await fs.readFile('test/fixtures/no-extension.mjs')).toString() +describe('cli', () => { + // Helps detect unresolved ProcessPromise. + let promiseResolved = false + + beforeEach(() => { + $.verbose = false + process.on('exit', () => { + if (!promiseResolved) { + console.error('Error: ProcessPromise never resolved.') + process.exitCode = 1 + } + }) + }) + + test('promise resolved', async () => { + await $`echo` + promiseResolved = true + }) + + test('prints version', async () => { + assert.match((await $`node build/cli.js -v`).toString(), /\d+.\d+.\d+/) + }) + + test('prints help', async () => { + let p = $`node build/cli.js -h` + p.stdin.end() + let help = await p + assert.match(help.stdout, /zx/) + }) + + test('zx prints usage', async () => { + let p = $`node build/cli.js` + p.stdin.end() + let out = await p + assert.match(out.stdout, /A tool for writing better scripts/) + }) + + test('starts repl with --repl', async () => { + let p = $`node build/cli.js --repl` + p.stdin.write('await $`echo f"o"o`\n') + p.stdin.write('"b"+"ar"\n') + p.stdin.end() + let out = await p + assert.match(out.stdout, /foo/) + assert.match(out.stdout, /bar/) + }) + + test('starts repl with verbosity off', async () => { + let p = $`node build/cli.js --repl` + p.stdin.write('"verbose" + " is " + $.verbose\n') + p.stdin.end() + let out = await p + assert.match(out.stdout, /verbose is false/) + }) + + test('supports `--experimental` flag', async () => { + let out = await $`echo 'echo("test")' | node build/cli.js --experimental` + assert.match(out.stdout, /test/) + }) + + test('supports `--quiet` flag', async () => { + let p = await $`node build/cli.js test/fixtures/markdown.md` + assert.ok(!p.stderr.includes('ignore'), 'ignore was printed') + assert.ok(p.stderr.includes('hello'), 'no hello') + assert.ok(p.stdout.includes('world'), 'no world') + }) + + test('supports `--shell` flag ', async () => { + let shell = $.shell + let p = + await $`node build/cli.js --shell=${shell} <<< '$\`echo \${$.shell}\`'` + assert.ok(p.stderr.includes(shell)) + }) + + test('supports `--prefix` flag ', async () => { + let prefix = 'set -e;' + let p = + await $`node build/cli.js --prefix=${prefix} <<< '$\`echo \${$.prefix}\`'` + assert.ok(p.stderr.includes(prefix)) + }) + + test('scripts from https', async () => { + $`cat ${path.resolve('test/fixtures/echo.http')} | nc -l 8080` + let out = await $`node build/cli.js http://127.0.0.1:8080/echo.mjs` + assert.match(out.stderr, /test/) + }) + + test('scripts from https not ok', async () => { + $`echo $'HTTP/1.1 500\n\n' | nc -l 8081` + let out = await $`node build/cli.js http://127.0.0.1:8081`.nothrow() + assert.match(out.stderr, /Error: Can't get/) + }) + + test('scripts with no extension', async () => { + await $`node build/cli.js test/fixtures/no-extension` + assert.ok( + /Test file to verify no-extension didn't overwrite similarly name .mjs file./.test( + (await fs.readFile('test/fixtures/no-extension.mjs')).toString() + ) ) - ) -}) - -test('require() is working from stdin', async () => { - let out = - await $`node build/cli.js <<< 'console.log(require("./package.json").name)'` - assert.match(out.stdout, 'zx') -}) - -test('require() is working in ESM', async () => { - await $`node build/cli.js test/fixtures/require.mjs` -}) - -test('__filename & __dirname are defined', async () => { - await $`node build/cli.js test/fixtures/filename-dirname.mjs` -}) - -test('markdown scripts are working', async () => { - await $`node build/cli.js test/fixtures/markdown.md` -}) - -test('markdown scripts are working', async () => { - await $`node build/cli.js test/fixtures/markdown.md` -}) - -test('exceptions are caught', async () => { - let out1 = await $`node build/cli.js <<<${'await $`wtf`'}`.nothrow() - assert.match(out1.stderr, 'Error:') - let out2 = await $`node build/cli.js <<<'throw 42'`.nothrow() - assert.match(out2.stderr, '42') -}) - -test('eval works', async () => { - assert.is((await $`node build/cli.js --eval 'echo(42)'`).stdout, '42\n') - assert.is((await $`node build/cli.js -e='echo(69)'`).stdout, '69\n') -}) - -test('eval works with stdin', async () => { - let p = $`(printf foo; sleep 0.1; printf bar) | node build/cli.js --eval 'echo(await stdin())'` - assert.is((await p).stdout, 'foobar\n') -}) - -test('executes a script from $PATH', async () => { - const isWindows = process.platform === 'win32' - const oldPath = process.env.PATH - - const envPathSeparator = isWindows ? ';' : ':' - process.env.PATH += envPathSeparator + path.resolve('/tmp/') - - const toPOSIXPath = (_path) => _path.split(path.sep).join(path.posix.sep) - - const zxPath = path.resolve('./build/cli.js') - const zxLocation = isWindows ? toPOSIXPath(zxPath) : zxPath - const scriptCode = `#!/usr/bin/env ${zxLocation}\nconsole.log('The script from path runs.')` - - try { - await $`chmod +x ${zxLocation}` - await $`echo ${scriptCode}`.pipe( - fs.createWriteStream('/tmp/script-from-path', { mode: 0o744 }) + }) + + test('require() is working from stdin', async () => { + let out = + await $`node build/cli.js <<< 'console.log(require("./package.json").name)'` + assert.match(out.stdout, /zx/) + }) + + test('require() is working in ESM', async () => { + await $`node build/cli.js test/fixtures/require.mjs` + }) + + test('__filename & __dirname are defined', async () => { + await $`node build/cli.js test/fixtures/filename-dirname.mjs` + }) + + test('markdown scripts are working', async () => { + await $`node build/cli.js test/fixtures/markdown.md` + }) + + test('markdown scripts are working', async () => { + await $`node build/cli.js test/fixtures/markdown.md` + }) + + test('exceptions are caught', async () => { + let out1 = await $`node build/cli.js <<<${'await $`wtf`'}`.nothrow() + assert.match(out1.stderr, /Error:/) + let out2 = await $`node build/cli.js <<<'throw 42'`.nothrow() + assert.match(out2.stderr, /42/) + }) + + test('eval works', async () => { + assert.equal((await $`node build/cli.js --eval 'echo(42)'`).stdout, '42\n') + assert.equal((await $`node build/cli.js -e='echo(69)'`).stdout, '69\n') + }) + + test('eval works with stdin', async () => { + let p = $`(printf foo; sleep 0.1; printf bar) | node build/cli.js --eval 'echo(await stdin())'` + assert.equal((await p).stdout, 'foobar\n') + }) + + test('executes a script from $PATH', async () => { + const isWindows = process.platform === 'win32' + const oldPath = process.env.PATH + + const envPathSeparator = isWindows ? ';' : ':' + process.env.PATH += envPathSeparator + path.resolve('/tmp/') + + const toPOSIXPath = (_path) => _path.split(path.sep).join(path.posix.sep) + + const zxPath = path.resolve('./build/cli.js') + const zxLocation = isWindows ? toPOSIXPath(zxPath) : zxPath + const scriptCode = `#!/usr/bin/env ${zxLocation}\nconsole.log('The script from path runs.')` + + try { + await $`chmod +x ${zxLocation}` + await $`echo ${scriptCode}`.pipe( + fs.createWriteStream('/tmp/script-from-path', { mode: 0o744 }) + ) + await $`script-from-path` + } finally { + process.env.PATH = oldPath + fs.rmSync('/tmp/script-from-path') + } + }) + + test('argv works with zx and node', async () => { + assert.equal( + (await $`node build/cli.js test/fixtures/argv.mjs foo`).toString(), + `global {"_":["foo"]}\nimported {"_":["foo"]}\n` ) - await $`script-from-path` - } finally { - process.env.PATH = oldPath - fs.rmSync('/tmp/script-from-path') - } -}) - -test('argv works with zx and node', async () => { - assert.is( - (await $`node build/cli.js test/fixtures/argv.mjs foo`).toString(), - `global {"_":["foo"]}\nimported {"_":["foo"]}\n` - ) - assert.is( - (await $`node test/fixtures/argv.mjs bar`).toString(), - `global {"_":["bar"]}\nimported {"_":["bar"]}\n` - ) - assert.is( - ( - await $`node build/cli.js --eval 'console.log(argv._.join(''))' baz` - ).toString(), - `baz\n` - ) -}) + assert.equal( + (await $`node test/fixtures/argv.mjs bar`).toString(), + `global {"_":["bar"]}\nimported {"_":["bar"]}\n` + ) + assert.equal( + ( + await $`node build/cli.js --eval 'console.log(argv._.join(''))' baz` + ).toString(), + `baz\n` + ) + }) -test('exit code can be set', async () => { - let p = await $`node build/cli.js test/fixtures/exit-code.mjs`.nothrow() - assert.is(p.exitCode, 42) + test('exit code can be set', async () => { + let p = await $`node build/cli.js test/fixtures/exit-code.mjs`.nothrow() + assert.equal(p.exitCode, 42) + }) }) - -test.run() diff --git a/test/core.test.js b/test/core.test.js index 62787bc65c..5f689ee592 100644 --- a/test/core.test.js +++ b/test/core.test.js @@ -12,457 +12,464 @@ // See the License for the specific language governing permissions and // limitations under the License. -import chalk from 'chalk' -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import assert from 'node:assert' +import { test, describe, beforeEach } from 'node:test' import { inspect } from 'node:util' import { Writable } from 'node:stream' import { Socket } from 'node:net' import { ProcessPromise, ProcessOutput } from '../build/index.js' import '../build/globals.js' -const test = suite('core') - -$.verbose = false +describe('core', () => { + beforeEach(() => { + $.verbose = false + }) -test('only stdout is used during command substitution', async () => { - let hello = await $`echo Error >&2; echo Hello` - let len = +(await $`echo ${hello} | wc -c`) - assert.is(len, 6) -}) + test('only stdout is used during command substitution', async () => { + let hello = await $`echo Error >&2; echo Hello` + let len = +(await $`echo ${hello} | wc -c`) + assert.equal(len, 6) + }) -test('env vars works', async () => { - process.env.ZX_TEST_FOO = 'foo' - let foo = await $`echo $ZX_TEST_FOO` - assert.is(foo.stdout, 'foo\n') -}) + test('env vars works', async () => { + process.env.ZX_TEST_FOO = 'foo' + let foo = await $`echo $ZX_TEST_FOO` + assert.equal(foo.stdout, 'foo\n') + }) -test('env vars is safe to pass', async () => { - process.env.ZX_TEST_BAR = 'hi; exit 1' - await $`echo $ZX_TEST_BAR` -}) + test('env vars is safe to pass', async () => { + process.env.ZX_TEST_BAR = 'hi; exit 1' + await $`echo $ZX_TEST_BAR` + }) -test('arguments are quoted', async () => { - let bar = 'bar"";baz!$#^$\'&*~*%)({}||\\/' - assert.is((await $`echo ${bar}`).stdout.trim(), bar) -}) + test('arguments are quoted', async () => { + let bar = 'bar"";baz!$#^$\'&*~*%)({}||\\/' + assert.equal((await $`echo ${bar}`).stdout.trim(), bar) + }) -test('undefined and empty string correctly quoted', async () => { - assert.is((await $`echo -n ${undefined}`).toString(), 'undefined') - assert.is((await $`echo -n ${''}`).toString(), '') -}) + test('undefined and empty string correctly quoted', async () => { + assert.equal((await $`echo -n ${undefined}`).toString(), 'undefined') + assert.equal((await $`echo -n ${''}`).toString(), '') + }) -test('can create a dir with a space in the name', async () => { - let name = 'foo bar' - try { - await $`mkdir /tmp/${name}` - } catch { - assert.unreachable() - } finally { - await fs.rmdir('/tmp/' + name) - } -}) + test('can create a dir with a space in the name', async () => { + let name = 'foo bar' + try { + await $`mkdir /tmp/${name}` + } catch { + assert.unreachable() + } finally { + await fs.rmdir('/tmp/' + name) + } + }) -test('pipefail is on', async () => { - let p - try { - p = await $`cat /dev/not_found | sort` - } catch (e) { - p = e - } - assert.is.not(p.exitCode, 0) -}) + test('pipefail is on', async () => { + let p + try { + p = await $`cat /dev/not_found | sort` + } catch (e) { + p = e + } + assert.notEqual(p.exitCode, 0) + }) -test('toString() is called on arguments', async () => { - let foo = 0 - let p = await $`echo ${foo}` - assert.is(p.stdout, '0\n') -}) + test('toString() is called on arguments', async () => { + let foo = 0 + let p = await $`echo ${foo}` + assert.equal(p.stdout, '0\n') + }) -test('can use array as an argument', async () => { - let args = ['-n', 'foo'] - assert.is((await $`echo ${args}`).toString(), 'foo') -}) + test('can use array as an argument', async () => { + let args = ['-n', 'foo'] + assert.equal((await $`echo ${args}`).toString(), 'foo') + }) -test('quiet() mode is working', async () => { - let stdout = '' - let log = console.log - console.log = (...args) => { - stdout += args.join(' ') - } - await $`echo 'test'`.quiet() - console.log = log - assert.is(stdout, '') - { - // Deprecated. + test('quiet() mode is working', async () => { let stdout = '' let log = console.log console.log = (...args) => { stdout += args.join(' ') } - await quiet($`echo 'test'`) + await $`echo 'test'`.quiet() console.log = log - assert.is(stdout, '') - } -}) + assert.equal(stdout, '') + { + // Deprecated. + let stdout = '' + let log = console.log + console.log = (...args) => { + stdout += args.join(' ') + } + await quiet($`echo 'test'`) + console.log = log + assert.equal(stdout, '') + } + }) -test('pipes are working', async () => { - let { stdout } = await $`echo "hello"` - .pipe($`awk '{print $1" world"}'`) - .pipe($`tr '[a-z]' '[A-Z]'`) - assert.is(stdout, 'HELLO WORLD\n') - - try { - await $`echo foo`.pipe(fs.createWriteStream('/tmp/output.txt')) - assert.is((await fs.readFile('/tmp/output.txt')).toString(), 'foo\n') - - let r = $`cat` - fs.createReadStream('/tmp/output.txt').pipe(r.stdin) - assert.is((await r).stdout, 'foo\n') - } finally { - await fs.rm('/tmp/output.txt') - } -}) + test('pipes are working', async () => { + let { stdout } = await $`echo "hello"` + .pipe($`awk '{print $1" world"}'`) + .pipe($`tr '[a-z]' '[A-Z]'`) + assert.equal(stdout, 'HELLO WORLD\n') -test('ProcessPromise', async () => { - let contents = '' - let stream = new Writable({ - write: function (chunk, encoding, next) { - contents += chunk.toString() - next() - }, - }) - let p = $`echo 'test'`.pipe(stream) - await p - assert.ok(p._piped) - assert.is(contents, 'test\n') - assert.instance(p.stderr, Socket) - - let err - try { - $`echo 'test'`.pipe('str') - } catch (p) { - err = p - } - assert.is(err.message, 'The pipe() method does not take strings. Forgot $?') -}) + try { + await $`echo foo`.pipe(fs.createWriteStream('/tmp/output.txt')) + assert.equal((await fs.readFile('/tmp/output.txt')).toString(), 'foo\n') + + let r = $`cat` + fs.createReadStream('/tmp/output.txt').pipe(r.stdin) + assert.equal((await r).stdout, 'foo\n') + } finally { + await fs.rm('/tmp/output.txt') + } + }) -test('ProcessPromise: inherits native Promise', async () => { - const p1 = $`echo 1` - const p2 = p1.then((v) => v) - const p3 = p2.then((v) => v) - const p4 = p3.catch((v) => v) - const p5 = p1.finally((v) => v) - - assert.instance(p1, Promise) - assert.instance(p1, ProcessPromise) - assert.instance(p2, ProcessPromise) - assert.instance(p3, ProcessPromise) - assert.instance(p4, ProcessPromise) - assert.instance(p5, ProcessPromise) - assert.ok(p1 !== p2) - assert.ok(p2 !== p3) - assert.ok(p3 !== p4) - assert.ok(p5 !== p1) -}) + test('ProcessPromise', async () => { + let contents = '' + let stream = new Writable({ + write: function (chunk, encoding, next) { + contents += chunk.toString() + next() + }, + }) + let p = $`echo 'test'`.pipe(stream) + await p + assert.ok(p._piped) + assert.equal(contents, 'test\n') + assert.ok(p.stderr instanceof Socket) -test('cd() works with relative paths', async () => { - let cwd = process.cwd() - try { - fs.mkdirpSync('/tmp/zx-cd-test/one/two') - cd('/tmp/zx-cd-test/one/two') - let p1 = $`pwd` - assert.is($.cwd, undefined) - assert.match(process.cwd(), '/two') - - cd('..') - let p2 = $`pwd` - assert.is($.cwd, undefined) - assert.match(process.cwd(), '/one') - - cd('..') - let p3 = $`pwd` - assert.is($.cwd, undefined) - assert.match(process.cwd(), '/tmp/zx-cd-test') - - const results = (await Promise.all([p1, p2, p3])).map((p) => - path.basename(p.stdout.trim()) + let err + try { + $`echo 'test'`.pipe('str') + } catch (p) { + err = p + } + assert.equal( + err.message, + 'The pipe() method does not take strings. Forgot $?' ) - assert.equal(results, ['two', 'one', 'zx-cd-test']) - } catch (e) { - assert.ok(!e, e) - } finally { - fs.rmSync('/tmp/zx-cd-test', { recursive: true }) - cd(cwd) - } -}) + }) -test('cd() does affect parallel contexts', async () => { - const cwd = process.cwd() - try { - fs.mkdirpSync('/tmp/zx-cd-parallel/one/two') - await Promise.all([ - within(async () => { - assert.is(process.cwd(), cwd) - await sleep(1) - cd('/tmp/zx-cd-parallel/one') - assert.match(process.cwd(), '/tmp/zx-cd-parallel/one') - }), - within(async () => { - assert.is(process.cwd(), cwd) - await sleep(2) - assert.is(process.cwd(), cwd) - }), - within(async () => { - assert.is(process.cwd(), cwd) - await sleep(3) - $.cwd = '/tmp/zx-cd-parallel/one/two' - assert.is(process.cwd(), cwd) - assert.match((await $`pwd`).stdout, '/tmp/zx-cd-parallel/one/two') - }), - ]) - } catch (e) { - assert.ok(!e, e) - } finally { - fs.rmSync('/tmp/zx-cd-parallel', { recursive: true }) - cd(cwd) - } -}) + test('ProcessPromise: inherits native Promise', async () => { + const p1 = $`echo 1` + const p2 = p1.then((v) => v) + const p3 = p2.then((v) => v) + const p4 = p3.catch((v) => v) + const p5 = p1.finally((v) => v) + + assert(p1 instanceof Promise) + assert(p1 instanceof ProcessPromise) + assert(p2 instanceof ProcessPromise) + assert(p3 instanceof ProcessPromise) + assert(p4 instanceof ProcessPromise) + assert(p5 instanceof ProcessPromise) + assert.ok(p1 !== p2) + assert.ok(p2 !== p3) + assert.ok(p3 !== p4) + assert.ok(p5 !== p1) + }) -test('cd() fails on entering not existing dir', async () => { - assert.throws(() => cd('/tmp/abra-kadabra')) -}) + test('cd() works with relative paths', async () => { + let cwd = process.cwd() + try { + fs.mkdirpSync('/tmp/zx-cd-test/one/two') + cd('/tmp/zx-cd-test/one/two') + let p1 = $`pwd` + assert.equal($.cwd, undefined) + assert.ok(process.cwd().endsWith('/two')) + + cd('..') + let p2 = $`pwd` + assert.equal($.cwd, undefined) + assert.ok(process.cwd().endsWith('/one')) + + cd('..') + let p3 = $`pwd` + assert.equal($.cwd, undefined) + assert.ok(process.cwd().endsWith('/tmp/zx-cd-test')) + + const results = (await Promise.all([p1, p2, p3])).map((p) => + path.basename(p.stdout.trim()) + ) + assert.deepEqual(results, ['two', 'one', 'zx-cd-test']) + } catch (e) { + assert.ok(!e, e) + } finally { + fs.rmSync('/tmp/zx-cd-test', { recursive: true }) + cd(cwd) + } + }) + + test('cd() does affect parallel contexts', async () => { + const cwd = process.cwd() + try { + fs.mkdirpSync('/tmp/zx-cd-parallel/one/two') + await Promise.all([ + within(async () => { + assert.equal(process.cwd(), cwd) + await sleep(1) + cd('/tmp/zx-cd-parallel/one') + assert.ok(process.cwd().endsWith('/tmp/zx-cd-parallel/one')) + }), + within(async () => { + assert.equal(process.cwd(), cwd) + await sleep(2) + assert.equal(process.cwd(), cwd) + }), + within(async () => { + assert.equal(process.cwd(), cwd) + await sleep(3) + $.cwd = '/tmp/zx-cd-parallel/one/two' + assert.equal(process.cwd(), cwd) + assert.ok( + (await $`pwd`).stdout + .toString() + .trim() + .endsWith('/tmp/zx-cd-parallel/one/two') + ) + }), + ]) + } catch (e) { + assert.ok(!e, e) + } finally { + fs.rmSync('/tmp/zx-cd-parallel', { recursive: true }) + cd(cwd) + } + }) -test('cd() accepts ProcessOutput in addition to string', async () => { - within(async () => { - const tmpDir = await $`mktemp -d` - cd(tmpDir) - assert.match(process.cwd(), tmpDir.toString().trimEnd()) + test('cd() fails on entering not existing dir', async () => { + assert.throws(() => cd('/tmp/abra-kadabra')) }) -}) -test('kill() method works', async () => { - let p = $`sleep 9999`.nothrow() - setTimeout(() => { - p.kill() - }, 100) - await p -}) + test('cd() accepts ProcessOutput in addition to string', async () => { + within(async () => { + const tmpDir = await $`mktemp -d` + cd(tmpDir) + assert.equal(process.cwd(), tmpDir.toString().trimEnd()) + }) + }) -test('a signal is passed with kill() method', async () => { - let p = $`while true; do :; done` - setTimeout(() => p.kill('SIGKILL'), 100) - let signal - try { + test('kill() method works', async () => { + let p = $`sleep 9999`.nothrow() + setTimeout(() => { + p.kill() + }, 100) await p - } catch (p) { - signal = p.signal - } - assert.equal(signal, 'SIGKILL') -}) + }) -test('within() works', async () => { - let resolve, reject - let promise = new Promise((...args) => ([resolve, reject] = args)) + test('a signal is passed with kill() method', async () => { + let p = $`while true; do :; done` + setTimeout(() => p.kill('SIGKILL'), 100) + let signal + try { + await p + } catch (p) { + signal = p.signal + } + assert.equal(signal, 'SIGKILL') + }) - function yes() { - assert.equal($.verbose, true) - resolve() - } + test('within() works', async () => { + let resolve, reject + let promise = new Promise((...args) => ([resolve, reject] = args)) - $.verbose = false - assert.equal($.verbose, false) + function yes() { + assert.equal($.verbose, true) + resolve() + } - within(() => { - $.verbose = true - }) - assert.equal($.verbose, false) + $.verbose = false + assert.equal($.verbose, false) - within(async () => { - $.verbose = true - setTimeout(yes, 10) - }) - assert.equal($.verbose, false) + within(() => { + $.verbose = true + }) + assert.equal($.verbose, false) - await promise -}) + within(async () => { + $.verbose = true + setTimeout(yes, 10) + }) + assert.equal($.verbose, false) -test('within() restores previous cwd', async () => { - let resolve, reject - let promise = new Promise((...args) => ([resolve, reject] = args)) + await promise + }) - let pwd = await $`pwd` + test('within() restores previous cwd', async () => { + let resolve, reject + let promise = new Promise((...args) => ([resolve, reject] = args)) - within(async () => { - $.verbose = false - cd('/tmp') - setTimeout(async () => { - assert.match((await $`pwd`).stdout, '/tmp') - resolve() - }, 1000) - }) + let pwd = await $`pwd` - assert.equal((await $`pwd`).stdout, pwd.stdout) - await promise -}) + within(async () => { + $.verbose = false + cd('/tmp') + setTimeout(async () => { + assert.ok((await $`pwd`).stdout.trim().endsWith('/tmp')) + resolve() + }, 1000) + }) -test(`within() isolates nested context and returns cb result`, async () => { - within(async () => { - const res = await within(async () => { - $.verbose = true + assert.equal((await $`pwd`).stdout, pwd.stdout) + await promise + }) - return within(async () => { - assert.equal($.verbose, true) - $.verbose = false + test(`within() isolates nested context and returns cb result`, async () => { + within(async () => { + const res = await within(async () => { + $.verbose = true return within(async () => { - assert.equal($.verbose, false) - $.verbose = true - return 'foo' + assert.equal($.verbose, true) + $.verbose = false + + return within(async () => { + assert.equal($.verbose, false) + $.verbose = true + return 'foo' + }) }) }) + assert.equal($.verbose, false) + assert.equal(res, 'foo') }) - assert.equal($.verbose, false) - assert.equal(res, 'foo') }) -}) -test('stdio() works', async () => { - let p = $`printf foo` - await p - assert.throws(() => p.stdin) - assert.is((await p).stdout, 'foo') + test('stdio() works', async () => { + let p = $`printf foo` + await p + assert.throws(() => p.stdin) + assert.equal((await p).stdout, 'foo') - let b = $`read; printf $REPLY` - b.stdin.write('bar\n') - assert.is((await b).stdout, 'bar') -}) + let b = $`read; printf $REPLY` + b.stdin.write('bar\n') + assert.equal((await b).stdout, 'bar') + }) -test('snapshots works', async () => { - await within(async () => { - $.prefix += 'echo success;' - let p = $`:` - $.prefix += 'echo fail;' - let out = await p - assert.is(out.stdout, 'success\n') - assert.not.match(out.stdout, 'fail') + test('snapshots works', async () => { + await within(async () => { + $.prefix += 'echo success;' + let p = $`:` + $.prefix += 'echo fail;' + let out = await p + assert.equal(out.stdout, 'success\n') + assert.doesNotMatch(out.stdout, /fail/) + }) }) -}) -test('timeout() works', async () => { - let exitCode, signal - try { - await $`sleep 9999`.timeout(10, 'SIGKILL') - } catch (p) { - exitCode = p.exitCode - signal = p.signal - } - assert.is(exitCode, null) - assert.is(signal, 'SIGKILL') -}) + test('timeout() works', async () => { + let exitCode, signal + try { + await $`sleep 9999`.timeout(10, 'SIGKILL') + } catch (p) { + exitCode = p.exitCode + signal = p.signal + } + assert.equal(exitCode, null) + assert.equal(signal, 'SIGKILL') + }) -test('timeout() expiration works', async () => { - let exitCode, signal - try { - await $`sleep 1`.timeout(999) - } catch (p) { - exitCode = p.exitCode - signal = p.signal - } - assert.is(exitCode, undefined) - assert.is(signal, undefined) -}) + test('timeout() expiration works', async () => { + let exitCode, signal + try { + await $`sleep 1`.timeout(999) + } catch (p) { + exitCode = p.exitCode + signal = p.signal + } + assert.equal(exitCode, undefined) + assert.equal(signal, undefined) + }) -test('$ thrown as error', async () => { - let err - try { - await $`wtf` - } catch (p) { - err = p - } - assert.ok(err.exitCode > 0) - assert.ok(err.stderr.includes('wtf: command not found')) - assert.ok(err[inspect.custom]().includes('Command not found')) -}) + test('$ thrown as error', async () => { + let err + try { + await $`wtf` + } catch (p) { + err = p + } + assert.ok(err.exitCode > 0) + assert.ok(err.stderr.includes('wtf: command not found')) + assert.ok(err[inspect.custom]().includes('Command not found')) + }) -test('error event is handled', async () => { - await within(async () => { - $.cwd = 'wtf' + test('error event is handled', async () => { + await within(async () => { + $.cwd = 'wtf' + try { + await $`pwd` + assert.unreachable('should have thrown') + } catch (err) { + assert.ok(err instanceof ProcessOutput) + assert.match(err.message, /No such file or directory/) + } + }) + }) + + test('pipe() throws if already resolved', async (t) => { + let ok = true + let p = $`echo "Hello"` + await p try { - await $`pwd` - assert.unreachable('should have thrown') + await p.pipe($`less`) + ok = false } catch (err) { - assert.instance(err, ProcessOutput) - assert.match(err.message, /No such file or directory/) + assert.equal( + err.message, + `The pipe() method shouldn't be called after promise is already resolved!` + ) } + assert.ok(ok, 'Expected failure!') }) -}) - -test('pipe() throws if already resolved', async (t) => { - let ok = true - let p = $`echo "Hello"` - await p - try { - await p.pipe($`less`) - ok = false - } catch (err) { - assert.is( - err.message, - `The pipe() method shouldn't be called after promise is already resolved!` - ) - } - assert.ok(ok, 'Expected failure!') -}) -test('await $`cmd`.exitCode does not throw', async () => { - assert.is.not(await $`grep qwerty README.md`.exitCode, 0) - assert.is(await $`[[ -f README.md ]]`.exitCode, 0) -}) + test('await $`cmd`.exitCode does not throw', async () => { + assert.notEqual(await $`grep qwerty README.md`.exitCode, 0) + assert.equal(await $`[[ -f README.md ]]`.exitCode, 0) + }) -test('nothrow() do not throw', async () => { - let { exitCode } = await $`exit 42`.nothrow() - assert.is(exitCode, 42) - { - // Deprecated. - let { exitCode } = await nothrow($`exit 42`) - assert.is(exitCode, 42) - } -}) + test('nothrow() do not throw', async () => { + let { exitCode } = await $`exit 42`.nothrow() + assert.equal(exitCode, 42) + { + // Deprecated. + let { exitCode } = await nothrow($`exit 42`) + assert.equal(exitCode, 42) + } + }) -test('malformed cmd error', async () => { - assert.throws(() => $`\033`, /malformed/i) -}) + test('malformed cmd error', async () => { + assert.throws(() => $`\033`, /malformed/i) + }) -test('$ is a regular function', async () => { - const _$ = $.bind(null) - let foo = await _$`echo foo` - assert.is(foo.stdout, 'foo\n') - assert.ok(typeof $.call === 'function') - assert.ok(typeof $.apply === 'function') -}) + test('$ is a regular function', async () => { + const _$ = $.bind(null) + let foo = await _$`echo foo` + assert.equal(foo.stdout, 'foo\n') + assert.ok(typeof $.call === 'function') + assert.ok(typeof $.apply === 'function') + }) -test('halt() works', async () => { - let filepath = `/tmp/${Math.random().toString()}` - let p = $`touch ${filepath}`.halt() - await sleep(1) - assert.not.ok( - fs.existsSync(filepath), - 'The cmd called, but it should not have been called' - ) - await p.run() - assert.ok(fs.existsSync(filepath), 'The cmd should have been called') -}) + test('halt() works', async () => { + let filepath = `/tmp/${Math.random().toString()}` + let p = $`touch ${filepath}`.halt() + await sleep(1) + assert.ok( + !fs.existsSync(filepath), + 'The cmd called, but it should not have been called' + ) + await p.run() + assert.ok(fs.existsSync(filepath), 'The cmd should have been called') + }) -test('await on halted throws', async () => { - let p = $`sleep 1`.halt() - let ok = true - try { - await p - ok = false - } catch (err) { - assert.is(err.message, 'The process is halted!') - } - assert.ok(ok, 'Expected failure!') + test('await on halted throws', async () => { + let p = $`sleep 1`.halt() + let ok = true + try { + await p + ok = false + } catch (err) { + assert.equal(err.message, 'The process is halted!') + } + assert.ok(ok, 'Expected failure!') + }) }) - -test.run() diff --git a/test/deps.test.js b/test/deps.test.js index c0e75b6a05..3755d68e7d 100644 --- a/test/deps.test.js +++ b/test/deps.test.js @@ -12,74 +12,77 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import assert from 'node:assert' +import { test, describe, before, beforeEach } from 'node:test' import { $ } from '../build/index.js' import { installDeps, parseDeps } from '../build/deps.js' -const test = suite('deps') - -$.verbose = false +describe('deps', () => { + beforeEach(() => { + $.verbose = false + }) -test('installDeps() loader works via JS API', async () => { - await installDeps({ - cpy: '9.0.1', - 'lodash-es': '4.17.21', + test('installDeps() loader works via JS API', async () => { + await installDeps({ + cpy: '9.0.1', + 'lodash-es': '4.17.21', + }) + assert((await import('cpy')).default instanceof Function) + assert((await import('lodash-es')).pick instanceof Function) }) - assert.instance((await import('cpy')).default, Function) - assert.instance((await import('lodash-es')).pick, Function) -}) -test('installDeps() loader works via CLI', async () => { - let out = - await $`node build/cli.js --install <<< 'import _ from "lodash" /* @4.17.15 */; console.log(_.VERSION)'` - assert.match(out.stdout, '4.17.15') -}) + test('installDeps() loader works via CLI', async () => { + let out = + await $`node build/cli.js --install <<< 'import _ from "lodash" /* @4.17.15 */; console.log(_.VERSION)'` + assert.match(out.stdout, /4.17.15/) + }) -test('parseDeps(): import or require', async () => { - ;[ - [`import "foo"`, { foo: 'latest' }], - [`import "foo"`, { foo: 'latest' }], - [`import * as bar from "foo"`, { foo: 'latest' }], - [`import('foo')`, { foo: 'latest' }], - [`require('foo')`, { foo: 'latest' }], - [`require('foo/bar')`, { foo: 'latest' }], - [`require('foo/bar.js')`, { foo: 'latest' }], - [`require('foo-bar')`, { 'foo-bar': 'latest' }], - [`require('foo_bar')`, { foo_bar: 'latest' }], - [`require('@foo/bar')`, { '@foo/bar': 'latest' }], - [`require('@foo/bar/baz')`, { '@foo/bar': 'latest' }], - [`require('foo.js')`, { 'foo.js': 'latest' }], + test('parseDeps(): import or require', async () => { + ;[ + [`import "foo"`, { foo: 'latest' }], + [`import "foo"`, { foo: 'latest' }], + [`import * as bar from "foo"`, { foo: 'latest' }], + [`import('foo')`, { foo: 'latest' }], + [`require('foo')`, { foo: 'latest' }], + [`require('foo/bar')`, { foo: 'latest' }], + [`require('foo/bar.js')`, { foo: 'latest' }], + [`require('foo-bar')`, { 'foo-bar': 'latest' }], + [`require('foo_bar')`, { foo_bar: 'latest' }], + [`require('@foo/bar')`, { '@foo/bar': 'latest' }], + [`require('@foo/bar/baz')`, { '@foo/bar': 'latest' }], + [`require('foo.js')`, { 'foo.js': 'latest' }], - // ignores local deps - [`import '.'`, {}], - [`require('.')`, {}], - [`require('..')`, {}], - [`require('../foo.js')`, {}], - [`require('./foo.js')`, {}], + // ignores local deps + [`import '.'`, {}], + [`require('.')`, {}], + [`require('..')`, {}], + [`require('../foo.js')`, {}], + [`require('./foo.js')`, {}], - // ignores invalid pkg names - [`require('_foo')`, {}], - [`require('@')`, {}], - [`require('@/_foo')`, {}], - [`require('@foo')`, {}], - ].forEach(([input, result]) => { - assert.equal(parseDeps(input), result) + // ignores invalid pkg names + [`require('_foo')`, {}], + [`require('@')`, {}], + [`require('@/_foo')`, {}], + [`require('@foo')`, {}], + ].forEach(([input, result]) => { + assert.deepEqual(parseDeps(input), result) + }) }) -}) -test('parseDeps(): import with org and filename', async () => { - assert.equal(parseDeps(`import "@foo/bar/file"`), { '@foo/bar': 'latest' }) -}) + test('parseDeps(): import with org and filename', async () => { + assert.deepEqual(parseDeps(`import "@foo/bar/file"`), { + '@foo/bar': 'latest', + }) + }) -test('parseDeps(): import with version', async () => { - assert.equal(parseDeps(`import "foo" // @2.x`), { foo: '2.x' }) - assert.equal(parseDeps(`import "foo" // @^7`), { foo: '^7' }) - assert.equal(parseDeps(`import "foo" /* @1.2.x */`), { foo: '1.2.x' }) -}) + test('parseDeps(): import with version', async () => { + assert.deepEqual(parseDeps(`import "foo" // @2.x`), { foo: '2.x' }) + assert.deepEqual(parseDeps(`import "foo" // @^7`), { foo: '^7' }) + assert.deepEqual(parseDeps(`import "foo" /* @1.2.x */`), { foo: '1.2.x' }) + }) -test('parseDeps(): multiline', () => { - const contents = ` + test('parseDeps(): multiline', () => { + const contents = ` require('a') // @1.0.0 const b =require('b') /* @2.0.0 */ const c = { @@ -105,25 +108,24 @@ test('parseDeps(): multiline', () => { const { pick } = require("lodash") // @4.17.15 ` - assert.equal(parseDeps(contents), { - a: '1.0.0', - b: '2.0.0', - c: '3.0.0', - d: '4.0.0', - e: '5.0.0', - f: '6.0.0', - g: '7.0.0', - h: '8.0.0', - i: '9.0.0', - j: '10.0.0', - foo: 'latest', - bar: '1.0.0', - baz: '^2.0', - '@qux/pkg': '^3.0', - qux: '^4.0.0-beta.0', - cpy: 'latest', - lodash: '4.17.15', + assert.deepEqual(parseDeps(contents), { + a: '1.0.0', + b: '2.0.0', + c: '3.0.0', + d: '4.0.0', + e: '5.0.0', + f: '6.0.0', + g: '7.0.0', + h: '8.0.0', + i: '9.0.0', + j: '10.0.0', + foo: 'latest', + bar: '1.0.0', + baz: '^2.0', + '@qux/pkg': '^3.0', + qux: '^4.0.0-beta.0', + cpy: 'latest', + lodash: '4.17.15', + }) }) }) - -test.run() diff --git a/test/experimental.test.js b/test/experimental.test.js index a6438358ea..bc55de3325 100644 --- a/test/experimental.test.js +++ b/test/experimental.test.js @@ -12,12 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import { describe, before } from 'node:test' import '../build/globals.js' -const test = suite('experimental') - -$.verbose = false - -test.run() +describe('experimental', () => { + before(() => { + $.verbose = false + }) +}) diff --git a/test/extra.test.js b/test/extra.test.js index 3197f0115d..1c584201ca 100644 --- a/test/extra.test.js +++ b/test/extra.test.js @@ -12,35 +12,36 @@ // See the License for the specific language governing permissions and // limitations under the License. +import assert from 'node:assert' import fs from 'node:fs' -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import { test, describe } from 'node:test' import { globby } from 'globby' -const test = suite('extra') - -test('every file should have a license', async () => { - const files = await globby(['**/*.{ts,js,mjs}'], { gitignore: true }) - for (const file of files) { - const content = fs.readFileSync(file).toString() - assert.match( - content.replace(/\d{4}/g, 'YEAR'), - '// Copyright YEAR Google LLC\n' + - '//\n' + - '// Licensed under the Apache License, Version 2.0 (the "License");\n' + - '// you may not use this file except in compliance with the License.\n' + - '// You may obtain a copy of the License at\n' + - '//\n' + - '// https://www.apache.org/licenses/LICENSE-2.0\n' + - '//\n' + - '// Unless required by applicable law or agreed to in writing, software\n' + - '// distributed under the License is distributed on an "AS IS" BASIS,\n' + - '// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' + - '// See the License for the specific language governing permissions and\n' + - '// limitations under the License.', - `No license header in ${file}.` - ) - } +describe('extra', () => { + test('every file should have a license', async () => { + const files = await globby(['**/*.{ts,js,mjs}'], { gitignore: true }) + for (const file of files) { + const content = fs.readFileSync(file).toString() + assert( + content + .replace(/\d{4}/g, 'YEAR') + .includes( + '// Copyright YEAR Google LLC\n' + + '//\n' + + '// Licensed under the Apache License, Version 2.0 (the "License");\n' + + '// you may not use this file except in compliance with the License.\n' + + '// You may obtain a copy of the License at\n' + + '//\n' + + '// https://www.apache.org/licenses/LICENSE-2.0\n' + + '//\n' + + '// Unless required by applicable law or agreed to in writing, software\n' + + '// distributed under the License is distributed on an "AS IS" BASIS,\n' + + '// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' + + '// See the License for the specific language governing permissions and\n' + + '// limitations under the License.' + ), + `No license header in ${file}.` + ) + } + }) }) - -test.run() diff --git a/test/global.test.js b/test/global.test.js index 6ce22393bf..946f72514f 100644 --- a/test/global.test.js +++ b/test/global.test.js @@ -12,25 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import assert from 'node:assert' +import { test, describe } from 'node:test' import '../build/globals.js' import * as index from '../build/index.js' -const test = suite('global') +describe('global', () => { + test('global cd()', async () => { + const cwd = (await $`pwd`).toString().trim() + cd('/') + assert.equal((await $`pwd`).toString().trim(), path.resolve('/')) + cd(cwd) + assert.equal((await $`pwd`).toString().trim(), cwd) + }) -test('global cd()', async () => { - const cwd = (await $`pwd`).toString().trim() - cd('/') - assert.is((await $`pwd`).toString().trim(), path.resolve('/')) - cd(cwd) - assert.is((await $`pwd`).toString().trim(), cwd) + test('injects zx index to global', () => { + for (let [key, value] of Object.entries(index)) { + assert.equal(global[key], value) + } + }) }) - -test('injects zx index to global', () => { - for (let [key, value] of Object.entries(index)) { - assert.is(global[key], value) - } -}) - -test.run() diff --git a/test/goods.test.js b/test/goods.test.js index ea8b9ec46c..e7be2cd9fd 100644 --- a/test/goods.test.js +++ b/test/goods.test.js @@ -13,100 +13,101 @@ // limitations under the License. import chalk from 'chalk' -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import assert from 'node:assert' +import { test, describe, beforeEach } from 'node:test' import '../build/globals.js' -const test = suite('goods') +describe('goods', () => { + beforeEach(() => { + $.verbose = false + }) -$.verbose = false - -function zx(script) { - return $`node build/cli.js --eval ${script}`.nothrow().timeout('5s') -} + function zx(script) { + return $`node build/cli.js --eval ${script}`.nothrow().timeout('5s') + } -test('question() works', async () => { - let p = $`node build/cli.js --eval " + test('question() works', async () => { + let p = $`node build/cli.js --eval " let answer = await question('foo or bar? ', { choices: ['foo', 'bar'] }) echo('Answer is', answer) "` - p.stdin.write('foo\n') - p.stdin.end() - assert.match((await p).stdout, 'Answer is foo') -}) - -test('globby available', async () => { - assert.is(globby, glob) - assert.is(typeof globby, 'function') - assert.is(typeof globby.globbySync, 'function') - assert.is(typeof globby.globbyStream, 'function') - assert.is(typeof globby.generateGlobTasks, 'function') - assert.is(typeof globby.isDynamicPattern, 'function') - assert.is(typeof globby.isGitIgnored, 'function') - assert.is(typeof globby.isGitIgnoredSync, 'function') - assert.equal(await globby('*.md'), ['README.md']) -}) - -test('fetch() works', async () => { - assert.match( - await fetch('https://medv.io').then((res) => res.text()), - /Anton Medvedev/ - ) -}) - -test('echo() works', async () => { - let stdout = '' - let log = console.log - console.log = (...args) => { - stdout += args.join(' ') - } - echo(chalk.cyan('foo'), chalk.green('bar'), chalk.bold('baz')) - echo`${chalk.cyan('foo')} ${chalk.green('bar')} ${chalk.bold('baz')}` - echo( - await $`echo ${chalk.cyan('foo')}`, - await $`echo ${chalk.green('bar')}`, - await $`echo ${chalk.bold('baz')}` - ) - console.log = log - assert.match(stdout, 'foo') -}) - -test('YAML works', async () => { - assert.equal(YAML.parse(YAML.stringify({ foo: 'bar' })), { foo: 'bar' }) -}) - -test('which() available', async () => { - assert.is(which.sync('npm'), await which('npm')) -}) - -test('minimist available', async () => { - assert.is(typeof minimist, 'function') -}) - -test('minimist works', async () => { - assert.equal( - minimist( - ['--foo', 'bar', '-a', '5', '-a', '42', '--force', './some.file'], - { boolean: 'force' } - ), - { - a: [5, 42], - foo: 'bar', - force: true, - _: ['./some.file'], + p.stdin.write('foo\n') + p.stdin.end() + assert.match((await p).stdout, /Answer is foo/) + }) + + test('globby available', async () => { + assert.equal(globby, glob) + assert.equal(typeof globby, 'function') + assert.equal(typeof globby.globbySync, 'function') + assert.equal(typeof globby.globbyStream, 'function') + assert.equal(typeof globby.generateGlobTasks, 'function') + assert.equal(typeof globby.isDynamicPattern, 'function') + assert.equal(typeof globby.isGitIgnored, 'function') + assert.equal(typeof globby.isGitIgnoredSync, 'function') + assert.deepEqual(await globby('*.md'), ['README.md']) + }) + + test('fetch() works', async () => { + assert.match( + await fetch('https://medv.io').then((res) => res.text()), + /Anton Medvedev/ + ) + }) + + test('echo() works', async () => { + let stdout = '' + let log = console.log + console.log = (...args) => { + stdout += args.join(' ') } - ) -}) - -test('sleep() works', async () => { - const now = Date.now() - await sleep(100) - assert.ok(Date.now() >= now + 99) -}) - -test('retry() works', async () => { - const now = Date.now() - let p = await zx(` + echo(chalk.cyan('foo'), chalk.green('bar'), chalk.bold('baz')) + echo`${chalk.cyan('foo')} ${chalk.green('bar')} ${chalk.bold('baz')}` + echo( + await $`echo ${chalk.cyan('foo')}`, + await $`echo ${chalk.green('bar')}`, + await $`echo ${chalk.bold('baz')}` + ) + console.log = log + assert.match(stdout, /foo/) + }) + + test('YAML works', async () => { + assert.deepEqual(YAML.parse(YAML.stringify({ foo: 'bar' })), { foo: 'bar' }) + }) + + test('which() available', async () => { + assert.equal(which.sync('npm'), await which('npm')) + }) + + test('minimist available', async () => { + assert.equal(typeof minimist, 'function') + }) + + test('minimist works', async () => { + assert.deepEqual( + minimist( + ['--foo', 'bar', '-a', '5', '-a', '42', '--force', './some.file'], + { boolean: 'force' } + ), + { + a: [5, 42], + foo: 'bar', + force: true, + _: ['./some.file'], + } + ) + }) + + test('sleep() works', async () => { + const now = Date.now() + await sleep(100) + assert.ok(Date.now() >= now + 99) + }) + + test('retry() works', async () => { + const now = Date.now() + let p = await zx(` try { await retry(5, '50ms', () => $\`exit 123\`) } catch (e) { @@ -115,14 +116,14 @@ test('retry() works', async () => { await retry(5, () => $\`exit 0\`) echo('success') `) - assert.match(p.toString(), 'exitCode: 123') - assert.match(p.toString(), 'success') - assert.ok(Date.now() >= now + 50 * (5 - 1)) -}) - -test('retry() with expBackoff() works', async () => { - const now = Date.now() - let p = await zx(` + assert.ok(p.toString().includes('exitCode: 123')) + assert.ok(p.toString().includes('success')) + assert.ok(Date.now() >= now + 50 * (5 - 1)) + }) + + test('retry() with expBackoff() works', async () => { + const now = Date.now() + let p = await zx(` try { await retry(5, expBackoff('60s', 0), () => $\`exit 123\`) } catch (e) { @@ -130,37 +131,36 @@ test('retry() with expBackoff() works', async () => { } echo('success') `) - assert.match(p.toString(), 'exitCode: 123') - assert.match(p.toString(), 'success') - assert.ok(Date.now() >= now + 2 + 4 + 8 + 16 + 32) -}) + assert.ok(p.toString().includes('exitCode: 123')) + assert.ok(p.toString().includes('success')) + assert.ok(Date.now() >= now + 2 + 4 + 8 + 16 + 32) + }) -test('spinner() works', async () => { - let out = await zx(` + test('spinner() works', async () => { + let out = await zx(` echo(await spinner(async () => { await sleep(100) await $\`echo hidden\` return $\`echo result\` })) `) - assert.match(out.stdout, 'result') - assert.not.match(out.stderr, 'result') - assert.not.match(out.stderr, 'hidden') -}) + assert(out.stdout.includes('result')) + assert(!out.stderr.includes('result')) + assert(!out.stderr.includes('hidden')) + }) -test('spinner() with title works', async () => { - let out = await zx(` + test('spinner() with title works', async () => { + let out = await zx(` await spinner('processing', () => sleep(100)) `) - assert.match(out.stderr, 'processing') -}) + assert.match(out.stderr, /processing/) + }) -test('spinner() stops on throw', async () => { - let out = await zx(` + test('spinner() stops on throw', async () => { + let out = await zx(` await spinner('processing', () => $\`wtf-cmd\`) `) - assert.match(out.stderr, 'Error:') - assert.is.not(out.exitCode, 0) + assert.match(out.stderr, /Error:/) + assert(out.exitCode !== 0) + }) }) - -test.run() diff --git a/test/package.test.js b/test/package.test.js index 40095d7f9a..1b3b4b8cca 100644 --- a/test/package.test.js +++ b/test/package.test.js @@ -12,46 +12,44 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import assert from 'node:assert' +import { test, describe, beforeEach } from 'node:test' import '../build/globals.js' -const test = suite('package') - -test.before.each(async () => { - $.verbose = false - const pack = await $`npm pack` - await $`tar xf ${pack}` - await $`rm ${pack}`.nothrow() -}) +describe('package', () => { + beforeEach(async () => { + $.verbose = false + const pack = await $`npm pack` + await $`tar xf ${pack}` + await $`rm ${pack}`.nothrow() + }) -test('ts project', async () => { - const pack = path.resolve('package') - const out = await within(async () => { - cd('test/fixtures/ts-project') - await $`npm i` - await $`rm -rf node_modules/zx` - await $`mv ${pack} node_modules/zx` - try { - await $`npx tsc` - } catch (err) { - throw new Error(err.stdout) - } - return $`node build/script.js` + test('ts project', async () => { + const pack = path.resolve('package') + const out = await within(async () => { + cd('test/fixtures/ts-project') + await $`npm i` + await $`rm -rf node_modules/zx` + await $`mv ${pack} node_modules/zx` + try { + await $`npx tsc` + } catch (err) { + throw new Error(err.stdout) + } + return $`node build/script.js` + }) + assert.match(out.stderr, /ts-script/) }) - assert.match(out.stderr, 'ts-script') -}) -test('js project with zx', async () => { - const pack = path.resolve('package') - const out = await within(async () => { - cd('test/fixtures/js-project') - await $`rm -rf node_modules` - await $`mkdir node_modules` - await $`mv ${pack} node_modules/zx` - return $`node node_modules/zx/build/cli.js script.js` + test('js project with zx', async () => { + const pack = path.resolve('package') + const out = await within(async () => { + cd('test/fixtures/js-project') + await $`rm -rf node_modules` + await $`mkdir node_modules` + await $`mv ${pack} node_modules/zx` + return $`node node_modules/zx/build/cli.js script.js` + }) + assert.match(out.stderr, /js-script/) }) - assert.match(out.stderr, 'js-script') }) - -test.run() diff --git a/test/util.test.js b/test/util.test.js index 047940ec19..17dc23796d 100644 --- a/test/util.test.js +++ b/test/util.test.js @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import assert from 'node:assert' +import { test, describe } from 'node:test' import { exitCodeInfo, errnoMessage, @@ -26,70 +26,68 @@ import { randomId, } from '../build/util.js' -const test = suite('util') +describe('util', () => { + test('exitCodeInfo()', () => { + assert.equal(exitCodeInfo(2), 'Misuse of shell builtins') + }) -test('exitCodeInfo()', () => { - assert.is(exitCodeInfo(2), 'Misuse of shell builtins') -}) + test('errnoMessage()', () => { + assert.equal(errnoMessage(-2), 'No such file or directory') + assert.equal(errnoMessage(1e9), 'Unknown error') + assert.equal(errnoMessage(undefined), 'Unknown error') + }) -test('errnoMessage()', () => { - assert.is(errnoMessage(-2), 'No such file or directory') - assert.is(errnoMessage(1e9), 'Unknown error') - assert.is(errnoMessage(undefined), 'Unknown error') -}) + test('randomId()', () => { + assert.ok(/^[a-z0-9]+$/.test(randomId())) + assert.ok( + new Set(Array.from({ length: 1000 }).map(() => randomId())).size === 1000 + ) + }) -test('randomId()', () => { - assert.ok(/^[a-z0-9]+$/.test(randomId())) - assert.ok( - new Set(Array.from({ length: 1000 }).map(() => randomId())).size === 1000 - ) -}) - -test('noop()', () => { - assert.ok(noop() === undefined) -}) + test('noop()', () => { + assert.ok(noop() === undefined) + }) -test('isString()', () => { - assert.ok(isString('string')) - assert.not.ok(isString(1)) -}) + test('isString()', () => { + assert.ok(isString('string')) + assert.ok(!isString(1)) + }) -test('quote()', () => { - assert.ok(quote('string') === 'string') - assert.ok(quote(`'\f\n\r\t\v\0`) === `$'\\'\\f\\n\\r\\t\\v\\0'`) -}) + test('quote()', () => { + assert.ok(quote('string') === 'string') + assert.ok(quote(`'\f\n\r\t\v\0`) === `$'\\'\\f\\n\\r\\t\\v\\0'`) + }) -test('quotePowerShgell()', () => { - assert.is(quotePowerShell('string'), 'string') - assert.is(quotePowerShell(`'`), `''''`) -}) + test('quotePowerShgell()', () => { + assert.equal(quotePowerShell('string'), 'string') + assert.equal(quotePowerShell(`'`), `''''`) + }) -test('duration parsing works', () => { - assert.is(parseDuration(1000), 1000) - assert.is(parseDuration('2s'), 2000) - assert.is(parseDuration('500ms'), 500) - assert.throws(() => parseDuration('100')) - assert.throws(() => parseDuration(NaN)) - assert.throws(() => parseDuration(-1)) -}) + test('duration parsing works', () => { + assert.equal(parseDuration(1000), 1000) + assert.equal(parseDuration('2s'), 2000) + assert.equal(parseDuration('500ms'), 500) + assert.throws(() => parseDuration('100')) + assert.throws(() => parseDuration(NaN)) + assert.throws(() => parseDuration(-1)) + }) -test('formatCwd works', () => { - assert.is( - formatCmd(`echo $'hi'`), - "$ \u001b[92mecho\u001b[39m \u001b[93m$\u001b[39m\u001b[93m'hi\u001b[39m\u001b[93m'\u001b[39m\n" - ) - assert.is( - formatCmd(`while true; do "$" done`), - '$ \u001b[96mwhile\u001b[39m \u001b[92mtrue\u001b[39m\u001b[96m;\u001b[39m \u001b[96mdo\u001b[39m \u001b[93m"$\u001b[39m\u001b[93m"\u001b[39m \u001b[96mdone\u001b[39m\n' - ) - assert.is( - formatCmd(`echo '\n str\n'`), - "$ \u001b[92mecho\u001b[39m \u001b[93m'\u001b[39m\n> \u001b[93m str\u001b[39m\n> \u001b[93m'\u001b[39m\n" - ) - assert.is( - formatCmd(`$'\\''`), - "$ \u001b[93m$\u001b[39m\u001b[93m'\u001b[39m\u001b[93m\\\u001b[39m\u001b[93m'\u001b[39m\u001b[93m'\u001b[39m\n" - ) + test('formatCwd works', () => { + assert.equal( + formatCmd(`echo $'hi'`), + "$ \u001b[92mecho\u001b[39m \u001b[93m$\u001b[39m\u001b[93m'hi\u001b[39m\u001b[93m'\u001b[39m\n" + ) + assert.equal( + formatCmd(`while true; do "$" done`), + '$ \u001b[96mwhile\u001b[39m \u001b[92mtrue\u001b[39m\u001b[96m;\u001b[39m \u001b[96mdo\u001b[39m \u001b[93m"$\u001b[39m\u001b[93m"\u001b[39m \u001b[96mdone\u001b[39m\n' + ) + assert.equal( + formatCmd(`echo '\n str\n'`), + "$ \u001b[92mecho\u001b[39m \u001b[93m'\u001b[39m\n> \u001b[93m str\u001b[39m\n> \u001b[93m'\u001b[39m\n" + ) + assert.equal( + formatCmd(`$'\\''`), + "$ \u001b[93m$\u001b[39m\u001b[93m'\u001b[39m\u001b[93m\\\u001b[39m\u001b[93m'\u001b[39m\u001b[93m'\u001b[39m\n" + ) + }) }) - -test.run() diff --git a/test/win32.test.js b/test/win32.test.js index 43e5a81210..e52c0d43d2 100644 --- a/test/win32.test.js +++ b/test/win32.test.js @@ -12,34 +12,34 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { suite } from 'uvu' -import * as assert from 'uvu/assert' +import assert from 'node:assert' +import { test, describe, before, beforeEach } from 'node:test' import '../build/globals.js' -const test = suite('win32') - -$.verbose = false - -if (process.platform === 'win32') { - test('should work with windows-specific commands', async () => { - const p = await $`echo $0` // Bash is first by default. - assert.match(p.stdout, /bash/) - await within(async () => { - $.shell = which.sync('powershell.exe') - $.quote = quotePowerShell - const p = await $`get-host` - assert.match(p.stdout, /PowerShell/) - }) +describe('win32', () => { + beforeEach(() => { + $.verbose = false }) - test('quotePowerShell works', async () => { - await within(async () => { - $.shell = which.sync('powershell.exe') - $.quote = quotePowerShell - const p = await $`echo ${`Windows 'rulez!'`}` - assert.match(p.stdout, /Windows 'rulez!'/) + if (process.platform === 'win32') { + test('should work with windows-specific commands', async () => { + const p = await $`echo $0` // Bash is first by default. + assert.match(p.stdout, /bash/) + await within(async () => { + $.shell = which.sync('powershell.exe') + $.quote = quotePowerShell + const p = await $`get-host` + assert.match(p.stdout, /PowerShell/) + }) }) - }) -} -test.run() + test('quotePowerShell works', async () => { + await within(async () => { + $.shell = which.sync('powershell.exe') + $.quote = quotePowerShell + const p = await $`echo ${`Windows 'rulez!'`}` + assert.match(p.stdout, /Windows 'rulez!'/) + }) + }) + } +})