diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 12cd4a41..5bee9a84 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,6 +1,7 @@
### 在提交PR之前,请确保你执行以下操作:
+
- [ ] 曾阅读过[翻译须知](https://github.com/vitest-dev/docs-cn/issues/391)。
- [ ] 检查是否已经有PR以同样的方式解决问题,以避免创建重复。
- [ ] 在此PR中描述正在解决的问题,或引用它解决的问题(例如:`fixes #123`)。
@@ -8,7 +9,9 @@
---
### 描述
+
### 附加上下文
+
diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml
index e6e8d01d..e8102065 100644
--- a/.github/workflows/autofix.yml
+++ b/.github/workflows/autofix.yml
@@ -25,6 +25,6 @@ jobs:
run: nci
- name: Lint
- run: nr lint --fix
+ run: nr lint:fix
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27
diff --git a/.oxfmtrc.jsonc b/.oxfmtrc.jsonc
new file mode 100644
index 00000000..065c6dcb
--- /dev/null
+++ b/.oxfmtrc.jsonc
@@ -0,0 +1,48 @@
+{
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
+ "semi": false,
+ "singleQuote": true,
+ "trailingComma": "all",
+ "sortPackageJson": false,
+ "sortImports": {
+ "groups": [
+ "type-import",
+ ["type-parent", "type-sibling", "type-index", "type-internal"],
+ "value-builtin",
+ "value-external",
+ "value-internal",
+ ["value-parent", "value-sibling", "value-index"],
+ "side_effect",
+ "unknown",
+ ],
+ "newlinesBetween": false,
+ },
+ "overrides": [
+ {
+ // code blocks in the docs are written by hand to look good
+ "files": ["**/*.md"],
+ "options": { "embeddedLanguageFormatting": "off" },
+ },
+ ],
+ "ignorePatterns": [
+ "**/fixtures",
+ "**/*.timestamp-*",
+ "pnpm-lock.yaml",
+ // generated by the build
+ "packages/*/LICENSE.md",
+ "docs/guide/cli-generated.md",
+ "docs/.vitepress/contributor-names.json",
+ "packages/browser/src/client/public/esm-client-injector.js",
+ "test/unit/src/wasm/wasm-bindgen*",
+ // test inputs that depend on the exact source text
+ "test/unit/src/self",
+ "test/unit/test/mocking/already-hoisted.test.ts",
+ "test/unit/test/tab-effect.spec.mjs",
+ "test/unit/test/mocking/external.test.ts",
+ "test/workspaces/results.json",
+ "test/workspaces-browser/results.json",
+ "test/e2e/deps/error",
+ "test/e2e/deps/malformed-source-map",
+ "test/e2e/deps/optimizer/unparsable",
+ ],
+}
diff --git a/.oxlintrc.jsonc b/.oxlintrc.jsonc
new file mode 100644
index 00000000..39ccff8f
--- /dev/null
+++ b/.oxlintrc.jsonc
@@ -0,0 +1,365 @@
+{
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
+ "plugins": ["typescript", "unicorn", "oxc", "import", "node", "vue", "jsdoc"],
+ "env": {
+ "browser": true,
+ "node": true,
+ },
+ "options": {
+ "reportUnusedDisableDirectives": "error",
+ },
+ "ignorePatterns": [
+ "**/fixtures",
+ "**/*.d.ts",
+ "**/*.timestamp-*",
+ "packages/browser/src/client/public/esm-client-injector.js",
+ "test/unit/src/wasm/wasm-bindgen*",
+ "test/unit/src/self",
+ "test/unit/test/mocking/already-hoisted.test.ts",
+ "test/*/deps",
+ ],
+ "categories": {
+ "correctness": "error",
+ },
+ "rules": {
+ "accessor-pairs": "error",
+ "array-callback-return": "error",
+ "block-scoped-var": "error",
+ "curly": ["error", "all"],
+ "default-case-last": "error",
+ "dot-notation": "error",
+ "eqeqeq": ["error", "smart"],
+ "new-cap": ["error", { "capIsNew": false }],
+ "no-alert": "error",
+ "no-array-constructor": "error",
+ "no-async-promise-executor": "error",
+ "no-caller": "error",
+ "no-case-declarations": "error",
+ "no-compare-neg-zero": "error",
+ "no-cond-assign": ["error", "always"],
+ "no-console": ["error", { "allow": ["warn", "error"] }],
+ "no-control-regex": "error",
+ "no-debugger": "error",
+ "no-delete-var": "error",
+ "no-dupe-class-members": "error",
+ "no-duplicate-case": "error",
+ "no-empty": ["error", { "allowEmptyCatch": true }],
+ "no-eval": "error",
+ "no-ex-assign": "error",
+ "no-extend-native": "error",
+ "no-extra-bind": "error",
+ "no-extra-boolean-cast": "error",
+ "no-fallthrough": "error",
+ "no-global-assign": "error",
+ "no-implied-eval": "error",
+ "no-irregular-whitespace": "error",
+ "no-iterator": "error",
+ "no-labels": "error",
+ "no-lone-blocks": "error",
+ "no-loss-of-precision": "error",
+ "no-misleading-character-class": "error",
+ "no-multi-str": "error",
+ "no-new": "error",
+ "no-new-func": "error",
+ "no-new-wrappers": "error",
+ "no-proto": "error",
+ "no-prototype-builtins": "error",
+ "no-redeclare": "error",
+ "no-regex-spaces": "error",
+ "no-restricted-globals": [
+ "error",
+ { "name": "global", "message": "Use `globalThis` instead." },
+ { "name": "self", "message": "Use `globalThis` instead." },
+ ],
+ "no-restricted-imports": ["error", { "paths": ["path"] }],
+ "no-restricted-properties": [
+ "error",
+ {
+ "property": "__proto__",
+ "message": "Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.",
+ },
+ { "property": "__defineGetter__", "message": "Use `Object.defineProperty` instead." },
+ { "property": "__defineSetter__", "message": "Use `Object.defineProperty` instead." },
+ {
+ "property": "__lookupGetter__",
+ "message": "Use `Object.getOwnPropertyDescriptor` instead.",
+ },
+ {
+ "property": "__lookupSetter__",
+ "message": "Use `Object.getOwnPropertyDescriptor` instead.",
+ },
+ ],
+ "no-self-assign": "error",
+ "no-self-compare": "error",
+ "no-sequences": "error",
+ "no-shadow-restricted-names": "error",
+ "no-sparse-arrays": "error",
+ "no-template-curly-in-string": "error",
+ "no-throw-literal": "error",
+ "no-unexpected-multiline": "error",
+ "no-unmodified-loop-condition": "error",
+ "no-unneeded-ternary": ["error", { "defaultAssignment": false }],
+ "no-unreachable-loop": "error",
+ "no-unsafe-finally": "error",
+ "no-unused-expressions": [
+ "error",
+ { "allowShortCircuit": true, "allowTaggedTemplates": true, "allowTernary": true },
+ ],
+ "no-unused-vars": [
+ "error",
+ {
+ "args": "after-used",
+ "argsIgnorePattern": "^_",
+ "ignoreRestSiblings": true,
+ "vars": "all",
+ "varsIgnorePattern": "^_",
+ },
+ ],
+ "no-use-before-define": ["error", { "classes": false, "functions": false, "variables": true }],
+ "no-useless-call": "error",
+ "no-useless-catch": "error",
+ "no-useless-computed-key": "error",
+ "no-useless-rename": "error",
+ "no-useless-return": "error",
+ "no-var": "error",
+ "object-shorthand": ["error", "always", { "avoidQuotes": true }],
+ "one-var": ["error", { "initialized": "never" }],
+ "prefer-arrow-callback": ["error", { "allowUnboundThis": true }],
+ "prefer-const": ["error", { "destructuring": "all", "ignoreReadBeforeAssign": true }],
+ "prefer-exponentiation-operator": "error",
+ "prefer-object-has-own": "error",
+ "prefer-promise-reject-errors": "error",
+ "prefer-regex-literals": ["error", { "disallowRedundantWrapping": true }],
+ "prefer-rest-params": "error",
+ "prefer-spread": "error",
+ "prefer-template": "error",
+ "symbol-description": "error",
+ "unicode-bom": "error",
+ "use-isnan": ["error", { "enforceForIndexOf": true, "enforceForSwitchCase": true }],
+ "valid-typeof": ["error", { "requireStringLiterals": true }],
+ "vars-on-top": "error",
+ "yoda": "error",
+
+ "typescript/ban-ts-comment": ["error", { "ts-expect-error": "allow-with-description" }],
+ "typescript/consistent-type-definitions": ["error", "interface"],
+ "typescript/consistent-type-imports": [
+ "error",
+ {
+ "prefer": "type-imports",
+ "fixStyle": "separate-type-imports",
+ "disallowTypeAnnotations": false,
+ },
+ ],
+ "typescript/method-signature-style": ["error", "property"],
+ "typescript/no-duplicate-enum-values": "error",
+ "typescript/no-empty-object-type": ["error", { "allowInterfaces": "always" }],
+ "typescript/no-extra-non-null-assertion": "error",
+ "typescript/no-import-type-side-effects": "error",
+ "typescript/no-misused-new": "error",
+ "typescript/no-namespace": "error",
+ "typescript/no-non-null-asserted-nullish-coalescing": "error",
+ "typescript/no-non-null-asserted-optional-chain": "error",
+ "typescript/no-require-imports": "error",
+ "typescript/no-this-alias": "error",
+ "typescript/no-unnecessary-type-constraint": "error",
+ "typescript/no-unsafe-declaration-merging": "error",
+ "typescript/no-wrapper-object-types": "error",
+ "typescript/prefer-as-const": "error",
+ "typescript/prefer-literal-enum-member": "error",
+ "typescript/prefer-namespace-keyword": "error",
+
+ "unicorn/consistent-empty-array-spread": "error",
+ "unicorn/error-message": "error",
+ "unicorn/escape-case": "error",
+ "unicorn/new-for-builtins": "error",
+ "unicorn/no-instanceof-builtins": ["error", { "strategy": "loose" }],
+ "unicorn/no-new-array": "error",
+ "unicorn/no-new-buffer": "error",
+ "unicorn/prefer-array-some": "error",
+ "unicorn/prefer-date-now": "error",
+ "unicorn/prefer-dom-node-text-content": "error",
+ "unicorn/prefer-includes": "error",
+ "unicorn/prefer-node-protocol": "error",
+ "unicorn/prefer-number-properties": ["error", { "checkInfinity": false, "checkNaN": false }],
+ "unicorn/prefer-regexp-test": "error",
+ "unicorn/prefer-string-starts-ends-with": "error",
+ "unicorn/prefer-type-error": "error",
+ "unicorn/throw-new-error": "error",
+
+ "import/consistent-type-specifier-style": ["error", "prefer-top-level"],
+ "import/first": "error",
+ "import/no-duplicates": "error",
+ "import/no-mutable-exports": "error",
+ "import/no-named-default": "error",
+
+ "node/handle-callback-err": ["error", "^(err|error)$"],
+ "node/no-exports-assign": "error",
+ "node/no-new-require": "error",
+ "node/no-path-concat": "error",
+ "node/no-top-level-await": "error",
+
+ "vue/component-definition-name-casing": "warn",
+ "vue/no-arrow-functions-in-watch": "error",
+ "vue/no-async-in-computed-properties": "error",
+ "vue/no-computed-properties-in-data": "error",
+ "vue/no-deprecated-data-object-declaration": "error",
+ "vue/no-deprecated-delete-set": "error",
+ "vue/no-deprecated-destroyed-lifecycle": "error",
+ "vue/no-deprecated-events-api": "error",
+ "vue/no-deprecated-model-definition": "error",
+ "vue/no-deprecated-props-default-this": "error",
+ "vue/no-deprecated-vue-config-keycodes": "error",
+ "vue/no-dupe-keys": "error",
+ "vue/no-export-in-script-setup": "error",
+ "vue/no-expose-after-await": "error",
+ "vue/no-lifecycle-after-await": "error",
+ "vue/no-multiple-slot-args": "warn",
+ "vue/no-required-prop-with-default": "warn",
+ "vue/no-reserved-component-names": "error",
+ "vue/no-reserved-keys": "error",
+ "vue/no-reserved-props": "error",
+ "vue/no-shared-component-data": "error",
+ "vue/no-side-effects-in-computed-properties": "error",
+ "vue/no-watch-after-await": "error",
+ "vue/prefer-import-from-vue": "error",
+ "vue/prop-name-casing": ["error", "camelCase"],
+ "vue/require-prop-type-constructor": "error",
+ "vue/require-render-return": "error",
+ "vue/require-slots-as-functions": "error",
+ "vue/return-in-computed-property": "error",
+ "vue/return-in-emits-validator": "error",
+ "vue/valid-define-emits": "error",
+ "vue/valid-define-options": "error",
+ "vue/valid-define-props": "error",
+ "vue/valid-next-tick": "error",
+
+ "jsdoc/check-access": "warn",
+ "jsdoc/check-property-names": "warn",
+ "jsdoc/empty-tags": "warn",
+ "jsdoc/implements-on-classes": "warn",
+ "jsdoc/no-defaults": "warn",
+ "jsdoc/require-param-name": "warn",
+ "jsdoc/require-property": "warn",
+ "jsdoc/require-property-description": "warn",
+ "jsdoc/require-property-name": "warn",
+ "jsdoc/require-returns-description": "warn",
+
+ // part of the correctness category, but not wanted here
+ "no-constant-condition": "off",
+ "no-empty-pattern": "off",
+ "import/default": "off",
+ "import/namespace": "off",
+ "jsdoc/check-tag-names": "off",
+ "jsdoc/require-yields": "off",
+ "typescript/no-useless-empty-export": "off",
+ "unicorn/no-empty-file": "off",
+ "unicorn/no-thenable": "off",
+ "unicorn/no-useless-spread": "off",
+ // the formatter lowercases hex digits
+ "unicorn/number-literal-case": "off",
+ },
+ "overrides": [
+ {
+ "files": ["packages/**"],
+ "rules": {
+ "no-restricted-imports": [
+ "error",
+ {
+ "paths": [
+ {
+ "name": "vitest",
+ "message": "Importing from vitest is restricted. Re-check the import and import the source directly instead if it's really needed.",
+ },
+ { "name": "path", "message": "Import from pathe instead" },
+ {
+ "name": "vitest/node",
+ "message": "Importing from vitest/node is restricted. Re-check the import and import the source directly instead if it's really needed.",
+ },
+ ],
+ },
+ ],
+ },
+ },
+ {
+ // these packages declare vitest as a peer dependency
+ "files": ["packages/{coverage-*,ui,browser,web-worker,browser-*}/**"],
+ "rules": {
+ "no-restricted-imports": ["error", { "paths": ["path"] }],
+ },
+ },
+ {
+ "files": ["packages/browser/src/client/orchestrator.ts"],
+ "rules": {
+ "no-restricted-imports": ["error", { "paths": ["vitest/internal/browser", "vitest/node"] }],
+ },
+ },
+ {
+ // ivya must stay in a single rollup chunk, see the browser rollup config
+ "files": ["packages/browser/**"],
+ "excludeFiles": [
+ "packages/browser/src/vendor-types.ts",
+ "packages/browser/src/client/tester/aria.ts",
+ "packages/browser/src/client/tester/locators.ts",
+ "packages/browser/src/client/tester/expect/**",
+ "packages/browser/src/client/utils.ts",
+ ],
+ "rules": {
+ "no-restricted-imports": ["error", { "paths": ["ivya", "ivya/utils", "ivya/aria"] }],
+ },
+ },
+ {
+ "files": ["test/unit/**"],
+ "rules": {
+ "no-restricted-imports": [
+ "error",
+ {
+ "paths": ["path"],
+ "patterns": [
+ {
+ "group": ["**/test-utils", "**/test-utils/**"],
+ "importNames": ["runVitest", "runInlineTests", "runVitestCli"],
+ "message": "Tests that start Vitest belong in test/e2e.",
+ },
+ ],
+ },
+ ],
+ },
+ },
+ {
+ "files": ["docs/**", "packages/web-worker/**", "test/unit/**"],
+ "rules": {
+ "no-restricted-globals": "off",
+ },
+ },
+ {
+ "files": ["docs/**"],
+ "rules": {
+ "prefer-arrow-callback": "off",
+ "typescript/method-signature-style": "off",
+ "no-self-compare": "off",
+ "no-throw-literal": "off",
+ "no-unused-vars": "off",
+ "import/no-duplicates": "off",
+ "import/no-mutable-exports": "off",
+ },
+ },
+ {
+ "files": ["test/**", "scripts/**", "docs/**", "examples/**", "**/*.config.*"],
+ "rules": {
+ "node/no-top-level-await": "off",
+ },
+ },
+ {
+ "files": ["**/scripts/**", "**/cli.*", "**/cli/**"],
+ "rules": {
+ "no-console": "off",
+ },
+ },
+ {
+ "files": ["**/*.cjs"],
+ "rules": {
+ "typescript/no-require-imports": "off",
+ },
+ },
+ ],
+}
diff --git a/.vitepress/blog.data.ts b/.vitepress/blog.data.ts
index 8aa4dc4a..d9799a45 100644
--- a/.vitepress/blog.data.ts
+++ b/.vitepress/blog.data.ts
@@ -17,8 +17,7 @@ export default createContentLoader('blog/*.md', {
transform(raw): Post[] {
return raw
.map(({ url, frontmatter }) => ({
- title: frontmatter.head.find((e: any) => e[1].property === 'og:title')[1]
- .content,
+ title: frontmatter.head.find((e: any) => e[1].property === 'og:title')[1].content,
url,
date: formatDate(frontmatter.date),
}))
diff --git a/.vitepress/components/Advanced.vue b/.vitepress/components/Advanced.vue
index 58ef886d..6a784df3 100644
--- a/.vitepress/components/Advanced.vue
+++ b/.vitepress/components/Advanced.vue
@@ -1,5 +1,9 @@
-
+
高级 API
diff --git a/.vitepress/components/BlogIndex.vue b/.vitepress/components/BlogIndex.vue
index b702f161..95244ffb 100644
--- a/.vitepress/components/BlogIndex.vue
+++ b/.vitepress/components/BlogIndex.vue
@@ -10,9 +10,7 @@ function getDateTime(time: number) {
- {{
- post.date.string
- }}
+ {{ post.date.string }}
diff --git a/.vitepress/components/CRoot.vue b/.vitepress/components/CRoot.vue
index b01d6508..464f4c5f 100644
--- a/.vitepress/components/CRoot.vue
+++ b/.vitepress/components/CRoot.vue
@@ -3,7 +3,11 @@ import { Icon } from '@iconify/vue'
-
+
diff --git a/.vitepress/components/ChangelogButton.vue b/.vitepress/components/ChangelogButton.vue
index 59f37c9c..a78fa125 100644
--- a/.vitepress/components/ChangelogButton.vue
+++ b/.vitepress/components/ChangelogButton.vue
@@ -7,7 +7,7 @@ defineProps<{
-
+
Changelog
@@ -15,42 +15,8 @@ defineProps<{
diff --git a/.vitepress/components/CopyPrompt.vue b/.vitepress/components/CopyPrompt.vue
index 5ff18d37..7cc1b209 100644
--- a/.vitepress/components/CopyPrompt.vue
+++ b/.vitepress/components/CopyPrompt.vue
@@ -7,59 +7,30 @@ const props = defineProps<{
}>()
const state = ref<'idle' | 'copied' | 'error'>('idle')
-const label = computed(() => state.value === 'copied' ? 'Copied' : 'Copy prompt')
+const label = computed(() => (state.value === 'copied' ? 'Copied' : 'Copy prompt'))
async function copyPrompt() {
try {
await navigator.clipboard.writeText(props.prompt)
state.value = 'copied'
- }
- catch {
+ } catch {
state.value = 'error'
}
}
-
+
{{ label }}
- {{ state === 'copied' ? 'Prompt copied to clipboard.' : state === 'error' ? 'Could not copy prompt.' : '' }}
+ {{
+ state === 'copied'
+ ? 'Prompt copied to clipboard.'
+ : state === 'error'
+ ? 'Could not copy prompt.'
+ : ''
+ }}
-
-
diff --git a/.vitepress/components/Deprecated.vue b/.vitepress/components/Deprecated.vue
index 482e42ed..f581b0ad 100644
--- a/.vitepress/components/Deprecated.vue
+++ b/.vitepress/components/Deprecated.vue
@@ -1,5 +1,3 @@
-
- 弃用
-
+ 弃用
diff --git a/.vitepress/components/Experimental.vue b/.vitepress/components/Experimental.vue
index 5d371371..df1db5ed 100644
--- a/.vitepress/components/Experimental.vue
+++ b/.vitepress/components/Experimental.vue
@@ -1,5 +1,5 @@
-
+
实验性
diff --git a/.vitepress/components/FeaturesList.vue b/.vitepress/components/FeaturesList.vue
index 3c211264..8e00ffe1 100644
--- a/.vitepress/components/FeaturesList.vue
+++ b/.vitepress/components/FeaturesList.vue
@@ -16,16 +16,23 @@ import ListItem from './ListItem.vue'
ESM 优先,支持模块顶级 await
通过
- Tinypool
+ Tinypool
使用 Worker 线程并行执行测试
使用
- Tinybench
+ Tinybench
支持基准测试
支持套件和测试的过滤、超时、并发配置
- 支持 Projects / Workspace
+ 支持 Projects /
+ Workspace
Jest 快照功能
@@ -33,23 +40,36 @@ import ListItem from './ListItem.vue'
内置
Chai
进行断言,并兼容
- Jest expect API
-
-
- 兼容 Jest 对象模拟
+ Jest expect
+ API
+ 兼容 Jest 对象模拟
使用
- jsdom 或
- happy-dom
+ jsdom
+ 或
+ happy-dom
模拟 DOM
- 浏览器模式 :在浏览器中运行组件测试
+ 浏览器模式 :在浏览器中运行组件测试
通过
- v8
+ v8
或
- istanbul 输出代码覆盖率
+ istanbul
+ 输出代码覆盖率
类似 Rust 的
@@ -57,7 +77,9 @@ import ListItem from './ListItem.vue'
使用
- expect-type
+ expect-type
进行类型测试
支持分片执行
diff --git a/.vitepress/components/HomePage.vue b/.vitepress/components/HomePage.vue
new file mode 100644
index 00000000..dca0aa5c
--- /dev/null
+++ b/.vitepress/components/HomePage.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.vitepress/components/ListItem.vue b/.vitepress/components/ListItem.vue
index 28154971..86c0aafb 100644
--- a/.vitepress/components/ListItem.vue
+++ b/.vitepress/components/ListItem.vue
@@ -8,21 +8,25 @@ const state = ref(0)
function reset() {
state.value = 0
- setTimeout(() => {
- state.value = Math.random() > 0.9 ? 2 : 1
- if (state.value === 2) {
- setTimeout(reset, 1000)
- }
- }, Math.round(Math.random() * 3000) + 400)
+ setTimeout(
+ () => {
+ state.value = Math.random() > 0.9 ? 2 : 1
+ if (state.value === 2) {
+ setTimeout(reset, 1000)
+ }
+ },
+ Math.round(Math.random() * 3000) + 400,
+ )
}
const color = computed(() => {
return {
- '--vp-c-brand-1': state.value === 1
- ? 'var(--color-brand)'
- : state.value === 2
- ? 'var(--vp-c-red-1)'
- : 'var(--vp-c-yellow-1)',
+ '--vp-c-brand-1':
+ state.value === 1
+ ? 'var(--color-brand)'
+ : state.value === 2
+ ? 'var(--vp-c-red-1)'
+ : 'var(--vp-c-yellow-1)',
} as any
})
@@ -42,8 +46,22 @@ onMounted(async () => {
-
-
+
+
diff --git a/.vitepress/components/MoonPhase.vue b/.vitepress/components/MoonPhase.vue
index c8b64d55..bd601a5f 100644
--- a/.vitepress/components/MoonPhase.vue
+++ b/.vitepress/components/MoonPhase.vue
@@ -10,9 +10,7 @@ const phases = [
{ emoji: '🌘', alt: 'waning crescent moon' },
]
-const phase = phases[
- Math.floor(Math.random() * phases.length)
-]
+const phase = phases[Math.floor(Math.random() * phases.length)]
diff --git a/.vitepress/components/Version.vue b/.vitepress/components/Version.vue
index 544d10a8..aa005afd 100644
--- a/.vitepress/components/Version.vue
+++ b/.vitepress/components/Version.vue
@@ -9,7 +9,11 @@ const { type = 'stable' } = defineProps<{
+
diff --git a/.vitepress/config.ts b/.vitepress/config.ts
index 69d1c470..16617faa 100644
--- a/.vitepress/config.ts
+++ b/.vitepress/config.ts
@@ -3,10 +3,7 @@ import { transformerTwoslash } from '@shikijs/vitepress-twoslash'
import { withPwa } from '@vite-pwa/vitepress'
import { extendConfig } from '@voidzero-dev/vitepress-theme/config'
import { defineConfig } from 'vitepress'
-import {
- groupIconMdPlugin,
- groupIconVitePlugin,
-} from 'vitepress-plugin-group-icons'
+import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
import { version } from '../package.json'
import { teamMembers } from './contributors'
@@ -27,173 +24,200 @@ import { pwa } from './scripts/pwa'
import { transformHead } from './scripts/transformHead'
export default ({ mode }: { mode: string }) => {
- return withPwa(extendConfig(defineConfig({
- lang: 'en-US',
- title: vitestName,
- description: vitestDescription,
- srcExclude: [
- '**/guide/examples/*',
- '**/guide/cli-generated.md',
- 'AGENTS.md',
- ],
- locales: {
- root: {
- label: '简体中文',
- lang: 'zh',
- },
- zh: {
- label: 'English',
+ return withPwa(
+ extendConfig(
+ defineConfig({
lang: 'en-US',
- link: 'https://vitest.dev/',
- },
- },
- head: [
- ['meta', { name: 'theme-color', content: '#22FF84' }],
- ['link', { rel: 'icon', href: '/favicon.ico', sizes: '48x48' }],
- ['link', { rel: 'icon', href: '/logo-without-border.svg', type: 'image/svg+xml' }],
- ['meta', { name: 'author', content: `${teamMembers.map(c => c.name).join(', ')} and ${vitestName} contributors` }],
- ['meta', { name: 'keywords', content: 'vitest, vite, test, coverage, snapshot, react, vue, preact, svelte, solid, lit, marko, ruby, cypress, puppeteer, jsdom, happy-dom, test-runner, jest, typescript, esm, node' }],
- ['meta', { property: 'og:title', content: vitestName }],
- ['meta', { property: 'og:description', content: vitestDescription }],
- ['meta', { property: 'og:url', content: ogUrl }],
- ['meta', { property: 'og:image', content: ogImage }],
- ['meta', { name: 'twitter:card', content: 'summary_large_image' }],
- ['link', { rel: 'preload', as: 'style', onload: 'this.onload=null;this.rel=\'stylesheet\'', href: font }],
- ['noscript', {}, ` `],
- ['link', { rel: 'me', href: 'https://m.webtoo.ls/@vitest' }],
- ['link', { rel: 'mask-icon', href: '/logo.svg', color: '#ffffff' }],
- ['link', { rel: 'apple-touch-icon', href: '/apple-touch-icon.png', sizes: '180x180' }],
- ],
- lastUpdated: true,
- vite: {
- plugins: [
- groupIconVitePlugin({
- customIcon: {
- 'CLI': 'vscode-icons:file-type-shell',
- '.spec.ts': 'vscode-icons:file-type-testts',
- '.test.ts': 'vscode-icons:file-type-testts',
- '.spec.js': 'vscode-icons:file-type-testjs',
- '.test.js': 'vscode-icons:file-type-testjs',
- 'next': '',
+ title: vitestName,
+ description: vitestDescription,
+ cleanUrls: true,
+ srcExclude: ['**/guide/examples/*', '**/guide/cli-generated.md', 'AGENTS.md'],
+ locales: {
+ root: {
+ label: 'English',
+ lang: 'en-US',
+ },
+ zh: {
+ label: '简体中文',
+ lang: 'zh',
+ link: 'https://cn.vitest.dev/',
},
- }) as any,
- ],
- define: {
- __VITEST_VERSION__: JSON.stringify(version),
- },
- },
- markdown: {
- container: {
- tipLabel: '提示',
- warningLabel: '注意',
- dangerLabel: '警告',
- infoLabel: '说明',
- detailsLabel: '详情',
- },
- config(md) {
- md.use(tabsMarkdownPlugin)
- md.use(groupIconMdPlugin)
- },
- theme: {
- light: 'github-light',
- dark: 'github-dark',
- },
- codeTransformers: mode === 'development'
- ? [transformerNotationWordHighlight()]
- : [
- transformerNotationWordHighlight(),
- transformerTwoslash({
- processHoverInfo: (info) => {
- if (info.includes(process.cwd())) {
- return info.replace(new RegExp(process.cwd(), 'g'), '')
- }
- return info
+ },
+ head: [
+ ['meta', { name: 'theme-color', content: '#22FF84' }],
+ ['link', { rel: 'icon', href: '/favicon.ico', sizes: '48x48' }],
+ ['link', { rel: 'icon', href: '/logo-without-border.svg', type: 'image/svg+xml' }],
+ [
+ 'meta',
+ {
+ name: 'author',
+ content: `${teamMembers.map((c) => c.name).join(', ')} and ${vitestName} contributors`,
+ },
+ ],
+ [
+ 'meta',
+ {
+ name: 'keywords',
+ content:
+ 'vitest, vite, test, coverage, snapshot, react, vue, preact, svelte, solid, lit, marko, ruby, cypress, puppeteer, jsdom, happy-dom, test-runner, jest, typescript, esm, node',
+ },
+ ],
+ ['meta', { property: 'og:title', content: vitestName }],
+ ['meta', { property: 'og:description', content: vitestDescription }],
+ ['meta', { property: 'og:url', content: ogUrl }],
+ ['meta', { property: 'og:image', content: ogImage }],
+ ['meta', { name: 'twitter:card', content: 'summary_large_image' }],
+ [
+ 'link',
+ {
+ rel: 'preload',
+ as: 'style',
+ onload: "this.onload=null;this.rel='stylesheet'",
+ href: font,
+ },
+ ],
+ ['noscript', {}, ` `],
+ ['link', { rel: 'me', href: 'https://m.webtoo.ls/@vitest' }],
+ ['link', { rel: 'mask-icon', href: '/logo.svg', color: '#ffffff' }],
+ ['link', { rel: 'apple-touch-icon', href: '/apple-touch-icon.png', sizes: '180x180' }],
+ ],
+ lastUpdated: true,
+ vite: {
+ plugins: [
+ groupIconVitePlugin({
+ customIcon: {
+ CLI: 'vscode-icons:file-type-shell',
+ '.spec.ts': 'vscode-icons:file-type-testts',
+ '.test.ts': 'vscode-icons:file-type-testts',
+ '.spec.js': 'vscode-icons:file-type-testjs',
+ '.test.js': 'vscode-icons:file-type-testjs',
+ next: '',
},
}),
],
- languages: ['js', 'jsx', 'ts', 'tsx'],
- },
- themeConfig: {
- variant: 'vitest',
- logo: '/logo.svg',
- lastUpdatedText: '最后更新',
- docFooter: {
- prev: '上一页',
- next: '下一页',
- },
- editLink: {
- pattern: 'https://github.com/vitest-dev/docs-cn/edit/dev/:path',
- text: '在 GitHub 上编辑此页面',
- },
+ define: {
+ __VITEST_VERSION__: JSON.stringify(version),
+ },
+ },
+ markdown: {
+ container: {
+ tipLabel: '提示',
+ warningLabel: '注意',
+ dangerLabel: '警告',
+ infoLabel: '说明',
+ detailsLabel: '详情',
+ },
+ config(md) {
+ md.use(tabsMarkdownPlugin)
+ md.use(groupIconMdPlugin)
+ },
+ theme: {
+ light: 'github-light',
+ dark: 'github-dark',
+ },
+ codeTransformers:
+ mode === 'development'
+ ? [transformerNotationWordHighlight()]
+ : [
+ transformerNotationWordHighlight(),
+ transformerTwoslash({
+ // resolve types from source, so the docs don't need built packages
+ twoslashOptions: {
+ compilerOptions: {
+ moduleResolution: 100 /* Bundler */,
+ customConditions: ['__vitest_source__'],
+ },
+ },
+ processHoverInfo: (info) => {
+ if (info.includes(process.cwd())) {
+ return info.replace(new RegExp(process.cwd(), 'g'), '')
+ }
+ return info
+ },
+ }),
+ ],
+ languages: ['js', 'jsx', 'ts', 'tsx'],
+ },
+ themeConfig: {
+ variant: 'vitest',
+ logo: '/logo.svg',
+ docFooter: {
+ prev: '上一页',
+ next: '下一页',
+ },
+ editLink: {
+ pattern: 'https://github.com/vitest-dev/docs-cn/edit/dev/:path',
+ text: '在 GitHub 上编辑此页面',
+ },
- search: {
- provider: 'local',
- options: {
- translations: {
- button: {
- buttonText: '搜索',
- },
- modal: {
- resetButtonTitle: '清除查询条件',
- noResultsText: '无法找到相关结果',
- footer: {
- selectText: '选择',
- navigateText: '切换',
- closeText: '关闭',
+ search: {
+ provider: 'local',
+ options: {
+ translations: {
+ button: {
+ buttonText: '搜索',
+ },
+ modal: {
+ resetButtonTitle: '清除查询条件',
+ noResultsText: '无法找到相关结果',
+ footer: {
+ selectText: '选择',
+ navigateText: '切换',
+ closeText: '关闭',
+ },
+ },
},
},
},
- },
- },
- // banner: {
- // id: 'viteplus-alpha',
- // text: 'Vite+ Alpha 发布:开源、统一、下一代工具链。',
- // url: 'https://voidzero.dev/posts/announcing-vite-plus-alpha?utm_source=vitest&utm_content=top_banner',
- // },
+ // banner: {
+ // id: 'viteplus-alpha',
+ // text: 'Announcing Vite+ Alpha: Open source. Unified. Next-gen.',
+ // url: 'https://voidzero.dev/posts/announcing-vite-plus-alpha?utm_source=vitest&utm_content=top_banner',
+ // },
- carbonAds: {
- code: 'CW7DVKJE',
- placement: 'vitestdev',
- },
+ carbonAds: {
+ code: 'CW7DVKJE',
+ placement: 'vitestdev',
+ },
- socialLinks: [
- { icon: 'bluesky', link: bluesky },
- { icon: 'mastodon', link: mastodon },
- { icon: 'discord', link: discord },
- { icon: 'github', link: github },
- ],
+ socialLinks: [
+ { icon: 'bluesky', link: bluesky },
+ { icon: 'mastodon', link: mastodon },
+ { icon: 'discord', link: discord },
+ { icon: 'github', link: github },
+ ],
- footer: {
- copyright: `© ${new Date().getFullYear()} VoidZero Inc. 和 Vitest 贡献者。`,
- nav: [
- {
- title: 'Vitest',
- items: [
- { text: '指南', link: '/guide/' },
- { text: 'API', link: '/api/test' },
- { text: '配置', link: '/config/' },
- ],
- },
- {
- title: '资源',
- items: [
- { text: '团队', link: '/team' },
- { text: '博客', link: '/blog' },
- { text: '更新日志', link: releases },
- ],
- },
- {
- title: '版本',
- items: [
- { text: '未发布', link: 'https://dev.cn.vitest.dev/' },
- { text: 'Vitest v4 文档', link: 'https://v4.cn.vitest.dev/' },
- { text: 'Vitest v3 文档', link: 'https://v3.cn.vitest.dev/' },
- { text: 'Vitest v2 文档', link: 'https://v2.cn.vitest.dev/' },
- { text: 'Vitest v1 文档', link: 'https://v1.cn.vitest.dev/' },
- ],
- },
- /* {
+ footer: {
+ copyright: `© ${new Date().getFullYear()} VoidZero Inc. 和 Vitest 贡献者。`,
+ nav: [
+ {
+ title: 'Vitest',
+ items: [
+ { text: '指南', link: '/guide/' },
+ { text: 'API', link: '/api/test' },
+ { text: '配置', link: '/config/' },
+ ],
+ },
+ {
+ title: '资源',
+ items: [
+ { text: '团队', link: '/team' },
+ { text: '博客', link: '/blog' },
+ { text: '更新日志', link: releases },
+ ],
+ },
+ {
+ title: '版本',
+ items: [
+ { text: '未发布', link: 'https://main.vitest.dev/' },
+ { text: 'Vitest v4 文档', link: 'https://v4.vitest.dev/' },
+ { text: 'Vitest v3 文档', link: 'https://v3.vitest.dev/' },
+ { text: 'Vitest v2 文档', link: 'https://v2.vitest.dev/' },
+ { text: 'Vitest v1 文档', link: 'https://v1.vitest.dev/' },
+ ],
+ },
+ /* {
title: 'Legal',
items: [
{ text: 'Terms & Conditions', link: 'https://voidzero.dev/terms' },
@@ -201,1177 +225,1179 @@ export default ({ mode }: { mode: string }) => {
{ text: 'Cookie Policy', link: 'https://voidzero.dev/cookies' },
],
}, */
- ],
- social: [
- { icon: 'github', link: github },
- { icon: 'discord', link: discord },
- // { icon: 'mastodon', link: mastodon }, -- the link shows github
- { icon: 'bluesky', link: bluesky },
- ],
- },
+ ],
+ social: [
+ { icon: 'github', link: github },
+ { icon: 'discord', link: discord },
+ // { icon: 'mastodon', link: mastodon }, -- the link shows github
+ { icon: 'bluesky', link: bluesky },
+ ],
+ },
- nav: [
- { text: '指南', link: '/guide/', activeMatch: '^/guide/' },
- { text: 'API', link: '/api/test', activeMatch: '^/api/' },
- { text: '配置', link: '/config/', activeMatch: '^/config/' },
- {
- text: '博客',
- link: '/blog',
- },
- {
- text: `v${version}`,
- items: [
+ nav: [
+ { text: '指南', link: '/guide/', activeMatch: '^/guide/' },
+ { text: 'API', link: '/api/test', activeMatch: '^/api/' },
+ { text: '配置', link: '/config/', activeMatch: '^/config/' },
{
- items: [
- {
- text: `v${version}`,
- link: `https://github.com/vitest-dev/vitest/releases/tag/v${version}`,
- },
- {
- text: '更新日志',
- link: releases,
- },
- {
- text: '贡献指南',
- link: contributing,
- },
- {
- text: '团队',
- link: '/team',
- },
- {
- text: 'Releases',
- link: '/releases',
- },
- ],
+ text: '博客',
+ link: '/blog',
},
{
+ text: `v${version}`,
items: [
{
- text: '未发布',
- link: 'https://dev.cn.vitest.dev/',
- },
- {
- text: 'v4.x',
- link: 'https://v4.cn.vitest.dev/',
- },
- {
- text: 'v3.x',
- link: 'https://v3.cn.vitest.dev/',
- },
- {
- text: 'v2.x',
- link: 'https://v2.cn.vitest.dev/',
+ items: [
+ {
+ text: `v${version}`,
+ link: `https://github.com/vitest-dev/vitest/releases/tag/v${version}`,
+ },
+ {
+ text: '更新日志',
+ link: releases,
+ },
+ {
+ text: '贡献指南',
+ link: contributing,
+ },
+ {
+ text: '团队',
+ link: '/team',
+ },
+ {
+ text: 'Releases',
+ link: '/releases',
+ },
+ ],
},
{
- text: 'v1.x',
- link: 'https://v1.cn.vitest.dev/',
+ items: [
+ {
+ text: '未发布',
+ link: 'https://dev.cn.vitest.dev/',
+ },
+ {
+ text: 'v4.x',
+ link: 'https://v4.cn.vitest.dev/',
+ },
+ {
+ text: 'v3.x',
+ link: 'https://v3.cn.vitest.dev/',
+ },
+ {
+ text: 'v2.x',
+ link: 'https://v2.cn.vitest.dev/',
+ },
+ {
+ text: 'v1.x',
+ link: 'https://v1.cn.vitest.dev/',
+ },
+ ],
},
],
},
],
- },
- ],
- sidebar: {
- '/config': [
- {
- text: '配置索引',
- collapsed: false,
- items: [
- {
- text: '配置 Vitest',
- link: '/config/',
- },
- {
- text: 'include',
- link: '/config/include',
- },
- {
- text: 'exclude',
- link: '/config/exclude',
- },
- {
- text: 'includeSource',
- link: '/config/include-source',
- },
- {
- text: 'name',
- link: '/config/name',
- },
- {
- text: 'server',
- link: '/config/server',
- },
- {
- text: 'deps',
- link: '/config/deps',
- },
- {
- text: 'runner',
- link: '/config/runner',
- },
- {
- text: 'benchmark',
- link: '/config/benchmark',
- },
- {
- text: 'alias',
- link: '/config/alias',
- },
- {
- text: 'globals',
- link: '/config/globals',
- },
- {
- text: 'injectCjsGlobals',
- link: '/config/injectcjsglobals',
- },
- {
- text: 'environment',
- link: '/config/environment',
- },
- {
- text: 'environmentOptions',
- link: '/config/environmentoptions',
- },
- {
- text: 'watch',
- link: '/config/watch',
- },
- {
- text: 'watchTriggerPatterns',
- link: '/config/watchtriggerpatterns',
- },
- {
- text: 'root',
- link: '/config/root',
- },
- {
- text: 'dir',
- link: '/config/dir',
- },
- {
- text: 'reporters',
- link: '/config/reporters',
- },
- {
- text: 'outputFile',
- link: '/config/outputfile',
- },
- {
- text: 'pool',
- link: '/config/pool',
- },
- {
- text: 'execArgv',
- link: '/config/execargv',
- },
- {
- text: 'vmMemoryLimit',
- link: '/config/vmmemorylimit',
- },
- {
- text: 'fileParallelism',
- link: '/config/fileparallelism',
- },
- {
- text: 'maxWorkers',
- link: '/config/maxworkers',
- },
- {
- text: 'testTimeout',
- link: '/config/testtimeout',
- },
- {
- text: 'hookTimeout',
- link: '/config/hooktimeout',
- },
- {
- text: 'teardownTimeout',
- link: '/config/teardowntimeout',
- },
- {
- text: 'silent',
- link: '/config/silent',
- },
- {
- text: 'setupFiles',
- link: '/config/setupfiles',
- },
- {
- text: 'provide',
- link: '/config/provide',
- },
- {
- text: 'globalSetup',
- link: '/config/globalsetup',
- },
- {
- text: 'forceRerunTriggers',
- link: '/config/forcereruntriggers',
- },
- {
- text: 'coverage',
- link: '/config/coverage',
- },
- {
- text: 'testNamePattern',
- link: '/config/testnamepattern',
- },
- {
- text: 'ui',
- link: '/config/ui',
- },
- {
- text: 'open',
- link: '/config/open',
- },
- {
- text: 'api',
- link: '/config/api',
- },
- {
- text: 'clearMocks',
- link: '/config/clearmocks',
- },
- {
- text: 'mockReset',
- link: '/config/mockreset',
- },
- {
- text: 'restoreMocks',
- link: '/config/restoremocks',
- },
- {
- text: 'unstubEnvs',
- link: '/config/unstubenvs',
- },
- {
- text: 'unstubGlobals',
- link: '/config/unstubglobals',
- },
- {
- text: 'snapshotFormat',
- link: '/config/snapshotformat',
- },
- {
- text: 'snapshotSerializers',
- link: '/config/snapshotserializers',
- },
- {
- text: 'resolveSnapshotPath',
- link: '/config/resolvesnapshotpath',
- },
- {
- text: 'allowOnly',
- link: '/config/allowonly',
- },
- {
- text: 'passWithNoTests',
- link: '/config/passwithnotests',
- },
- {
- text: 'logHeapUsage',
- link: '/config/logheapusage',
- },
- {
- text: 'css',
- link: '/config/css',
- },
- {
- text: 'maxConcurrency',
- link: '/config/maxconcurrency',
- },
- {
- text: 'cache',
- link: '/config/cache',
- },
- {
- text: 'fsModuleCache',
- link: '/config/fsmodulecache',
- },
- {
- text: 'fsModuleCachePath',
- link: '/config/fsmodulecachepath',
- },
- {
- text: 'sequence',
- link: '/config/sequence',
- },
- {
- text: 'tags',
- link: '/config/tags',
- },
- {
- text: 'strictTags',
- link: '/config/stricttags',
- },
- {
- text: 'typecheck',
- link: '/config/typecheck',
- },
- {
- text: 'slowTestThreshold',
- link: '/config/slowtestthreshold',
- },
- {
- text: 'chaiConfig',
- link: '/config/chaiconfig',
- },
- {
- text: 'bail',
- link: '/config/bail',
- },
- {
- text: 'retry',
- link: '/config/retry',
- },
- {
- text: 'repeats',
- link: '/config/repeats',
- },
- {
- text: 'onConsoleLog',
- link: '/config/onconsolelog',
- },
- {
- text: 'onStackTrace',
- link: '/config/onstacktrace',
- },
- {
- text: 'onUnhandledError',
- link: '/config/onunhandlederror',
- },
- {
- text: 'dangerouslyIgnoreUnhandled...',
- link: '/config/dangerouslyignoreunhandlederrors',
- },
- {
- text: 'diff',
- link: '/config/diff',
- },
- {
- text: 'fakeTimers',
- link: '/config/faketimers',
- },
- {
- text: 'projects',
- link: '/config/projects',
- },
- {
- text: 'sharedViteServer',
- link: '/config/sharedviteserver',
- },
- {
- text: 'isolate',
- link: '/config/isolate',
- },
- {
- text: 'includeTaskLocation',
- link: '/config/includetasklocation',
- },
- {
- text: 'snapshotEnvironment',
- link: '/config/snapshotenvironment',
- },
- {
- text: 'env',
- link: '/config/env',
- },
- {
- text: 'expect',
- link: '/config/expect',
- },
- {
- text: 'printConsoleTrace',
- link: '/config/printconsoletrace',
- },
- {
- text: 'attachmentsDir',
- link: '/config/attachmentsdir',
- },
- {
- text: 'hideSkippedTests',
- link: '/config/hideskippedtests',
- },
- {
- text: 'mode',
- link: '/config/mode',
- },
- {
- text: 'expandSnapshotDiff',
- link: '/config/expandsnapshotdiff',
- },
- {
- text: 'disableConsoleIntercept',
- link: '/config/disableconsoleintercept',
- },
- {
- text: 'changed',
- link: '/config/changed',
- },
- {
- text: 'experimental',
- link: '/config/experimental',
- },
- ],
- },
- {
- text: '浏览器模式',
- collapsed: false,
- items: [
+ sidebar: {
+ '/config': [
{
- text: 'Providers',
+ text: '配置索引',
collapsed: false,
items: [
{
- text: 'playwright',
- link: '/config/browser/playwright',
+ text: '配置 Vitest',
+ link: '/config/',
},
{
- text: 'webdriverio',
- link: '/config/browser/webdriverio',
+ text: 'include',
+ link: '/config/include',
},
{
- text: 'preview',
- link: '/config/browser/preview',
+ text: 'exclude',
+ link: '/config/exclude',
},
- ],
- },
- {
- text: 'browser.enabled',
- link: '/config/browser/enabled',
- },
- {
- text: 'browser.instances',
- link: '/config/browser/instances',
- },
- {
- text: 'browser.headless',
- link: '/config/browser/headless',
- },
- {
- text: 'browser.testerHtmlPath',
- link: '/config/browser/testerhtmlpath',
- },
- {
- text: 'browser.provider',
- link: '/config/browser/provider',
- },
- {
- text: 'browser.ui',
- link: '/config/browser/ui',
- },
- {
- text: 'browser.detailsPanelPosition',
- link: '/config/browser/detailspanelposition',
- },
- {
- text: 'browser.viewport',
- link: '/config/browser/viewport',
- },
- {
- text: 'browser.locators',
- link: '/config/browser/locators',
- },
- {
- text: 'browser.screenshotDirectory',
- link: '/config/browser/screenshotdirectory',
- },
- {
- text: 'browser.screenshotFailures',
- link: '/config/browser/screenshotfailures',
- },
- {
- text: 'browser.dependencySourcemaps',
- link: '/config/browser/dependencysourcemaps',
- },
- {
- text: 'browser.orchestratorScripts',
- link: '/config/browser/orchestratorscripts',
- },
- {
- text: 'browser.commands',
- link: '/config/browser/commands',
- },
- {
- text: 'browser.connectTimeout',
- link: '/config/browser/connecttimeout',
- },
- {
- text: 'browser.trace',
- link: '/config/browser/trace',
- },
- {
- text: 'browser.trackUnhandledErrors',
- link: '/config/browser/trackunhandlederrors',
- },
- {
- text: 'browser.expect',
- link: '/config/browser/expect',
- },
- ],
- },
- // {
- // text: '@vitest/plugin-eslint',
- // collapsed: true,
- // items: [
- // {
- // text: 'Lints',
- // link: '/config/eslint',
- // },
- // // TODO: generate
- // {
- // text: 'consistent-test-filename',
- // link: '/config/eslint/consistent-test-filename',
- // },
- // {
- // text: 'consistent-test-it',
- // link: '/config/eslint/consistent-test-it',
- // },
- // ],
- // },
- // {
- // text: 'vscode',
- // link: '/config/vscode',
- // },
- ],
- '/guide': [
- {
- text: '简介',
- collapsed: false,
- items: [
- {
- text: '为什么是 Vitest?',
- link: '/guide/why',
- },
- {
- text: '快速起步',
- link: '/guide/',
- },
- {
- text: '主要功能',
- link: '/guide/features',
- },
- ],
- },
- {
- text: '学习',
- collapsed: false,
- items: [
- {
- text: '编写测试用例',
- link: '/guide/learn/writing-tests',
- docFooterText: '编写测试用例 | 学习',
- },
- {
- text: '使用匹配器',
- link: '/guide/learn/matchers',
- docFooterText: '使用匹配器 | 学习',
- },
- {
- text: '测试异步代码',
- link: '/guide/learn/async',
- docFooterText: '测试异步代码 | 学习',
- },
- {
- text: '初始化与清理',
- link: '/guide/learn/setup-teardown',
- docFooterText: 'Setup and Teardown | 学习',
- },
- {
- text: '模拟函数',
- link: '/guide/learn/mock-functions',
- docFooterText: '模拟函数 | 学习',
- },
- {
- text: '快照测试',
- link: '/guide/learn/snapshots',
- docFooterText: '快照测试 | 学习',
- },
- {
- text: '测试实践',
- link: '/guide/learn/testing-in-practice',
- docFooterText: '测试实践 | 学习',
- },
- {
- text: '调试测试',
- link: '/guide/learn/debugging-tests',
- docFooterText: '调试测试 | 学习',
- },
- {
- text: '使用 AI 编写测试',
- link: '/guide/learn/writing-tests-with-ai',
- docFooterText: '使用 AI 编写测试 | 学习',
- },
- ],
- },
- {
- text: '浏览器模式',
- collapsed: false,
- items: [
- {
- text: '什么是浏览器模式?',
- link: '/guide/browser/why',
- docFooterText: '什么是浏览器模式? | 浏览器模式',
- },
- {
- text: '快速起步',
- link: '/guide/browser/',
- docFooterText: '快速起步 | 浏览器模式',
- },
- {
- text: '多环境配置',
- link: '/guide/browser/multiple-setups',
- docFooterText: '多环境配置 | 浏览器模式',
- },
- {
- text: '组件测试',
- link: '/guide/browser/component-testing',
- docFooterText: '组件测试 | 浏览器模式',
- },
- {
- text: '可视化回归测试',
- link: '/guide/browser/visual-regression-testing',
- docFooterText: '可视化回归测试 | 浏览器模式',
- },
- {
- text: '追踪查看器',
- link: '/guide/browser/trace-view',
- docFooterText: '追踪查看器 | 浏览器模式',
- },
- {
- text: 'Playwright 追踪',
- link: '/guide/browser/playwright-traces',
- docFooterText: 'Playwright 追踪 | 浏览器模式',
+ {
+ text: 'includeSource',
+ link: '/config/include-source',
+ },
+ {
+ text: 'name',
+ link: '/config/name',
+ },
+ {
+ text: 'server',
+ link: '/config/server',
+ },
+ {
+ text: 'deps',
+ link: '/config/deps',
+ },
+ {
+ text: 'runner',
+ link: '/config/runner',
+ },
+ {
+ text: 'benchmark',
+ link: '/config/benchmark',
+ },
+ {
+ text: 'alias',
+ link: '/config/alias',
+ },
+ {
+ text: 'globals',
+ link: '/config/globals',
+ },
+ {
+ text: 'injectCjsGlobals',
+ link: '/config/injectcjsglobals',
+ },
+ {
+ text: 'environment',
+ link: '/config/environment',
+ },
+ {
+ text: 'environmentOptions',
+ link: '/config/environmentoptions',
+ },
+ {
+ text: 'watch',
+ link: '/config/watch',
+ },
+ {
+ text: 'watchTriggerPatterns',
+ link: '/config/watchtriggerpatterns',
+ },
+ {
+ text: 'root',
+ link: '/config/root',
+ },
+ {
+ text: 'dir',
+ link: '/config/dir',
+ },
+ {
+ text: 'reporters',
+ link: '/config/reporters',
+ },
+ {
+ text: 'outputFile',
+ link: '/config/outputfile',
+ },
+ {
+ text: 'pool',
+ link: '/config/pool',
+ },
+ {
+ text: 'execArgv',
+ link: '/config/execargv',
+ },
+ {
+ text: 'vmMemoryLimit',
+ link: '/config/vmmemorylimit',
+ },
+ {
+ text: 'fileParallelism',
+ link: '/config/fileparallelism',
+ },
+ {
+ text: 'maxWorkers',
+ link: '/config/maxworkers',
+ },
+ {
+ text: 'testTimeout',
+ link: '/config/testtimeout',
+ },
+ {
+ text: 'hookTimeout',
+ link: '/config/hooktimeout',
+ },
+ {
+ text: 'teardownTimeout',
+ link: '/config/teardowntimeout',
+ },
+ {
+ text: 'silent',
+ link: '/config/silent',
+ },
+ {
+ text: 'setupFiles',
+ link: '/config/setupfiles',
+ },
+ {
+ text: 'provide',
+ link: '/config/provide',
+ },
+ {
+ text: 'globalSetup',
+ link: '/config/globalsetup',
+ },
+ {
+ text: 'forceRerunTriggers',
+ link: '/config/forcereruntriggers',
+ },
+ {
+ text: 'coverage',
+ link: '/config/coverage',
+ },
+ {
+ text: 'testNamePattern',
+ link: '/config/testnamepattern',
+ },
+ {
+ text: 'ui',
+ link: '/config/ui',
+ },
+ {
+ text: 'open',
+ link: '/config/open',
+ },
+ {
+ text: 'api',
+ link: '/config/api',
+ },
+ {
+ text: 'clearMocks',
+ link: '/config/clearmocks',
+ },
+ {
+ text: 'mockReset',
+ link: '/config/mockreset',
+ },
+ {
+ text: 'restoreMocks',
+ link: '/config/restoremocks',
+ },
+ {
+ text: 'unstubEnvs',
+ link: '/config/unstubenvs',
+ },
+ {
+ text: 'unstubGlobals',
+ link: '/config/unstubglobals',
+ },
+ {
+ text: 'snapshotFormat',
+ link: '/config/snapshotformat',
+ },
+ {
+ text: 'snapshotSerializers',
+ link: '/config/snapshotserializers',
+ },
+ {
+ text: 'resolveSnapshotPath',
+ link: '/config/resolvesnapshotpath',
+ },
+ {
+ text: 'allowOnly',
+ link: '/config/allowonly',
+ },
+ {
+ text: 'passWithNoTests',
+ link: '/config/passwithnotests',
+ },
+ {
+ text: 'logHeapUsage',
+ link: '/config/logheapusage',
+ },
+ {
+ text: 'css',
+ link: '/config/css',
+ },
+ {
+ text: 'maxConcurrency',
+ link: '/config/maxconcurrency',
+ },
+ {
+ text: 'cache',
+ link: '/config/cache',
+ },
+ {
+ text: 'fsModuleCache',
+ link: '/config/fsmodulecache',
+ },
+ {
+ text: 'fsModuleCachePath',
+ link: '/config/fsmodulecachepath',
+ },
+ {
+ text: 'sequence',
+ link: '/config/sequence',
+ },
+ {
+ text: 'tags',
+ link: '/config/tags',
+ },
+ {
+ text: 'strictTags',
+ link: '/config/stricttags',
+ },
+ {
+ text: 'typecheck',
+ link: '/config/typecheck',
+ },
+ {
+ text: 'slowTestThreshold',
+ link: '/config/slowtestthreshold',
+ },
+ {
+ text: 'chaiConfig',
+ link: '/config/chaiconfig',
+ },
+ {
+ text: 'bail',
+ link: '/config/bail',
+ },
+ {
+ text: 'retry',
+ link: '/config/retry',
+ },
+ {
+ text: 'repeats',
+ link: '/config/repeats',
+ },
+ {
+ text: 'onConsoleLog',
+ link: '/config/onconsolelog',
+ },
+ {
+ text: 'onStackTrace',
+ link: '/config/onstacktrace',
+ },
+ {
+ text: 'onUnhandledError',
+ link: '/config/onunhandlederror',
+ },
+ {
+ text: 'dangerouslyIgnoreUnhandled...',
+ link: '/config/dangerouslyignoreunhandlederrors',
+ },
+ {
+ text: 'diff',
+ link: '/config/diff',
+ },
+ {
+ text: 'fakeTimers',
+ link: '/config/faketimers',
+ },
+ {
+ text: 'projects',
+ link: '/config/projects',
+ },
+ {
+ text: 'sharedViteServer',
+ link: '/config/sharedviteserver',
+ },
+ {
+ text: 'isolate',
+ link: '/config/isolate',
+ },
+ {
+ text: 'includeTaskLocation',
+ link: '/config/includetasklocation',
+ },
+ {
+ text: 'snapshotEnvironment',
+ link: '/config/snapshotenvironment',
+ },
+ {
+ text: 'env',
+ link: '/config/env',
+ },
+ {
+ text: 'expect',
+ link: '/config/expect',
+ },
+ {
+ text: 'printConsoleTrace',
+ link: '/config/printconsoletrace',
+ },
+ {
+ text: 'attachmentsDir',
+ link: '/config/attachmentsdir',
+ },
+ {
+ text: 'hideSkippedTests',
+ link: '/config/hideskippedtests',
+ },
+ {
+ text: 'mode',
+ link: '/config/mode',
+ },
+ {
+ text: 'expandSnapshotDiff',
+ link: '/config/expandsnapshotdiff',
+ },
+ {
+ text: 'disableConsoleIntercept',
+ link: '/config/disableconsoleintercept',
+ },
+ {
+ text: 'changed',
+ link: '/config/changed',
+ },
+ {
+ text: 'experimental',
+ link: '/config/experimental',
+ },
+ ],
},
{
- text: 'ARIA 快照',
- link: '/guide/browser/aria-snapshots',
- docFooterText: 'ARIA 快照 | 浏览器模式',
+ text: '浏览器模式',
+ collapsed: false,
+ items: [
+ {
+ text: 'Providers',
+ collapsed: false,
+ items: [
+ {
+ text: 'playwright',
+ link: '/config/browser/playwright',
+ },
+ {
+ text: 'webdriverio',
+ link: '/config/browser/webdriverio',
+ },
+ {
+ text: 'preview',
+ link: '/config/browser/preview',
+ },
+ ],
+ },
+ {
+ text: 'browser.enabled',
+ link: '/config/browser/enabled',
+ },
+ {
+ text: 'browser.instances',
+ link: '/config/browser/instances',
+ },
+ {
+ text: 'browser.headless',
+ link: '/config/browser/headless',
+ },
+ {
+ text: 'browser.testerHtmlPath',
+ link: '/config/browser/testerhtmlpath',
+ },
+ {
+ text: 'browser.provider',
+ link: '/config/browser/provider',
+ },
+ {
+ text: 'browser.ui',
+ link: '/config/browser/ui',
+ },
+ {
+ text: 'browser.detailsPanelPosition',
+ link: '/config/browser/detailspanelposition',
+ },
+ {
+ text: 'browser.viewport',
+ link: '/config/browser/viewport',
+ },
+ {
+ text: 'browser.locators',
+ link: '/config/browser/locators',
+ },
+ {
+ text: 'browser.screenshotDirectory',
+ link: '/config/browser/screenshotdirectory',
+ },
+ {
+ text: 'browser.screenshotFailures',
+ link: '/config/browser/screenshotfailures',
+ },
+ {
+ text: 'browser.dependencySourcemaps',
+ link: '/config/browser/dependencysourcemaps',
+ },
+ {
+ text: 'browser.orchestratorScripts',
+ link: '/config/browser/orchestratorscripts',
+ },
+ {
+ text: 'browser.commands',
+ link: '/config/browser/commands',
+ },
+ {
+ text: 'browser.connectTimeout',
+ link: '/config/browser/connecttimeout',
+ },
+ {
+ text: 'browser.trace',
+ link: '/config/browser/trace',
+ },
+ {
+ text: 'browser.trackUnhandledErrors',
+ link: '/config/browser/trackunhandlederrors',
+ },
+ {
+ text: 'browser.expect',
+ link: '/config/browser/expect',
+ },
+ ],
},
+ // {
+ // text: '@vitest/plugin-eslint',
+ // collapsed: true,
+ // items: [
+ // {
+ // text: 'Lints',
+ // link: '/config/eslint',
+ // },
+ // // TODO: generate
+ // {
+ // text: 'consistent-test-filename',
+ // link: '/config/eslint/consistent-test-filename',
+ // },
+ // {
+ // text: 'consistent-test-it',
+ // link: '/config/eslint/consistent-test-it',
+ // },
+ // ],
+ // },
+ // {
+ // text: 'vscode',
+ // link: '/config/vscode',
+ // },
],
- },
- // Authoring — how to express a test in code: constructing it,
- // asserting, mocking dependencies, attaching metadata. The page is
- // about *test content*, not the runner. Discriminator: "How do I
- // write X in a test?" If yes, it belongs here. Mocking sub-pages
- // live nested because they're a multi-page subtopic.
- {
- text: '编写测试',
- collapsed: false,
- items: [
+ '/guide': [
{
- text: '测试上下文',
- link: '/guide/test-context',
+ text: '简介',
+ collapsed: false,
+ items: [
+ {
+ text: '为什么是 Vitest?',
+ link: '/guide/why',
+ },
+ {
+ text: '快速起步',
+ link: '/guide/',
+ },
+ {
+ text: '主要功能',
+ link: '/guide/features',
+ },
+ ],
},
{
- text: '运行生命周期',
- link: '/guide/lifecycle',
+ text: '学习',
+ collapsed: false,
+ items: [
+ {
+ text: '编写测试用例',
+ link: '/guide/learn/writing-tests',
+ docFooterText: '编写测试用例 | 学习',
+ },
+ {
+ text: '使用匹配器',
+ link: '/guide/learn/matchers',
+ docFooterText: '使用匹配器 | 学习',
+ },
+ {
+ text: '测试异步代码',
+ link: '/guide/learn/async',
+ docFooterText: '测试异步代码 | 学习',
+ },
+ {
+ text: '初始化与清理',
+ link: '/guide/learn/setup-teardown',
+ docFooterText: '初始化与清理 | 学习',
+ },
+ {
+ text: '模拟函数',
+ link: '/guide/learn/mock-functions',
+ docFooterText: '模拟函数 | 学习',
+ },
+ {
+ text: '快照测试',
+ link: '/guide/learn/snapshots',
+ docFooterText: '快照测试 | 学习',
+ },
+ {
+ text: '测试实践',
+ link: '/guide/learn/testing-in-practice',
+ docFooterText: '测试实践 | 学习',
+ },
+ {
+ text: '调试测试',
+ link: '/guide/learn/debugging-tests',
+ docFooterText: '调试测试 | 学习',
+ },
+ {
+ text: '使用 AI 编写测试',
+ link: '/guide/learn/writing-tests-with-ai',
+ docFooterText: '使用 AI 编写测试 | 学习',
+ },
+ ],
},
{
- text: '快照',
- link: '/guide/snapshot',
+ text: '浏览器模式',
+ collapsed: false,
+ items: [
+ {
+ text: '什么是浏览器模式?',
+ link: '/guide/browser/why',
+ docFooterText: '什么是浏览器模式? | 浏览器模式',
+ },
+ {
+ text: '快速起步',
+ link: '/guide/browser/',
+ docFooterText: '快速起步 | 浏览器模式',
+ },
+ {
+ text: '多环境配置',
+ link: '/guide/browser/multiple-setups',
+ docFooterText: '多环境配置 | 浏览器模式',
+ },
+ {
+ text: '组件测试',
+ link: '/guide/browser/component-testing',
+ docFooterText: '组件测试 | 浏览器模式',
+ },
+ {
+ text: '可视化回归测试',
+ link: '/guide/browser/visual-regression-testing',
+ docFooterText: '可视化回归测试 | 浏览器模式',
+ },
+ {
+ text: '追踪查看器',
+ link: '/guide/browser/trace-view',
+ docFooterText: '追踪查看器 | 浏览器模式',
+ },
+ {
+ text: 'Playwright 追踪',
+ link: '/guide/browser/playwright-traces',
+ docFooterText: 'Playwright 追踪 | 浏览器模式',
+ },
+ {
+ text: 'ARIA 快照',
+ link: '/guide/browser/aria-snapshots',
+ docFooterText: 'ARIA 快照 | 浏览器模式',
+ },
+ ],
},
+ // Authoring — how to express a test in code: constructing it,
+ // asserting, mocking dependencies, attaching metadata. The page is
+ // about *test content*, not the runner. Discriminator: "How do I
+ // write X in a test?" If yes, it belongs here. Mocking sub-pages
+ // live nested because they're a multi-page subtopic.
{
- text: '模拟对象',
- link: '/guide/mocking',
- collapsed: true,
+ text: '编写测试',
+ collapsed: false,
items: [
{
- text: '日期',
- link: '/guide/mocking/dates',
+ text: '测试上下文',
+ link: '/guide/test-context',
+ },
+ {
+ text: '运行生命周期',
+ link: '/guide/lifecycle',
+ },
+ {
+ text: '快照',
+ link: '/guide/snapshot',
},
{
- text: '函数',
- link: '/guide/mocking/functions',
+ text: '模拟对象',
+ link: '/guide/mocking',
+ collapsed: true,
+ items: [
+ {
+ text: '日期',
+ link: '/guide/mocking/dates',
+ },
+ {
+ text: '函数',
+ link: '/guide/mocking/functions',
+ },
+ {
+ text: '全局对象',
+ link: '/guide/mocking/globals',
+ },
+ {
+ text: '模块',
+ link: '/guide/mocking/modules',
+ },
+ {
+ text: '文件系统',
+ link: '/guide/mocking/file-system',
+ },
+ {
+ text: '请求',
+ link: '/guide/mocking/requests',
+ },
+ {
+ text: '计时器',
+ link: '/guide/mocking/timers',
+ },
+ {
+ text: '类',
+ link: '/guide/mocking/classes',
+ },
+ ],
},
{
- text: '全局对象',
- link: '/guide/mocking/globals',
+ text: '测试标签',
+ link: '/guide/test-tags',
},
{
- text: '模块',
- link: '/guide/mocking/modules',
+ text: '测试注释',
+ link: '/guide/test-annotations',
},
{
- text: '文件系统',
- link: '/guide/mocking/file-system',
+ text: '扩展匹配器',
+ link: '/guide/extending-matchers',
},
{
- text: '请求',
- link: '/guide/mocking/requests',
+ text: '类型测试',
+ link: '/guide/testing-types',
},
{
- text: '计时器',
- link: '/guide/mocking/timers',
+ text: '基准测试',
+ link: '/guide/benchmarking',
},
{
- text: '类',
- link: '/guide/mocking/classes',
+ text: '内联测试',
+ link: '/guide/in-source',
},
],
},
+ // Workflow — how to invoke, select, and orchestrate test runs
+ // across files/projects/processes. The page is about the *runner
+ // and tooling around it*, not what's inside a test. Discriminator:
+ // "How do I run / filter / parallelize / integrate Vitest?" If a
+ // page is about the runtime environment of the tests themselves
+ // (jsdom, node), it still belongs here — that's a workflow choice.
{
- text: '测试标签',
- link: '/guide/test-tags',
- },
- {
- text: '测试注释',
- link: '/guide/test-annotations',
- },
- {
- text: '扩展匹配器',
- link: '/guide/extending-matchers',
- },
- {
- text: '类型测试',
- link: '/guide/testing-types',
- },
- {
- text: '基准测试',
- link: '/guide/benchmarking',
- },
- {
- text: '内联测试',
- link: '/guide/in-source',
- },
- ],
- },
- // Workflow — how to invoke, select, and orchestrate test runs
- // across files/projects/processes. The page is about the *runner
- // and tooling around it*, not what's inside a test. Discriminator:
- // "How do I run / filter / parallelize / integrate Vitest?" If a
- // page is about the runtime environment of the tests themselves
- // (jsdom, node), it still belongs here — that's a workflow choice.
- {
- text: '工作流',
- collapsed: false,
- items: [
- {
- text: '命令行界面',
- link: '/guide/cli',
- },
- {
- text: '测试筛选',
- link: '/guide/filtering',
- },
- {
- text: '测试项目',
- link: '/guide/projects',
- },
- {
- text: '测试环境',
- link: '/guide/environment',
- },
- {
- text: '并行测试',
- link: '/guide/parallelism',
- },
- {
- text: '报告器',
- link: '/guide/reporters',
- },
- {
- text: 'UI 模式',
- link: '/guide/ui',
- },
- {
- text: 'IDE 插件',
- link: '/guide/ide',
- },
- ],
- },
- // Quality & Debugging — how to verify the test run is healthy and
- // diagnose it when it isn't. Coverage, perf, leak detection, error
- // triage, observability. Discriminator: "Is my suite good?" or
- // "Why did this fail / leak / slow down?" If a page primarily
- // measures or fixes the suite (rather than authoring or running
- // it), put it here.
- {
- text: '质量与调试',
- collapsed: false,
- items: [
- {
- text: '覆盖率',
- link: '/guide/coverage',
- },
- {
- text: '调试',
- link: '/guide/debugging',
- },
- {
- text: '常见错误',
- link: '/guide/common-errors',
- },
- {
- text: '性能',
+ text: '工作流',
collapsed: false,
items: [
{
- text: '性能测试分析',
- link: '/guide/profiling-test-performance',
+ text: '命令行界面',
+ link: '/guide/cli',
+ },
+ {
+ text: '测试筛选',
+ link: '/guide/filtering',
+ },
+ {
+ text: '测试项目',
+ link: '/guide/projects',
+ },
+ {
+ text: '测试环境',
+ link: '/guide/environment',
+ },
+ {
+ text: '并行测试',
+ link: '/guide/parallelism',
+ },
+ {
+ text: '报告器',
+ link: '/guide/reporters',
+ },
+ {
+ text: 'UI 模式',
+ link: '/guide/ui',
},
{
- text: '性能优化',
- link: '/guide/improving-performance',
+ text: 'IDE 插件',
+ link: '/guide/ide',
},
],
},
+ // Quality & Debugging — how to verify the test run is healthy and
+ // diagnose it when it isn't. Coverage, perf, leak detection, error
+ // triage, observability. Discriminator: "Is my suite good?" or
+ // "Why did this fail / leak / slow down?" If a page primarily
+ // measures or fixes the suite (rather than authoring or running
+ // it), put it here.
{
- text: 'OpenTelemetry',
- link: '/guide/open-telemetry',
- },
- ],
- },
- // Recipes — end-to-end patterns that solve a concrete problem by
- // combining multiple features. Each entry is titled by the problem
- // ("Database Transaction per Test"), not the feature. Add a recipe
- // when a single feature page would over-explain, when the value
- // comes from composition, or when users would search by intent
- // rather than by API name.
- {
- text: '技巧',
- collapsed: false,
- items: [
- {
- text: '一个测试对应一个数据库事务',
- link: '/guide/recipes/db-transaction',
- },
- {
- text: '优雅地取消长时间运行的操作',
- link: '/guide/recipes/cancellable',
- },
- {
- text: '等待异步条件',
- link: '/guide/recipes/wait-for',
- },
- {
- text: '在测试中收窄类型',
- link: '/guide/recipes/type-narrowing',
- },
- {
- text: '自定义断言工具函数',
- link: '/guide/recipes/custom-assertions',
- },
- {
- text: '监视非直接导入的文件',
- link: '/guide/recipes/watch-templates',
- },
- {
- text: '扩展浏览器定位器',
- link: '/guide/recipes/browser-locators',
- },
- {
- text: 'Schema 驱动断言',
- link: '/guide/recipes/schema-matching',
- },
- {
- text: '使用 `using` 自动清理',
- link: '/guide/recipes/explicit-resources',
- },
- {
- text: '使用 `vi.when` 进行条件模拟',
- link: '/guide/recipes/conditional-mocking',
- },
- {
- text: '按文件配置隔离',
- link: '/guide/recipes/disable-isolation',
- },
- {
- text: '并行和串行测试文件',
- link: '/guide/recipes/parallel-sequential',
- },
- ],
- },
- {
- text: '高级指南',
- collapsed: true,
- items: [
- {
- text: '快速开始',
- link: '/guide/advanced/',
- },
- {
- text: '运行测试 API',
- link: '/guide/advanced/tests',
- },
- {
- text: '扩展报告器',
- link: '/guide/advanced/reporters',
- },
- {
- text: '自定义运行池',
- link: '/guide/advanced/pool',
- },
- {
- text: 'Benchmark Provider',
- link: '/guide/advanced/benchmark-provider',
- },
- ],
- },
- // Migration — one-time transitional content: cross-version
- // upgrades and porting from other test runners (Jest, Mocha).
- // Sits near the bottom because it's not daily-use and would push
- // active-use guides further from the user's first scroll.
- {
- text: '迁移',
- link: '/guide/migration',
- collapsed: false,
- items: [
- {
- text: '迁移至 Vitest 5.0',
- link: '/guide/migration/',
- },
- {
- text: '从 Jest 迁移',
- link: '/guide/migration/jest',
- },
- {
- text: '从 Mocha + Chai + Sinon 迁移',
- link: '/guide/migration/mocha',
- },
- ],
- },
- {
- items: [
- {
- text: '测试框架比较',
- link: '/guide/comparisons',
- },
- ],
- },
- ],
- '/api': [
- {
- text: 'Test API',
- items: [
- {
- text: 'Test',
- link: '/api/test',
- },
- {
- text: 'Describe',
- link: '/api/describe',
- },
- {
- text: 'Hooks',
- link: '/api/hooks',
- },
- ],
- },
- {
- text: 'Mocks',
- link: '/api/mock',
- },
- {
- text: 'Vi Utility',
- link: '/api/vi',
- },
- {
- text: 'Expect',
- link: '/api/expect',
- },
- {
- text: 'ExpectTypeOf',
- link: '/api/expect-typeof',
- },
- {
- text: 'Assert',
- link: '/api/assert',
- },
- {
- text: 'AssertType',
- link: '/api/assert-type',
- },
- {
- text: '浏览器模式',
- items: [
- {
- text: 'Render Function',
+ text: '质量与调试',
collapsed: false,
items: [
{
- text: 'react',
- link: '/api/browser/react',
+ text: '覆盖率',
+ link: '/guide/coverage',
+ },
+ {
+ text: '调试',
+ link: '/guide/debugging',
},
{
- text: 'vue',
- link: '/api/browser/vue',
+ text: '常见错误',
+ link: '/guide/common-errors',
},
{
- text: 'svelte',
- link: '/api/browser/svelte',
+ text: '性能',
+ collapsed: false,
+ items: [
+ {
+ text: '性能测试分析',
+ link: '/guide/profiling-test-performance',
+ },
+ {
+ text: '性能优化',
+ link: '/guide/improving-performance',
+ },
+ ],
+ },
+ {
+ text: 'OpenTelemetry',
+ link: '/guide/open-telemetry',
},
- // {
- // text: 'angular',
- // link: '/api/browser/angular',
- // },
],
},
+ // Recipes — end-to-end patterns that solve a concrete problem by
+ // combining multiple features. Each entry is titled by the problem
+ // ("Database Transaction per Test"), not the feature. Add a recipe
+ // when a single feature page would over-explain, when the value
+ // comes from composition, or when users would search by intent
+ // rather than by API name.
{
- text: 'Context API',
- link: '/api/browser/context',
- },
- {
- text: 'Interactivity API',
- link: '/api/browser/interactivity',
+ text: '技巧',
+ collapsed: false,
+ items: [
+ {
+ text: '一个测试对应一个数据库事务',
+ link: '/guide/recipes/db-transaction',
+ },
+ {
+ text: '优雅地取消长时间运行的操作',
+ link: '/guide/recipes/cancellable',
+ },
+ {
+ text: '等待异步条件',
+ link: '/guide/recipes/wait-for',
+ },
+ {
+ text: '在测试中收窄类型',
+ link: '/guide/recipes/type-narrowing',
+ },
+ {
+ text: '自定义断言工具函数',
+ link: '/guide/recipes/custom-assertions',
+ },
+ {
+ text: '监视非直接导入的文件',
+ link: '/guide/recipes/watch-templates',
+ },
+ {
+ text: '扩展浏览器定位器',
+ link: '/guide/recipes/browser-locators',
+ },
+ {
+ text: 'Schema 驱动断言',
+ link: '/guide/recipes/schema-matching',
+ },
+ {
+ text: '使用 `using` 自动清理',
+ link: '/guide/recipes/explicit-resources',
+ },
+ {
+ text: '使用 `vi.when` 进行条件模拟',
+ link: '/guide/recipes/conditional-mocking',
+ },
+ {
+ text: '按文件配置隔离',
+ link: '/guide/recipes/disable-isolation',
+ },
+ {
+ text: '并行和串行测试文件',
+ link: '/guide/recipes/parallel-sequential',
+ },
+ ],
},
{
- text: 'Locators',
- link: '/api/browser/locators',
+ text: '高级指南',
+ collapsed: true,
+ items: [
+ {
+ text: '快速开始',
+ link: '/guide/advanced/',
+ },
+ {
+ text: '运行测试 API',
+ link: '/guide/advanced/tests',
+ },
+ {
+ text: '扩展报告器',
+ link: '/guide/advanced/reporters',
+ },
+ {
+ text: '自定义运行池',
+ link: '/guide/advanced/pool',
+ },
+ {
+ text: 'Benchmark Provider',
+ link: '/guide/advanced/benchmark-provider',
+ },
+ ],
},
+ // Migration — one-time transitional content: cross-version
+ // upgrades and porting from other test runners (Jest, Mocha).
+ // Sits near the bottom because it's not daily-use and would push
+ // active-use guides further from the user's first scroll.
{
- text: 'Assertions API',
- link: '/api/browser/assertions',
+ text: '迁移',
+ link: '/guide/migration',
+ collapsed: false,
+ items: [
+ {
+ text: '迁移至 Vitest 5.0',
+ link: '/guide/migration/',
+ },
+ {
+ text: '从 Jest 迁移',
+ link: '/guide/migration/jest',
+ },
+ {
+ text: '从 Mocha + Chai + Sinon 迁移',
+ link: '/guide/migration/mocha',
+ },
+ ],
},
{
- text: 'Commands API',
- link: '/api/browser/commands',
+ items: [
+ {
+ text: '测试框架比较',
+ link: '/guide/comparisons',
+ },
+ ],
},
],
- },
- {
- text: '高级 API',
- collapsed: true,
- items: [
- {
- text: 'Vitest',
- link: '/api/advanced/vitest',
- },
- {
- text: 'TestProject',
- link: '/api/advanced/test-project',
- },
- {
- text: 'TestSpecification',
- link: '/api/advanced/test-specification',
- },
+ '/api': [
{
- text: 'TestCase',
- link: '/api/advanced/test-case',
+ text: 'Test API',
+ items: [
+ {
+ text: 'Test',
+ link: '/api/test',
+ },
+ {
+ text: 'Describe',
+ link: '/api/describe',
+ },
+ {
+ text: 'Hooks',
+ link: '/api/hooks',
+ },
+ ],
},
{
- text: 'TestSuite',
- link: '/api/advanced/test-suite',
+ text: 'Mocks',
+ link: '/api/mock',
},
{
- text: 'TestModule',
- link: '/api/advanced/test-module',
+ text: 'Vi Utility',
+ link: '/api/vi',
},
{
- text: 'TestCollection',
- link: '/api/advanced/test-collection',
+ text: 'Expect',
+ link: '/api/expect',
},
{
- text: 'VitestPlugin',
- link: '/api/advanced/plugin',
+ text: 'ExpectTypeOf',
+ link: '/api/expect-typeof',
},
{
- text: 'VitestRunner',
- link: '/api/advanced/runner',
+ text: 'Assert',
+ link: '/api/assert',
},
{
- text: 'Reporter',
- link: '/api/advanced/reporters',
+ text: 'AssertType',
+ link: '/api/assert-type',
},
{
- text: 'TaskMeta',
- link: '/api/advanced/metadata',
+ text: '浏览器模式',
+ items: [
+ {
+ text: 'Render Function',
+ collapsed: false,
+ items: [
+ {
+ text: 'react',
+ link: '/api/browser/react',
+ },
+ {
+ text: 'vue',
+ link: '/api/browser/vue',
+ },
+ {
+ text: 'svelte',
+ link: '/api/browser/svelte',
+ },
+ // {
+ // text: 'angular',
+ // link: '/api/browser/angular',
+ // },
+ ],
+ },
+ {
+ text: 'Context',
+ link: '/api/browser/context',
+ },
+ {
+ text: 'Interactivity',
+ link: '/api/browser/interactivity',
+ },
+ {
+ text: 'Locators',
+ link: '/api/browser/locators',
+ },
+ {
+ text: 'Assertions',
+ link: '/api/browser/assertions',
+ },
+ {
+ text: 'Commands',
+ link: '/api/browser/commands',
+ },
+ ],
},
{
- text: 'TestArtifact',
- link: '/api/advanced/artifacts',
+ text: '高级 API',
+ collapsed: false,
+ items: [
+ {
+ text: 'Vitest',
+ link: '/api/advanced/vitest',
+ },
+ {
+ text: 'TestProject',
+ link: '/api/advanced/test-project',
+ },
+ {
+ text: 'TestSpecification',
+ link: '/api/advanced/test-specification',
+ },
+ {
+ text: 'TestCase',
+ link: '/api/advanced/test-case',
+ },
+ {
+ text: 'TestSuite',
+ link: '/api/advanced/test-suite',
+ },
+ {
+ text: 'TestModule',
+ link: '/api/advanced/test-module',
+ },
+ {
+ text: 'TestCollection',
+ link: '/api/advanced/test-collection',
+ },
+ {
+ text: 'VitestPlugin',
+ link: '/api/advanced/plugin',
+ },
+ {
+ text: 'VitestRunner',
+ link: '/api/advanced/runner',
+ },
+ {
+ text: 'Reporter',
+ link: '/api/advanced/reporters',
+ },
+ {
+ text: 'TaskMeta',
+ link: '/api/advanced/metadata',
+ },
+ {
+ text: 'TestArtifact',
+ link: '/api/advanced/artifacts',
+ },
+ ],
},
+ // {
+ // text: 'Text Runner',
+ // collapsed: false,
+ // items: [
+ // // TODO: generate
+ // {
+ // text: 'test',
+ // link: '/api/test',
+ // },
+ // {
+ // text: 'describe',
+ // link: '/api/describe',
+ // },
+ // {
+ // text: 'beforeEach',
+ // link: '/api/before-each',
+ // },
+ // {
+ // text: 'afterEach',
+ // link: '/api/after-each',
+ // },
+ // ],
+ // },
+ // {
+ // text: 'Assertion API',
+ // collapsed: false,
+ // items: [
+ // {
+ // text: 'expect',
+ // link: '/api/expect',
+ // },
+ // {
+ // text: 'assert',
+ // link: '/api/assert',
+ // },
+ // {
+ // text: 'expectTypeOf',
+ // link: '/api/expect-typeof',
+ // },
+ // {
+ // text: 'assertType',
+ // link: '/api/assert-type',
+ // },
+ // ],
+ // },
+ // {
+ // text: 'Vi Utility API',
+ // collapsed: false,
+ // items: [
+ // {
+ // text: 'Mock Modules',
+ // link: '/api/vi/mock-modiles',
+ // },
+ // {
+ // text: 'Mock Functions',
+ // link: '/api/vi/mock-functions',
+ // },
+ // {
+ // text: 'Mock Timers',
+ // link: '/api/vi/mock-timers',
+ // },
+ // {
+ // text: 'Miscellaneous',
+ // link: '/api/vi/miscellaneous',
+ // },
+ // ],
+ // },
+ // {
+ // text: 'Browser Mode',
+ // collapsed: false,
+ // items: [
+ // // TODO: generate
+ // {
+ // text: 'page',
+ // link: '/api/browser/page',
+ // },
+ // {
+ // text: 'locators',
+ // link: '/api/browser/locators',
+ // },
+ // ],
+ // },
],
},
- // {
- // text: 'Text Runner',
- // collapsed: false,
- // items: [
- // // TODO: generate
- // {
- // text: 'test',
- // link: '/api/test',
- // },
- // {
- // text: 'describe',
- // link: '/api/describe',
- // },
- // {
- // text: 'beforeEach',
- // link: '/api/before-each',
- // },
- // {
- // text: 'afterEach',
- // link: '/api/after-each',
- // },
- // ],
- // },
- // {
- // text: 'Assertion API',
- // collapsed: false,
- // items: [
- // {
- // text: 'expect',
- // link: '/api/expect',
- // },
- // {
- // text: 'assert',
- // link: '/api/assert',
- // },
- // {
- // text: 'expectTypeOf',
- // link: '/api/expect-typeof',
- // },
- // {
- // text: 'assertType',
- // link: '/api/assert-type',
- // },
- // ],
- // },
- // {
- // text: 'Vi Utility API',
- // collapsed: false,
- // items: [
- // {
- // text: 'Mock Modules',
- // link: '/api/vi/mock-modiles',
- // },
- // {
- // text: 'Mock Functions',
- // link: '/api/vi/mock-functions',
- // },
- // {
- // text: 'Mock Timers',
- // link: '/api/vi/mock-timers',
- // },
- // {
- // text: 'Miscellaneous',
- // link: '/api/vi/miscellaneous',
- // },
- // ],
- // },
- // {
- // text: 'Browser Mode',
- // collapsed: false,
- // items: [
- // // TODO: generate
- // {
- // text: 'page',
- // link: '/api/browser/page',
- // },
- // {
- // text: 'locators',
- // link: '/api/browser/locators',
- // },
- // ],
- // },
- ],
- },
- },
- pwa,
- transformHead,
- })))
+ },
+ pwa,
+ transformHead,
+ }),
+ ),
+ )
}
diff --git a/.vitepress/contributors.ts b/.vitepress/contributors.ts
index 6499e1e3..042b4e70 100644
--- a/.vitepress/contributors.ts
+++ b/.vitepress/contributors.ts
@@ -95,7 +95,7 @@ const plainTeamMembers: CoreTeam[] = [
bluesky: 'https://bsky.app/profile/userquin.bsky.social',
mastodon: 'https://elk.zone/m.webtoo.ls/@userquin',
title: 'A fullstack and android developer',
- desc: 'Vite\'s fanatical follower',
+ desc: "Vite's fanatical follower",
},
{
avatar: getAvatarUrl('patak-cat'),
@@ -147,7 +147,7 @@ const plainTeamEmeritiMembers: CoreTeam[] = [
avatar: getAvatarUrl('poyoho'),
name: 'Yoho Po',
github: 'poyoho',
- title: 'It\'s no problem in my locall',
+ title: "It's no problem in my locall",
desc: 'Core team member of Vite & Team member of Vitest',
},
{
@@ -200,8 +200,8 @@ const plainTranslationTeamMembers: CoreTeam[] = [
},
]
-const teamMembers = plainTeamMembers.map(tm => createLinks(tm))
-const teamEmeritiMembers = plainTeamEmeritiMembers.map(tm => createLinks(tm))
-const translationTeamMembers = plainTranslationTeamMembers.map(tm => createLinks(tm))
+const teamMembers = plainTeamMembers.map((tm) => createLinks(tm))
+const teamEmeritiMembers = plainTeamEmeritiMembers.map((tm) => createLinks(tm))
+const translationTeamMembers = plainTranslationTeamMembers.map((tm) => createLinks(tm))
export { teamEmeritiMembers, teamMembers, translationTeamMembers }
diff --git a/.vitepress/meta.ts b/.vitepress/meta.ts
index 843ca19a..d3ba0010 100644
--- a/.vitepress/meta.ts
+++ b/.vitepress/meta.ts
@@ -29,5 +29,8 @@ export const preconnectHomeLinks = [googleapis, gstatic]
/* PWA runtime caching urlPattern regular expressions */
export const pwaFontsRegex = new RegExp(`^${googleapis}/.*`, 'i')
export const pwaFontStylesRegex = new RegExp(`^${gstatic}/.*`, 'i')
-// eslint-disable-next-line prefer-regex-literals
-export const githubusercontentRegex = new RegExp('^https://((i.ibb.co)|((raw|user-images).githubusercontent.com))/.*', 'i')
+// oxlint-disable-next-line prefer-regex-literals
+export const githubusercontentRegex = new RegExp(
+ '^https://((i.ibb.co)|((raw|user-images).githubusercontent.com))/.*',
+ 'i',
+)
diff --git a/.vitepress/scripts/cli-generator.ts b/.vitepress/scripts/cli-generator.ts
index 6a4e98e8..717b037b 100644
--- a/.vitepress/scripts/cli-generator.ts
+++ b/.vitepress/scripts/cli-generator.ts
@@ -1,18 +1,15 @@
-import type { CLIOption, CLIOptions } from '../../../packages/vitest/src/node/cli/cli-config'
+import type { CLIOption, CLIOptions } from '../../../packages/vitest/src/node/cli/cli-config.ts'
import { writeFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
-import { cliOptionsConfig } from '../../../packages/vitest/src/node/cli/cli-config'
+import { cliOptionsConfig } from '../../../packages/vitest/src/node/cli/cli-config.ts'
const docsDir = resolve(dirname(fileURLToPath(import.meta.url)), '../..')
const cliTablePath = resolve(docsDir, './guide/cli-generated.md')
const nonNullable = (value: T): value is NonNullable => value !== null && value !== undefined
-const skipCli = new Set([
- 'mergeReports',
- 'shard',
-])
+const skipCli = new Set(['mergeReports', 'shard'])
const skipConfig = new Set([
'config',
@@ -45,12 +42,14 @@ const skipConfig = new Set([
])
function resolveOptions(options: CLIOptions, parentName?: string) {
- return Object.entries(options).flatMap(
- ([subcommandName, subcommandConfig]) => resolveCommand(
- parentName ? `${parentName}.${subcommandName}` : subcommandName,
- subcommandConfig,
- ),
- ).filter(nonNullable)
+ return Object.entries(options)
+ .flatMap(([subcommandName, subcommandConfig]) =>
+ resolveCommand(
+ parentName ? `${parentName}.${subcommandName}` : subcommandName,
+ subcommandConfig,
+ ),
+ )
+ .filter(nonNullable)
}
function resolveCommand(name: string, config: CLIOption | null): any {
@@ -63,7 +62,7 @@ function resolveCommand(name: string, config: CLIOption | null): any {
title += `-${config.shorthand}, `
}
title += `--${config.alias || name}`
- if ('argument' in config) {
+ if ('argument' in config && config.argument) {
title += ` ${config.argument}`
}
title += '`'
@@ -80,13 +79,18 @@ function resolveCommand(name: string, config: CLIOption | null): any {
const options = resolveOptions(cliOptionsConfig)
-const template = options.map((option) => {
- const title = option.title
- const cli = option.cli
- const [page, ...hash] = (title.startsWith('browser.') ? title.slice(8) : title).toLowerCase().split('.')
- const config = skipConfig.has(title) ? '' : `[${title}](${title.includes('browser.') ? '/config/browser/' : '/config/'}${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''})`
-
- return `### ${title}\n\n- **CLI:** ${cli}\n${config ? `- **Config:** ${config}\n` : ''}\n${option.description.replace(/https:\/\/vitest\.dev\//g, '/')}\n`
-}).join('\n')
+const template = options
+ .map((option) => {
+ const title = option.title
+ const cli = option.cli
+ const [page, ...hash] = (title.startsWith('browser.') ? title.slice(8) : title)
+ .toLowerCase()
+ .split('.')
+ const config = skipConfig.has(title)
+ ? ''
+ : `[${title}](${title.includes('browser.') ? '/config/browser/' : '/config/'}${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''})`
+ return `### ${title}\n\n- **CLI:** ${cli}\n${config ? `- **Config:** ${config}\n` : ''}\n${option.description.replace(/https:\/\/vitest\.dev\//g, '/')}\n`
+ })
+ .join('\n')
writeFileSync(cliTablePath, template, 'utf-8')
diff --git a/.vitepress/scripts/fetch-avatars.ts b/.vitepress/scripts/fetch-avatars.ts
index cc18db5d..360e9dbc 100644
--- a/.vitepress/scripts/fetch-avatars.ts
+++ b/.vitepress/scripts/fetch-avatars.ts
@@ -2,7 +2,7 @@ import { Buffer } from 'node:buffer'
import { existsSync, promises as fsp } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { dirname, join, resolve } from 'pathe'
-import { teamEmeritiMembers, teamMembers } from '../contributors'
+import { teamEmeritiMembers, teamMembers } from '../contributors.ts'
const docsDir = resolve(dirname(fileURLToPath(import.meta.url)), '../..')
@@ -17,8 +17,7 @@ async function download(url: string, fileName: string) {
try {
const image = await (await fetch(url)).arrayBuffer()
await fsp.writeFile(fileName, Buffer.from(image))
- }
- catch { }
+ } catch {}
}
async function fetchAvatars() {
@@ -26,7 +25,13 @@ async function fetchAvatars() {
await fsp.mkdir(dirAvatars, { recursive: true })
}
- await Promise.all([...teamEmeritiMembers, ...teamMembers].map(c => c.github).map(name => download(`https://github.com/${name}.png?size=100`, join(dirAvatars, `${name}.png`))))
+ await Promise.all(
+ [...teamEmeritiMembers, ...teamMembers]
+ .map((c) => c.github)
+ .map((name) =>
+ download(`https://github.com/${name}.png?size=100`, join(dirAvatars, `${name}.png`)),
+ ),
+ )
}
fetchAvatars()
diff --git a/.vitepress/scripts/pwa.ts b/.vitepress/scripts/pwa.ts
index 43369ab0..f9b5c150 100644
--- a/.vitepress/scripts/pwa.ts
+++ b/.vitepress/scripts/pwa.ts
@@ -49,12 +49,14 @@ export const pwa: PwaOptions = {
purpose: 'maskable',
},
],
- screenshots: [{
- src: 'og.jpg',
- sizes: '2258x1185',
- type: 'image/jpeg',
- label: `Screenshot of ${vitestName}`,
- }],
+ screenshots: [
+ {
+ src: 'og.jpg',
+ sizes: '2258x1185',
+ type: 'image/jpeg',
+ label: `Screenshot of ${vitestName}`,
+ },
+ ],
handle_links: 'preferred',
launch_handler: {
client_mode: ['navigate-existing', 'auto'],
diff --git a/.vitepress/scripts/transformHead.ts b/.vitepress/scripts/transformHead.ts
index 63b48a29..75a56ba5 100644
--- a/.vitepress/scripts/transformHead.ts
+++ b/.vitepress/scripts/transformHead.ts
@@ -1,5 +1,4 @@
import type { HeadConfig, TransformContext } from 'vitepress'
-
import { preconnectHomeLinks, preconnectLinks } from '../meta'
export async function transformHead({ pageData }: TransformContext): Promise {
diff --git a/.vitepress/theme/FeatureGrid.vue b/.vitepress/theme/FeatureGrid.vue
index f1915ae1..97fb47ec 100644
--- a/.vitepress/theme/FeatureGrid.vue
+++ b/.vitepress/theme/FeatureGrid.vue
@@ -5,16 +5,16 @@ import RiveAnimation from '@components/shared/RiveAnimation.vue'
-
+
-
- 基于 Vite 驱动
-
+
基于 Vite 驱动
- 可复用 Vite 的配置和插件,使得应用和测试之间保持一致。但是使用 Vitest 并不强制使用 Vite!
+ 可复用
+ Vite
+ 的配置和插件,使得应用和测试之间保持一致。但是使用 Vitest 并不强制使用 Vite!
-
- 与 Jest 兼容
-
+
与 Jest 兼容
支持 Expect 断言、快照测试、覆盖率等功能,从
Jest
@@ -36,27 +34,27 @@ import RiveAnimation from '@components/shared/RiveAnimation.vue'
-
+
-
- 智能且即时的 watch 模式
-
-
- 只重新运行相关变更,让测试也拥有 HMR 般的体验!
-
+
智能且即时的 watch 模式
+
只重新运行相关变更,让测试也拥有 HMR 般的体验!
-
+
-
- ESM、TypeScript、JSX 支持
-
+
ESM、TypeScript、JSX 支持
开箱即用的 ESM、TypeScript 和 JSX 支持,由
Oxc
diff --git a/.vitepress/theme/Hero.vue b/.vitepress/theme/Hero.vue
index b5aa9684..a19d5f9f 100644
--- a/.vitepress/theme/Hero.vue
+++ b/.vitepress/theme/Hero.vue
@@ -1,14 +1,14 @@
-
+
By
-
+
-
- 下一代测试框架
-
+
下一代测试框架
一个原生支持 Vite 的测试框架。快得惊人!
@@ -16,15 +16,26 @@
快速开始
-
+
查看源码
-
-
+
+
diff --git a/.vitepress/theme/Intro.vue b/.vitepress/theme/Intro.vue
index 2ee9ce8f..10e4d608 100644
--- a/.vitepress/theme/Intro.vue
+++ b/.vitepress/theme/Intro.vue
@@ -1,10 +1,14 @@
-
+
-
-
为什么是 Vitest?
+
+
为什么是 Vitest?
Vite 原生
diff --git a/.vitepress/theme/SupportedVersions.vue b/.vitepress/theme/SupportedVersions.vue
index dcb948bf..32219dcf 100644
--- a/.vitepress/theme/SupportedVersions.vue
+++ b/.vitepress/theme/SupportedVersions.vue
@@ -40,20 +40,15 @@ const checkedResult = computed(() => {
const satisfies = (targetVersion: string) => {
const compared = parseVersion(targetVersion)!
- return (
- parsedVersion.major === compared.major
- && parsedVersion.minor >= compared.minor
- )
+ return parsedVersion.major === compared.major && parsedVersion.minor >= compared.minor
}
- const satisfiesOneSupportedVersion
- = parsedVersion.major >= parsedViteVersion.major // Treat future major versions as supported
- || supportInfo.regularPatches.some(satisfies)
- || supportInfo.importantFixes.some(satisfies)
- || supportInfo.securityPatches.some(satisfies)
-
- return satisfiesOneSupportedVersion
- ? supportedVersionMessage
- : notSupportedVersionMessage
+ const satisfiesOneSupportedVersion =
+ parsedVersion.major >= parsedViteVersion.major || // Treat future major versions as supported
+ supportInfo.regularPatches.some(satisfies) ||
+ supportInfo.importantFixes.some(satisfies) ||
+ supportInfo.securityPatches.some(satisfies)
+
+ return satisfiesOneSupportedVersion ? supportedVersionMessage : notSupportedVersionMessage
})
function parseVersion(version: string) {
@@ -70,13 +65,11 @@ function parseVersion(version: string) {
return { major, minor, patch }
}
-function computeSupportInfo(
- version: NonNullable>,
-) {
+function computeSupportInfo(version: NonNullable>) {
const { major, minor } = version
const f = (versions: string[]) => {
return versions
- .map(v => previousMajorLatestMinors[v] ?? v)
+ .map((v) => previousMajorLatestMinors[v] ?? v)
.filter((version) => {
if (!isValidVitestVersion(version)) {
return false
@@ -98,16 +91,14 @@ function computeSupportInfo(
}
function versionsToText(versions: string[]) {
- versions = versions.map(v => `vitest@${v}`)
+ versions = versions.map((v) => `vitest@${v}`)
if (versions.length === 0) {
return ''
}
if (versions.length === 1) {
return versions[0]
}
- return (
- `${versions.slice(0, -1).join(', ')} and ${versions.at(-1)}`
- )
+ return `${versions.slice(0, -1).join(', ')} and ${versions.at(-1)}`
}
function isValidVitestVersion(version: string) {
@@ -134,21 +125,14 @@ function isValidVitestVersion(version: string) {
.
- All versions before these are no longer supported. Users should upgrade
- to receive updates.
+ All versions before these are no longer supported. Users should upgrade to receive updates.
If you're using Vitest
- , it is
- {{
- checkedResult.text
- }} .
+ , it
+ is {{ checkedResult.text }} .
diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts
index 0db709fa..c2ddd5f1 100644
--- a/.vitepress/theme/index.ts
+++ b/.vitepress/theme/index.ts
@@ -35,7 +35,11 @@ function getRedirectPath(url: URL) {
// /config/#reporters -> /config/reporters
// /config/#coverage-provider -> /config/coverage#coverage-provider
// /config/#browser.enabled -> /config/browser/enabled
- if (url.pathname === '/config' || url.pathname === '/config/' || url.pathname === '/config.html') {
+ if (
+ url.pathname === '/config' ||
+ url.pathname === '/config/' ||
+ url.pathname === '/config.html'
+ ) {
if (url.hash.startsWith('#browser.')) {
const [page, ...hash] = url.hash.slice('#browser.'.length).toLowerCase().split('-')
return `/config/browser/${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''}`
@@ -44,7 +48,11 @@ function getRedirectPath(url: URL) {
return `/config/${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''}`
}
// /guide/browser/config#browser.locators-testidattribute -> /config/browser/locators#browser-locators-testidattribute
- if (url.pathname === '/guide/browser/config' || url.pathname === '/guide/browser/config/' || url.pathname === '/guide/browser/config.html') {
+ if (
+ url.pathname === '/guide/browser/config' ||
+ url.pathname === '/guide/browser/config/' ||
+ url.pathname === '/guide/browser/config.html'
+ ) {
const [page, ...hash] = url.hash.slice('#browser.'.length).toLowerCase().split('-')
return `/config/browser/${page}${hash.length ? `#${[page, ...hash].join('-')}` : ''}`
}
diff --git a/.vitepress/theme/styles.css b/.vitepress/theme/styles.css
index ac0512b6..776336c2 100644
--- a/.vitepress/theme/styles.css
+++ b/.vitepress/theme/styles.css
@@ -1,9 +1,9 @@
-@import "@voidzero-dev/vitepress-theme/src/styles/index.css";
+@import '@voidzero-dev/vitepress-theme/src/styles/index.css';
@source "./**/*.vue";
/* Vitest */
-:root[data-variant="vitest"] {
+:root[data-variant='vitest'] {
--color-brand: #008039;
/* TODO: home page wcag-aa color contrast (remove this once fixed at void0 theme):
* - why vitest section and texts
@@ -13,19 +13,18 @@
--color-grey: #867e8e;
}
-:root.dark:not([data-theme])[data-variant="vitest"],
-:root[data-theme="dark"][data-variant="vitest"] {
+:root.dark:not([data-theme])[data-variant='vitest'],
+:root[data-theme='dark'][data-variant='vitest'] {
--color-brand: var(--color-zest);
}
-:root[data-variant="vitest"]:not(.dark):not([data-theme="light"]),
-:root[data-theme="light"][data-variant="vitest"] {
+:root[data-variant='vitest']:not(.dark):not([data-theme='light']),
+:root[data-theme='light'][data-variant='vitest'] {
--color-brand: #008039;
/* TODO: code block (remove this once fixed at void0 theme) */
--vp-code-color: #007d38;
}
-
.highlighted-word {
background-color: var(--vp-code-line-highlight-color);
transition: background-color 0.5s;
@@ -39,6 +38,34 @@
margin: 1rem 0 0.5rem;
}
+.vp-doc a.button,
+.vp-doc a.button:hover {
+ color: revert-layer;
+ text-decoration: none;
+ transition: revert-layer;
+}
+
+.button.button--brand {
+ --button-brand-outline: color-mix(in srgb, var(--color-brand) 40%, transparent);
+ background-color: var(--vp-c-tip-soft);
+ outline-color: var(--button-brand-outline);
+}
+
+.button.button--brand:hover {
+ background-color: color-mix(in srgb, var(--color-brand) 24%, transparent);
+}
+
+.dark .button.button--brand:hover {
+ color: var(--color-white);
+}
+
+/* the base reset removes the outline on a clicked button, but .button draws its border with it */
+@layer base {
+ button.button--brand:focus:not(:focus-visible) {
+ outline: 1px solid var(--button-brand-outline) !important;
+ }
+}
+
/* credit goes to https://dylanatsmith.com/wrote/styling-the-kbd-element */
html:not(.dark) .VPContent kbd {
--kbd-color-background: #f7f7f7;
diff --git a/api/advanced/artifacts.md b/api/advanced/artifacts.md
index 0243944c..51a7dc3a 100644
--- a/api/advanced/artifacts.md
+++ b/api/advanced/artifacts.md
@@ -2,7 +2,9 @@
outline: deep
title: Test Artifacts
---
+
+
# Test Artifacts
4.0.11
::: warning
diff --git a/api/advanced/plugin.md b/api/advanced/plugin.md
index 1940d115..5ee10998 100644
--- a/api/advanced/plugin.md
+++ b/api/advanced/plugin.md
@@ -14,6 +14,7 @@ outline: deep
Vitest 自 3.1 版起支持 `configureVitest` [插件](https://cn.vite.dev/guide/api-plugin) hook。
::: code-group
+
```ts [only vitest]
import type { Vite, VitestPluginContext } from 'vitest/node'
@@ -26,6 +27,7 @@ export function plugin(): Vite.Plugin {
}
}
```
+
```ts [vite and vitest]
///
@@ -43,14 +45,16 @@ export function plugin(): Plugin {
}
}
```
+
:::
::: tip TypeScript
-Vitest 通过 `Vite` namespace 重新导出所有仅 Vite 类型的导入,我们可以使用它来保持版本同步。但是,如果我们正在为 Vite 和 Vitest 编写插件,则可以继续使用 `vite` 入口点的 `Plugin` 类型。只需确保我们在某处引用了 `vitest/config` ,以便正确增强 `configureVitest` 即可:
+Vitest 通过 `Vite` namespace 重新导出所有仅 Vite 类型的导入,我们可以使用它来保持版本同步。但是,如果我们正在为 Vite 和 Vitest 编写插件,则可以继续使用 `vite` 入口点的 `Plugin` 类型。只需确保我们在某处引用了 `vitest/config` ,以便正确增强 `configureVitest` 即可:
```ts
///
```
+
:::
与 [`reporter.onInit`](/api/advanced/reporters#oninit) 不同,此 hooks 在 Vitest 生命周期的早期运行,允许我们更改 `coverage` 和 `reporters` 等配置。更值得注意的变化是,如果我们的插件是在项目中定义而不是在全局配置中定义的,我们可以从 [工作区项目](/guide/projects) 操作全局配置。
@@ -124,7 +128,6 @@ vitest.config.project.push('my-project-name')
请注意,`name` 永远不会被继承,因为 Vitest 不允许多个项目使用相同的名称。请确保每个项目都有唯一的名称。可以通过 `project.name` 属性访问当前名称,所有已使用的名称都可以在 `vitest.projects` 数组中找到。
:::
-
### defineCacheKeyGenerator
5.0.0 {#definecachekeygenerator}
```ts
diff --git a/api/advanced/reporters.md b/api/advanced/reporters.md
index ea1cf7b7..7ac12871 100644
--- a/api/advanced/reporters.md
+++ b/api/advanced/reporters.md
@@ -61,6 +61,7 @@ class MyReporter implements Reporter {
export default new MyReporter()
```
+
:::
## onBrowserInit {#onbrowserinit}
@@ -95,6 +96,7 @@ class MyReporter implements Reporter {
export default new MyReporter()
```
+
:::
## onTestRunEnd
@@ -158,6 +160,7 @@ class MyReporter implements Reporter {
export default new MyReporter()
```
+
:::
## onCoverage
diff --git a/api/advanced/runner.md b/api/advanced/runner.md
index ab31176a..ee7f6944 100644
--- a/api/advanced/runner.md
+++ b/api/advanced/runner.md
@@ -138,6 +138,7 @@ export default class Runner {
}
}
```
+
:::
::: warning
@@ -263,16 +264,18 @@ export interface TaskResult {
*/
hooks?: Partial
>
/**
- * 任务重试的次数。只有在任务失败且设置了 `retry` 选项时才会进行重试。
+ * The number of times the task has been retried across all repeats.
*/
retryCount?: number
/**
- * 任务重复的次数。只有在设置了 `repeats` 选项时才会重复任务。此数字也包括 `retryCount`。
+ * The zero-based index of the current repeat.
*/
repeatCount?: number
}
```
+
+
## 你的任务函数 {#your-task-function}
Vitest 提供了 `createTaskCollector` 工具来创建你自己的 `test` 方法。它的行为与测试相同,但在收集期间会调用自定义方法。
diff --git a/api/advanced/test-case.md b/api/advanced/test-case.md
index fd05f14d..740532be 100644
--- a/api/advanced/test-case.md
+++ b/api/advanced/test-case.md
@@ -143,6 +143,7 @@ test('the validation works correctly', ({ task }) => {
task.meta.decorated = false
})
```
+
如果测试尚未运行完毕,元数据将是一个空对象,除非它定义了静态元数据:
```ts
diff --git a/api/advanced/test-collection.md b/api/advanced/test-collection.md
index f19c83db..8b391f00 100644
--- a/api/advanced/test-collection.md
+++ b/api/advanced/test-collection.md
@@ -12,6 +12,7 @@ for (const child of module.children) {
console.log(child.type, child.name)
}
```
+
:::
## size
diff --git a/api/advanced/test-project.md b/api/advanced/test-project.md
index d5e6dfb5..3d963052 100644
--- a/api/advanced/test-project.md
+++ b/api/advanced/test-project.md
@@ -13,6 +13,7 @@ title: TestProject
名称是由用户分配或由 Vitest 解析的唯一字符串。如果用户没有提供名称,Vitest 会尝试加载项目根目录中的 `package.json` 并从中获取 `name` 属性。如果没有 `package.json`,Vitest 默认使用文件夹的名称。内联项目使用数字作为名称(转换为字符串)。
::: code-group
+
```ts [node.js]
import { createVitest } from 'vitest/node'
@@ -49,6 +50,7 @@ export default defineConfig({
},
})
```
+
:::
::: info
@@ -75,6 +77,7 @@ const config: SerializedConfig = vitest.projects[0].serializedConfig
```ts
project.serializedConfig === project.serializedConfig // ❌
```
+
:::
## globalConfig
@@ -129,6 +132,7 @@ function provide(
除了 [`config.provide`](/config/provide) 字段外,还提供了一种向测试提供自定义值的方法。所有值在存储之前都通过 [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone) 进行验证,但 `providedContext` 上的值本身不会被克隆。
::: code-group
+
```ts [node.js]
import { createVitest } from 'vitest/node'
@@ -142,6 +146,7 @@ await vitest.start()
import { inject } from 'vitest'
const value = inject('key')
```
+
:::
这些值可以动态提供。测试中提供的值将在下次运行时更新。
@@ -154,6 +159,7 @@ export default function setup({ provide }) {
provide('wsPort', 3000)
}
```
+
:::
## getProvidedContext
@@ -296,6 +302,7 @@ const dynamicExample = await project.import('./example.js')
dynamicExample !== staticExample // ✅
```
+
:::
::: info
diff --git a/api/advanced/test-specification.md b/api/advanced/test-specification.md
index d403ef4c..d69b80e2 100644
--- a/api/advanced/test-specification.md
+++ b/api/advanced/test-specification.md
@@ -55,12 +55,14 @@ Vite 模块图中的模块 ID。通常,它是一个使用 POSIX 分隔符的
请注意,如果这些行中的至少一行没有测试,整个测试套件将会失败。以下是一个正确的 `testLines` 配置示例:
::: code-group
+
```ts [script.js]
const specification = project.createSpecification(
resolve('./example.test.ts'),
[3, 8, 9],
)
```
+
```ts:line-numbers{3,8,9} [example.test.js]
import { test, describe } from 'vitest'
@@ -73,6 +75,7 @@ describe('a group of tests', () => { // [!code error]
test.skip('skipped test')
})
```
+
:::
## testNamePattern 4.1.0 {#testnamepattern}
diff --git a/api/advanced/vitest.md b/api/advanced/vitest.md
index dcee42e5..dc32cfa3 100644
--- a/api/advanced/vitest.md
+++ b/api/advanced/vitest.md
@@ -44,7 +44,42 @@ const testCase = vitest.state.getReportedEntity(task) // 新 API
## cache
-缓存管理器,存储有关最新测试结果和测试文件状态的信息。在 Vitest 中,这仅由默认的排序器用于排序测试。
+
+
+Cache manager that stores the results of the previous test runs. In Vitest itself this is only used by the default sequencer to sort tests.
+
+### cache.getTestSpecificationResult 5.0.4 {#cache-gettestspecificationresult}
+
+Returns the result of the test file from the previous test runs, or `undefined` if Vitest has no result for it.
+
+```ts
+function getTestSpecificationResult(
+ specification: TestSpecification
+): CachedTestFileResult | undefined
+
+interface CachedTestFileResult {
+ /**
+ * The file has a known failure.
+ * Only a complete run of the file clears it.
+ */
+ failed: boolean
+ /**
+ * Duration of the last complete run in milliseconds.
+ */
+ duration: number
+ /**
+ * Unix timestamp in milliseconds of the start of the last complete run.
+ * It is not set if the file never ran completely.
+ */
+ lastRun?: number
+}
+```
+
+A run is not complete if it was cancelled or if it executed only a part of the file, for example, because of a test name pattern, a line filter or `.only`.
+
+::: warning
+`vitest.cache.getFileTestResults` and `vitest.cache.getFileStats` are deprecated and will be removed in the next major version. Use `getTestSpecificationResult` instead of `getFileTestResults`. Vitest does not cache file sizes anymore; read the size from the file system if your sequencer needs it.
+:::
## watcher 4.0.0 {#watcher}
@@ -410,6 +445,7 @@ const dynamicExample = await vitest.import('./example.js')
dynamicExample !== staticExample // ✅
```
+
:::
::: info
@@ -587,6 +623,7 @@ import { escapeTestName } from 'vitest/node'
// 转换为 /hello, .+?/
const escapedPattern = new RegExp(escapeTestName('hello, %s', true))
```
+
:::
::: warning
@@ -667,6 +704,7 @@ export interface SourceModuleDiagnostic {
untrackedModules: UntrackedModuleDefinitionDiagnostic[]
}
```
+
:::
返回模块的诊断信息。如果未提供 [`testModule`](/api/advanced/test-module),则 `selfTime` 和 `totalTime` 将聚合上次运行的所有测试。如果模块未被转换或执行,诊断信息将为空。
@@ -778,7 +816,6 @@ const filenames: string[] = await report.readdir()
### Report.delete
-
```ts
function delete(filename: string): Promise
```
diff --git a/api/assert.md b/api/assert.md
index c2c0410a..4bf82141 100644
--- a/api/assert.md
+++ b/api/assert.md
@@ -6,7 +6,8 @@ Vitest 从 [`chai`](https://www.chaijs.com/api/assert/) 重新导出了 `assert`
在 [源码内联测试](/guide/in-source) 中使用来自 `import.meta.vitest` 的 [断言函数](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions)(例如 `assert`)时,TypeScript 会报告 `TS2775` 错误,因为这类函数必须通过带有显式类型注解的名称来调用。请为该变量标注 Chai.Assert,或直接调用它:
::: code-group
-```ts [类型注释变量]
+
+```ts [带类型注解的变量]
if (import.meta.vitest) {
const { test, assert } = import.meta.vitest // [!code --]
const { test } = import.meta.vitest // [!code ++]
@@ -17,6 +18,7 @@ if (import.meta.vitest) {
})
}
```
+
```ts [直接调用]
if (import.meta.vitest) {
const { test, assert } = import.meta.vitest // [!code --]
@@ -28,6 +30,7 @@ if (import.meta.vitest) {
})
}
```
+
:::
## assert
@@ -763,7 +766,7 @@ test('assert.notInclude', () => {
- `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void`
- `(haystack: T, needle: T extends WeakSet ? never : Partial, message?: string) => void`
-断言 `haystack` 包含 `needle` 。可以用来断言数组中是否包含一个值或对象中是否包含一组属性。使用深度相等。
+断言 `haystack` 包含 `needle` 。可以用来断言数组中是否包含一个值或对象中是否包含一组属性。使用深度相等。
```ts
import { assert, test } from 'vitest'
diff --git a/api/browser/assertions.md b/api/browser/assertions.md
index b6558b6d..c56f944a 100644
--- a/api/browser/assertions.md
+++ b/api/browser/assertions.md
@@ -12,6 +12,7 @@ Vitest 默认提供了一组丰富的 DOM 断言,这些断言源自 [`@testing
```ts
///
```
+
:::
浏览器中的测试由于其异步特性,可能会不一致地失败。因此,即使条件延迟(如超时、网络请求或动画),也必须有办法保证断言成功。为此,Vitest 通过 [`expect.poll`](/api/expect#poll) 和 `expect.element` API 提供了可重试的断言:
@@ -62,6 +63,7 @@ interface ExpectPollOptions {
// 如果 .textContent 不是 `'Error!'`,则会立即失败。
expect(banner).toMatchTextContent('Error!')
```
+
:::
## toBeDisabled
@@ -697,7 +699,7 @@ await expect.element(button).not.toHaveStyle({
```ts
function toHaveTextContent(
- text: string | number,
+ text?: string | number,
options?: { normalizeWhitespace: boolean }
): Promise
```
@@ -715,6 +717,10 @@ const element = getByTestId('text-content')
await expect.element(element).toHaveTextContent('Text Content')
await expect.element(element).not.toHaveTextContent('Content')
+// element has some text content
+await expect.element(element).toHaveTextContent()
+// element has no text content
+await expect.element(page.getByTestId('empty')).not.toHaveTextContent()
```
## toMatchTextContent
@@ -965,6 +971,7 @@ await expect.element(getByTestId('link-invalid')).toHaveRole('generic')
await expect.element(getByTestId('switch')).toHaveRole('switch') // ✅
await expect.element(getByTestId('switch')).toHaveRole('alert') // ❌
```
+
:::
## toHaveSelection
@@ -1137,6 +1144,7 @@ await expect.element(getByTestId('button')).toMatchScreenshot('fancy-button', {
},
})
```
+
:::
- `screenshotOptions: object`
diff --git a/api/browser/commands.md b/api/browser/commands.md
index 1a727878..9ae8c50d 100644
--- a/api/browser/commands.md
+++ b/api/browser/commands.md
@@ -193,8 +193,7 @@ export const uploadFixture: BrowserCommand<[name: string]> = async (
如果未启用浏览器追踪,或当前会话中没有正在运行的测试,`context.mark` 不会执行任何操作。与 `page.mark` 不同,它不支持传入回调函数。
-
-### 自定义 `playwright` 命令 {#custom-playwright-commands}
+### 自定义 `playwright` 命令 {#custom-playwright-commands}
Vitest 在命令上下文中公开了几个`playwright`特定属性。
diff --git a/api/browser/context.md b/api/browser/context.md
index 90b714b4..b828b9f7 100644
--- a/api/browser/context.md
+++ b/api/browser/context.md
@@ -191,7 +191,7 @@ await button.click() // 交互式方法可以正常工作 ✅
await expect.element(button).toBeVisible() // 查询元素失败 ❌
```
-如果你需要处理跨域 iframe,你需要在 [`launchOptions`](/config/browser/playwright.html#launchoptions) 中传递 `args: ["--disable-web-security"]`。或者创建一个自定义的 [浏览器命令](/api/browser/commands.html#custom-commands),在服务器端访问可用的 iframe。
+如果你需要处理跨域 iframe,你需要在 [`launchOptions`](/config/browser/playwright#launchoptions) 中传递 `args: ["--disable-web-security"]`。或者创建一个自定义的 [浏览器命令](/api/browser/commands#custom-commands),在服务器端访问可用的 iframe。
:::
::: danger 重要
diff --git a/api/browser/locators.md b/api/browser/locators.md
index fa7e2701..c5c46cf1 100644
--- a/api/browser/locators.md
+++ b/api/browser/locators.md
@@ -34,6 +34,7 @@ const deleteButton = page
await deleteButton.click()
await expect.element(deleteButton).toBeEnabled()
```
+
:::
## getByRole
@@ -88,11 +89,13 @@ await page.getByRole('button', { name: /submit/i }).click()
根据 ARIA 指南,**强烈不建议** 通过 `role` 或 `aria-*` 属性为已经具有隐式角色的内置元素提供角色。
:::
+
+
**Options**
- `exact: boolean`
- `name` 是否精确匹配:区分大小写且完全匹配字符串。默认情况下禁用此选项。如果 `name` 是正则表达式,则忽略此选项。请注意,精确匹配仍然会修剪空白字符。
+ Whether the `name` is matched exactly: case-sensitive and whole-string. Defaults to [`browser.locators.exact`](/config/browser/locators#browser-locators-exact), which is `true` by default. This option is ignored if `name` is a regular expression. Note that exact match still trims whitespace.
```tsx
Hello World
@@ -236,11 +239,13 @@ page.getByAltText(/incredibles.*? poster/i) // ✅
page.getByAltText('non existing alt text') // ❌
```
+
+
**Options**
- `exact: boolean`
- `text` 是否精确匹配:区分大小写且完全匹配字符串。默认情况下禁用此选项。如果 `text` 是正则表达式,则忽略此选项。请注意,精确匹配仍然会修剪空白字符。
+ Whether the `text` is matched exactly: case-sensitive and whole-string. Defaults to [`browser.locators.exact`](/config/browser/locators#browser-locators-exact), which is `true` by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace.
**更多内容请参阅**
@@ -283,11 +288,13 @@ function getByLabelText(
```
+
+
**Options**
- `exact: boolean`
- `text` 是否精确匹配:区分大小写且完全匹配字符串。默认情况下禁用此选项。如果 `text` 是正则表达式,则忽略此选项。请注意,精确匹配仍然会修剪空白字符。
+ Whether the `text` is matched exactly: case-sensitive and whole-string. Defaults to [`browser.locators.exact`](/config/browser/locators#browser-locators-exact), which is `true` by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace.
**更多内容请参阅**
@@ -315,11 +322,13 @@ page.getByPlaceholder('not found') // ❌
通常情况下,使用 [`getByLabelText`](#getbylabeltext) 依赖标签比依赖占位符更好。
:::
+
+
**Options**
- `exact: boolean`
- `text` 是否精确匹配:区分大小写且完全匹配字符串。默认情况下禁用此选项。如果 `text` 是正则表达式,则忽略此选项。请注意,精确匹配仍然会修剪空白字符。
+ Whether the `text` is matched exactly: case-sensitive and whole-string. Defaults to [`browser.locators.exact`](/config/browser/locators#browser-locators-exact), which is `true` by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace.
**更多内容请参阅**
@@ -347,11 +356,13 @@ page.getByText('about', { exact: true }) // ❌
此定位器适用于定位非交互式元素。如果你需要定位交互式元素,比如按钮或输入框,建议使用 [`getByRole`](#getbyrole)。
:::
+
+
**Options**
- `exact: boolean`
- `text` 是否精确匹配:区分大小写且完全匹配字符串。默认情况下禁用此选项。如果 `text` 是正则表达式,则忽略此选项。请注意,精确匹配仍然会修剪空白字符。
+ Whether the `text` is matched exactly: case-sensitive and whole-string. Defaults to [`browser.locators.exact`](/config/browser/locators#browser-locators-exact), which is `true` by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace.
**更多内容请参阅**
@@ -375,11 +386,13 @@ page.getByTitle('Delete') // ✅
page.getByTitle('Create') // ❌
```
+
+
**Options**
- `exact: boolean`
- `text` 是否精确匹配:区分大小写且完全匹配字符串。默认情况下禁用此选项。如果 `text` 是正则表达式,则忽略此选项。请注意,精确匹配仍然会修剪空白字符。
+ Whether the `text` is matched exactly: case-sensitive and whole-string. Defaults to [`browser.locators.exact`](/config/browser/locators#browser-locators-exact), which is `true` by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace.
**更多内容请参阅**
@@ -404,12 +417,6 @@ page.getByTestId('non-existing-element') // ❌
建议仅在其他定位器不适用于你的使用场景时才使用此方法。使用 `data-testid` 属性并不符合用户实际使用软件的方式,因此如果可能应避免使用。
:::
-**Options**
-
-- `exact: boolean`
-
- `text` 是否精确匹配:区分大小写和整个字符串。默认情况下禁用此选项。如果 `text` 是正则表达式,则忽略此选项。请注意,精确匹配仍然会修剪空格。
-
**更多内容请参阅**
- [testing-library's `ByTestId`](https://testing-library.com/docs/queries/bytestid/)
@@ -510,6 +517,7 @@ page.getByRole('button')
.or(page.getByRole('link'))
.click() // ❌ 匹配到多个元素
```
+
:::
## filter
@@ -561,6 +569,7 @@ page.getByRole('article')
.filter({ has: page.getByRole('button', { name: 'delete row' }) })
.filter({ has: page.getByText('Vitest') })
```
+
:::
### hasNot
@@ -926,6 +935,7 @@ function element(): Element
```ts
await expect.element(page.getByRole('button')).toBeDisabled()
```
+
:::
考虑以下 DOM 结构:
@@ -1133,6 +1143,7 @@ test('works correctly', async () => {
await commands.test(page.getByText('Hello')) // ✅
})
```
+
:::
### length
diff --git a/api/browser/react.md b/api/browser/react.md
index 169e8008..6874f44b 100644
--- a/api/browser/react.md
+++ b/api/browser/react.md
@@ -49,6 +49,7 @@ import { render } from 'vitest-browser-react'
const screen = render( ) // [!code --]
const screen = await render( ) // [!code ++]
```
+
:::
### 选项 {#options}
@@ -258,6 +259,7 @@ await renderHook(() => {}, {
wrapper: createWrapper(Wrapper, { value: 'foo' }),
})
```
+
:::
`renderHook` 返回包含以下工具方法和属性的对象:
diff --git a/api/browser/svelte.md b/api/browser/svelte.md
index e6db3af6..542e5861 100644
--- a/api/browser/svelte.md
+++ b/api/browser/svelte.md
@@ -206,6 +206,7 @@ await expect.element(
对于简单的代码片段,你可以使用包装组件和 “占位” 子元素进行测试。通过设置 `data-testid` 属性帮助测试插槽内容。
::: code-group
+
```ts [basic.test.js]
import { render } from 'vitest-browser-svelte'
import { expect, test } from 'vitest'
@@ -221,6 +222,7 @@ test('basic snippet', async () => {
await expect.element(child).toBeInTheDocument()
})
```
+
```svelte [basic-snippet.svelte]