API
Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →
Cette bibliothèque étant inspirée de la preact-testing-library, vous pouvez consulter sa documentation pour plus d'informations.
Plusieurs différences importantes sont à noter.
render
La fonction render prend en argument une fonction qui retourne un composant Solid, plutôt que le composant lui-même directement.
const results = render(() => <YourComponent />, options)
Solid.js ne procède pas à un re-rendu : il exécute simplement des effets secondaires déclenchés par l'état réactif qui modifient le DOM. Par conséquent, il n'existe pas de méthode rerender. Vous pouvez utiliser des signaux globaux pour manipuler votre composant de test de manière à le mettre à jour.
En complément de l'API d'origine, la fonction render de cette bibliothèque de tests propose une option pratique location qui configure un routeur en mémoire pointant vers l'emplacement spécifié. Cette configuration n'étant pas synchrone, vous devez utiliser des requêtes asynchrones (findBy) après l'avoir employée :
it('uses params', async () => {
const App = () => (
<>
<Route path="/ids/:id" component={() => <p>Id: {useParams()?.id}</p>} />
<Route path="/" component={() => <p>Start</p>} />
</>
)
const {findByText} = render(() => <App />, {location: 'ids/1234'})
expect(await findByText('Id: 1234')).not.toBeFalsy()
})
Elle utilise @solidjs/router. Si vous souhaitez utiliser un routeur différent, privilégiez plutôt l'option wrapper. Une tentative d'utilisation sans avoir le package installé générera un message d'erreur.
renderHook
L'état réactif externe de Solid.js ne nécessite aucun élément DOM pour fonctionner. Ainsi, notre appel renderHook destiné à tester des hooks dans le contexte d'un composant (si votre hook ne nécessite pas ce contexte, createRoot suffit à tester le comportement réactif ; par commodité, nous proposons également createEffect, décrit dans la section Async methods) n'inclut pas container, baseElement ou des requêtes dans ses options ou valeurs de retour. Il expose plutôt un owner à utiliser avec runWithOwner si nécessaire. Une fonction cleanup est également fournie, bien qu'elle soit appelée automatiquement à la fin du test.
function renderHook<Args extends any[], Result>(
hook: (...args: Args) => Result,
options: {
initialProps?: Args,
wrapper?: Component<{ children: JSX.Element }>
}
) => {
result: Result;
owner: Owner | null;
cleanup: () => void;
}
Ceci permet de tester aisément un hook/primitif :
const {result} = renderHook(createResult)
expect(result).toBe(true)
Si vous utilisez un wrapper avec renderHook, assurez-vous qu'il renvoie toujours props.children - particulièrement si vous utilisez un contexte avec du code asynchrone combiné à <Show>. Ceci est indispensable pour récupérer la valeur du hook, obtenue uniquement de manière synchrone lors du premier rendu. Dans le cas contraire, vous obtiendrez undefined sans comprendre pourquoi.
renderDirective
Solid.js prend en charge les directives personnalisées, un modèle pratique pour associer des comportements personnalisés à des éléments. Nous proposons donc également un appel renderDirective qui étend renderHook pour :
- Prendre une directive comme premier argument
- Accepter une
initialValuepour l'argument - Prendre un
targetElement(chaîne, HTMLElement ou fonction retournant un HTMLElement) dans lesoptions - Renvoyer
argetsetArgpour lire et manipuler l'argument de la directive.
function renderDirective<
Arg extends any,
Elem extends HTMLElement
>(
directive: (ref: Elem, arg: Accessor<Arg>) => void,
options?: {
...renderOptions,
initialValue: Arg,
targetElement:
| Lowercase<Elem['nodeName']>
| Elem
| (() => Elem)
}
): Result & { arg: Accessor<Arg>, setArg: Setter<Arg> };
Ceci permet un test de directives très efficace et concis :
const {asFragment, setArg} = renderDirective(myDirective)
expect(asFragment()).toBe('<div data-directive="works"></div>')
setArg('perfect')
expect(asFragment()).toBe('<div data-directive="perfect"></div>')
Méthodes asynchrones
Les changements réactifs de Solid.js sont quasi instantanés. L'utilisation de requêtes asynchrones comme waitFor(…), await findByRole(…) pour tester le résultat rendu est rarement nécessaire, sauf pour les transitions, le suspense, les ressources et la navigation via routeur.
Solid.js gère les effets de bord avec différentes variantes de createEffect. Bien que
vous puissiez utiliser waitFor pour tester les effets asynchrones, cette approche utilise du polling plutôt que
de permettre à la réactivité de Solid de déclencher l'étape suivante. Pour simplifier
le test de ces effets asynchrones, nous proposons un helper testEffect qui
complète les hooks pour les directives et les hooks :
testEffect(fn: (done: (result: T) => void) => void, owner?: Owner): Promise<T>
// use it like this:
test("testEffect allows testing an effect asynchronously", () => {
const [value, setValue] = createSignal(0);
return testEffect(done => createEffect((run: number = 0) => {
if (run === 0) {
expect(value()).toBe(0);
setValue(1);
} else if (run === 1) {
expect(value()).toBe(1);
done();
}
return run + 1;
}));
});
Il permet d'exécuter l'effet dans un owner défini, reçu comme second argument
optionnel. Cela peut être utile en combinaison avec renderHook, qui fournit
un champ owner dans son résultat. La valeur de retour est une Promise contenant
la valeur passée au callback done(). Vous pouvez soit attendre le résultat pour
effectuer des assertions supplémentaires, soit le renvoyer à votre runner de test.
Problèmes connus
Si vous utilisez vitest, les tests peuvent échouer car les paquets solid-js et @solidjs/router (si utilisé) doivent être chargés une seule fois, mais pourraient l'être à la fois via le serveur interne vite et via node. Les bugs typiques causés par ce problème sont que dispose est supposément non défini ou que le routeur n'a pas pu être chargé.
Depuis la version 2.8.2, notre plugin Vite peut configurer automatiquement tout le nécessaire pour les tests. Vous ne devriez donc avoir besoin que de configurations supplémentaires pour les globals, la couverture, etc.