From 2d04f62446229f9eb4ca916b40216e1e1c024561 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sat, 1 Jul 2023 23:41:11 +0100 Subject: [PATCH 1/3] test: add `nuxt-vitest` and composable unit tests --- .github/workflows/ci.yml | 3 + package.json | 3 + pnpm-lock.yaml | 215 +++++++++++++++++++++++++++++++++- test/nuxt/composables.test.ts | 206 ++++++++++++++++++++++++++++++++ vitest.config.ts | 2 +- vitest.nuxt.config.ts | 8 ++ 6 files changed, 434 insertions(+), 3 deletions(-) create mode 100644 test/nuxt/composables.test.ts create mode 100644 vitest.nuxt.config.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0db06104111b..e6f3c3de7eef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -226,6 +226,9 @@ jobs: - name: Test (unit) run: pnpm test:unit + - name: Test (runtime unit) + run: pnpm test:runtime + - name: Test (fixtures) run: pnpm test:fixtures env: diff --git a/package.json b/package.json index 57eb05088b67..b0a3c7f2bfd0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "test:fixtures:payload": "TEST_PAYLOAD=js pnpm test:fixtures", "test:fixtures:dev": "TEST_ENV=dev pnpm test:fixtures", "test:fixtures:webpack": "TEST_BUILDER=webpack pnpm test:fixtures", + "test:runtime": "vitest -c vitest.nuxt.config.ts", "test:types": "pnpm --filter './test/fixtures/**' test:types", "test:unit": "vitest run --dir packages", "typecheck": "tsc --noEmit" @@ -63,6 +64,7 @@ "markdownlint-cli": "^0.33.0", "nuxi": "workspace:*", "nuxt": "workspace:*", + "nuxt-vitest": "^0.8.7", "ofetch": "1.1.1", "pathe": "1.1.1", "playwright": "1.35.1", @@ -73,6 +75,7 @@ "ufo": "1.1.2", "vite": "4.3.9", "vitest": "0.32.2", + "vitest-environment-nuxt": "^0.8.7", "vue": "3.3.4", "vue-eslint-parser": "9.3.1", "vue-tsc": "1.8.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 88519e21f3ee..a7356294e380 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,6 +92,9 @@ importers: nuxt: specifier: workspace:* version: link:packages/nuxt + nuxt-vitest: + specifier: ^0.8.7 + version: 0.8.7(@vitejs/plugin-vue-jsx@3.0.1)(@vitejs/plugin-vue@4.2.3)(vite@4.3.9)(vitest@0.32.2)(vue-router@4.2.2)(vue@3.3.4) ofetch: specifier: 1.1.1 version: 1.1.1 @@ -122,6 +125,9 @@ importers: vitest: specifier: 0.32.2 version: 0.32.2(playwright@1.35.1) + vitest-environment-nuxt: + specifier: ^0.8.7 + version: 0.8.7(vitest@0.32.2)(vue-router@4.2.2)(vue@3.3.4) vue: specifier: 3.3.4 version: 3.3.4 @@ -2107,7 +2113,6 @@ packages: /@polka/url@1.0.0-next.21: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} - dev: false /@rollup/plugin-alias@5.0.0(rollup@3.25.3): resolution: {integrity: sha512-l9hY5chSCjuFRPsnRm16twWBiSApl2uYFLsepQYwtBuAxNMQ/1dJqADld40P0Jkqm65GRTLy/AC6hnpVebtLsA==} @@ -2764,6 +2769,26 @@ packages: tinyspy: 2.1.0 dev: true + /@vitest/ui@0.30.1: + resolution: {integrity: sha512-Izz4ElDmdvX02KImSC2nCJI6CsGo9aETbKqxli55M0rbbPPAMtF0zDcJIqgEP5V6Y+4Ysf6wvsjLbLCTnaBvKw==} + dependencies: + '@vitest/utils': 0.30.1 + fast-glob: 3.2.12 + fflate: 0.7.4 + flatted: 3.2.7 + pathe: 1.1.1 + picocolors: 1.0.0 + sirv: 2.0.3 + dev: true + + /@vitest/utils@0.30.1: + resolution: {integrity: sha512-/c8Xv2zUVc+rnNt84QF0Y0zkfxnaGhp87K2dYJMLtLOIckPzuxLVzAtFCicGFdB4NeBHNzTRr1tNn7rCtQcWFA==} + dependencies: + concordance: 5.0.4 + loupe: 2.3.6 + pretty-format: 27.5.1 + dev: true + /@vitest/utils@0.32.2: resolution: {integrity: sha512-lnJ0T5i03j0IJaeW73hxe2AuVnZ/y1BhhCOuIcl9LIzXnbpXJT9Lrt6brwKHXLOiA7MZ6N5hSJjt0xE1dGNCzQ==} dependencies: @@ -2919,6 +2944,23 @@ packages: /@vue/shared@3.3.4: resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==} + /@vue/test-utils@2.4.0(vue@3.3.4): + resolution: {integrity: sha512-BKB9aj1yky63/I3IwSr1FjUeHYsKXI7D6S9F378AHt7a5vC0dLkOBtSsFXoRGC/7BfHmiB9HRhT+i9xrUHoAKw==} + peerDependencies: + '@vue/compiler-dom': ^3.0.1 + '@vue/server-renderer': ^3.0.1 + vue: ^3.0.1 + peerDependenciesMeta: + '@vue/compiler-dom': + optional: true + '@vue/server-renderer': + optional: true + dependencies: + js-beautify: 1.14.6 + vue: 3.3.4 + vue-component-type-helpers: 1.6.5 + dev: true + /@vue/typescript@1.8.3(typescript@5.0.4): resolution: {integrity: sha512-6bdgSnIFpRYHlt70pHmnmNksPU00bfXgqAISeaNz3W6d2cH0OTfH8h/IhligQ82sJIhsuyfftQJ5518ZuKIhtA==} dependencies: @@ -3782,6 +3824,13 @@ packages: well-known-symbols: 2.0.0 dev: true + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + /consola@2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} dev: false @@ -3950,6 +3999,10 @@ packages: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} + /css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + dev: true + /cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -4234,6 +4287,16 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + /editorconfig@0.15.3: + resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==} + hasBin: true + dependencies: + commander: 2.20.3 + lru-cache: 4.1.5 + semver: 5.7.1 + sigmund: 1.0.1 + dev: true + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -4951,6 +5014,10 @@ packages: node-domexception: 1.0.0 web-streams-polyfill: 3.2.1 + /fflate@0.7.4: + resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} + dev: true + /figures@5.0.0: resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} engines: {node: '>=14'} @@ -5356,6 +5423,17 @@ packages: ufo: 1.1.2 uncrypto: 0.1.3 + /happy-dom@9.20.3: + resolution: {integrity: sha512-eBsgauT435fXFvQDNcmm5QbGtYzxEzOaX35Ia+h6yP/wwa4xSWZh1CfP+mGby8Hk6Xu59mTkpyf72rUXHNxY7A==} + dependencies: + css.escape: 1.5.1 + entities: 4.5.0 + iconv-lite: 0.6.3 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + dev: true + /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true @@ -5488,6 +5566,13 @@ packages: dependencies: safer-buffer: 2.1.2 + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + /icss-utils@5.1.0(postcss@8.4.24): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} @@ -5965,6 +6050,17 @@ packages: resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==} hasBin: true + /js-beautify@1.14.6: + resolution: {integrity: sha512-GfofQY5zDp+cuHc+gsEXKPpNw2KbPddreEo35O6jT6i0RVK6LhsoYBhq5TvK4/n74wnA0QbK8gGd+jUZwTMKJw==} + engines: {node: '>=10'} + hasBin: true + dependencies: + config-chain: 1.1.13 + editorconfig: 0.15.3 + glob: 8.1.0 + nopt: 6.0.0 + dev: true + /js-string-escape@1.0.1: resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} engines: {node: '>= 0.8'} @@ -6213,6 +6309,13 @@ packages: resolution: {integrity: sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==} engines: {node: 14 || >=16.14} + /lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + dev: true + /lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: @@ -6489,7 +6592,6 @@ packages: /mrmime@1.0.1: resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} engines: {node: '>=10'} - dev: false /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -6666,6 +6768,14 @@ packages: dependencies: abbrev: 1.1.1 + /nopt@6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: true + /normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -6709,6 +6819,32 @@ packages: dependencies: boolbase: 1.0.0 + /nuxt-vitest@0.8.7(@vitejs/plugin-vue-jsx@3.0.1)(@vitejs/plugin-vue@4.2.3)(vite@4.3.9)(vitest@0.32.2)(vue-router@4.2.2)(vue@3.3.4): + resolution: {integrity: sha512-OaQqF8QxfLKzYzSrwcfkhv5BeyLQGGvrSvOedjV3OHuDjOaTJa6OPU2VTCZBVNvPMjnbm2h71/CsXbH6smvEJw==} + peerDependencies: + '@vitejs/plugin-vue': '*' + '@vitejs/plugin-vue-jsx': '*' + vite: '*' + vitest: ^0.30.0 + dependencies: + '@nuxt/kit': link:packages/kit + '@vitejs/plugin-vue': 4.2.3(vite@4.3.9)(vue@3.3.4) + '@vitejs/plugin-vue-jsx': 3.0.1(vite@4.3.9)(vue@3.3.4) + '@vitest/ui': 0.30.1 + defu: 6.1.2 + get-port-please: 3.0.1 + perfect-debounce: 1.0.0 + std-env: 3.3.3 + vite: 4.3.9(@types/node@18.16.18) + vitest: 0.32.2(playwright@1.35.1) + vitest-environment-nuxt: 0.8.7(vitest@0.32.2)(vue-router@4.2.2)(vue@3.3.4) + transitivePeerDependencies: + - '@vue/compiler-dom' + - '@vue/server-renderer' + - vue + - vue-router + dev: true + /nypm@0.2.2: resolution: {integrity: sha512-O7bumfWgUXlJefT1Y41SF4vsCvzeUYmnKABuOKStheCObzrkWPDmqJc+RJVU+57oFu9bITcrUq8sKFIHgjCnTg==} engines: {node: ^14.16.0 || >=16.10.0} @@ -7456,6 +7592,10 @@ packages: sisteransi: 1.0.5 dev: false + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true + /protocols@2.0.1: resolution: {integrity: sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==} @@ -7463,6 +7603,10 @@ packages: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} dev: false + /pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + dev: true + /pug-attrs@3.0.0: resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==} dependencies: @@ -7948,6 +8092,10 @@ packages: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} dev: true + /sigmund@1.0.1: + resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==} + dev: true + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -7965,6 +8113,15 @@ packages: totalist: 1.1.0 dev: false + /sirv@2.0.3: + resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.21 + mrmime: 1.0.1 + totalist: 3.0.1 + dev: true + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: false @@ -8366,6 +8523,11 @@ packages: engines: {node: '>=6'} dev: false + /totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + dev: true + /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -8814,6 +8976,30 @@ packages: optionalDependencies: fsevents: 2.3.2 + /vitest-environment-nuxt@0.8.7(vitest@0.32.2)(vue-router@4.2.2)(vue@3.3.4): + resolution: {integrity: sha512-wk0Jn+PmZqF3jT3R/pkrgP7C98K6+/B3yq0dG9I8Qanwmp3014ljQgpiukrAZ6ZeF4k/8wrNBRliBcqtxCKFcw==} + peerDependencies: + vitest: ^0.24.5 || ^0.26.0 || ^0.27.0 || ^0.28.0 || ^0.29.0 || ^0.30.0 + vue: ^3.2.45 + vue-router: ^4.0.0 + dependencies: + '@nuxt/kit': link:packages/kit + '@vue/test-utils': 2.4.0(vue@3.3.4) + defu: 6.1.2 + estree-walker: 3.0.3 + h3: 1.7.1 + happy-dom: 9.20.3 + magic-string: 0.30.0 + ofetch: 1.1.1 + unenv: 1.5.1 + vitest: 0.32.2(playwright@1.35.1) + vue: 3.3.4 + vue-router: 4.2.2(vue@3.3.4) + transitivePeerDependencies: + - '@vue/compiler-dom' + - '@vue/server-renderer' + dev: true + /vitest@0.32.2(playwright@1.35.1): resolution: {integrity: sha512-hU8GNNuQfwuQmqTLfiKcqEhZY72Zxb7nnN07koCUNmntNxbKQnVbeIS6sqUgR3eXSlbOpit8+/gr1KpqoMgWCQ==} engines: {node: '>=v14.18.0'} @@ -8930,6 +9116,10 @@ packages: dependencies: ufo: 1.1.2 + /vue-component-type-helpers@1.6.5: + resolution: {integrity: sha512-iGdlqtajmiqed8ptURKPJ/Olz0/mwripVZszg6tygfZSIL9kYFPJTNY6+Q6OjWGznl2L06vxG5HvNvAnWrnzbg==} + dev: true + /vue-devtools-stub@0.1.0: resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==} dev: false @@ -9029,6 +9219,11 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: true + /webpack-bundle-analyzer@4.9.0: resolution: {integrity: sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw==} engines: {node: '>= 10.13.0'} @@ -9143,6 +9338,18 @@ packages: engines: {node: '>=6'} dev: true + /whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + dependencies: + iconv-lite: 0.6.3 + dev: true + + /whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + dev: true + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: @@ -9261,6 +9468,10 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + /yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + dev: true + /yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} diff --git a/test/nuxt/composables.test.ts b/test/nuxt/composables.test.ts new file mode 100644 index 000000000000..8c3299b6e903 --- /dev/null +++ b/test/nuxt/composables.test.ts @@ -0,0 +1,206 @@ +/// + +import { describe, expect, it, vi } from 'vitest' + +import * as composables from '#app/composables' + +import { clearNuxtData, refreshNuxtData, useAsyncData, useNuxtData } from '#app/composables/asyncData' +import { clearError, createError, isNuxtError, showError, useError } from '#app/composables/error' +import { onNuxtReady } from '#app/composables/ready' +import { setResponseStatus, useRequestEvent, useRequestFetch, useRequestHeaders } from '#app/composables/ssr' +import { clearNuxtState, useState } from '#app/composables/state' +import { useRequestURL } from '#app/composables/url' + +vi.mock('#app/compat/idle-callback', () => ({ + requestIdleCallback: (cb: Function) => cb() +})) + +describe('composables', () => { + it('are all tested', () => { + const testedComposables: string[] = [ + 'clearNuxtData', + 'refreshNuxtData', + 'useAsyncData', + 'useNuxtData', + 'createError', + 'isNuxtError', + 'clearError', + 'showError', + 'useError', + 'onNuxtReady', + 'setResponseStatus', + 'useRequestEvent', + 'useRequestFetch', + 'useRequestHeaders', + 'clearNuxtState', + 'useState', + 'useRequestURL' + ] + const skippedComposables: string[] = [ + 'abortNavigation', + 'addRouteMiddleware', + 'defineNuxtComponent', + 'defineNuxtRouteMiddleware', + 'definePayloadReducer', + 'definePayloadReviver', + 'isPrerendered', + 'loadPayload', + 'navigateTo', + 'onBeforeRouteLeave', + 'onBeforeRouteUpdate', + 'prefetchComponents', + 'preloadComponents', + 'preloadPayload', + 'preloadRouteComponents', + 'reloadNuxtApp', + 'setPageLayout', + 'useCookie', + 'useFetch', + 'useHead', + 'useHydration', + 'useLazyFetch', + 'useLazyAsyncData', + 'useRoute', + 'useRouter', + 'useSeoMeta', + 'useServerSeoMeta' + ] + expect(Object.keys(composables).sort()).toEqual([...new Set([...testedComposables, ...skippedComposables])].sort()) + }) +}) + +describe('useAsyncData', () => { + it('should work at basic level', async () => { + const res = useAsyncData(() => Promise.resolve('test')) + expect(Object.keys(res)).toMatchInlineSnapshot(` + [ + "data", + "pending", + "error", + "status", + "execute", + "refresh", + ] + `) + expect(res instanceof Promise).toBeTruthy() + expect(res.data.value).toBe(null) + await res + expect(res.data.value).toBe('test') + }) + + it('should not execute with immediate: false', async () => { + const immediate = await useAsyncData(() => Promise.resolve('test')) + expect(immediate.data.value).toBe('test') + expect(immediate.status.value).toBe('success') + expect(immediate.pending.value).toBe(false) + + const nonimmediate = await useAsyncData(() => Promise.resolve('test'), { immediate: false }) + expect(nonimmediate.data.value).toBe(null) + expect(nonimmediate.status.value).toBe('idle') + expect(nonimmediate.pending.value).toBe(true) + }) + + it('should capture errors', async () => { + const { error, status, pending } = await useAsyncData(() => Promise.reject(new Error('test'))) + expect(error.value).toMatchInlineSnapshot('[Error: test]') + expect(status.value).toBe('error') + expect(pending.value).toBe(false) + }) + + it('should be accessible with useNuxtData', async () => { + await useAsyncData('key', () => Promise.resolve('test')) + const data = useNuxtData('key') + expect(data.data.value).toMatchInlineSnapshot('"test"') + clearNuxtData('key') + expect(data.data.value).toBeUndefined() + expect(useNuxtData('key').data.value).toBeUndefined() + }) + + it('should be refreshable', async () => { + await useAsyncData('key', () => Promise.resolve('test')) + clearNuxtData('key') + const data = useNuxtData('key') + expect(data.data.value).toBeUndefined() + await refreshNuxtData('key') + expect(data.data.value).toMatchInlineSnapshot('"test"') + }) +}) + +describe('errors', () => { + it('createError', () => { + expect(createError({ statusCode: 404 }).toJSON()).toMatchInlineSnapshot(` + { + "message": "", + "statusCode": 404, + } + `) + expect(createError('Message').toJSON()).toMatchInlineSnapshot(` + { + "message": "Message", + "statusCode": 500, + } + `) + }) + + it('isNuxtError', () => { + const error = createError({ statusCode: 404 }) + expect(isNuxtError(error)).toBe(true) + expect(isNuxtError(new Error('test'))).toBe(false) + }) + + it('global nuxt errors', () => { + const err = useError() + expect(err.value).toBeUndefined() + showError('new error') + expect(err.value).toMatchInlineSnapshot('[Error: new error]') + clearError() + // TODO: should this return to being undefined? + expect(err.value).toBeNull() + }) +}) + +describe('onNuxtReady', () => { + it('should call callback immediately once nuxt is hydrated', () => { + const fn = vi.fn() + onNuxtReady(fn) + expect(fn).toHaveBeenCalled() + }) +}) + +describe('ssr composables', () => { + it('work on client', () => { + // @ts-expect-error This should work for backward compatibility + expect(setResponseStatus()).toBeUndefined() + expect(useRequestEvent()).toBeUndefined() + expect(useRequestFetch()).toEqual($fetch) + expect(useRequestHeaders()).toEqual({}) + }) +}) + +describe('useState', () => { + it('default', () => { + expect(useState(() => 'default').value).toBe('default') + }) + + it('registers state in payload', () => { + useState('key', () => 'value') + expect(Object.entries(useNuxtApp().payload.state)).toContainEqual(['$skey', 'value']) + }) + + it.todo('clearNuxtState', () => { + const state = useState(() => 'test') + expect(state.value).toBe('test') + clearNuxtState() + expect.soft(state.value).toBeUndefined() + }) +}) + +describe('url', () => { + it('useRequestURL', () => { + const url = useRequestURL() + expect(url).toMatchInlineSnapshot('"http://localhost:3000/"') + expect(url.hostname).toMatchInlineSnapshot('"localhost"') + expect(url.port).toMatchInlineSnapshot('"3000"') + expect(url.protocol).toMatchInlineSnapshot('"http:"') + }) +}) diff --git a/vitest.config.ts b/vitest.config.ts index db0155cb7f16..199698d5e2f3 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -15,7 +15,7 @@ export default defineConfig({ globalSetup: './test/setup.ts', testTimeout: isWindows ? 60000 : 10000, // Excluded plugin because it should throw an error when accidentally loaded via Nuxt - exclude: [...configDefaults.exclude, '**/test.ts', '**/this-should-not-load.spec.js'], + exclude: [...configDefaults.exclude, './test/nuxt', '**/test.ts', '**/this-should-not-load.spec.js'], maxThreads: process.env.TEST_ENV === 'dev' ? 1 : undefined, minThreads: process.env.TEST_ENV === 'dev' ? 1 : undefined } diff --git a/vitest.nuxt.config.ts b/vitest.nuxt.config.ts new file mode 100644 index 000000000000..a3b2962270db --- /dev/null +++ b/vitest.nuxt.config.ts @@ -0,0 +1,8 @@ +import { defineVitestConfig } from 'nuxt-vitest/config' + +export default defineVitestConfig({ + test: { + dir: './test/nuxt', + environment: 'nuxt' + } +}) From 1faf9857a0670c3013c1d6c68b1151e895af3de1 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sat, 1 Jul 2023 23:46:08 +0100 Subject: [PATCH 2/3] chore: exclude directory from workspace type testing --- tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 7c6d195b17e6..f5885e12231c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,6 +44,7 @@ "**/examples/**", "**/docs/**", "**/playground/**", - "**/test/fixtures/**" + "**/test/fixtures/**", + "test/nuxt/**" ] } From b5bea29a723de846bcd780e898cdbac3fc239830 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Sat, 1 Jul 2023 23:55:50 +0100 Subject: [PATCH 3/3] chore: exclude test/nuxt directory --- vitest.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vitest.config.ts b/vitest.config.ts index 199698d5e2f3..29ce8108e2e8 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -15,7 +15,7 @@ export default defineConfig({ globalSetup: './test/setup.ts', testTimeout: isWindows ? 60000 : 10000, // Excluded plugin because it should throw an error when accidentally loaded via Nuxt - exclude: [...configDefaults.exclude, './test/nuxt', '**/test.ts', '**/this-should-not-load.spec.js'], + exclude: [...configDefaults.exclude, '**/test/nuxt/**', '**/test.ts', '**/this-should-not-load.spec.js'], maxThreads: process.env.TEST_ENV === 'dev' ? 1 : undefined, minThreads: process.env.TEST_ENV === 'dev' ? 1 : undefined }