Skip to main content

Tests

Les tests vous aident à écrire et maintenir votre code et vous protègent contre les régressions. Les frameworks de test vous aident à ça, vous permettant de décrire des assertions ou des attentes sur le comportement de votre code. Svelte ne vous oriente pas sur tel ou tel framework à utiliser — vous pouvez écrire des tests unitaires, des tests d’intégration, des tests end-to-end en utilisant des solutions comme Vitest, Jasmine, Cypress et Playwright

Tests unitaires et d’intégration avec Vitest

Les tests unitaires vous permettent de tester des petites parties isolées de votre code. Les tests d’intégration vous permettent de vérifier comment des morceaux de votre application fonctionnent ensemble. Si vous utilisez Vite (notamment via SvelteKit), nous vous recommandons d’utiliser Vitest.

Pour commencer, installer Vitest :

npm install -D vitest

Puis ajuster votre fichier vite.config.js :

vite.config
import { function defineConfig(config: UserConfig): UserConfig (+3 overloads)defineConfig } from 'vitest/config';

export default function defineConfig(config: UserConfig): UserConfig (+3 overloads)defineConfig({
	// ...
	// Dit à Vitest d'utiliser les points d'entrée `browser` dans les fichiers `package.json`, même si
	// Vitest utilise Node
	
UserConfig.resolve?: (ResolveOptions & {
    alias?: AliasOptions;
}) | undefined

Configure resolver

resolve
: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv

The 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.

@sincev0.1.27
env
.string | undefinedVITEST
? { ResolveOptions.conditions?: string[] | undefinedconditions: ['browser'] } : var undefinedundefined });

Si charger la version navigateur de tous vos paquets n’est pas possible, parce que vous testez également des librairies backend, vous pourriez avoir également besoin d’une configuration d’alias

Vous pouvez maintenant écrire vos tests unitaires dans des fichiers .js/.ts :

multiplier.svelte.test
import { function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
} from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPI

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
} from 'vitest';
import { import multipliermultiplier } from './multiplier.svelte.js'; test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestOptions): void (+2 overloads)

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
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) => voidtoEqual(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) => voidtoEqual(10); });
multiplier.svelte
/**
 * @param {number} initial
 * @param {number} k
 */
export function 
function multiplier(initial: number, k: number): {
    readonly value: number;
    set: (c: number) => void;
}
@paraminitial
@paramk
multiplier
(initial: number
@paraminitial
initial
, k: number
@paramk
k
) {
let let count: numbercount =
function $state<number>(initial: number): number (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$state
(initial: number
@paraminitial
initial
);
return { get value: numbervalue() { return let count: numbercount * k: number
@paramk
k
;
}, /** @param {number} c */ set: (c: number) => void
@paramc
set
: (c: number
@paramc
c
) => {
let count: numbercount = c: number
@paramc
c
;
} }; }
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

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$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; } }; }

Utiliser les runes dans vos fichiers de test

Puisque Vitest traite vos fichiers de test de la même façon que vos fichiers source, vous pouvez utiliser des runes au sein de vos tests tant que leur nom de fichier inclut .svelte :

multiplier.svelte.test
import { function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
} from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPI

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
} from 'vitest';
import { import multipliermultiplier } from './multiplier.svelte.js'; test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestOptions): void (+2 overloads)

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
('Multiplier', () => {
let let count: numbercount =
function $state<0>(initial: 0): 0 (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$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) => voidtoEqual(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) => voidtoEqual(10); });
multiplier.svelte
/**
 * @param {() => number} getCount
 * @param {number} k
 */
export function 
function multiplier(getCount: () => number, k: number): {
    readonly value: number;
}
@paramgetCount
@paramk
multiplier
(getCount: () => number
@paramgetCount
getCount
, k: number
@paramk
k
) {
return { get value: numbervalue() { return getCount: () => number
@paramgetCount
getCount
() * k: number
@paramk
k
;
} }; }
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; } }; }

Si le code testé utilise des effets, vous aurez besoin de placer le test dans un $effect.root :

