[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

feat(ui): initial ui implementation (#115)

Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>

authored by

wheat
Anthony Fu
and committed by
GitHub
(Dec 13, 2021, 4:33 PM +0800) 2961983d 1cb38cdf

+696 -5
+2
package.json
··· 45 45 "docs": "npm -C docs run dev", 46 46 "docs:build": "npm -C docs run build", 47 47 "docs:serve": "npm -C docs run serve", 48 + "ui:build": "vite build packages/ui", 49 + "ui:dev": "vite packages/ui", 48 50 "lint": "eslint \"{src,test}/**/*.ts\"", 49 51 "prepublishOnly": "nr build", 50 52 "release": "bumpp --commit --push --tag && esmo scripts/publish.ts",
+119 -5
pnpm-lock.yaml
··· 136 136 unplugin-vue-components: 0.17.8_vite@2.7.1+vue@3.2.24 137 137 vitepress: 0.20.4 138 138 139 + packages/ui: 140 + specifiers: 141 + '@types/ws': ^8.2.2 142 + '@vueuse/core': ^7.2.2 143 + sirv: ^1.0.19 144 + unocss: ^0.16.1 145 + unplugin-auto-import: ^0.5.3 146 + unplugin-vue-components: ^0.17.8 147 + ws: ^8.3.0 148 + dependencies: 149 + '@vueuse/core': 7.3.0 150 + sirv: 1.0.19 151 + ws: 8.3.0 152 + devDependencies: 153 + '@types/ws': 8.2.2 154 + unocss: 0.16.1 155 + unplugin-auto-import: 0.5.3_@vueuse+core@7.3.0+vite@2.7.1 156 + unplugin-vue-components: 0.17.8_vite@2.7.1 157 + 139 158 test/cjs: 140 159 specifiers: 141 160 prettier: ^2.5.1 ··· 1032 1051 1033 1052 /@polka/url/1.0.0-next.21: 1034 1053 resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} 1035 - dev: true 1036 1054 1037 1055 /@rollup/plugin-alias/3.1.8: 1038 1056 resolution: {integrity: sha512-tf7HeSs/06wO2LPqKNY3Ckbvy0JRe7Jyn98bXnt/gfrxbe+AJucoNJlsEVi9sdgbQtXemjbakCpO/76JVgnHpA==} ··· 1409 1427 resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==} 1410 1428 dev: false 1411 1429 1430 + /@types/ws/8.2.2: 1431 + resolution: {integrity: sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==} 1432 + dependencies: 1433 + '@types/node': 16.11.12 1434 + dev: true 1435 + 1412 1436 /@types/yargs-parser/20.2.0: 1413 1437 resolution: {integrity: sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==} 1414 1438 dev: true ··· 1725 1749 vue: 3.2.24 1726 1750 dev: true 1727 1751 1752 + /@vueuse/core/7.3.0: 1753 + resolution: {integrity: sha512-gPJyMMAquva9Qwqz63qGQT122m5hWI8Kuy8kfPV/JLQU7m01CXooyv8FIrX9TV8OxVcHBTPXPJHY0oyUiFoNgw==} 1754 + peerDependencies: 1755 + '@vue/composition-api': ^1.1.0 1756 + vue: ^2.6.0 || ^3.2.0 1757 + peerDependenciesMeta: 1758 + '@vue/composition-api': 1759 + optional: true 1760 + vue: 1761 + optional: true 1762 + dependencies: 1763 + '@vueuse/shared': 7.3.0 1764 + vue-demi: 0.12.1 1765 + dev: false 1766 + 1728 1767 /@vueuse/core/7.3.0_vue@3.2.24: 1729 1768 resolution: {integrity: sha512-gPJyMMAquva9Qwqz63qGQT122m5hWI8Kuy8kfPV/JLQU7m01CXooyv8FIrX9TV8OxVcHBTPXPJHY0oyUiFoNgw==} 1730 1769 peerDependencies: ··· 1739 1778 '@vueuse/shared': 7.3.0_vue@3.2.24 1740 1779 vue: 3.2.24 1741 1780 vue-demi: 0.12.1_vue@3.2.24 1781 + dev: false 1782 + 1783 + /@vueuse/shared/7.3.0: 1784 + resolution: {integrity: sha512-vOAeI84tIXKVkzm8s/Mxbrzhj0QN6NyVc/sC6LrW0AjVNdvpD8sB1dZiDn9yh8T77WJmloCEt4zZVIppeq7I+w==} 1785 + peerDependencies: 1786 + '@vue/composition-api': ^1.1.0 1787 + vue: ^2.6.0 || ^3.2.0 1788 + peerDependenciesMeta: 1789 + '@vue/composition-api': 1790 + optional: true 1791 + vue: 1792 + optional: true 1793 + dependencies: 1794 + vue-demi: 0.12.1 1742 1795 dev: false 1743 1796 1744 1797 /@vueuse/shared/7.3.0_vue@3.2.24: ··· 4319 4372 /mrmime/1.0.0: 4320 4373 resolution: {integrity: sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==} 4321 4374 engines: {node: '>=10'} 4322 - dev: true 4323 4375 4324 4376 /ms/2.0.0: 4325 4377 resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} ··· 5222 5274 '@polka/url': 1.0.0-next.21 5223 5275 mrmime: 1.0.0 5224 5276 totalist: 1.1.0 5225 - dev: true 5226 5277 5227 5278 /sisteransi/1.0.5: 5228 5279 resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} ··· 5511 5562 /totalist/1.1.0: 5512 5563 resolution: {integrity: sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==} 5513 5564 engines: {node: '>=6'} 5514 - dev: true 5515 5565 5516 5566 /tough-cookie/4.0.0: 5517 5567 resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==} ··· 5682 5732 '@unocss/vite': 0.16.1 5683 5733 dev: true 5684 5734 5735 + /unplugin-auto-import/0.5.3_@vueuse+core@7.3.0+vite@2.7.1: 5736 + resolution: {integrity: sha512-jXs0rnKTS+/TyXOild6RkVp73yz1DeLXVJWR6VRn+oageEsvHgW6zofvc2BmpAm/pTSANxZ48kLi12wOa/iwdw==} 5737 + engines: {node: '>=14'} 5738 + peerDependencies: 5739 + '@vueuse/core': '*' 5740 + peerDependenciesMeta: 5741 + '@vueuse/core': 5742 + optional: true 5743 + dependencies: 5744 + '@antfu/utils': 0.3.0 5745 + '@rollup/pluginutils': 4.1.1 5746 + '@vueuse/core': 7.3.0 5747 + local-pkg: 0.4.0 5748 + magic-string: 0.25.7 5749 + resolve: 1.20.0 5750 + unplugin: 0.2.21_vite@2.7.1 5751 + transitivePeerDependencies: 5752 + - rollup 5753 + - vite 5754 + - webpack 5755 + dev: true 5756 + 5685 5757 /unplugin-auto-import/0.5.3_vite@2.7.1: 5686 5758 resolution: {integrity: sha512-jXs0rnKTS+/TyXOild6RkVp73yz1DeLXVJWR6VRn+oageEsvHgW6zofvc2BmpAm/pTSANxZ48kLi12wOa/iwdw==} 5687 5759 engines: {node: '>=14'} ··· 5699 5771 unplugin: 0.2.21_vite@2.7.1 5700 5772 transitivePeerDependencies: 5701 5773 - rollup 5774 + - vite 5775 + - webpack 5776 + dev: true 5777 + 5778 + /unplugin-vue-components/0.17.8_vite@2.7.1: 5779 + resolution: {integrity: sha512-biyIE8TQwolK/ii3vu3xJ4w83+bliAgka3AuhU2cX5OOvJP+xuq39Skhf+BkNkZoY8xoeU65WHOgLFl//m3/YQ==} 5780 + engines: {node: '>=14'} 5781 + peerDependencies: 5782 + '@babel/parser': ^7.15.8 5783 + '@babel/traverse': ^7.15.4 5784 + vue: 2 || 3 5785 + peerDependenciesMeta: 5786 + '@babel/parser': 5787 + optional: true 5788 + '@babel/traverse': 5789 + optional: true 5790 + dependencies: 5791 + '@antfu/utils': 0.3.0 5792 + '@rollup/pluginutils': 4.1.1 5793 + chokidar: 3.5.2 5794 + debug: 4.3.3 5795 + fast-glob: 3.2.7 5796 + local-pkg: 0.4.0 5797 + magic-string: 0.25.7 5798 + minimatch: 3.0.4 5799 + resolve: 1.20.0 5800 + unplugin: 0.2.21_vite@2.7.1 5801 + transitivePeerDependencies: 5802 + - rollup 5803 + - supports-color 5702 5804 - vite 5703 5805 - webpack 5704 5806 dev: true ··· 5826 5928 - sass 5827 5929 - stylus 5828 5930 dev: true 5931 + 5932 + /vue-demi/0.12.1: 5933 + resolution: {integrity: sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==} 5934 + engines: {node: '>=12'} 5935 + hasBin: true 5936 + requiresBuild: true 5937 + peerDependencies: 5938 + '@vue/composition-api': ^1.0.0-rc.1 5939 + vue: ^3.0.0-0 || ^2.6.0 5940 + peerDependenciesMeta: 5941 + '@vue/composition-api': 5942 + optional: true 5943 + dev: false 5829 5944 5830 5945 /vue-demi/0.12.1_vue@3.2.24: 5831 5946 resolution: {integrity: sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==} ··· 5995 6110 optional: true 5996 6111 utf-8-validate: 5997 6112 optional: true 5998 - dev: true 5999 6113 6000 6114 /xml-name-validator/4.0.0: 6001 6115 resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+24
packages/ui/index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <link rel="icon" href="/favicon.svg" type="image/svg+xml"> 6 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 7 + <title>Vitest</title> 8 + <link rel="preconnect" href="https://fonts.googleapis.com"> 9 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 10 + <link href="https://fonts.googleapis.com/css2?family=Readex+Pro:wght@300;400&display=swap" rel="stylesheet"> 11 + <script> 12 + (function() { 13 + const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches 14 + const setting = localStorage.getItem('vueuse-color-scheme') || 'auto' 15 + if (setting === 'dark' || (prefersDark && setting !== 'light')) 16 + document.documentElement.classList.toggle('dark', true) 17 + })() 18 + </script> 19 + </head> 20 + <body> 21 + <div id="app"></div> 22 + <script type="module" src="/client/main.ts"></script> 23 + </body> 24 + </html>
+27
packages/ui/package.json
··· 1 + { 2 + "name": "@vitest/ui", 3 + "version": "0.0.1", 4 + "description": "Vitest UI", 5 + "sideEffects": false, 6 + "exports": { 7 + "import": "./dist/index.mjs", 8 + "require": "./dist/index.cjs" 9 + }, 10 + "main": "./dist/index.cjs", 11 + "module": "./dist/index.mjs", 12 + "types": "./dist/index.d.ts", 13 + "files": [ 14 + "dist" 15 + ], 16 + "dependencies": { 17 + "@vueuse/core": "^7.2.2", 18 + "sirv": "^1.0.19", 19 + "ws": "^8.3.0" 20 + }, 21 + "devDependencies": { 22 + "@types/ws": "^8.2.2", 23 + "unocss": "^0.16.1", 24 + "unplugin-auto-import": "^0.5.3", 25 + "unplugin-vue-components": "^0.17.8" 26 + } 27 + }
+21
packages/ui/tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "baseUrl": ".", 4 + "module": "ESNext", 5 + "target": "es2016", 6 + "lib": ["DOM", "ESNext"], 7 + "strict": true, 8 + "esModuleInterop": true, 9 + "incremental": false, 10 + "skipLibCheck": true, 11 + "moduleResolution": "node", 12 + "resolveJsonModule": true, 13 + "noUnusedLocals": true, 14 + "strictNullChecks": true, 15 + "forceConsistentCasingInFileNames": true, 16 + "paths": { 17 + "~/*": ["client/*"] 18 + } 19 + }, 20 + "exclude": ["dist", "node_modules"] 21 + }
+11
packages/ui/types.ts
··· 1 + export interface WSMessage { 2 + /** 3 + * Message type 4 + */ 5 + type: string 6 + 7 + /** 8 + * Message Data 9 + */ 10 + data: any 11 + }
+45
packages/ui/vite.config.ts
··· 1 + import { resolve } from 'path' 2 + import { defineConfig } from 'vite' 3 + import Vue from '@vitejs/plugin-vue' 4 + import Components from 'unplugin-vue-components/vite' 5 + import AutoImport from 'unplugin-auto-import/vite' 6 + import Unocss from 'unocss/vite' 7 + import { presetUno, presetAttributify, presetIcons } from 'unocss' 8 + 9 + export default defineConfig({ 10 + root: __dirname, 11 + resolve: { 12 + alias: { 13 + '~/': `${resolve(__dirname, 'client')}/`, 14 + }, 15 + }, 16 + plugins: [ 17 + Vue(), 18 + Unocss({ 19 + presets: [ 20 + presetUno(), 21 + presetAttributify(), 22 + presetIcons(), 23 + ], 24 + }), 25 + Components({ 26 + dirs: ['client/components'], 27 + dts: resolve(__dirname, './client/components.d.ts'), 28 + }), 29 + AutoImport({ 30 + dts: resolve(__dirname, './client/auto-imports.d.ts'), 31 + imports: [ 32 + 'vue', 33 + '@vueuse/core', 34 + ], 35 + }), 36 + ], 37 + build: { 38 + outDir: '../../dist/client', 39 + }, 40 + optimizeDeps: { 41 + include: [ 42 + 'vue', 43 + ], 44 + }, 45 + })
+1
src/node/cli.ts
··· 16 16 .option('-c, --config <path>', 'path to config file') 17 17 .option('-u, --update', 'update snapshot') 18 18 .option('-w, --watch', 'watch mode') 19 + .option('-o, --open', 'open Vitest UI') 19 20 .option('--threads', 'enabled threads', { default: true }) 20 21 .option('--silent', 'silent') 21 22 .option('--global', 'inject apis globally')
+12
src/node/init.ts
··· 4 4 import type { CliOptions, ResolvedConfig } from '../types' 5 5 import { defaultExcludes, defaultIncludes } from '../constants' 6 6 import { toArray } from '../utils' 7 + // import { VitestUIPlugin } from '../../packages/ui/node' 7 8 8 9 const configFiles = [ 9 10 'vitest.config.ts', ··· 42 43 logLevel: 'error', 43 44 clearScreen: false, 44 45 configFile: resolved.config, 46 + plugins: [ 47 + // TODO: UI 48 + // ...(options.open ? [VitestUIPlugin()] : []), 49 + ], 50 + server: { 51 + open: true, 52 + port: 3000, 53 + }, 45 54 optimizeDeps: { 46 55 exclude: [ 47 56 'vitest', ··· 49 58 }, 50 59 }) 51 60 await server.pluginContainer.buildStart({}) 61 + 62 + if (options.open) 63 + server.listen(3000) 52 64 53 65 Object.assign(resolved, server.config.test) 54 66
+5
src/types/options.ts
··· 126 126 silent?: boolean 127 127 128 128 /** 129 + * Open Vitest UI 130 + */ 131 + open?: boolean 132 + 133 + /** 129 134 * Path to setup files 130 135 */ 131 136 setupFiles?: string | string[]
+8
packages/ui/client/App.vue
··· 1 + <script setup lang="ts"> 2 + </script> 3 + 4 + <template> 5 + <div> 6 + <Navigation /> 7 + </div> 8 + </template>
+196
packages/ui/client/auto-imports.d.ts
··· 1 + // Generated by 'unplugin-auto-import' 2 + // We suggest you to commit this file into source control 3 + declare global { 4 + const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] 5 + const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] 6 + const biSyncRef: typeof import('@vueuse/core')['biSyncRef'] 7 + const computed: typeof import('vue')['computed'] 8 + const computedInject: typeof import('@vueuse/core')['computedInject'] 9 + const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] 10 + const controlledRef: typeof import('@vueuse/core')['controlledRef'] 11 + const createApp: typeof import('vue')['createApp'] 12 + const createEventHook: typeof import('@vueuse/core')['createEventHook'] 13 + const createGlobalState: typeof import('@vueuse/core')['createGlobalState'] 14 + const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn'] 15 + const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable'] 16 + const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn'] 17 + const customRef: typeof import('vue')['customRef'] 18 + const debouncedRef: typeof import('@vueuse/core')['debouncedRef'] 19 + const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] 20 + const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] 21 + const defineComponent: typeof import('vue')['defineComponent'] 22 + const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] 23 + const effectScope: typeof import('vue')['effectScope'] 24 + const EffectScope: typeof import('vue')['EffectScope'] 25 + const extendRef: typeof import('@vueuse/core')['extendRef'] 26 + const getCurrentInstance: typeof import('vue')['getCurrentInstance'] 27 + const getCurrentScope: typeof import('vue')['getCurrentScope'] 28 + const h: typeof import('vue')['h'] 29 + const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] 30 + const inject: typeof import('vue')['inject'] 31 + const isDefined: typeof import('@vueuse/core')['isDefined'] 32 + const isReadonly: typeof import('vue')['isReadonly'] 33 + const isRef: typeof import('vue')['isRef'] 34 + const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] 35 + const markRaw: typeof import('vue')['markRaw'] 36 + const nextTick: typeof import('vue')['nextTick'] 37 + const onActivated: typeof import('vue')['onActivated'] 38 + const onBeforeMount: typeof import('vue')['onBeforeMount'] 39 + const onBeforeUnmount: typeof import('vue')['onBeforeUnmount'] 40 + const onBeforeUpdate: typeof import('vue')['onBeforeUpdate'] 41 + const onClickOutside: typeof import('@vueuse/core')['onClickOutside'] 42 + const onDeactivated: typeof import('vue')['onDeactivated'] 43 + const onErrorCaptured: typeof import('vue')['onErrorCaptured'] 44 + const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke'] 45 + const onMounted: typeof import('vue')['onMounted'] 46 + const onRenderTracked: typeof import('vue')['onRenderTracked'] 47 + const onRenderTriggered: typeof import('vue')['onRenderTriggered'] 48 + const onScopeDispose: typeof import('vue')['onScopeDispose'] 49 + const onServerPrefetch: typeof import('vue')['onServerPrefetch'] 50 + const onStartTyping: typeof import('@vueuse/core')['onStartTyping'] 51 + const onUnmounted: typeof import('vue')['onUnmounted'] 52 + const onUpdated: typeof import('vue')['onUpdated'] 53 + const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] 54 + const provide: typeof import('vue')['provide'] 55 + const reactify: typeof import('@vueuse/core')['reactify'] 56 + const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] 57 + const reactive: typeof import('vue')['reactive'] 58 + const reactivePick: typeof import('@vueuse/core')['reactivePick'] 59 + const readonly: typeof import('vue')['readonly'] 60 + const ref: typeof import('vue')['ref'] 61 + const refDefault: typeof import('@vueuse/core')['refDefault'] 62 + const shallowReactive: typeof import('vue')['shallowReactive'] 63 + const shallowReadonly: typeof import('vue')['shallowReadonly'] 64 + const shallowRef: typeof import('vue')['shallowRef'] 65 + const syncRef: typeof import('@vueuse/core')['syncRef'] 66 + const templateRef: typeof import('@vueuse/core')['templateRef'] 67 + const throttledRef: typeof import('@vueuse/core')['throttledRef'] 68 + const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] 69 + const toRaw: typeof import('vue')['toRaw'] 70 + const toReactive: typeof import('@vueuse/core')['toReactive'] 71 + const toRef: typeof import('vue')['toRef'] 72 + const toRefs: typeof import('vue')['toRefs'] 73 + const triggerRef: typeof import('vue')['triggerRef'] 74 + const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount'] 75 + const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted'] 76 + const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose'] 77 + const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted'] 78 + const unref: typeof import('vue')['unref'] 79 + const unrefElement: typeof import('@vueuse/core')['unrefElement'] 80 + const until: typeof import('@vueuse/core')['until'] 81 + const useActiveElement: typeof import('@vueuse/core')['useActiveElement'] 82 + const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue'] 83 + const useAsyncState: typeof import('@vueuse/core')['useAsyncState'] 84 + const useAttrs: typeof import('vue')['useAttrs'] 85 + const useBase64: typeof import('@vueuse/core')['useBase64'] 86 + const useBattery: typeof import('@vueuse/core')['useBattery'] 87 + const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints'] 88 + const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation'] 89 + const useClamp: typeof import('@vueuse/core')['useClamp'] 90 + const useClipboard: typeof import('@vueuse/core')['useClipboard'] 91 + const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog'] 92 + const useCounter: typeof import('@vueuse/core')['useCounter'] 93 + const useCssModule: typeof import('vue')['useCssModule'] 94 + const useCssVar: typeof import('@vueuse/core')['useCssVar'] 95 + const useDark: typeof import('@vueuse/core')['useDark'] 96 + const useDebounce: typeof import('@vueuse/core')['useDebounce'] 97 + const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory'] 98 + const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn'] 99 + const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion'] 100 + const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation'] 101 + const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio'] 102 + const useDevicesList: typeof import('@vueuse/core')['useDevicesList'] 103 + const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia'] 104 + const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility'] 105 + const useDraggable: typeof import('@vueuse/core')['useDraggable'] 106 + const useElementBounding: typeof import('@vueuse/core')['useElementBounding'] 107 + const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint'] 108 + const useElementHover: typeof import('@vueuse/core')['useElementHover'] 109 + const useElementSize: typeof import('@vueuse/core')['useElementSize'] 110 + const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility'] 111 + const useEventBus: typeof import('@vueuse/core')['useEventBus'] 112 + const useEventListener: typeof import('@vueuse/core')['useEventListener'] 113 + const useEventSource: typeof import('@vueuse/core')['useEventSource'] 114 + const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper'] 115 + const useFavicon: typeof import('@vueuse/core')['useFavicon'] 116 + const useFetch: typeof import('@vueuse/core')['useFetch'] 117 + const useFocus: typeof import('@vueuse/core')['useFocus'] 118 + const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin'] 119 + const useFps: typeof import('@vueuse/core')['useFps'] 120 + const useFullscreen: typeof import('@vueuse/core')['useFullscreen'] 121 + const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] 122 + const useIdle: typeof import('@vueuse/core')['useIdle'] 123 + const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver'] 124 + const useInterval: typeof import('@vueuse/core')['useInterval'] 125 + const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn'] 126 + const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier'] 127 + const useLastChanged: typeof import('@vueuse/core')['useLastChanged'] 128 + const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage'] 129 + const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys'] 130 + const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory'] 131 + const useMediaControls: typeof import('@vueuse/core')['useMediaControls'] 132 + const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery'] 133 + const useMemory: typeof import('@vueuse/core')['useMemory'] 134 + const useMounted: typeof import('@vueuse/core')['useMounted'] 135 + const useMouse: typeof import('@vueuse/core')['useMouse'] 136 + const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement'] 137 + const useMousePressed: typeof import('@vueuse/core')['useMousePressed'] 138 + const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver'] 139 + const useNetwork: typeof import('@vueuse/core')['useNetwork'] 140 + const useNow: typeof import('@vueuse/core')['useNow'] 141 + const useOnline: typeof import('@vueuse/core')['useOnline'] 142 + const usePageLeave: typeof import('@vueuse/core')['usePageLeave'] 143 + const useParallax: typeof import('@vueuse/core')['useParallax'] 144 + const usePermission: typeof import('@vueuse/core')['usePermission'] 145 + const usePointer: typeof import('@vueuse/core')['usePointer'] 146 + const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe'] 147 + const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme'] 148 + const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark'] 149 + const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages'] 150 + const useRafFn: typeof import('@vueuse/core')['useRafFn'] 151 + const useRefHistory: typeof import('@vueuse/core')['useRefHistory'] 152 + const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver'] 153 + const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea'] 154 + const useScriptTag: typeof import('@vueuse/core')['useScriptTag'] 155 + const useScroll: typeof import('@vueuse/core')['useScroll'] 156 + const useScrollLock: typeof import('@vueuse/core')['useScrollLock'] 157 + const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage'] 158 + const useShare: typeof import('@vueuse/core')['useShare'] 159 + const useSlots: typeof import('vue')['useSlots'] 160 + const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition'] 161 + const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis'] 162 + const useStorage: typeof import('@vueuse/core')['useStorage'] 163 + const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync'] 164 + const useSwipe: typeof import('@vueuse/core')['useSwipe'] 165 + const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList'] 166 + const useTextSelection: typeof import('@vueuse/core')['useTextSelection'] 167 + const useThrottle: typeof import('@vueuse/core')['useThrottle'] 168 + const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory'] 169 + const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn'] 170 + const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo'] 171 + const useTimeout: typeof import('@vueuse/core')['useTimeout'] 172 + const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn'] 173 + const useTimestamp: typeof import('@vueuse/core')['useTimestamp'] 174 + const useTitle: typeof import('@vueuse/core')['useTitle'] 175 + const useToggle: typeof import('@vueuse/core')['useToggle'] 176 + const useTransition: typeof import('@vueuse/core')['useTransition'] 177 + const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams'] 178 + const useUserMedia: typeof import('@vueuse/core')['useUserMedia'] 179 + const useVirtualList: typeof import('@vueuse/core')['useVirtualList'] 180 + const useVModel: typeof import('@vueuse/core')['useVModel'] 181 + const useVModels: typeof import('@vueuse/core')['useVModels'] 182 + const useWakeLock: typeof import('@vueuse/core')['useWakeLock'] 183 + const useWebSocket: typeof import('@vueuse/core')['useWebSocket'] 184 + const useWebWorker: typeof import('@vueuse/core')['useWebWorker'] 185 + const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn'] 186 + const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus'] 187 + const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll'] 188 + const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] 189 + const watch: typeof import('vue')['watch'] 190 + const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] 191 + const watchEffect: typeof import('vue')['watchEffect'] 192 + const watchOnce: typeof import('@vueuse/core')['watchOnce'] 193 + const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter'] 194 + const whenever: typeof import('@vueuse/core')['whenever'] 195 + } 196 + export {}
+13
packages/ui/client/components.d.ts
··· 1 + // generated by unplugin-vue-components 2 + // We suggest you to commit this file into source control 3 + // Read more: https://github.com/vuejs/vue-next/pull/3399 4 + 5 + declare module 'vue' { 6 + export interface GlobalComponents { 7 + Navigation: typeof import('./components/Navigation.vue')['default'] 8 + Suites: typeof import('./components/Suites.vue')['default'] 9 + TestSuite: typeof import('./components/TestSuite.vue')['default'] 10 + } 11 + } 12 + 13 + export { }
+8
packages/ui/client/main.ts
··· 1 + import { createApp } from 'vue' 2 + import App from './App.vue' 3 + import 'uno.css' 4 + import '@unocss/reset/tailwind.css' 5 + import './styles/main.css' 6 + 7 + createApp(App) 8 + .mount('#app')
packages/ui/node/api.ts
+66
packages/ui/node/index.ts
··· 1 + import { resolve, dirname } from 'path' 2 + import { fileURLToPath } from 'url' 3 + import type { Plugin } from 'vite' 4 + import sirv from 'sirv' 5 + import { WebSocketServer } from 'ws' 6 + import { getSuitesAsJson } from './utils' 7 + 8 + const _dirname = typeof __dirname !== 'undefined' 9 + ? __dirname 10 + : dirname(fileURLToPath(import.meta.url)) 11 + 12 + export const VitestUIPlugin = (): Plugin => { 13 + return { 14 + name: 'vitest:ui', 15 + apply: 'serve', 16 + async configureServer(server) { 17 + // const vitest = process.__vitest__ 18 + const wss = new WebSocketServer({ noServer: true }) 19 + 20 + server.httpServer?.on('upgrade', (request, socket, head) => { 21 + if (request.url) { 22 + const { pathname } = new URL(request.url) 23 + 24 + if (pathname === '/__vitest_api') { 25 + wss.handleUpgrade(request, socket, head, (ws) => { 26 + wss.emit('connection', ws, request) 27 + 28 + /** 29 + * When user opens connection send inital list of tasks. 30 + */ 31 + ws.on('message', (data) => { 32 + 33 + }) 34 + }) 35 + } 36 + } 37 + }) 38 + 39 + server.middlewares.use('/__vitest_api', async(req, res, next) => { 40 + // const vitest = process.__vitest__ 41 + 42 + // const suites = getSuites(vitest.state.getFiles()) 43 + // .map(suite => ({ 44 + // id: suite.id, 45 + // name: suite.name, 46 + // type: suite.type, 47 + // mode: suite.mode, 48 + // result: suite.result, 49 + // })) 50 + 51 + // const info = { 52 + // suites, 53 + // } 54 + 55 + res.setHeader('Content-Type', 'application/json') 56 + res.write(getSuitesAsJson()) 57 + res.end() 58 + }) 59 + 60 + server.middlewares.use('/', sirv(resolve(_dirname, '../dist/client'), { 61 + single: true, 62 + dev: true, 63 + })) 64 + }, 65 + } 66 + }
+21
packages/ui/node/utils.ts
··· 1 + import { getSuites } from '../../../src/utils' 2 + 3 + const getCircularReplacer = () => { 4 + const seen = new WeakSet() 5 + return (key: any, value: any) => { 6 + if (typeof value === 'object' && value !== null) { 7 + if (seen.has(value)) 8 + return 9 + 10 + seen.add(value) 11 + } 12 + return value 13 + } 14 + } 15 + 16 + export const getSuitesAsJson = () => { 17 + const vitest = process.__vitest__ 18 + const suites = getSuites(vitest.state.getFiles()).filter(x => x) 19 + 20 + return JSON.stringify(suites, getCircularReplacer()) 21 + }
packages/ui/public/bg.png

This is a binary file and will not be displayed.

+5
packages/ui/public/favicon.svg
··· 1 + <svg width="165" height="165" viewBox="0 0 165 165" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 + <path d="M120.831 57.2543L84.693 109.505C84.3099 110.059 83.7558 110.474 83.1148 110.687C82.4738 110.9 81.7809 110.898 81.1412 110.684C80.5015 110.469 79.95 110.052 79.5702 109.497C79.1905 108.941 79.0032 108.277 79.037 107.606L80.4833 78.7582L57.1343 73.8064C56.6353 73.7007 56.1704 73.474 55.7807 73.1465C55.391 72.8191 55.0885 72.4009 54.9001 71.929C54.7117 71.4571 54.6432 70.9461 54.7006 70.4412C54.758 69.9364 54.9395 69.4532 55.2291 69.0345L91.3675 16.7837C91.7507 16.2294 92.3048 15.8145 92.9458 15.6018C93.5869 15.3891 94.2798 15.3902 94.9196 15.6051C95.5593 15.8199 96.1109 16.2367 96.4906 16.7923C96.8703 17.3478 97.0575 18.0117 97.0236 18.6833L95.5773 47.5314L118.926 52.4828C119.425 52.5885 119.89 52.8152 120.28 53.1426C120.67 53.4701 120.972 53.8883 121.16 54.3602C121.349 54.8321 121.417 55.3431 121.36 55.8479C121.303 56.3528 121.121 56.836 120.831 57.2547L120.831 57.2543Z" fill="#FCC72B"/> 3 + <path d="M82.9866 153.343C82.0254 153.344 81.0735 153.156 80.1855 152.788C79.2975 152.42 78.4909 151.88 77.8122 151.2L43.6658 117.056C42.2998 115.683 41.5341 113.824 41.5366 111.887C41.5392 109.95 42.3098 108.092 43.6796 106.723C45.0493 105.353 46.9064 104.582 48.8435 104.579C50.7807 104.577 52.6399 105.342 54.0134 106.708L82.9866 135.678L146.105 72.5626C147.481 71.2088 149.336 70.4536 151.266 70.4615C153.197 70.4693 155.046 71.2396 156.41 72.6045C157.775 73.9695 158.546 75.8184 158.554 77.7487C158.561 79.679 157.806 81.5342 156.452 82.9101L88.1597 151.2C87.4811 151.881 86.6747 152.42 85.7869 152.788C84.8992 153.156 83.9475 153.344 82.9866 153.343Z" fill="#729B1B"/> 4 + <path d="M82.9572 153.343C83.9184 153.344 84.8703 153.156 85.7583 152.788C86.6463 152.42 87.4528 151.88 88.1316 151.2L122.278 117.056C123.644 115.683 124.41 113.824 124.407 111.887C124.405 109.95 123.634 108.092 122.264 106.723C120.894 105.353 119.037 104.582 117.1 104.579C115.163 104.577 113.304 105.342 111.93 106.708L82.9572 135.678L19.8389 72.5626C18.4629 71.2088 16.6077 70.4536 14.6775 70.4615C12.7472 70.4693 10.8982 71.2396 9.53331 72.6045C8.16839 73.9695 7.39811 75.8184 7.39025 77.7487C7.38239 79.679 8.13759 81.5342 9.49135 82.9101L77.784 151.2C78.4627 151.881 79.2691 152.42 80.1568 152.788C81.0446 153.156 81.9963 153.344 82.9572 153.343Z" fill="#729B1B" fill-opacity="0.5"/> 5 + </svg>
+46
packages/ui/client/components/Navigation.vue
··· 1 + <script setup lang="ts"> 2 + import { isDark, toggleDark } from '~/composables' 3 + </script> 4 + 5 + <template> 6 + <nav 7 + top-4 8 + left-4 9 + bottom-4 10 + fixed 11 + bg-light-300 12 + dark:bg-dark-600 13 + w-72 14 + border-1 15 + border-rounded 16 + border-light-900 17 + dark:border-dark-200 18 + flex 19 + flex-col 20 + > 21 + <div 22 + grid="~ cols-[max-content,1fr,min-content]" 23 + gap-2 24 + items-center 25 + px-4 26 + h-14 27 + border-b-1 28 + border-light-900 29 + dark:border-dark-200 30 + > 31 + <img w-6 h-6 src="/favicon.svg"> 32 + <span text-lg font-light>Vitest</span> 33 + <button 34 + text-xl 35 + text-dark-100 36 + dark:text-light-900 37 + :class="{ 38 + 'i-carbon-moon': isDark, 39 + 'i-carbon-sun': !isDark 40 + }" 41 + @click="toggleDark" 42 + /> 43 + </div> 44 + <Suites /> 45 + </nav> 46 + </template>
+21
packages/ui/client/components/Suites.vue
··· 1 + <script setup lang="ts"> 2 + const { status, data, send } = useWebSocket('ws://localhost:3000/__vitest_api') 3 + </script> 4 + 5 + <template> 6 + <div overflow-auto flex-1> 7 + <div 8 + h-8 9 + px-4 10 + flex 11 + flex-row 12 + items-center 13 + bg-dark-300 14 + > 15 + <span font-light text-sm flex-1>Test Suites</span> 16 + <button i-carbon-play /> 17 + </div> 18 + {{ status }} 19 + {{ data }} 20 + </div> 21 + </template>
+31
packages/ui/client/components/TestSuite.vue
··· 1 + <script setup lang="ts"> 2 + defineProps<{ 3 + id: string 4 + name: string 5 + type: string 6 + mode: string 7 + result: { 8 + start: number 9 + state: string 10 + end: number 11 + } 12 + }>() 13 + </script> 14 + 15 + <template> 16 + <div 17 + v-if="name" 18 + flex 19 + flex-row 20 + items-center 21 + px-4 22 + h-12 23 + border-rounded 24 + > 25 + <span text-green-500 i-carbon:checkmark-outline mr-4 block text-xl /> 26 + <div flex flex-col> 27 + <span text-sm>{{ name }}</span> 28 + <span text-xs text-light-900 text-opacity-50>Took {{ Math.round(result.end - result.start) }}ms</span> 29 + </div> 30 + </div> 31 + </template>
+2
packages/ui/client/composables/dark.ts
··· 1 + export const isDark = useDark() 2 + export const toggleDark = useToggle(isDark)
+1
packages/ui/client/composables/index.ts
··· 1 + export * from './dark'
+11
packages/ui/client/styles/main.css
··· 1 + html, 2 + body { 3 + height: 100%; 4 + font-family: 'Readex Pro', sans-serif; 5 + } 6 + 7 + html.dark { 8 + color: white; 9 + background-color: #141414; 10 + color-scheme: dark; 11 + }