{"id":3165,"date":"2020-02-18T16:42:33","date_gmt":"2020-02-18T16:42:33","guid":{"rendered":"https:\/\/ionicframework.com\/blog\/?p=3165"},"modified":"2020-10-15T22:07:20","modified_gmt":"2020-10-15T22:07:20","slug":"testing-ionic-react-apps-with-jest-and-react-testing-library","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library","title":{"rendered":"Testing Ionic React Apps with Jest and React Testing Library"},"content":{"rendered":"<p>It&#8217;s 2020, and the testing frameworks for JavaScript applications have improved dramatically over the past few years.<\/p>\n<p>Thanks to tools like Jest and React Testing Library, you can be testing your apps with a few lines of code.<\/p>\n<p>Have you been looking into testing an Ionic React project, but not sure where to start? In this post, I&#8217;ll go over the basics of how to get started as we build out an app using tests.<\/p>\n<p><!--more--><\/p>\n<h2>The Tools<\/h2>\n<p>First, let us go over a few of the tools that we will be using.<\/p>\n<p><a href=\"https:\/\/jestjs.io\/\" rel=\"noopener\" target=\"_blank\">Jest<\/a> is a testing framework built by the teams at Facebook (like React) and is very similar to other test frameworks like Jasmine and Mocha. Jest has been the defacto standard in React testing for quite a while and is gaining popularity in other communities as well. What makes Jest great is it is easy to use, is flexible in the types of tests you want to create, and has a powerful test runner that is smart about running only tests for code that has changed.<\/p>\n<p>Jest is already included and set up when you create a new Ionic React project, so getting started is super easy.<\/p>\n<p><a href=\"https:\/\/testing-library.com\/docs\/react-testing-library\/intro\" rel=\"noopener\" target=\"_blank\">React Testing Library<\/a> (RTL from here on out) is a relative newcomer to the React landscape, but it is also grown immensely in popularity. RTL lets you test React components without relying on the internal implementation details of the component. This approach mimics more of the way an actual user would use the app and promises to make tests more reliable and less brittle to change.<\/p>\n<p><!-- more --><\/p>\n<p>React Testing Library is now included when creating a new Ionic App and is our recommended test library.<\/p>\n<p>The <a href=\"http:\/\/github.com\/ionic-team\/ionic-react-test-utils\" rel=\"noopener\" target=\"_blank\">Ionic React Test Utils<\/a> is a small suite of utilities that can help when testing Ionic React apps. It includes helpers to fire off custom Ionic events and mocks around some of our more complex components.<\/p>\n<h2>Getting Started<\/h2>\n<p>Our demo will keep a list of things we need to get done (don&#8217;t call it a todo app!). Okay, it&#8217;s a todo app, but a todo app is basic enough yet covers a few of the points I want to hit on testing Ionic components.<\/p>\n<p>Start off creating a New Ionic React project via the Ionic CLI:<\/p>\n<pre><code class=\"language-bash\">ionic start ionic-react-todos blank --type=react  \n<\/code><\/pre>\n<blockquote><p>\n  If you are new to Ionic and need to get things set up, view our getting started guide <a href=\"https:\/\/ionicframework.com\/docs\/react\/your-first-app\" target=\"_blank\" rel=\"noopener\">here<\/a>.\n<\/p><\/blockquote>\n<p>This command kicks off a new Ionic React project named &#8220;react-todos&#8221;  using the blank template.<\/p>\n<p>Next, we need to install Ionic React Test Utils, which we will use a bit later on. Go into the new directory and install the npm package:<\/p>\n<pre><code class=\"language-bash\">cd ionic-react-todos\nnpm i @ionic\/react-test-utils\n<\/code><\/pre>\n<p>Open up the project in your code editor. If you look in the <code>src<\/code> folder, you might notice that we already have a test file created for us in <code>App.test.tsx<\/code>. It is a simple test that just makes sure the main app component renders correctly without throwing an error.<\/p>\n<pre><code class=\"language-typescript\">it(&#039;renders without crashing&#039;, () =&gt; {\n  const div = document.createElement(&#039;div&#039;);\n  ReactDOM.render(&lt;App \/&gt;, div);\n  ReactDOM.unmountComponentAtNode(div);\n});\n<\/code><\/pre>\n<p>Go back into your command line, and fire up the Jest test runner:<\/p>\n<pre><code class=\"language-bash\">npm run test\n<\/code><\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"450\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-no-test.jpg\" alt=\"\" class=\"aligncenter size-full wp-image-3167 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-no-test.jpg 1200w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-no-test-300x113.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-no-test-1024x384.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-no-test-768x288.jpg 768w\" data-sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1200px; --smush-placeholder-aspect-ratio: 1200\/450;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"450\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-no-test.jpg\" alt=\"\" class=\"aligncenter size-full wp-image-3167\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-no-test.jpg 1200w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-no-test-300x113.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-no-test-1024x384.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-no-test-768x288.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/noscript><\/p>\n<p>You might get a message saying there were no tests found, which is because Jest (by default) only runs tests on files that are modified since the last git commit. This is pretty handy and helps speed up testing by only running tests on files you are currently working on. Fortunately, we can see on the menu that we can change the &#8220;Watch Usage&#8221;. Press &#8220;a&#8221; to run all tests.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"412\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-pass.jpg\" alt=\"\" class=\"aligncenter size-full wp-image-3168 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-pass.jpg 1200w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-pass-300x103.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-pass-1024x352.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-pass-768x264.jpg 768w\" data-sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1200px; --smush-placeholder-aspect-ratio: 1200\/412;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"412\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-pass.jpg\" alt=\"\" class=\"aligncenter size-full wp-image-3168\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-pass.jpg 1200w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-pass-300x103.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-pass-1024x352.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-pass-768x264.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/noscript><\/p>\n<p>Go ahead and leave Jest running. If we make any changes to our code, Jest automatically reruns the tests.<\/p>\n<h2>Your First Test<\/h2>\n<p>Okay, back in the code, let&#8217;s create a test at <code>src\/pages\/Home.test.tsx<\/code>. We will create a basic test that makes sure the title of our page is &#8220;Ionic React Todos&#8221;. Paste the following code into the test:<\/p>\n<pre><code class=\"language-typescript\">import React from &#039;react&#039;;\nimport { render } from &#039;@testing-library\/react&#039;;\nimport Home from &#039;.\/Home&#039;;\n\ntest(&#039;page should have a title of Ionic React Todos&#039;, async () =&gt; {\n  const { findByText } = render(&lt;Home \/&gt;);\n  await findByText(&#039;Ionic React Todos&#039;);\n});\n<\/code><\/pre>\n<p>Let&#8217;s break down the basic anatomy of a test. First, we have our imports, including the <code>render<\/code> method from RTL and then our <code>Home<\/code> component. Then we have our actual test. The <code>test<\/code> method is from Jest and is available globally, so there is no need to import it. For its first parameter, it takes in the name of the test, in which we usually provide some detailed text about what we are trying to accomplish, and then an anonymous function that contains the code for the test.<\/p>\n<p>The <code>render<\/code> method takes our component and returns a bunch of helper methods to aid us in selecting pieces of the DOM that was rendered. The <code>findByText<\/code> method is one of them, and it looks for an element that contains the text passed into it. If it doesn&#8217;t find one (or finds more than one), <code>findByText<\/code> throws an error. Therefore, there is no need for us to test the return value of <code>findByText<\/code> in this case.<\/p>\n<p>For a list of all the helper methods <code>render<\/code> returns, check out the RTL docs <a href=\"https:\/\/testing-library.com\/docs\/dom-testing-library\/api-queries#queries\" rel=\"noopener\" target=\"_blank\">here<\/a>.<\/p>\n<p>If you view Jest again you will see that the test failed:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"290\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-failed-test.jpg\" alt=\"\" class=\"aligncenter size-full wp-image-3169 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-failed-test.jpg 1200w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-failed-test-300x73.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-failed-test-1024x247.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-failed-test-768x186.jpg 768w\" data-sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1200px; --smush-placeholder-aspect-ratio: 1200\/290;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"290\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-failed-test.jpg\" alt=\"\" class=\"aligncenter size-full wp-image-3169\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-failed-test.jpg 1200w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-failed-test-300x73.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-failed-test-1024x247.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-failed-test-768x186.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/noscript><\/p>\n<p>We get a descriptive output about what happened and where. But basically, our text &#8220;Ionic React Todos&#8221; wasn&#8217;t found. Update the <code>&lt;IonTitle&gt;<\/code> text in the Home component and come back, the tests should now pass:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"410\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-passed-test.jpg\" alt=\"\" class=\"aligncenter size-full wp-image-3170 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-passed-test.jpg 1200w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-passed-test-300x103.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-passed-test-1024x350.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-passed-test-768x262.jpg 768w\" data-sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1200px; --smush-placeholder-aspect-ratio: 1200\/410;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"410\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-passed-test.jpg\" alt=\"\" class=\"aligncenter size-full wp-image-3170\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-passed-test.jpg 1200w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-passed-test-300x103.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-passed-test-1024x350.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-passed-test-768x262.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/noscript><\/p>\n<p>Now that&#8217;s what we like to see!<\/p>\n<h2>Building the Todo List<\/h2>\n<p>We have our first test passing, so that means it is time to write another failing one! When we have no todos to display, we want a message that says there are none. Here is the test for that:<\/p>\n<pre><code class=\"language-typescript\">test(&#039;when there are no todos, a no todos message should show&#039;, async () =&gt; {\n  const { findByText } = render(&lt;Home \/&gt;);\n  await findByText(&#039;No todos, add some!&#039;)\n});\n<\/code><\/pre>\n<p>Take a look at the Jest runner, and you should see the new test failing. Let&#8217;s update the component to display the message when there are no todos.<\/p>\n<p>To get started, add a <code>Todo<\/code> interface to the top of <code>Home<\/code> and create a state variable using the <code>useState<\/code> hook to hold the todos. Also, update the <code>&lt;IonContent&gt;<\/code> to display a message if there are no todos.<\/p>\n<p>Update <code>Home<\/code> to:<\/p>\n<pre><code class=\"language-typescript\">export interface Todo {\n  id: number;\n  text: string;\n}\n\nconst Home: React.FC = () =&gt; {\n  const [todos, setTodos] = useState&lt;Todo[]&gt;([]);\n  return (\n    &lt;IonPage&gt;\n      &lt;IonHeader&gt;\n        &lt;IonToolbar&gt;\n          &lt;IonTitle&gt;Ionic React Todos&lt;\/IonTitle&gt;\n        &lt;\/IonToolbar&gt;\n      &lt;\/IonHeader&gt;\n      &lt;IonContent className=&quot;ion-padding&quot;&gt;\n        {todos.length === 0 ? (\n          &lt;div&gt;No todos, add some!&lt;\/div&gt;\n        ) : (\n            &lt;div&gt;todos will go here&lt;\/div&gt;\n          )}\n      &lt;\/IonContent&gt;\n    &lt;\/IonPage&gt;\n  );\n};\n<\/code><\/pre>\n<p>Our last test should now be passing. Let us write another to make sure our todos appear when there are some:<\/p>\n<pre><code class=\"language-typescript\">test(&#039;when TodoList is loaded with todos, then the todos should be in the list&#039;, async () =&gt; {\n  const todos: Todo[] = [\n    { id: 1, text: &#039;review PR&#039; },\n    { id: 2, text: &#039;update docs&#039; }\n  ];\n  const { findByText } = render(&lt;Home \/&gt;);\n  await findByText(todos[0].text);\n  await findByText(todos[1].text);\n});\n<\/code><\/pre>\n<p>We are running a couple of <code>findByText<\/code> calls here to make sure both the todos are added. If either of these fails to find an element, then an error is thrown.<\/p>\n<p>Next, replace the <code>&lt;div&gt;todos will go here&lt;\/div&gt;<\/code> placeholder with this snippet which creates an <code>IonList<\/code> with an <code>IonItem<\/code> for each of the todos:<\/p>\n<pre><code class=\"language-typescript\">&lt;IonList&gt;\n  {todos.map((todo, i) =&gt; (\n    &lt;IonItem key={i}&gt;\n      &lt;IonLabel&gt;\n        &lt;h2&gt;{todo.text}&lt;\/h2&gt;\n      &lt;\/IonLabel&gt;\n      &lt;IonIcon data-icon=&quot;trash&quot; icon={trash} color=&quot;danger&quot; slot=&quot;end&quot; \/&gt;\n    &lt;\/IonItem&gt;\n  ))}\n&lt;\/IonList&gt;\n<\/code><\/pre>\n<blockquote><p>\n  All the Ionic components are imported from <code>@ionic\/react<\/code>, and the <code>trash<\/code> icon is imported from <code>ionicons\/icons<\/code>.\n<\/p><\/blockquote>\n<p>But wait, how&#8217;s our <code>Home<\/code> component getting the array of Todos? Right now, it&#8217;s not. Let&#8217;s pretend the <code>Home<\/code> component calls into an API to fetch the todos. We won&#8217;t have a real API, but we will create and load a json file with some data. Create a file at <code>public\/assets\/todos.json<\/code> and paste the following into it:<\/p>\n<pre><code class=\"language-json\">[\n  {\n    &quot;id&quot;: 1, &quot;text&quot;: &quot;review PR&quot;\n  },\n  {\n    &quot;id&quot;: 2, &quot;text&quot;: &quot;update readme&quot;\n  },\n  {\n    &quot;id&quot;: 3, &quot;text&quot;: &quot;write docs&quot;\n  }\n]\n<\/code><\/pre>\n<p>Back in <code>Home<\/code>, add a <code>useEffect<\/code> hook to call into the API and set the todos state:<\/p>\n<pre><code class=\"language-typescript\">useEffect(() =&gt; {\n  async function doFetch() {\n    const result = await fetch(&#039;\/assets\/todos.json&#039;);\n    const data = await result.json();\n    setTodos(data);\n  }\n  doFetch();\n}, []);\n<\/code><\/pre>\n<h2>Mocking an HTTP Request<\/h2>\n<p>Our tests start to fail because the fetch call won&#8217;t be able to make the request while running in Jest. Fortunately, Jest allows us to mock fetch and return specific data. Add the following <code>mockFetch<\/code> method to the test file, which allows us to pass in some data that returns from the <code>fetch<\/code> call, as well as the <code>beforeEach<\/code> Jest helper, which calls the mock function before each test runs:<\/p>\n<pre><code class=\"language-typescript\">function mockFetch(data: any) {\n  return jest.spyOn(window, &#039;fetch&#039;).mockResolvedValue(new Response(JSON.stringify(data)));\n}\n\nbeforeEach(() =&gt; mockFetch([]));\n<\/code><\/pre>\n<p>In the last test, we can call <code>mockFetch<\/code> and pass in our todo array:<\/p>\n<pre><code class=\"language-typescript\">\/\/ todos array\nmockFetch(todos);\n\/\/ render and fetchByText methods\n<\/code><\/pre>\n<p>When the component calls <code>fetch<\/code>, it now returns the mock test data we have set up for it, and our test passes.<\/p>\n<h2>Adding a Todo<\/h2>\n<p>Next up is the bulk of our functionality, adding a todo to the list!<\/p>\n<p>The next test does quite a bit. We will test clicking an add todo button, verifying the todo from loads, filling out the form, submitting the form, and lastly, making sure the todo appears in the list. This might seem like a lot, but RTL promotes testing a page, much like how a user would interact with it. This lets us cover quite a bit of functionality in a single test.<\/p>\n<p>Go ahead and start to stub out the test with our first step: clicking a button to display the new todo form:<\/p>\n<pre><code class=\"language-typescript\">test(&#039;when clicking the new button, we should be able to add a new todo&#039;, async () =&gt; {\n  const { findByTitle, findByText } = render(&lt;Home \/&gt;);\n  const addButton = await findByTitle(&#039;Add Todo&#039;);  \n  fireEvent.click(addButton);\n});\n<\/code><\/pre>\n<p><code>fireEvent<\/code> is imported from <code>@testing-library\/react<\/code> and helps us simulate user interaction with the DOM elements that get returned. Here we are using it to click the <code>addButton<\/code>.<\/p>\n<p>We are also using a new function returned from <code>render<\/code> here, <code>findByTitle<\/code>, which works very similarly to <code>findByText<\/code>, but instead looks for an element for a certain title. Update the <code>Home<\/code> component and add the following right above the closing <code>&lt;\/IonContent&gt;<\/code> tag:<\/p>\n<pre><code class=\"language-typescript\">&lt;IonFab vertical=&quot;bottom&quot; horizontal=&quot;end&quot;&gt;\n  &lt;IonFabButton title=&quot;Add Todo&quot; onClick={() =&gt; setShowModal(true)}&gt;\n    &lt;IonIcon data-icon=&quot;add&quot; icon={add} \/&gt;\n  &lt;\/IonFabButton&gt;\n&lt;\/IonFab&gt;\n&lt;IonModal\n  onDidDismiss={() =&gt; setShowModal(false)}\n  isOpen={showModal}\n&gt;\n  {\/* Todo Form will go here *\/}\n&lt;\/IonModal&gt;\n<\/code><\/pre>\n<p>Also add the state variable (right below the todos state) to maintain if we are displaying the modal containing the todo form:<\/p>\n<pre><code class=\"language-typescript\">const [showModal, setShowModal] = useState(false);\n<\/code><\/pre>\n<p>Buh uh oh, the tests have started to fail due to a new error:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1202\" height=\"336\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-modal-fail.jpg\" alt=\"\" class=\"aligncenter size-full wp-image-3171 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-modal-fail.jpg 1202w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-modal-fail-300x84.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-modal-fail-1024x286.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-modal-fail-768x215.jpg 768w\" data-sizes=\"auto, (max-width: 1202px) 100vw, 1202px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1202px; --smush-placeholder-aspect-ratio: 1202\/336;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1202\" height=\"336\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-modal-fail.jpg\" alt=\"\" class=\"aligncenter size-full wp-image-3171\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-modal-fail.jpg 1202w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-modal-fail-300x84.jpg 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-modal-fail-1024x286.jpg 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/jest-modal-fail-768x215.jpg 768w\" sizes=\"auto, (max-width: 1202px) 100vw, 1202px\" \/><\/noscript><\/p>\n<p>This error leads us up to a bit of a tricky spot when it comes to testing Ionic React.<\/p>\n<h2>Using Ionic React Test Utils to mock Ionic Web Components<\/h2>\n<blockquote><p>\n  Note: Right before this blog was published, the JSDOM team <a href=\"https:\/\/github.com\/jsdom\/jsdom\/blob\/master\/Changelog.md#1620\" target=\"_blank\" rel=\"noopener\">announced<\/a> customElement support for JSDOM. We are excited for this and will monitor if it fixes the issues we ran into and will update the post accordingly.\n<\/p><\/blockquote>\n<p>Ionic is written in web components, and Ionic React is a thin layer around those components to make them feel and behave more like the React components. Unfortunately, JSDOM does not currently support web components and errors if trying to access web component API, like <code>customElements<\/code> in the error above.<\/p>\n<p>What we can do here, though, is use Jest to mock out the Ionic React components that can&#8217;t render (like IonModal), which is where Ionic React Test Utils (IRTU) comes into play. IRTU provides a helper that can mock out all known Ionic components that have issues rendering in JSDOM. The mocked components simulate the actual ones by rendering minimal DOM to test.<\/p>\n<p>To set it up, go into <code>src\/setupTests.ts<\/code> file and update it with:<\/p>\n<pre><code class=\"language-typescript\">import { mockIonicReact } from &#039;@ionic\/react-test-utils&#039;;\nmockIonicReact();\n<\/code><\/pre>\n<p>That should get the test passing.<\/p>\n<blockquote><p>\n  For more info around this method, read <a href=\"https:\/\/dev.to\/ionic\/testing-web-components-in-react-4e49\" rel=\"noopener\" target=\"_blank\">Testing Web Components in React<\/a> on Dev.to.\n<\/p><\/blockquote>\n<h2>Testing the Form<\/h2>\n<p>Ok, let&#8217;s continue flushing this test out. Now that the modal is loading, we will get the input box and save button:<\/p>\n<pre><code class=\"language-typescript\">const input = await findByTitle(&#039;Todo Text&#039;);\nconst button = await findByText(&#039;Save&#039;);\n<\/code><\/pre>\n<p>Time to implement the functionality for the form. Add the following form to the <code>IonModal<\/code> in <code>Home<\/code>, replacing <code>{\/* Todo Form will go here *\/}<\/code>:<\/p>\n<pre><code class=\"language-typescript\">&lt;IonToolbar&gt;\n  &lt;IonTitle&gt;Add Todo&lt;\/IonTitle&gt;\n&lt;\/IonToolbar&gt;\n&lt;IonContent&gt;\n  &lt;IonList&gt;\n    &lt;IonItem&gt;\n      &lt;IonLabel position=&quot;stacked&quot;&gt;Todo&lt;\/IonLabel&gt;\n      &lt;IonInput id=&quot;todo&quot; title=&quot;Todo Text&quot; value={text} onIonChange={e =&gt; setText(e.detail.value!)} \/&gt;\n    &lt;\/IonItem&gt;\n  &lt;\/IonList&gt;\n  &lt;IonButton expand=&quot;block&quot; onClick={addTodo}&gt;\n    Save\n  &lt;\/IonButton&gt;\n&lt;\/IonContent&gt;\n<\/code><\/pre>\n<p>And add the new text\/setText state variables as well as the method to save the todos to the top of the function:<\/p>\n<pre><code class=\"language-typescript\">const [text, setText] = useState(&#039;&#039;);\n\nconst addTodo = () =&gt; {\n  const nextId = todos.reduce((id, todo) =&gt; Math.max(id, todo.id!), 0) + 1;\n  const todo: Todo = {\n    id: nextId,\n    text\n  };\n  setTodos([...todos, todo]);\n  setShowModal(false);\n  setText(&#039;&#039;);\n};\n<\/code><\/pre>\n<p>Next, we need to simulate filling out the form and clicking the save button. Typically, you would use the <code>fireEvent.change<\/code> method from RTU to simulate an input change. This fires the input element&#8217;s <code>change<\/code> event. However, Ionic components fire custom &#8216;ion&#8217; events like &#8216;ionChange&#8217;. Therefore, we can&#8217;t use <code>fireEvent.change<\/code> here.<\/p>\n<p>To help with this, IRTU exports <code>ionFireEvent<\/code>, which wraps RTU&#8217;s <code>fireEvent<\/code>, and augments it with all the custom Ionic events. Therefore, you can use <code>ionFireEvent<\/code> as a drop in replacement for <code>fireEvent<\/code>. To do so, import <code>ionFireEvent<\/code> and alias it to <code>fireEvent<\/code>:<\/p>\n<pre><code class=\"language-typescript\">import { ionFireEvent as fireEvent } from &#039;@ionic\/react-test-utils&#039;;\n<\/code><\/pre>\n<p>And remove the <code>fireEvent<\/code> import from <code>@testing-library\/react<\/code>.<\/p>\n<p>Now, we fire the <code>ionChange<\/code> event and click the button and verify that our todo gets added to the list:<\/p>\n<pre><code class=\"language-typescript\">fireEvent.ionChange(input, &#039;test todo&#039;);\nfireEvent.click(button);\nawait findByText(&#039;test todo&#039;);\n<\/code><\/pre>\n<h2>How did we do?<\/h2>\n<p>So far, we have written quite a bit of functionality driven entirely by tests. If we fire up the dev server:<\/p>\n<pre><code class=\"language-bash\">ionic serve \n<\/code><\/pre>\n<p>We should see the list of todos get loaded from the fetch request, and be able to create a new todo.<\/p>\n<p>You might notice there are delete icons for each of the todos. I&#8217;ll leave that as an exercise for you to implement the delete functionality.<\/p>\n<p>I&#8217;ve included a link to the full demo repository below that expands on the app a bit more, including deleting todos.<\/p>\n<h2>Wrapping up<\/h2>\n<p>It might have taken a bit of extra time to drive this development through tests, but now we have a good set of tests to run anytime we make updates to the app that gives us confidence that we didn&#8217;t break anything.<\/p>\n<p>Here is a list of resources for more info about the content covered in this post:<\/p>\n<ul>\n<li><a href=\"https:\/\/testing-library.com\/docs\/react-testing-library\/intro\" rel=\"noopener\" target=\"_blank\">React Testing Library<\/a><\/li>\n<li><a href=\"https:\/\/jestjs.io\/\" rel=\"noopener\" target=\"_blank\">Jest<\/a><\/li>\n<li><a href=\"http:\/\/github.com\/ionic-team\/ionic-react-test-utils\" rel=\"noopener\" target=\"_blank\">Ionic React Test Utils<\/a><\/li>\n<li><a href=\"https:\/\/kentcdodds.com\/\" rel=\"noopener\" target=\"_blank\">Kent C Dodd&#8217;s Blog<\/a> (author of React Testing Library and great articles on testing)<\/li>\n<li><a href=\"http:\/\/github.com\/elylucas\/ionic-react-todos\" rel=\"noopener\" target=\"_blank\">Github repo for the demo app<\/a><\/li>\n<\/ul>\n<p>Interested in learning more about testing Ionic React apps? Let us know down below with your questions or comments.<\/p>\n<p>Until next time, may your tests be green and your todos list complete!<\/p>\n<p>Happy coding.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s 2020, and the testing frameworks for JavaScript applications have improved dramatically over the past few years. Thanks to tools like Jest and React Testing Library, you can be testing your apps with a few lines of code. Have you been looking into testing an Ionic React project, but not sure where to start? In [&hellip;]<\/p>\n","protected":false},"author":66,"featured_media":3172,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"","publish_post_category":"","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"","discourse_permalink":"","wpdc_publishing_response":"","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,124],"tags":[136,39],"class_list":["post-3165","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-all","category-tutorials","tag-react","tag-testing"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.0 (Yoast SEO v23.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Testing Ionic React Apps with Jest and React Testing Library - Ionic Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Testing Ionic React Apps with Jest and React Testing Library\" \/>\n<meta property=\"og:description\" content=\"It&#8217;s 2020, and the testing frameworks for JavaScript applications have improved dramatically over the past few years. Thanks to tools like Jest and React Testing Library, you can be testing your apps with a few lines of code. Have you been looking into testing an Ionic React project, but not sure where to start? In [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2020-02-18T16:42:33+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-10-15T22:07:20+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1600\" \/>\n\t<meta property=\"og:image:height\" content=\"880\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Ely Lucas\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@elylucas\" \/>\n<meta name=\"twitter:site\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ely Lucas\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library\"},\"author\":{\"name\":\"Ely Lucas\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/d4a019b9a30f6c3db51b24803ab2be9b\"},\"headline\":\"Testing Ionic React Apps with Jest and React Testing Library\",\"datePublished\":\"2020-02-18T16:42:33+00:00\",\"dateModified\":\"2020-10-15T22:07:20+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library\"},\"wordCount\":2050,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png\",\"keywords\":[\"react\",\"testing\"],\"articleSection\":[\"All\",\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library\",\"url\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library\",\"name\":\"Testing Ionic React Apps with Jest and React Testing Library - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png\",\"datePublished\":\"2020-02-18T16:42:33+00:00\",\"dateModified\":\"2020-10-15T22:07:20+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#primaryimage\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png\",\"width\":1600,\"height\":880,\"caption\":\"Ionic React Testing Image\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Testing Ionic React Apps with Jest and React Testing Library\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ionic.io\/blog\/#website\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"name\":\"ionic.io\/blog\",\"description\":\"Build amazing native and progressive web apps with the web\",\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ionic.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ionic.io\/blog\/#organization\",\"name\":\"Ionic\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"width\":1920,\"height\":854,\"caption\":\"Ionic\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/ionicframework\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/d4a019b9a30f6c3db51b24803ab2be9b\",\"name\":\"Ely Lucas\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/224137763c00c380285e911184f2139f2f4e2f15ecc2fcd9528feebc6d2ddab6?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/224137763c00c380285e911184f2139f2f4e2f15ecc2fcd9528feebc6d2ddab6?s=96&d=mm&r=g\",\"caption\":\"Ely Lucas\"},\"sameAs\":[\"https:\/\/x.com\/elylucas\"],\"url\":\"https:\/\/ionic.io\/blog\/author\/ely\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Testing Ionic React Apps with Jest and React Testing Library - Ionic Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library","og_locale":"en_US","og_type":"article","og_title":"Testing Ionic React Apps with Jest and React Testing Library","og_description":"It&#8217;s 2020, and the testing frameworks for JavaScript applications have improved dramatically over the past few years. Thanks to tools like Jest and React Testing Library, you can be testing your apps with a few lines of code. Have you been looking into testing an Ionic React project, but not sure where to start? In [&hellip;]","og_url":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library","og_site_name":"Ionic Blog","article_published_time":"2020-02-18T16:42:33+00:00","article_modified_time":"2020-10-15T22:07:20+00:00","og_image":[{"width":1600,"height":880,"url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png","type":"image\/png"}],"author":"Ely Lucas","twitter_card":"summary_large_image","twitter_creator":"@elylucas","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Ely Lucas","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library"},"author":{"name":"Ely Lucas","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/d4a019b9a30f6c3db51b24803ab2be9b"},"headline":"Testing Ionic React Apps with Jest and React Testing Library","datePublished":"2020-02-18T16:42:33+00:00","dateModified":"2020-10-15T22:07:20+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library"},"wordCount":2050,"commentCount":0,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"image":{"@id":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png","keywords":["react","testing"],"articleSection":["All","Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library","url":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library","name":"Testing Ionic React Apps with Jest and React Testing Library - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#primaryimage"},"image":{"@id":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png","datePublished":"2020-02-18T16:42:33+00:00","dateModified":"2020-10-15T22:07:20+00:00","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#primaryimage","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png","width":1600,"height":880,"caption":"Ionic React Testing Image"},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/testing-ionic-react-apps-with-jest-and-react-testing-library#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Testing Ionic React Apps with Jest and React Testing Library"}]},{"@type":"WebSite","@id":"https:\/\/ionic.io\/blog\/#website","url":"https:\/\/ionic.io\/blog\/","name":"ionic.io\/blog","description":"Build amazing native and progressive web apps with the web","publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ionic.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ionic.io\/blog\/#organization","name":"Ionic","url":"https:\/\/ionic.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","width":1920,"height":854,"caption":"Ionic"},"image":{"@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/ionicframework"]},{"@type":"Person","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/d4a019b9a30f6c3db51b24803ab2be9b","name":"Ely Lucas","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/224137763c00c380285e911184f2139f2f4e2f15ecc2fcd9528feebc6d2ddab6?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/224137763c00c380285e911184f2139f2f4e2f15ecc2fcd9528feebc6d2ddab6?s=96&d=mm&r=g","caption":"Ely Lucas"},"sameAs":["https:\/\/x.com\/elylucas"],"url":"https:\/\/ionic.io\/blog\/author\/ely"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/02\/ionic-react-testing-img.png","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/3165","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/users\/66"}],"replies":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/comments?post=3165"}],"version-history":[{"count":0,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/3165\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media\/3172"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=3165"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=3165"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=3165"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}