logger.svelte.test
import { function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
} from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPI

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
} from 'vitest';
import { import loggerlogger } from './logger.svelte.js'; test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestOptions): void (+2 overloads)

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
('Effet', () => {
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.

https://svelte.dev/docs/svelte/$effect

@paramfn The function to execute
$effect
.function $effect.root(fn: () => void | (() => void)): () => void

The $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:

&#x3C;script>
  let count = $state(0);

  const cleanup = $effect.root(() => {
	$effect(() => {
			console.log(count);
		})

	 return () => {
	   console.log('effect root cleanup');
			}
  });
&#x3C;/script>

&#x3C;button onclick={() => cleanup()}>cleanup&#x3C;/button>

https://svelte.dev/docs/svelte/$effect#$effect.root

root
(() => {
let let count: numbercount =
function $state<0>(initial: 0): 0 (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$state
(0);
// logger utilise un $effect pour afficher les mises à jour de son input let let log: anylog = import loggerlogger(() => let count: numbercount); // les effets sont en général exécutés après une micro-tâche, // utiliser `flushSync` pour exécuter tous les effets en attente de manière synchrone function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
();
expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let log: anylog.value).JestAssertion<any>.toEqual: <number[]>(expected: number[]) => voidtoEqual([0]); let count: numbercount = 1; function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
();
expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let log: anylog.value).JestAssertion<any>.toEqual: <number[]>(expected: number[]) => voidtoEqual([0, 1]); }); const cleanup: () => voidcleanup(); });
logger.svelte
/**
 * @param {() => any} getValue
 */
export function 
function logger(getValue: () => any): {
    readonly value: any[];
}
@paramgetValue
logger
(getValue: () => any
@paramgetValue
getValue
) {
/** @type {any[]} */ let let log: any[]
@type{any[]}
log
=
function $state<never[]>(initial: never[]): never[] (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$state
([]);
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.

https://svelte.dev/docs/svelte/$effect

@paramfn The function to execute
$effect
(() => {
let log: any[]
@type{any[]}
log
.Array<any>.push(...items: any[]): number

Appends new elements to the end of an array, and returns the new length of the array.

@paramitems New elements to add to the array.
push
(getValue: () => any
@paramgetValue
getValue
());
}); return { get value: any[]value() { return let log: any[]
@type{any[]}
log
;
} }; }
export function 
function logger(getValue: () => any): {
    readonly value: any[];
}
logger
(getValue: () => anygetValue: () => any) {
let let log: any[]log: any[] =
function $state<never[]>(initial: never[]): never[] (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$state
([]);
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.

https://svelte.dev/docs/svelte/$effect

@paramfn The function to execute
$effect
(() => {
let log: any[]log.Array<any>.push(...items: any[]): number

Appends new elements to the end of an array, and returns the new length of the array.

@paramitems New elements to add to the array.
push
(getValue: () => anygetValue());
}); return { get value: any[]value() { return let log: any[]log; } }; }

Tests de composants

Il est possible de tester vos composants en isolation en utilisant Vitest.

Avant d’écrire des tests de composant, posez-vous la question de si vous avez réellement besoin de tester le composant, ou si vous souhaitez plutôt tester la logique au sein de votre composant. Si c’est le cas, envisagez d’extraire cette logique afin de pouvoir la tester indépendamment du composant.

Pour commencer, installer jsdom (une librairie qui simule les APIs du DOM) :

npm install -D jsdom

Puis ajustez votre vite.config.js :

vite.config
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[] | undefined

Array of vite plugins to use.

plugins
: [
/* ... */ ], UserConfig.test?: InlineConfig | undefined

Options for Vitest

test
: {
// Si vous testez des composants côté client, vous aurez besoin de mettre en place un // environnement DOM. Si tous vos fichiers ne sont pas compatibles avec cet environnement, vous // pouvez plutôt ajouter un commentaire `// @vitest-environment jsdom` en haut des fichiers de // test. InlineConfig.environment?: VitestEnvironment | undefined

Running environment

Supports ‘node’, ‘jsdom’, ‘happy-dom’, ‘edge-runtime’

If used unsupported string, will try to load the package vitest-environment-${env}

@default'node'
environment
: 'jsdom'
}, // Dit à Vitest d'utiliser les points d'entrée `browser` dans les fichiers `package.json`, même si // Vitest utilise Node
UserConfig.resolve?: (ResolveOptions & {
    alias?: AliasOptions;
}) | undefined

Configure resolver

resolve
: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv

The 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;&#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.

@sincev0.1.27
env
.string | undefinedVITEST
? { ResolveOptions.conditions?: string[] | undefinedconditions: ['browser'] } : var undefinedundefined });

Vous pouvez ensuite créer un fichier de test dans lequel importer le composant à tester, interagir avec lui programmatiquement et définir les résultats attendus :

