mirror of
https://github.com/callstack/react-native-testing-library.git
synced 2026-09-18 23:09:04 +08:00
120 lines
59 KiB
HTML
120 lines
59 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head><meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Testing environment - React Native Testing Library</title>
|
|
<script>{;const saved = localStorage.getItem('rspress-theme-appearance');const preferDark = window.matchMedia('(prefers-color-scheme: dark)').matches;const isDark = !saved || saved === 'auto' ? preferDark : saved === 'dark';document.documentElement.classList.toggle('dark', isDark);document.documentElement.classList.toggle('rp-dark', isDark);document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';}</script>
|
|
<link href="/react-native-testing-library/static/css/styles.73041e5c.css" rel="stylesheet">
|
|
<script defer src="/react-native-testing-library/static/js/styles.60254868.js"></script>
|
|
<script defer src="/react-native-testing-library/static/js/lib-react.c776e48d.js"></script>
|
|
<script defer src="/react-native-testing-library/static/js/lib-router.0998d45d.js"></script>
|
|
<script defer src="/react-native-testing-library/static/js/8712.b675ec06.js"></script>
|
|
<script defer src="/react-native-testing-library/static/js/index.6c2873da.js"></script>
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
<meta name="generator" content="Rspress v2.0.0-rc.5">
|
|
<meta property="og:url" content="https://callstack.github.io/react-native-testing-library/">
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:title" content="Testing environment - React Native Testing Library">
|
|
<meta property="og:description" content="Helps you to write better tests with less effort.">
|
|
<link rel="icon" href="/react-native-testing-library/logo-light.png">
|
|
<meta name="description" content="Helps you to write better tests with less effort."></head>
|
|
|
|
<body>
|
|
<div id="__rspress_root"><link rel="preload" as="image" href="/react-native-testing-library/img/logo-light.svg"/><link rel="preload" as="image" href="/react-native-testing-library/img/logo-dark.svg"/><link rel="preload" as="image" href="/react-native-testing-library/static/image/abstract-atom.4eca0300.avif"/><header class="rp-nav"><div class="rp-nav__left"><div class="rp-nav__title"><a href="/react-native-testing-library/" class="rp-nav__title__link rp-link"><div class="rp-nav__title__logo"><img src="/react-native-testing-library/img/logo-light.svg" alt="logo" id="logo" class="rspress-logo rp-nav__title__logo-image rp-nav__title__logo-image--light"/><img src="/react-native-testing-library/img/logo-dark.svg" alt="logo" id="logo" class="rspress-logo rp-nav__title__logo-image rp-nav__title__logo-image--dark"/></div></a></div></div><div class="rp-nav__right"><button class="rp-search-button"><div class="rp-search-button__content"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0LjY2NjUgMTQuNjY2NEgxMy4zMzM1VjEzLjMzMzNIMTQuNjY2NVYxNC42NjY0Wk0xMy4zMzM1IDEzLjMzMzNIMTIuMDAwNVYxMi4wMDAzSDEzLjMzMzVWMTMuMzMzM1pNOS4zMzM1IDEyLjAwMDNINC4wMDA0OVYxMC42NjY0SDkuMzMzNVYxMi4wMDAzWk0xMi4wMDA1IDEyLjAwMDNIMTAuNjY2NVYxMC42NjY0SDEyLjAwMDVWMTIuMDAwM1pNNC4wMDA0OSAxMC42NjY0SDIuNjY2NVY5LjMzMzM0SDQuMDAwNDlWMTAuNjY2NFpNMTAuNjY2NSAxMC42NjY0SDkuMzMzNVY5LjMzMzM0SDEwLjY2NjVWMTAuNjY2NFpNMi42NjY1IDkuMzMzMzRIMS4zMzM1VjQuMDAwMzRIMi42NjY1VjkuMzMzMzRaTTEyLjAwMDUgOS4zMzMzNEgxMC42NjY1VjQuMDAwMzRIMTIuMDAwNVY5LjMzMzM0Wk00LjAwMDQ5IDQuMDAwMzRIMi42NjY1VjIuNjY2MzVINC4wMDA0OVY0LjAwMDM0Wk0xMC42NjY1IDQuMDAwMzRIOS4zMzM1VjIuNjY2MzVIMTAuNjY2NVY0LjAwMDM0Wk05LjMzMzUgMi42NjYzNUg0LjAwMDQ5VjEuMzMzMzRIOS4zMzM1VjIuNjY2MzVaIiBmaWxsPSIjODM4Mjg5Ii8+Cjwvc3ZnPgo=" alt="" class="rp-search-button__icon"/><span class="rp-search-button__word">Search</span></div><div class="rp-search-button__hotkey" style="opacity:0"><span></span><span>K</span></div></button><div class="rp-search-button--mobile"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE0LjY2NjUgMTQuNjY2NEgxMy4zMzM1VjEzLjMzMzNIMTQuNjY2NVYxNC42NjY0Wk0xMy4zMzM1IDEzLjMzMzNIMTIuMDAwNVYxMi4wMDAzSDEzLjMzMzVWMTMuMzMzM1pNOS4zMzM1IDEyLjAwMDNINC4wMDA0OVYxMC42NjY0SDkuMzMzNVYxMi4wMDAzWk0xMi4wMDA1IDEyLjAwMDNIMTAuNjY2NVYxMC42NjY0SDEyLjAwMDVWMTIuMDAwM1pNNC4wMDA0OSAxMC42NjY0SDIuNjY2NVY5LjMzMzM0SDQuMDAwNDlWMTAuNjY2NFpNMTAuNjY2NSAxMC42NjY0SDkuMzMzNVY5LjMzMzM0SDEwLjY2NjVWMTAuNjY2NFpNMi42NjY1IDkuMzMzMzRIMS4zMzM1VjQuMDAwMzRIMi42NjY1VjkuMzMzMzRaTTEyLjAwMDUgOS4zMzMzNEgxMC42NjY1VjQuMDAwMzRIMTIuMDAwNVY5LjMzMzM0Wk00LjAwMDQ5IDQuMDAwMzRIMi42NjY1VjIuNjY2MzVINC4wMDA0OVY0LjAwMDM0Wk0xMC42NjY1IDQuMDAwMzRIOS4zMzM1VjIuNjY2MzVIMTAuNjY2NVY0LjAwMDM0Wk05LjMzMzUgMi42NjYzNUg0LjAwMDQ5VjEuMzMzMzRIOS4zMzM1VjIuNjY2MzVaIiBmaWxsPSIjODM4Mjg5Ii8+Cjwvc3ZnPgo=" alt=""/></div><ul class="rp-nav-menu rp-nav-menu--right"><li class="rp-nav-menu__item rp-nav-menu__item--active rspress-nav-menu-item rspress-nav-menu-item-active"><a href="/react-native-testing-library/docs/start/intro" class="rp-nav-menu__item__container rp-link">Docs</a></li><li class="rp-nav-menu__item rspress-nav-menu-item"><a href="/react-native-testing-library/cookbook/" class="rp-nav-menu__item__container rp-link">Cookbook</a></li><li class="rp-nav-menu__item rspress-nav-menu-item"><a href="https://github.com/callstack/react-native-testing-library/tree/main/examples" target="_blank" rel="noopener noreferrer" class="rp-nav-menu__item__container rp-link">Examples</a></li></ul><div class="rp-nav__others"><div class="rp-nav-menu__divider"></div><li class="rp-nav-menu__item"><div class="rp-nav-menu__item__container">13.x<img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA4LjVINy41VjkuNUg2LjVWMTAuNUg1LjVWOS41SDQuNVY4LjVINS41VjIuNUg2LjVWOC41Wk00LjUgOC41SDMuNVY3LjVINC41VjguNVpNOC41IDguNUg3LjVWNy41SDguNVY4LjVaTTMuNSA2LjVWNy41SDIuNVY2LjVIMy41Wk05LjUgNy41SDguNVY2LjVIOS41VjcuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt="" class="rp-nav-menu__item__icon"/></div><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--center"><li class="rp-hover-group__item" style="padding-left:8px" data-depth="0"><a href="/react-native-testing-library/12.x/docs/advanced/testing-env" class="rp-hover-group__item__link rp-link" aria-label="12.x">12.x</a></li><li class="rp-hover-group__item rp-hover-group__item--active" style="padding-left:8px" data-depth="0"><a href="/react-native-testing-library/advanced/testing-env" class="rp-hover-group__item__link rp-link" aria-label="13.x">13.x</a></li><li class="rp-hover-group__item" style="padding-left:8px" data-depth="0"><a href="/react-native-testing-library/14.x/docs/advanced/testing-env" class="rp-hover-group__item__link rp-link" aria-label="14.x">14.x</a></li></ul></li><div class="rspress-nav-appearance"><div class="rp-p-1 rp-border rp-border-solid rp-border-gray-300 rp-text-gray-400 rp-cursor-pointer rp-rounded-md rp-transition-all rp-duration-300 rp-w-7 rp-h-7"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 24 24"><path fill="currentColor" d="M18 22H8v-2h10zM8 20H6v-2h2zm12 0h-2v-2h2zM6 18H4v-2h2zm16 0h-2v-4h-2v-2h2v-2h2zM4 16H2V6h2zm14 0h-6v-2h6zm-6-2h-2v-2h2zm-2-2H8V6h2zM6 6H4V4h2zm8-2h-2v2h-2V4H6V2h8z"></path></svg></div></div><div class="rp-social-links"><a href="https://github.com/callstack/react-native-testing-library" target="_blank" rel="noopener noreferrer" class="rp-social-links__item"><div class="rp-social-links__icon"><div class="rp-social-links__icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></div></div></a><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--right"></ul></div></div><button aria-label="mobile hamburger" class="rp-nav-hamburger rp-nav-hamburger__sm"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xIDlIN1YxNUgxVjlaTTMgMTFWMTNINVYxMUgzWk05IDlIMTVWMTVIOVY5Wk0xMSAxMVYxM0gxM1YxMUgxMVpNMTcgOUgyM1YxNUgxN1Y5Wk0xOSAxMVYxM0gyMVYxMUgxOVoiIGZpbGw9IiM4MzgyODkiLz4KPC9zdmc+Cg==" alt=""/></button><button aria-label="mobile hamburger" class="rp-nav-hamburger rp-nav-hamburger__md"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xIDlIN1YxNUgxVjlaTTMgMTFWMTNINVYxMUgzWk05IDlIMTVWMTVIOVY5Wk0xMSAxMVYxM0gxM1YxMUgxMVpNMTcgOUgyM1YxNUgxN1Y5Wk0xOSAxMVYxM0gyMVYxMUgxOVoiIGZpbGw9IiM4MzgyODkiLz4KPC9zdmc+Cg==" alt=""/><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--right"><div class="rp-nav-menu__others-mobile__container"><div class="rp-nav-hamburger__md__hover-group"><div class="rp-nav-screen-appearance"><div class="rp-nav-screen-appearance__left">Theme</div><div class="rp-nav-screen-appearance__right"></div></div><li class="rp-nav-menu__item"><div class="rp-nav-menu__item__container">13.x<img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA4LjVINy41VjkuNUg2LjVWMTAuNUg1LjVWOS41SDQuNVY4LjVINS41VjIuNUg2LjVWOC41Wk00LjUgOC41SDMuNVY3LjVINC41VjguNVpNOC41IDguNUg3LjVWNy41SDguNVY4LjVaTTMuNSA2LjVWNy41SDIuNVY2LjVIMy41Wk05LjUgNy41SDguNVY2LjVIOS41VjcuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt="" class="rp-nav-menu__item__icon"/></div><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--center"><li class="rp-hover-group__item" style="padding-left:8px" data-depth="0"><a href="/react-native-testing-library/12.x/docs/advanced/testing-env" class="rp-hover-group__item__link rp-link" aria-label="12.x">12.x</a></li><li class="rp-hover-group__item rp-hover-group__item--active" style="padding-left:8px" data-depth="0"><a href="/react-native-testing-library/advanced/testing-env" class="rp-hover-group__item__link rp-link" aria-label="13.x">13.x</a></li><li class="rp-hover-group__item" style="padding-left:8px" data-depth="0"><a href="/react-native-testing-library/14.x/docs/advanced/testing-env" class="rp-hover-group__item__link rp-link" aria-label="14.x">14.x</a></li></ul></li><div class="rp-nav-screen-divider"></div><div class="rp-social-links"><a href="https://github.com/callstack/react-native-testing-library" target="_blank" rel="noopener noreferrer" class="rp-social-links__item"><div class="rp-social-links__icon"><div class="rp-social-links__icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24"><path fill="currentColor" d="M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></div></div></a><ul class="rp-hover-group rp-hover-group--hidden rp-hover-group--right"></ul></div></div></div></ul></button></div></header><div class="rp-doc-layout__menu"><div class="rp-sidebar-menu"><button type="button" class="rp-sidebar-menu__left"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIwIDE4SDRWMTZIMjBWMThaTTIwIDEzSDRWMTFIMjBWMTNaTTIwIDhINFY2SDIwVjhaIiBmaWxsPSIjODM4Mjg5Ii8+Cjwvc3ZnPgo=" alt=""/><span>Menu</span></button><button type="button" disabled="" class="rp-sidebar-menu__right"><span>Contents</span><svg width="14" height="14" class="rp-progress-circle"><circle cx="7" cy="7" r="6" stroke-linecap="round" fill="none" stroke="var(--rp-c-divider-light)" stroke-width="2"></circle><circle cx="7" cy="7" r="6" stroke-linecap="round" fill="none" stroke="var(--rp-c-brand)" stroke-width="2" stroke-dasharray="37.69911184307752" stroke-dashoffset="37.69911184307752" transform="rotate(-90 7 7)" style="transition:stroke-dashoffset 0.3s"></circle></svg></button></div></div><div class="rp-doc-layout__container"><aside class="rp-doc-layout__sidebar rp-scrollbar"><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span>Getting started</span></div><div class="rp-sidebar-item__right"><div style="cursor:pointer;transition:transform 0.2s ease-out;transform:rotate(90deg)"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA0LjVINy41VjMuNUg2LjVWMi41SDUuNVYzLjVINC41VjQuNUg1LjVWMTAuNUg2LjVWNC41Wk00LjUgNC41SDMuNVY1LjVINC41VjQuNVpNOC41IDQuNUg3LjVWNS41SDguNVY0LjVaTTMuNSA2LjVWNS41SDIuNVY2LjVIMy41Wk05LjUgNS41SDguNVY2LjVIOS41VjUuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt=""/></div></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/react-native-testing-library/docs/start/intro" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Introduction</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/start/quick-start" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Quick Start</span></div><div class="rp-sidebar-item__right"></div></a></div></div><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span>API reference</span></div><div class="rp-sidebar-item__right"><div style="cursor:pointer;transition:transform 0.2s ease-out;transform:rotate(90deg)"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA0LjVINy41VjMuNUg2LjVWMi41SDUuNVYzLjVINC41VjQuNUg1LjVWMTAuNUg2LjVWNC41Wk00LjUgNC41SDMuNVY1LjVINC41VjQuNVpNOC41IDQuNUg3LjVWNS41SDguNVY0LjVaTTMuNSA2LjVWNS41SDIuNVY2LjVIMy41Wk05LjUgNS41SDguNVY2LjVIOS41VjUuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt=""/></div></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/react-native-testing-library/docs/api/render" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Render function</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/api/screen" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Screen object</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/api/queries" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Queries</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/api/jest-matchers" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Jest Matchers</span></div><div class="rp-sidebar-item__right"></div></a><div class="rp-sidebar-item rp-sidebar-group rp-sidebar-item--group-item" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Triggering events</span></div><div class="rp-sidebar-item__right"><div style="cursor:pointer;transition:transform 0.2s ease-out;transform:rotate(90deg)"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA0LjVINy41VjMuNUg2LjVWMi41SDUuNVYzLjVINC41VjQuNUg1LjVWMTAuNUg2LjVWNC41Wk00LjUgNC41SDMuNVY1LjVINC41VjQuNVpNOC41IDQuNUg3LjVWNS41SDguNVY0LjVaTTMuNSA2LjVWNS41SDIuNVY2LjVIMy41Wk05LjUgNS41SDguNVY2LjVIOS41VjUuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt=""/></div></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/react-native-testing-library/docs/api/events/fire-event" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>Fire Event</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/api/events/user-event" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>User Event</span></div><div class="rp-sidebar-item__right"></div></a></div></div><div class="rp-sidebar-item rp-sidebar-group rp-sidebar-item--group-item" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Miscellaneous</span></div><div class="rp-sidebar-item__right"><div style="cursor:pointer;transition:transform 0.2s ease-out;transform:rotate(90deg)"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA0LjVINy41VjMuNUg2LjVWMi41SDUuNVYzLjVINC41VjQuNUg1LjVWMTAuNUg2LjVWNC41Wk00LjUgNC41SDMuNVY1LjVINC41VjQuNVpNOC41IDQuNUg3LjVWNS41SDguNVY0LjVaTTMuNSA2LjVWNS41SDIuNVY2LjVIMy41Wk05LjUgNS41SDguNVY2LjVIOS41VjUuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt=""/></div></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/react-native-testing-library/docs/api/misc/accessibility" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>Accessibility</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/api/misc/async" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>Async utilities</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/api/misc/config" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>Config</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/api/misc/other" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>Other</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/api/misc/render-hook" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>Render Hook</span></div><div class="rp-sidebar-item__right"></div></a></div></div></div></div><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span>Guides</span></div><div class="rp-sidebar-item__right"><div style="cursor:pointer;transition:transform 0.2s ease-out;transform:rotate(90deg)"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA0LjVINy41VjMuNUg2LjVWMi41SDUuNVYzLjVINC41VjQuNUg1LjVWMTAuNUg2LjVWNC41Wk00LjUgNC41SDMuNVY1LjVINC41VjQuNVpNOC41IDQuNUg3LjVWNS41SDguNVY0LjVaTTMuNSA2LjVWNS41SDIuNVY2LjVIMy41Wk05LjUgNS41SDguNVY2LjVIOS41VjUuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt=""/></div></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/react-native-testing-library/docs/guides/how-to-query" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>How to Query</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/guides/react-19" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>React 19</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/guides/troubleshooting" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Troubleshooting</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/guides/faq" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>FAQ</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/guides/community-resources" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Community Resources</span></div><div class="rp-sidebar-item__right"></div></a></div></div><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span>Advanced Guides</span></div><div class="rp-sidebar-item__right"><div style="cursor:pointer;transition:transform 0.2s ease-out;transform:rotate(90deg)"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA0LjVINy41VjMuNUg2LjVWMi41SDUuNVYzLjVINC41VjQuNUg1LjVWMTAuNUg2LjVWNC41Wk00LjUgNC41SDMuNVY1LjVINC41VjQuNVpNOC41IDQuNUg3LjVWNS41SDguNVY0LjVaTTMuNSA2LjVWNS41SDIuNVY2LjVIMy41Wk05LjUgNS41SDguNVY2LjVIOS41VjUuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt=""/></div></div></div><div style="display:grid;grid-template-rows:1fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/react-native-testing-library/docs/advanced/testing-env" class="rp-sidebar-item rp-sidebar-item--active rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Testing Environment</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/advanced/third-party-integration" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Third-party Integration</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/advanced/understanding-act" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Understanding Act</span></div><div class="rp-sidebar-item__right"></div></a></div></div><div class="rp-sidebar-item rp-sidebar-group" style="padding-left:12px" data-depth="0"><div class="rp-sidebar-item__left"><span>Migration Guides</span></div><div class="rp-sidebar-item__right"><div style="cursor:pointer;transition:transform 0.2s ease-out;transform:rotate(0deg)"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA0LjVINy41VjMuNUg2LjVWMi41SDUuNVYzLjVINC41VjQuNUg1LjVWMTAuNUg2LjVWNC41Wk00LjUgNC41SDMuNVY1LjVINC41VjQuNVpNOC41IDQuNUg3LjVWNS41SDguNVY0LjVaTTMuNSA2LjVWNS41SDIuNVY2LjVIMy41Wk05LjUgNS41SDguNVY2LjVIOS41VjUuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt=""/></div></div></div><div style="display:grid;grid-template-rows:0fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/react-native-testing-library/docs/migration/v13" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>v13 Migration</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/migration/jest-matchers" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Jest Matchers</span></div><div class="rp-sidebar-item__right"></div></a><div class="rp-sidebar-item rp-sidebar-group rp-sidebar-item--group-item" style="padding-left:calc(12px * 1 + 12px)" data-depth="1"><div class="rp-sidebar-item__left"><span>Previous versions</span></div><div class="rp-sidebar-item__right"><div style="cursor:pointer;transition:transform 0.2s ease-out;transform:rotate(0deg)"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA0LjVINy41VjMuNUg2LjVWMi41SDUuNVYzLjVINC41VjQuNUg1LjVWMTAuNUg2LjVWNC41Wk00LjUgNC41SDMuNVY1LjVINC41VjQuNVpNOC41IDQuNUg3LjVWNS41SDguNVY0LjVaTTMuNSA2LjVWNS41SDIuNVY2LjVIMy41Wk05LjUgNS41SDguNVY2LjVIOS41VjUuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt=""/></div></div></div><div style="display:grid;grid-template-rows:0fr;transition:grid-template-rows 0.2s ease-out"><div style="overflow:hidden"><a href="/react-native-testing-library/docs/migration/previous/v12" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>v12</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/migration/previous/v11" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>v11</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/migration/previous/v9" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>v9</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/migration/previous/v7" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>v7</span></div><div class="rp-sidebar-item__right"></div></a><a href="/react-native-testing-library/docs/migration/previous/v2" class="rp-sidebar-item rp-sidebar-item--group-item rp-link" style="padding-left:calc(12px * 2 + 12px)" data-depth="2"><div class="rp-sidebar-item__left"><span>v2</span></div><div class="rp-sidebar-item__right"></div></a></div></div></div></div></aside><div class="rp-doc-layout__doc"><main class="rp-doc-layout__doc-container"><div class="rp-doc rspress-doc"><!--$--><h1 class="rp-toc-include" id="testing-environment"><a href="#testing-environment" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Testing environment</h1>
|
|
<div class="rp-callout rp-callout--info"><div class="rp-callout__title">Info</div><div class="rp-callout__content"><p>This document is intended for a more advanced audience who want to understand the internals of our testing environment better, e.g., to contribute to the codebase. You should be able to write integration or component tests without reading this.</p></div></div>
|
|
<p>React Native Testing Library allows you to write integration and component tests for your React Native app or library. While the JSX code used in tests closely resembles your React Native app, things are not as simple as they might appear. This document will describe the key elements of our testing environment and highlight things to be aware of when writing more advanced tests or diagnosing issues.</p>
|
|
<h2 class="rp-toc-include" id="react-renderers"><a href="#react-renderers" class="rp-header-anchor rp-link" aria-hidden="true">#</a>React renderers</h2>
|
|
<p>React allows you to write declarative code using JSX, write function or class components, or use hooks like <code>useState</code>. You need to use a renderer to output the results of your components. Every React app uses some renderer:</p>
|
|
<ul>
|
|
<li>React Native is a renderer for mobile apps,</li>
|
|
<li>React DOM is a renderer for web apps,</li>
|
|
<li>There are other more <a href="https://github.com/chentsulin/awesome-react-renderer" target="_blank" rel="noopener noreferrer" class="rp-link">specialized renderers</a> that can e.g., render to console or HTML canvas.</li>
|
|
</ul>
|
|
<p>When you run your tests in the React Native Testing Library, somewhat contrary to what the name suggests, they are actually <strong>not</strong> using React Native renderer. This is because this renderer needs to be run on an iOS or Android operating system, so it would need to run on a device or simulator.</p>
|
|
<h2 class="rp-toc-include" id="react-test-renderer"><a href="#react-test-renderer" class="rp-header-anchor rp-link" aria-hidden="true">#</a>React Test Renderer</h2>
|
|
<p>Instead, RNTL uses React Test Renderer, a specialized renderer that allows rendering to pure JavaScript objects without access to mobile OS and can run in a Node.js environment using Jest (or any other JavaScript test runner).</p>
|
|
<p>Using React Test Renderer has pros and cons.</p>
|
|
<p>Benefits:</p>
|
|
<ul>
|
|
<li>tests can run on most CIs (Linux, etc) and do not require a mobile device or emulator</li>
|
|
<li>faster test execution</li>
|
|
<li>light runtime environment</li>
|
|
</ul>
|
|
<p>Disadvantages:</p>
|
|
<ul>
|
|
<li>Tests do not execute native code</li>
|
|
<li>Tests are unaware of the view state that would be managed by native components, e.g., focus, unmanaged text boxes, etc.</li>
|
|
<li>Assertions do not operate on native view hierarchy</li>
|
|
<li>Runtime behaviors are simulated, sometimes imperfectly</li>
|
|
</ul>
|
|
<p>It's worth noting that the React Testing Library (web one) works a bit differently. While RTL also runs in Jest, it has access to a simulated browser DOM environment from the <code>jsdom</code> package, which allows it to use a regular React DOM renderer. Unfortunately, there is no similar React Native runtime environment package. This is probably because while the browser environment is well-defined and highly standardized, the React Native environment constantly evolves in sync with the evolution of underlying OS-es. Maintaining such an environment would require duplicating countless React Native behaviors and keeping them in sync as React Native develops.</p>
|
|
<h2 class="rp-toc-include" id="element-tree"><a href="#element-tree" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Element tree</h2>
|
|
<p>Calling the <code>render()</code> function creates an element tree. This is done internally by invoking <code>TestRenderer.create()</code> method. The output tree represents your React Native component tree, and each node of that tree is an "instance" of some React component (to be more precise, each node represents a React fiber, and only class components have instances, while function components store the hook state using fibers).</p>
|
|
<p>These tree elements are represented by <code>ReactTestInstance</code> type:</p>
|
|
<div class="rp-codeblock language-tsx"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="tsx"><code><span class="line"><span style="color:var(--shiki-token-keyword)">interface</span><span style="color:var(--shiki-token-function)"> ReactTestInstance</span><span style="color:var(--shiki-foreground)"> {</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> type</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> ElementType</span><span style="color:var(--shiki-foreground)">;</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> props</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-foreground)"> { [propName</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> string</span><span style="color:var(--shiki-foreground)">]</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> any</span><span style="color:var(--shiki-foreground)"> };</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> parent</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> ReactTestInstance</span><span style="color:var(--shiki-token-keyword)"> |</span><span style="color:var(--shiki-token-constant)"> null</span><span style="color:var(--shiki-foreground)">;</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> children</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> Array</span><span style="color:var(--shiki-foreground)"><</span><span style="color:var(--shiki-token-function)">ReactTestInstance</span><span style="color:var(--shiki-token-keyword)"> |</span><span style="color:var(--shiki-token-constant)"> string</span><span style="color:var(--shiki-foreground)">>;</span></span>
|
|
<span class="line"></span>
|
|
<span class="line"><span style="color:var(--shiki-token-comment)"> // Other props and methods</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M28 10v18H10V10zm0-2H10a2 2 0 0 0-2 2v18a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 18H2V4a2 2 0 0 1 2-2h14v2H4Z"></path></svg><svg width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m13 24-9-9 1.414-1.414L13 21.171 26.586 7.586 28 9z"></path></svg></button></div></div></div>
|
|
<p>Based on: <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-test-renderer/index.d.ts" target="_blank" rel="noopener noreferrer" class="rp-link">https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-test-renderer/index.d.ts</a></p>
|
|
<h2 class="rp-toc-include" id="host-and-composite-components"><a href="#host-and-composite-components" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Host and composite components</h2>
|
|
<p>One of the most important aspects of the element tree is that it is composed of both host and composite components:</p>
|
|
<ul>
|
|
<li><a href="https://reactnative.dev/architecture/glossary#react-host-components-or-host-components" target="_blank" rel="noopener noreferrer" class="rp-link">Host components</a> will have direct counterparts in the native view tree. Typical examples are <code><View></code>, <code><Text></code> , <code><TextInput></code>, and <code><Image></code> from React Native. You can think of these as an analog of <code><div></code>, <code><span></code> etc on the Web. You can also create custom host views as native modules or import them from 3rd party libraries, like React Navigation or React Native Gesture Handler.</li>
|
|
<li><a href="https://reactnative.dev/architecture/glossary#react-composite-components" target="_blank" rel="noopener noreferrer" class="rp-link">Composite components</a> are React code organization units that exist only on the JavaScript side of your app. Typical examples are components you create (function and class components), components imported from React Native (<code>View</code>, <code>Text</code>, etc.), or 3rd party packages.</li>
|
|
</ul>
|
|
<p>That might initially sound confusing since we put React Native's <code>View</code> in both categories. There are two <code>View</code> components: composite and host. The relation between them is as follows:</p>
|
|
<ul>
|
|
<li>composite <code>View</code> is the type imported from the <code>react-native</code> package. It is a JavaScript component that renders the host <code>View</code> as its only child in the element tree.</li>
|
|
<li>host <code>View</code>, which you do not render directly. React Native takes the props you pass to the composite <code>View</code>, does some processing on them and passes them to the host <code>View</code>.</li>
|
|
</ul>
|
|
<p>The part of the tree looks as follows:</p>
|
|
<div class="rp-codeblock language-jsx"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="jsx"><code><span class="line"><span style="color:var(--shiki-token-keyword)">*</span><span style="color:var(--shiki-foreground)"> <</span><span style="color:var(--shiki-token-constant)">View</span><span style="color:var(--shiki-foreground)">> (composite)</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> * <</span><span style="color:var(--shiki-token-constant)">View</span><span style="color:var(--shiki-foreground)">> (host)</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> * children prop passed in JSX</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M28 10v18H10V10zm0-2H10a2 2 0 0 0-2 2v18a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 18H2V4a2 2 0 0 1 2-2h14v2H4Z"></path></svg><svg width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m13 24-9-9 1.414-1.414L13 21.171 26.586 7.586 28 9z"></path></svg></button></div></div></div>
|
|
<p>A similar relation exists between other composite and host pairs: e.g. <code>Text</code> , <code>TextInput</code>, and <code>Image</code> components:</p>
|
|
<div class="rp-codeblock language-jsx"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="jsx"><code><span class="line"><span style="color:var(--shiki-token-keyword)">*</span><span style="color:var(--shiki-foreground)"> <</span><span style="color:var(--shiki-token-constant)">Text</span><span style="color:var(--shiki-foreground)">> (composite)</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> * <</span><span style="color:var(--shiki-token-constant)">Text</span><span style="color:var(--shiki-foreground)">> (host)</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> * string (or mixed) content</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M28 10v18H10V10zm0-2H10a2 2 0 0 0-2 2v18a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 18H2V4a2 2 0 0 1 2-2h14v2H4Z"></path></svg><svg width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m13 24-9-9 1.414-1.414L13 21.171 26.586 7.586 28 9z"></path></svg></button></div></div></div>
|
|
<p>Not all React Native components are organized this way, e.g., when you use <code>Pressable</code> (or <code>TouchableOpacity</code>), there is no host <code>Pressable</code>, but composite <code>Pressable</code> is rendering a host <code>View</code> with specific props being set:</p>
|
|
<div class="rp-codeblock language-jsx"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="jsx"><code><span class="line"><span style="color:var(--shiki-token-keyword)">*</span><span style="color:var(--shiki-foreground)"> <</span><span style="color:var(--shiki-token-constant)">Pressable</span><span style="color:var(--shiki-foreground)">> (composite)</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> * <</span><span style="color:var(--shiki-token-constant)">View</span><span style="color:var(--shiki-token-function)"> accessible</span><span style="color:var(--shiki-token-keyword)">=</span><span style="color:var(--shiki-foreground)">{</span><span style="color:var(--shiki-token-constant)">true</span><span style="color:var(--shiki-foreground)">} {</span><span style="color:var(--shiki-token-keyword)">...</span><span style="color:var(--shiki-foreground)">}> (host)</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> * children prop passed in JSX</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M28 10v18H10V10zm0-2H10a2 2 0 0 0-2 2v18a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 18H2V4a2 2 0 0 1 2-2h14v2H4Z"></path></svg><svg width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m13 24-9-9 1.414-1.414L13 21.171 26.586 7.586 28 9z"></path></svg></button></div></div></div>
|
|
<h3 class="rp-toc-include" id="differentiating-between-host-and-composite-elements"><a href="#differentiating-between-host-and-composite-elements" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Differentiating between host and composite elements</h3>
|
|
<p>Any easy way to differentiate between host and composite elements is the <code>type</code> prop of <code>ReactTestInstance</code>:</p>
|
|
<ul>
|
|
<li>for host components, it's always a string value representing a component name, e.g., <code>"View"</code></li>
|
|
<li>for composite components, it's a function or class corresponding to the component</li>
|
|
</ul>
|
|
<p>You can use the following code to check if a given element is a host one:</p>
|
|
<div class="rp-codeblock language-jsx"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="jsx"><code><span class="line"><span style="color:var(--shiki-token-keyword)">function</span><span style="color:var(--shiki-token-function)"> isHostElement</span><span style="color:var(--shiki-foreground)">(element</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> ReactTestInstance</span><span style="color:var(--shiki-foreground)">) {</span></span>
|
|
<span class="line"><span style="color:var(--shiki-token-keyword)"> return</span><span style="color:var(--shiki-token-keyword)"> typeof</span><span style="color:var(--shiki-token-constant)"> element</span><span style="color:var(--shiki-foreground)">.type </span><span style="color:var(--shiki-token-keyword)">===</span><span style="color:var(--shiki-token-string-expression)"> 'string'</span><span style="color:var(--shiki-foreground)">;</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M28 10v18H10V10zm0-2H10a2 2 0 0 0-2 2v18a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 18H2V4a2 2 0 0 1 2-2h14v2H4Z"></path></svg><svg width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m13 24-9-9 1.414-1.414L13 21.171 26.586 7.586 28 9z"></path></svg></button></div></div></div>
|
|
<h2 class="rp-toc-include" id="tree-nodes"><a href="#tree-nodes" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Tree nodes</h2>
|
|
<p>We encourage you to only assert values on host views in your tests because they represent the user interface view and controls which the user can see and interact with. Users cannot see or interact with composite views as they exist purely in the JavaScript domain and do not generate any visible UI.</p>
|
|
<h3 class="rp-toc-include" id="asserting-props"><a href="#asserting-props" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Asserting props</h3>
|
|
<p>For example, suppose you assert a <code>style</code> prop of a composite element. In that case, there is no guarantee that the style will be visible to the user, as the component author can forget to pass this prop to some underlying <code>View</code> or other host component. Similarly <code>onPress</code> event handler on a composite prop can be unreachable by the user.</p>
|
|
<div class="rp-codeblock language-jsx"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="jsx"><code><span class="line"><span style="color:var(--shiki-token-keyword)">function</span><span style="color:var(--shiki-token-function)"> ForgotToPassPropsButton</span><span style="color:var(--shiki-foreground)">({ title</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> onPress</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> style }) {</span></span>
|
|
<span class="line"><span style="color:var(--shiki-token-keyword)"> return</span><span style="color:var(--shiki-foreground)"> (</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> <</span><span style="color:var(--shiki-token-constant)">Pressable</span><span style="color:var(--shiki-foreground)">></span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> <</span><span style="color:var(--shiki-token-constant)">Text</span><span style="color:var(--shiki-foreground)">>{title}</</span><span style="color:var(--shiki-token-constant)">Text</span><span style="color:var(--shiki-foreground)">></span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> </</span><span style="color:var(--shiki-token-constant)">Pressable</span><span style="color:var(--shiki-foreground)">></span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)"> );</span></span>
|
|
<span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="Copy code"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M28 10v18H10V10zm0-2H10a2 2 0 0 0-2 2v18a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V10a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 18H2V4a2 2 0 0 1 2-2h14v2H4Z"></path></svg><svg width="32" height="32" viewBox="0 0 30 30" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m13 24-9-9 1.414-1.414L13 21.171 26.586 7.586 28 9z"></path></svg></button></div></div></div>
|
|
<p>In the above example, user-defined components accept both <code>onPress</code> and <code>style</code> props but do not pass them (through <code>Pressable</code>) to host views, so they will not affect the user interface. Additionally, React Native and other libraries might pass some of the props under different names or transform their values between composite and host components.</p>
|
|
<h2 class="rp-toc-include" id="tree-navigation"><a href="#tree-navigation" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Tree navigation</h2>
|
|
<div class="rp-callout rp-callout--caution"><div class="rp-callout__title">Caution</div><div class="rp-callout__content"><p>
|
|
You should avoid navigating over the element tree, as this makes your testing code fragile and may result in false positives. This section is more relevant for people who want to contribute to our codebase.</p></div></div>
|
|
<p>You will encounter host and composite elements when navigating a tree of react elements using <code>parent</code> or <code>children</code> props of a <code>ReactTestInstance</code> element. You should be careful when navigating the element tree, as the tree structure for third-party components can change independently from your code and cause unexpected test failures.</p>
|
|
<p>Inside RNTL, we have various tree navigation helpers: <code>getHostParent</code>, <code>getHostChildren</code>, etc. These are intentionally not exported, as using them is not recommended.</p>
|
|
<h2 class="rp-toc-include" id="queries"><a href="#queries" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Queries</h2>
|
|
<p>All recommended Testing Library queries return host components to encourage the best practices described above.</p>
|
|
<p>Only <code>UNSAFE_*ByType</code> and <code>UNSAFE_*ByProps</code> queries can return both host and composite components depending on used predicates. They are marked as unsafe precisely because testing composite components makes your test more fragile.</p><!--/$--></div><div class="container-uhcWmO"><img class="logo-IwzwG8" src="/react-native-testing-library/static/image/abstract-atom.4eca0300.avif" alt="Abstract Logo" aria-hidden="true"/><div class="content-uJxcIj"><div class="headlineContainer-NHNT4y"><h1 class="headline-iCUUUl">Need React or React Native
|
|
expertise you can count on?</h1></div><div class="buttonContainer-wOj8Y6"><div class="dark"><a href="https://www.callstack.com/contact" class="button-DogSnd brand-rT2ahT ">Let's talk<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16" class="button-icon-Okk0hT" aria-hidden="true" focusable="false"><path fill="currentColor" d="M9.333 13.333h1.334V12H9.333zm-6.666-2.666h8V12H12v-1.333h1.333V9.333H12V8h-1.333v1.333H4V2.667H2.667zM9.333 8h1.334V6.667H9.333z"></path></svg></a></div></div></div></div><footer class="rp-doc-footer"><div class="rp-doc-footer__edit"></div><div class="rp-doc-footer__divider"></div><div class="rp-prev-next-page"><a href="/react-native-testing-library/docs/guides/community-resources" class="rp-prev-next-page__item rp-prev-next-page__prev rp-link"><span class="rp-prev-next-page__item__desc">Previous page</span><span class="rp-prev-next-page__item__title"><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA0LjVINy41VjMuNUg2LjVWMi41SDUuNVYzLjVINC41VjQuNUg1LjVWMTAuNUg2LjVWNC41Wk00LjUgNC41SDMuNVY1LjVINC41VjQuNVpNOC41IDQuNUg3LjVWNS41SDguNVY0LjVaTTMuNSA2LjVWNS41SDIuNVY2LjVIMy41Wk05LjUgNS41SDguNVY2LjVIOS41VjUuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt="" class="rp-prev-next-page__icon rp-prev-next-page__rotate_180"/><span>Community Resources</span></span></a><a href="/react-native-testing-library/docs/advanced/third-party-integration" class="rp-prev-next-page__item rp-prev-next-page__next rp-link"><span class="rp-prev-next-page__item__desc">Next page</span><span class="rp-prev-next-page__item__title"><span>Third-party Integration</span><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTMiIHZpZXdCb3g9IjAgMCAxMiAxMyIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTYuNSA0LjVINy41VjMuNUg2LjVWMi41SDUuNVYzLjVINC41VjQuNUg1LjVWMTAuNUg2LjVWNC41Wk00LjUgNC41SDMuNVY1LjVINC41VjQuNVpNOC41IDQuNUg3LjVWNS41SDguNVY0LjVaTTMuNSA2LjVWNS41SDIuNVY2LjVIMy41Wk05LjUgNS41SDguNVY2LjVIOS41VjUuNVoiIGZpbGw9IiNCQ0JCQzIiLz4KPC9zdmc+Cg==" alt="" class="rp-prev-next-page__icon"/></span></a></div></footer></main></div><aside class="rp-doc-layout__outline rp-scrollbar"><div class="rp-outline__divider"></div><div class="container-LzbylO"><div><div class="headline-y75_LS">Need help with React or React Native projects?</div><div class="description-muYVs3">We support teams building scalable apps with React and React Native.</div></div><a href="https://www.callstack.com/contact" class="button-DogSnd brand-rT2ahT ">Let's talk<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16" class="button-icon-Okk0hT" aria-hidden="true" focusable="false"><path fill="currentColor" d="M9.333 13.333h1.334V12H9.333zm-6.666-2.666h8V12H12v-1.333h1.333V9.333H12V8h-1.333v1.333H4V2.667H2.667zM9.333 8h1.334V6.667H9.333z"></path></svg></a></div></aside></div></div>
|
|
<div id="__rspress_modal_container"></div>
|
|
</body>
|
|
</html>
|