generated from btholt/next-course-starter
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
123 additions
and
78 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,59 @@ | ||
# TODO – this is to be rewritten | ||
> 🚨 This is experimental and very likely to change in the future. Consider this a sneak peak into what is coming, not what to do today. | ||
Let's write a very basic test. We are going to test Pizza.jsx. Let's fathom we've had issues with the correct alt text being rendered and we want to write a test that test that our image gets rendered, with the correct src and the correct alt text. | ||
Vitest is beginning to support more deeply browser-based testing. To those of us that have been around long enough to remember Selenium, this may strike fear deep int your heart. But fear not! Browser-based testing tools have come so far along since then that it's both fast and reliable, in particular thanks to the Microsoft project [Playwright][playwright]. | ||
|
||
However this is still early days. I'm going to show you how to set it up but be warned that this prone to change as they're still actively working on it! | ||
|
||
So first let's install the libraries we need. | ||
|
||
```bash | ||
npm i -D @vitest/[email protected] [email protected] [email protected] | ||
``` | ||
|
||
You can see that the vitest-browser-react library is still 0.0.1 as of writing so be extra aware it's likely to have changed by the time you read this. | ||
|
||
Okay, so now we want to write browser based tests. But we have also have a bunch of existing Node.js-based tests. Some our already-existing Node.js based tests won't work in the browser. But no worries, Vitest/Vite has a tool just for this, workspaces. It's actually made to handle monorepos, but it will work here just as well. Make a file called `vitest.config.js` (you don't normally need this as your Vite config is normally enough.) | ||
|
||
```javascript | ||
import { defineWorkspace } from "vitest/config"; | ||
|
||
export default defineWorkspace([ | ||
{ | ||
extends: "./vite.config.js", | ||
test: { | ||
include: ["**/*.node.test.{js,jsx}"], | ||
name: "happy-dom", | ||
environment: "happy-dom", | ||
}, | ||
}, | ||
{ | ||
extends: "./vite.config.js", | ||
test: { | ||
setupFiles: ["vitest-browser-react"], | ||
include: ["**/*.browser.test.{js,jsx}"], | ||
name: "browser", | ||
browser: { | ||
provider: "playwright", | ||
enabled: true, | ||
name: "firefox", // you can use chromium or webkit here too | ||
}, | ||
}, | ||
}, | ||
]); | ||
``` | ||
|
||
Now delete the `test` items from your vite.config.js file | ||
|
||
This is a test-only config for Vite (and therefore Vitest.) Now if a test ends in `.node.test.jsx` it will run through the happy-dom based environment and if it ends in `.browser.test.jsx` it will run in our new browser-based environment with Playwright. Let's go rename our tests to reflect that. | ||
|
||
- Cart.browser.test.jsx | ||
- contact.lazy.node.test.jsx | ||
- Pizza.node.test.jsx | ||
- usePizzaOfTheDay.node.test.jsx | ||
|
||
Snapshotting works in the browser so that one works okay. Anything using vitest-fetch-mock is Node.js only so for those we need to mark them as node. We're going to make a new Pizza file so let's leave that one. And our custom hook test mocks fetch so that one is Node only. | ||
|
||
Okay, now create a Pizza.browser.test.jsx | ||
|
||
```javascript | ||
import { render } from "vitest-browser-react"; | ||
|
@@ -23,9 +76,75 @@ test("alt text renders on image", async () => { | |
``` | ||
|
||
- `render` will take a React component and render it in a vacuum. You can then poke and prod it as you need to test it. | ||
- `test` lets you set up tests. the string should be super descriptive so you can tell what test broke if your test fails. | ||
- `expect` lets you make assertions about your React components. | ||
- A big part of what Playwright and vitest-browser-react want you to do is not test implementation details but to test actual user experiences. Don't test the internal state of a React component but do test what users will see and experience. As such, a lot of we'll be testing will be around roles, attributes, etc. | ||
- [@testing-library][principles] has a good doc on why they choose to test this way. | ||
- In general, vitest-browser-react aims to be a drop in replacement for @testing-library/react. | ||
|
||
Looks really similar, right? Alright, let's run it. `npm run test`. You should see the same Vitest UI but now some of the tests are actually running in the browser. | ||
|
||
> It will likely prompt you to run a command like `npx playwright install`. You'll install local copies of browsers to able to run them super fast. | ||
Cool, right? And really fast! Let's do one more. Let's make a Header.jsx test. We're going to test that the cart number is correct. Remember when Facebook notification numbers were always wrong? We're going to make sure that doesn't happen with our cart indicator. In your Header.jsx file: | ||
|
||
```javascript | ||
data-testid="cart-number" // add to .nav-cart-number | ||
``` | ||
|
||
Now make a Header.browser.test.jsx | ||
|
||
```javascript | ||
import { render } from "vitest-browser-react"; | ||
import { expect, test } from "vitest"; | ||
import Header from "../Header"; | ||
import { | ||
RouterProvider, | ||
createRouter, | ||
createRootRoute, | ||
} from "@tanstack/react-router"; | ||
import { CartContext } from "../contexts"; | ||
|
||
test("correctly renders a header with a zero cart count", async () => { | ||
const rootRoute = createRootRoute({ | ||
component: () => ( | ||
<CartContext.Provider value={[[]]}> | ||
<Header /> | ||
</CartContext.Provider> | ||
), | ||
}); | ||
|
||
const router = createRouter({ routeTree: rootRoute }); | ||
const screen = render(<RouterProvider router={router}></RouterProvider>); | ||
|
||
const itemsInCart = await screen.getByTestId("cart-number"); | ||
|
||
await expect.element(itemsInCart).toBeInTheDocument(); | ||
await expect.element(itemsInCart).toHaveTextContent("0"); | ||
}); | ||
|
||
test("correctly renders a header with a three cart count", async () => { | ||
const rootRoute = createRootRoute({ | ||
component: () => ( | ||
<CartContext.Provider | ||
value={[[{ pizza: 1 }, { pizza: 2 }, { pizza: 3 }]]} | ||
> | ||
<Header /> | ||
</CartContext.Provider> | ||
), | ||
}); | ||
|
||
const router = createRouter({ routeTree: rootRoute }); | ||
const screen = render(<RouterProvider router={router}></RouterProvider>); | ||
|
||
const itemsInCart = await screen.getByTestId("cart-number"); | ||
|
||
await expect.element(itemsInCart).toBeInTheDocument(); | ||
await expect.element(itemsInCart).toHaveTextContent("3"); | ||
}); | ||
``` | ||
|
||
We do have to bend over a bit backwards to make sure TanStack Router is happy, hence all the making of root routes. Remember also that our cart gets its cart from context so we have to pass it in that way. Beyond that, it works very similar! | ||
|
||
Again, these are early days for browser-based testing with Vite so proceed in your professional settings with caution. However the future is bright with Playwright! | ||
|
||
[principles]: https://testing-library.com/docs/guiding-principles | ||
[playwright]: https://playwright.dev/ |