component.test
import { function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
, function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: MountOptions<Props>): Exports

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
,
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: TestAPI

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
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 | TestOptions): void (+2 overloads)

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
('Component', () => {
// Instantier le composant en utilisant l'API Svelte `mount` 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<...>): {
    ...;
} & 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 | ShadowRoot

Target element where the component will be mounted.

target
: var document: Documentdocument.Document.body: HTMLElement

Specifies the beginning and end of the document body.

MDN Reference

body
, // `document` existe grâce à jsdom
props?: Record<string, any> | undefined

Component properties.

props
: { initial: numberinitial: 0 }
}); expect<string>(actual: string, message?: string): Assertion<string> (+1 overload)expect(var document: Documentdocument.Document.body: HTMLElement

Specifies the beginning and end of the document body.

MDN Reference

body
.InnerHTML.innerHTML: stringinnerHTML).JestAssertion<string>.toBe: <string>(expected: string) => voidtoBe('<button>0</button>');
// Clic sur le bouton, puis synchronisation des changements pour définir les attentes de manière // synchrone var document: Documentdocument.Document.body: HTMLElement

Specifies the beginning and end of the document body.

MDN Reference

body
.ParentNode.querySelector<"button">(selectors: "button"): HTMLButtonElement | null (+4 overloads)

Returns the first element that is a descendant of node that matches selectors.

MDN Reference

querySelector
('button').HTMLElement.click(): voidclick();
function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
();
expect<string>(actual: string, message?: string): Assertion<string> (+1 overload)expect(var document: Documentdocument.Document.body: HTMLElement

Specifies the beginning and end of the document body.

MDN Reference

body
.InnerHTML.innerHTML: stringinnerHTML).JestAssertion<string>.toBe: <string>(expected: string) => voidtoBe('<button>1</button>');
// Suppression du composant du 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
);
});

Même le processus est plutôt simple à mettre en place, celui-ci est aussi bas niveau et plutôt fragile, puisque la structure de composant peut beaucoup évoluer. Des outils comme @testing-library/svelte peuvent aider à industrialiser l’écriture de vos tests. Le test ci-dessus peut ainsi être ré-écrit comme ceci :

component.test
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.

@template{import('./component-types.js').Component} C
@template{import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries]
@paramComponent - The component to render.
@paramoptions - Customize how Svelte renders the component.
@paramrenderOptions - Customize how Testing Library sets up the document and binds queries.
@returnsThe rendered component and bound testing functions.
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: TestAPI

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
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 | TestOptions): void (+2 overloads)

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
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) => UserEvent

Start 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.

@template{import('./component-types.js').Component} C
@template{import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries]
@paramComponent - The component to render.
@paramoptions - Customize how Svelte renders the component.
@paramrenderOptions - Customize how Testing Library sets up the document and binds queries.
@returnsThe rendered component and bound testing functions.
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); });

Lorsque vous écrivez des tests de composant qui impliquent des liaisons à double sens, du contexte ou des props de snippet, il est recommandé de créer un composant parent spécifiquement pour votre test, et interagir avec ce composant. @testing-library/svelte montre quelques exemples de cette technique.

Tests E2E avec Playwright

Les tests E2E (“end to end” en anglais, qui se traduit par “de bout en bout”) vous permettent de tester votre application toute entière depuis le point de vue de vos utilisateurs. Cette section prend Playwright comme exemple, mais vous pouvez aussi utiliser d’autres solutions comme Cypress ou NightwatchJS.

Pour commencer à utiliser Playwright, vous pouvez soit l’installer via l’extension VS Code, soit l’installer depuis votre ligne de commande en utilisant npm init playwright. Vous pouvez aussi l’installer via le CLI d’installation de Svelte, qui se lance avec npx sv create.

Une fois l’installation terminée, vous devriez voir apparaître un dossier tests ainsi qu’un fichier de configuration de Playwright. Il se peut que vous ayez besoin d’ajuster cette configuration pour dire à Playwright ce qu’il doit faire avant de lancer les tests — principalement lancer votre application sur un port particulier :

playwright.config
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
;

Vous pouvez maintenant commencer à écrire vos tests. Ils n’ont aucune conscience que Svelte existe en tant que framework, il vous faudra donc surtout interagir avec le DOM, et écrire vos assertions.

tests/hello-world.spec
import { import expectexpect, import testtest } from '@playwright/test';

import testtest("la page d'accueil a le h1 prévu", async ({ page }) => {
	await page: anypage.goto('/');
	await import expectexpect(page: anypage.locator('h1')).toBeVisible();
});

Modifier cette page sur Github