diff --git a/lessons/07-testing/G-vitest-browser.md b/lessons/07-testing/G-vitest-browser.md deleted file mode 100644 index 07c2e04..0000000 --- a/lessons/07-testing/G-vitest-browser.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -description: "" ---- - -# TODO – this is to be rewritten - -This is meant to be a very brief treatise on how to do testing on React applications. This will be a brief intro on how to set up Vitest tests for the application we just created. - -## Testing with Vitest - -[Vitest][vitest] is a test runner made by the fine folks who make Vite (as well as Vue.) The idea behind Vitest is that you already have a complete build pipeline for making an app, why should that pipeline be any different for test? It shouldn't; you want your testing environment to look as much like your app environment as possible. - -They designed it to be a drop-in replacement for [Jest][jest] which is what I have taught for this course since the beginning. Jest is great and still a very viable tool to use for testing, even with Vite. We're just going to use Vitest because 1. we don't have to do any more configuration and 2. 100% of what you will learn in here is going to be useful if you use Jest. Win-win. If you want to learn Jest specifically, [take a look at Intermediate React v4's testing section.][v4] - -> Fun side note: [Jest is now an OpenJS project and no longer directly under Facebook][fb]. - -While Vitest is not using Jasmine directly, its APIs mimic Jasmine APIs (just like Jest.) - -Let's get going Run `npm install -D vitest@2.1.3 @vitest/browser@2.1.3 playwright@1.48.0 vitest-browser-react@0.0.1`. - -> This course previously taught [@testing-library/react][tlr] and used a synthetic DOM tool called [happy-dom][hd]. Vitest now has a first class integration with [Playwright][playwright] and their own version @testing-library/react, vitest-browser-react. Hence this version will use those tools. If you want to learn those other tools, the [v8][v8] version of this course all still works. - -We are going to be using the latest-and-greatest testing tool from Microsoft called Playwright, the spirtual successor to Google's Puppeteer tool. Using this tool, your command line client will spin up a _real_ copy of Webkit, Firefox, or Chromium and run your tests inside of them. Can't beat the real thing, and it's finally fast and not-flaky enough for it to be worth it! - -Okay, let's go set up Vitest to work with our Vite project. In your `vite.config.js` - -```javascript - -// under server -test: { - setupFiles: ["vitest-browser-react"], - browser: { - enabled: true, - name: "firefox", // or chromium or webkit - provider: "playwright", - }, -}, -``` - -If you've never run Playwright before, you should run `npx playwright install` to install all the browser stuff necessary. - -Let's go add an npm script. In your package.json. - -```json -"test": "vitest" -``` - -> Fun trick: if you call it test, npm lets you run that command as just `npm t`. - -This command let's you run Vitest in an interactive mode where it will re-run tests selectively as you save them. This lets you get instant feedback if your test is working or not. This is probably my favorite feature of Vitest. So try it now. It should error because we have no tests, but it should open a browser with the testing harness loaded. - -```bash -# these all do the same thing -npm run test -npm test -npm t -``` - -Also good to go install the [Vitest VS Code extension][vitest-vsc] if you're using VS Code. - -Now that we've got that going, let's go write a test. - -[vitest-vsc]: https://marketplace.visualstudio.com/items?itemName=vitest.explorer -[jest]: https://jestjs.io -[jasmine]: https://jasmine.github.io/ -[enzyme]: http://airbnb.io/enzyme/ -[res]: https://raw.githubusercontent.com/btholt/complete-intro-to-react-v5/testing/__mocks__/@frontendmasters/res.json -[fb]: https://twitter.com/cpojer/status/1524419433938046977 -[hd]: https://github.com/capricorn86/happy-dom -[vitest]: https://vitest.dev/ -[v4]: https://frontendmasters.com/courses/intermediate-react-v4/setup-jest-testing-library/ -[tlr]: https://github.com/testing-library/react-testing-library -[v8]: https://frontendmasters.com/courses/intermediate-react-v5/setup-react-testing-library-vitest/ -[playwright]: https://playwright.dev/ diff --git a/lessons/07-testing/G-vitest-ui.md b/lessons/07-testing/G-vitest-ui.md new file mode 100644 index 0000000..e69de29 diff --git a/lessons/07-testing/H-browser-tests.md b/lessons/07-testing/H-browser-tests.md index bea5a7f..e221967 100644 --- a/lessons/07-testing/H-browser-tests.md +++ b/lessons/07-testing/H-browser-tests.md @@ -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/browser@2.1.3 playwright@1.48.0 vitest-browser-react@0.0.1 +``` + +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: () => ( + +
+ + ), + }); + + const router = createRouter({ routeTree: rootRoute }); + const screen = render(); + + 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: () => ( + +
+ + ), + }); + + const router = createRouter({ routeTree: rootRoute }); + const screen = render(); + + 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/