Testing
测试有助于您编写和维护代码,并防止回归。测试框架可以帮助您完成这些任务,允许您描述关于代码应该如何行为的断言或期望。Svelte 对您使用的测试框架没有意见——您可以使用像 Vitest、Jasmine、Cypress 和 Playwright 这样的解决方案编写单元测试、集成测试和端到端测试。
单元和集成测试使用 Vitest
单元测试允许您测试代码的独立小部分。集成测试允许您测试应用程序的某些部分,以查看它们是否可以协同工作。如果您使用 Vite(包括通过 SvelteKit),我们建议使用Vitest。您可以使用 Svelte CLI 在项目创建期间或之后设置 Vitest。
设置 Vitest 的手动步骤,首先安装它:
npm install -D vitest然后调整你的 vite.config.js:
import { function defineConfig(config: UserConfig): UserConfig (+3 overloads)defineConfig } from 'vitest/config';
export default function defineConfig(config: UserConfig): UserConfig (+3 overloads)defineConfig({
// ...
// Tell Vitest to use the `browser` entry points in `package.json` files, even though it's running in Node
resolve?: AllResolveOptions | undefinedresolve: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnvThe process.env property returns an object containing the user environment.
See environ(7).
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker threads.
In other words, the following example would not work:
node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'node:process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env will implicitly convert the value
to a string. This behavior is deprecated. Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'node:process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete to delete a property from process.env.
import { env } from 'node:process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'node:process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker instance,
each Worker thread has its own copy of process.env, based on its
parent thread’s process.env, or whatever was specified as the env option
to the Worker constructor. Changes to process.env will not be visible
across Worker threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons. On Windows, a copy of process.env on a Worker instance operates in a case-sensitive manner
unlike the main thread.
env.string | undefinedVITEST
? {
EnvironmentResolveOptions.conditions?: string[] | undefinedconditions: ['browser']
}
: var undefinedundefined
});[!注意] 如果不希望加载所有包的浏览器版本,因为(例如)您还测试后端库,您可能需要求助于别名配置
您现在可以为您的.js/.ts文件中的代码编写单元测试了:
import { function flushSync<T = void>(fn?: (() => T) | undefined): TSynchronously flush any pending updates.
Returns void if no callback is provided, otherwise returns the result of calling the callback.
flushSync } from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPIDefines a test case with a given name and test function. The test function can optionally be configured with test options.
test } from 'vitest';
import { import multipliermultiplier } from './multiplier.svelte.js';
test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestCollectorOptions): void (+2 overloads)Defines a test case with a given name and test function. The test function can optionally be configured with test options.
test('Multiplier', () => {
let let double: anydouble = import multipliermultiplier(0, 2);
expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let double: anydouble.value).JestAssertion<any>.toEqual: <number>(expected: number) => voidUsed when you want to check that two objects have the same value.
This matcher recursively checks the equality of all fields, rather than checking for object identity.
toEqual(0);
let double: anydouble.set(5);
expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let double: anydouble.value).JestAssertion<any>.toEqual: <number>(expected: number) => voidUsed when you want to check that two objects have the same value.
This matcher recursively checks the equality of all fields, rather than checking for object identity.
toEqual(10);
});/**
* @param {number} initial
* @param {number} k
*/
export function function multiplier(initial: number, k: number): {
readonly value: number;
set: (c: number) => void;
}
multiplier(initial: numberinitial, k: numberk) {
let let count: numbercount = function $state<number>(initial: number): number (+1 overload)
namespace $state
$state(initial: numberinitial);
return {
get value: numbervalue() {
return let count: numbercount * k: numberk;
},
/** @param {number} c */
set: (c: number) => voidset: (c: numberc) => {
let count: numbercount = c: numberc;
}
};
}export function function multiplier(initial: number, k: number): {
readonly value: number;
set: (c: number) => void;
}
multiplier(initial: numberinitial: number, k: numberk: number) {
let let count: numbercount = function $state<number>(initial: number): number (+1 overload)
namespace $state
$state(initial: numberinitial);
return {
get value: numbervalue() {
return let count: numbercount * k: numberk;
},
set: (c: number) => voidset: (c: numberc: number) => {
let count: numbercount = c: numberc;
}
};
}使用您的测试文件中的符文
由于 Vitest 以与您的源文件相同的方式处理测试文件,只要文件名包含.svelte,您就可以在测试中使用 runes。
import { function flushSync<T = void>(fn?: (() => T) | undefined): TSynchronously flush any pending updates.
Returns void if no callback is provided, otherwise returns the result of calling the callback.
flushSync } from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPIDefines a test case with a given name and test function. The test function can optionally be configured with test options.
test } from 'vitest';
import { import multipliermultiplier } from './multiplier.svelte.js';
test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestCollectorOptions): void (+2 overloads)Defines a test case with a given name and test function. The test function can optionally be configured with test options.
test('Multiplier', () => {
let let count: numbercount = function $state<0>(initial: 0): 0 (+1 overload)
namespace $state
$state(0);
let let double: anydouble = import multipliermultiplier(() => let count: numbercount, 2);
expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let double: anydouble.value).JestAssertion<any>.toEqual: <number>(expected: number) => voidUsed when you want to check that two objects have the same value.
This matcher recursively checks the equality of all fields, rather than checking for object identity.
toEqual(0);
let count: numbercount = 5;
expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let double: anydouble.value).JestAssertion<any>.toEqual: <number>(expected: number) => voidUsed when you want to check that two objects have the same value.
This matcher recursively checks the equality of all fields, rather than checking for object identity.
toEqual(10);
});/**
* @param {() => number} getCount
* @param {number} k
*/
export function function multiplier(getCount: () => number, k: number): {
readonly value: number;
}
multiplier(getCount: () => numbergetCount, k: numberk) {
return {
get value: numbervalue() {
return getCount: () => numbergetCount() * k: numberk;
}
};
}export function function multiplier(getCount: () => number, k: number): {
readonly value: number;
}
multiplier(getCount: () => numbergetCount: () => number, k: numberk: number) {
return {
get value: numbervalue() {
return getCount: () => numbergetCount() * k: numberk;
}
};
}如果被测试的代码使用了效果,您需要将测试包裹在$effect.root内:
import { function flushSync<T = void>(fn?: (() => T) | undefined): TSynchronously flush any pending updates.
Returns void if no callback is provided, otherwise returns the result of calling the callback.
flushSync } from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPIDefines a test case with a given name and test function. The test function can optionally be configured with test options.
test } from 'vitest';
import { import loggerlogger } from './logger.svelte.js';
test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestCollectorOptions): void (+2 overloads)Defines a test case with a given name and test function. The test function can optionally be configured with test options.
test('Effect', () => {
const const cleanup: () => voidcleanup = namespace $effect
function $effect(fn: () => void | (() => void)): void
Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values.
The timing of the execution is after the DOM has been updated.
Example:
$effect(() => console.log('The count is now ' + count));
If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.
Does not run during server side rendering.
$effect.function $effect.root(fn: () => void | (() => void)): () => voidThe $effect.root rune is an advanced feature that creates a non-tracked scope that doesn’t auto-cleanup. This is useful for
nested effects that you want to manually control. This rune also allows for creation of effects outside of the component
initialisation phase.
Example:
<script>
let count = $state(0);
const cleanup = $effect.root(() => {
$effect(() => {
console.log(count);
})
return () => {
console.log('effect root cleanup');
}
});
</script>
<button onclick={() => cleanup()}>cleanup</button>
root(() => {
let let count: numbercount = function $state<0>(initial: 0): 0 (+1 overload)
namespace $state
$state(0);
// logger uses an $effect to log updates of its input
let let log: anylog = import loggerlogger(() => let count: numbercount);
// effects normally run after a microtask,
// use flushSync to execute all pending effects synchronously
flushSync<void>(fn?: (() => void) | undefined): voidSynchronously flush any pending updates.
Returns void if no callback is provided, otherwise returns the result of calling the callback.
flushSync();
expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let log: anylog).JestAssertion<any>.toEqual: <number[]>(expected: number[]) => voidUsed when you want to check that two objects have the same value.
This matcher recursively checks the equality of all fields, rather than checking for object identity.
toEqual([0]);
let count: numbercount = 1;
flushSync<void>(fn?: (() => void) | undefined): voidSynchronously flush any pending updates.
Returns void if no callback is provided, otherwise returns the result of calling the callback.
flushSync();
expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let log: anylog).JestAssertion<any>.toEqual: <number[]>(expected: number[]) => voidUsed when you want to check that two objects have the same value.
This matcher recursively checks the equality of all fields, rather than checking for object identity.
toEqual([0, 1]);
});
const cleanup: () => voidcleanup();
});/**
* @param {() => any} getValue
*/
export function function logger(getValue: () => any): any[]logger(getValue: () => anygetValue) {
/** @type {any[]} */
let let log: any[]log = [];
function $effect(fn: () => void | (() => void)): void
namespace $effect
Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values.
The timing of the execution is after the DOM has been updated.
Example:
$effect(() => console.log('The count is now ' + count));
If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.
Does not run during server side rendering.
$effect(() => {
let log: any[]log.Array<any>.push(...items: any[]): numberAppends new elements to the end of an array, and returns the new length of the array.
push(getValue: () => anygetValue());
});
return let log: any[]log;
}export function function logger(getValue: () => any): any[]logger(getValue: () => anygetValue: () => any) {
let let log: any[]log: any[] = [];
function $effect(fn: () => void | (() => void)): void
namespace $effect
Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values.
The timing of the execution is after the DOM has been updated.
Example:
$effect(() => console.log('The count is now ' + count));
If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.
Does not run during server side rendering.
$effect(() => {
let log: any[]log.Array<any>.push(...items: any[]): numberAppends new elements to the end of an array, and returns the new length of the array.
push(getValue: () => anygetValue());
});
return let log: any[]log;
}组件测试
可以使用 Vitest 单独测试您的组件。
[注意] 在编写组件测试之前,考虑你是否真的需要测试该组件,或者这更多的是关于组件内部的逻辑 内部。如果是这样,考虑提取出该逻辑以单独测试,而无需组件的额外开销。
要开始,安装 jsdom(一个模拟 DOM API 的库):
npm install -D jsdom然后调整你的 vite.config.js:
import { function defineConfig(config: UserConfig): UserConfig (+3 overloads)defineConfig } from 'vitest/config';
export default function defineConfig(config: UserConfig): UserConfig (+3 overloads)defineConfig({
UserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use.
plugins: [
/* ... */
],
UserConfig.test?: InlineConfig | undefinedOptions for Vitest
test: {
// If you are testing components client-side, you need to setup a DOM environment.
// If not all your files should have this environment, you can use a
// `// @vitest-environment jsdom` comment at the top of the test files instead.
InlineConfig.environment?: VitestEnvironment | undefinedRunning environment
Supports ‘node’, ‘jsdom’, ‘happy-dom’, ‘edge-runtime’
If used unsupported string, will try to load the package vitest-environment-${env}
environment: 'jsdom'
},
// Tell Vitest to use the `browser` entry points in `package.json` files, even though it's running in Node
resolve?: AllResolveOptions | undefinedresolve: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnvThe process.env property returns an object containing the user environment.
See environ(7).
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker threads.
In other words, the following example would not work:
node -e 'process.env.foo = "bar"' &#x26;&#x26; echo $foo
While the following will:
import { env } from 'node:process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env will implicitly convert the value
to a string. This behavior is deprecated. Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'node:process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete to delete a property from process.env.
import { env } from 'node:process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'node:process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker instance,
each Worker thread has its own copy of process.env, based on its
parent thread’s process.env, or whatever was specified as the env option
to the Worker constructor. Changes to process.env will not be visible
across Worker threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons. On Windows, a copy of process.env on a Worker instance operates in a case-sensitive manner
unlike the main thread.
env.string | undefinedVITEST
? {
EnvironmentResolveOptions.conditions?: string[] | undefinedconditions: ['browser']
}
: var undefinedundefined
});之后,您可以创建一个测试文件,在其中导入组件进行测试,以编程方式与之交互,并编写关于结果的预期:
import { function flushSync<T = void>(fn?: (() => T) | undefined): TSynchronously flush any pending updates.
Returns void if no callback is provided, otherwise returns the result of calling the callback.
flushSync, function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: MountOptions<Props>): ExportsMounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true) of the component.
Transitions will play during the initial render unless the intro option is set to false.
mount, function unmount(component: Record<string, any>, options?: {
outro?: boolean;
} | undefined): Promise<void>
Unmounts a component that was previously mounted using mount or hydrate.
Since 5.13.0, if options.outro is true, transitions will play before the component is removed from the DOM.
Returns a Promise that resolves after transitions have completed if options.outro is true, or immediately otherwise (prior to 5.13.0, returns void).
import { mount, unmount } from 'svelte';
import App from './App.svelte';
const app = mount(App, { target: document.body });
// later...
unmount(app, { outro: true });
unmount } from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPIDefines a test case with a given name and test function. The test function can optionally be configured with test options.
test } from 'vitest';
import type Component = SvelteComponent<Record<string, any>, any, any>
const Component: LegacyComponentType
Component from './Component.svelte';
test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestCollectorOptions): void (+2 overloads)Defines a test case with a given name and test function. The test function can optionally be configured with test options.
test('Component', () => {
// Instantiate the component using Svelte's `mount` API
const const component: {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>
component = mount<Record<string, any>, {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<...>>(component: ComponentType<...> | Component<...>, options: MountOptions<...>): {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<...>
Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true) of the component.
Transitions will play during the initial render unless the intro option is set to false.
mount(const Component: LegacyComponentTypeComponent, {
target: Document | Element | ShadowRootTarget element where the component will be mounted.
target: var document: Documentdocument.Document.body: HTMLElementSpecifies the beginning and end of the document body.
body, // `document` exists because of jsdom
props?: Record<string, any> | undefinedComponent properties.
props: { initial: numberinitial: 0 }
});
expect<string>(actual: string, message?: string): Assertion<string> (+1 overload)expect(var document: Documentdocument.Document.body: HTMLElementSpecifies the beginning and end of the document body.
body.Element.innerHTML: stringinnerHTML).JestAssertion<string>.toBe: <string>(expected: string) => voidChecks that a value is what you expect. It calls Object.is to compare values.
Don’t use toBe with floating-point numbers.
toBe('<button>0</button>');
// Click the button, then flush the changes so you can synchronously write expectations
var document: Documentdocument.Document.body: HTMLElementSpecifies the beginning and end of the document body.
body.ParentNode.querySelector<"button">(selectors: "button"): HTMLButtonElement | null (+4 overloads)Returns the first element that is a descendant of node that matches selectors.
querySelector('button').HTMLElement.click(): voidclick();
flushSync<void>(fn?: (() => void) | undefined): voidSynchronously flush any pending updates.
Returns void if no callback is provided, otherwise returns the result of calling the callback.
flushSync();
expect<string>(actual: string, message?: string): Assertion<string> (+1 overload)expect(var document: Documentdocument.Document.body: HTMLElementSpecifies the beginning and end of the document body.
body.Element.innerHTML: stringinnerHTML).JestAssertion<string>.toBe: <string>(expected: string) => voidChecks that a value is what you expect. It calls Object.is to compare values.
Don’t use toBe with floating-point numbers.
toBe('<button>1</button>');
// Remove the component from the DOM
function unmount(component: Record<string, any>, options?: {
outro?: boolean;
} | undefined): Promise<void>
Unmounts a component that was previously mounted using mount or hydrate.
Since 5.13.0, if options.outro is true, transitions will play before the component is removed from the DOM.
Returns a Promise that resolves after transitions have completed if options.outro is true, or immediately otherwise (prior to 5.13.0, returns void).
import { mount, unmount } from 'svelte';
import App from './App.svelte';
const app = mount(App, { target: document.body });
// later...
unmount(app, { outro: true });
unmount(const component: {
$on?(type: string, callback: (e: any) => void): () => void;
$set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>
component);
});虽然这个过程非常直接,但它也是低级且有些脆弱的,因为您的组件的精确结构可能会频繁变化。像@testing-library/svelte这样的工具可以帮助简化您的测试。上述测试可以重写如下:
import { function render<C extends unknown, Q extends Queries = typeof import("/vercel/path0/node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@testing-library/dom/types/queries")>(Component: ComponentType<...>, options?: SvelteComponentOptions<C>, renderOptions?: RenderOptions<Q>): RenderResult<C, Q>Render a component into the document.
render, const screen: Screen<typeof import("/vercel/path0/node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@testing-library/dom/types/queries")>screen } from '@testing-library/svelte';
import const userEvent: {
readonly setup: typeof setupMain;
readonly clear: typeof clear;
readonly click: typeof click;
readonly copy: typeof copy;
... 12 more ...;
readonly tab: typeof tab;
}
userEvent from '@testing-library/user-event';
import { const expect: ExpectStaticexpect, const test: TestAPIDefines a test case with a given name and test function. The test function can optionally be configured with test options.
test } from 'vitest';
import type Component = SvelteComponent<Record<string, any>, any, any>
const Component: LegacyComponentType
Component from './Component.svelte';
test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestCollectorOptions): void (+2 overloads)Defines a test case with a given name and test function. The test function can optionally be configured with test options.
test('Component', async () => {
const const user: UserEventuser = const userEvent: {
readonly setup: typeof setupMain;
readonly clear: typeof clear;
readonly click: typeof click;
readonly copy: typeof copy;
... 12 more ...;
readonly tab: typeof tab;
}
userEvent.setup: (options?: Options) => UserEventStart a “session” with userEvent.
All APIs returned by this function share an input device state and a default configuration.
setup();
render<SvelteComponent<Record<string, any>, any, any>, typeof import("/vercel/path0/node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@testing-library/dom/types/queries")>(Component: ComponentType<...>, options?: SvelteComponentOptions<...> | undefined, renderOptions?: RenderOptions<...> | undefined): RenderResult<...>Render a component into the document.
render(const Component: LegacyComponentTypeComponent);
const const button: HTMLElementbutton = const screen: Screen<typeof import("/vercel/path0/node_modules/.pnpm/@testing-library+dom@10.4.0/node_modules/@testing-library/dom/types/queries")>screen.getByRole<HTMLElement>(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement (+1 overload)getByRole('button');
expect<HTMLElement>(actual: HTMLElement, message?: string): Assertion<HTMLElement> (+1 overload)expect(const button: HTMLElementbutton).toHaveTextContent(0);
await const user: UserEventuser.click: (element: Element) => Promise<void>click(const button: HTMLElementbutton);
expect<HTMLElement>(actual: HTMLElement, message?: string): Assertion<HTMLElement> (+1 overload)expect(const button: HTMLElementbutton).toHaveTextContent(1);
});在编写涉及双向绑定、上下文或片段属性的组件测试时,最好为您的特定测试创建一个包装组件并与它交互。@testing-library/svelte 包含一些 示例。
端到端测试使用 Playwright
E2E(代表“端到端”)测试允许您从用户的角度测试您的完整应用程序。本节以Playwright为例,但您也可以使用其他解决方案,如Cypress或NightwatchJS。
您可以使用 Svelte CLI 在项目创建期间或之后设置 Playwright。您还可以使用npm init playwright进行设置。此外,您可能还想安装一个 IDE 插件,例如VS Code 扩展,以便在 IDE 内部执行测试。
如果您已运行npm init playwright或未使用 Vite,您可能需要调整 Playwright 配置来告诉 Playwright 在运行测试之前要做什么 - 主要是在某个端口启动您的应用程序。例如:
const const config: {
webServer: {
command: string;
port: number;
};
testDir: string;
testMatch: RegExp;
}
config = {
webServer: {
command: string;
port: number;
}
webServer: {
command: stringcommand: 'npm run build && npm run preview',
port: numberport: 4173
},
testDir: stringtestDir: 'tests',
testMatch: RegExptestMatch: /(.+\.)?(test|spec)\.[jt]s/
};
export default const config: {
webServer: {
command: string;
port: number;
};
testDir: string;
testMatch: RegExp;
}
config;您现在可以开始编写测试。这些测试完全不了解 Svelte 作为框架,所以您主要与 DOM 交互并编写断言。
import { import expectexpect, import testtest } from '@playwright/test';
import testtest('home page has expected h1', async ({ page }) => {
await page: anypage.goto('/');
await import expectexpect(page: anypage.locator('h1')).toBeVisible();
});Edit this page on GitHub llms.txt