Intermediate React, v5 Testing User Interaction
Transcript from the "Testing User Interaction" Lesson
>> Next section here is gonna be testing some UI interactions. So far, we're basically testing, if I give it the correct properties, do I get the correct thing out of it, right? If I give it the correct images, will the user be display the right images? Very valid test, let's go with Carousel.
[00:00:16] If you remember in Carousel, I say, npm run rev here. Going back over here to my Adopt Me! I click on Luna. If I click on this one, I expect this image to change, right? If I click on this one, I expect this image to change to that, right?
[00:00:36] Let's say that we had a regression that a user, every time they click on just the second photo, for whatever reason, that one doesn't work. Let's write a test to make sure that we can click on everything and it works. So please make another file in your test directory, call it Carousel.test.jsx.
[00:01:01] Import, let's make this fullscreen Expect and test, one key difference here from Jasmine and Jest, expect and tests are not available globally, right? So you do have to import them from Vitest. I'm gonna argue that that is better because now these have types. And even though I'm not writing these in TypeScript, I still get all the type information from it.
[00:01:32] I like it better, plus it's just better to be explicit about imports, I don't like globals. I think you can set it up so that Vitest will make them globally available, but just please don't, unless you're migrating from just Vitest. Yeah, don't do that. You shouldn't do it in Jest either, to be honest with you.
[00:01:54] But that's behavior that came from Jasmine. Import Carousel from '../Carousel', here we go. Test["lets users click on thumbnails to make them the hero", async function that is). Okay, const images = ["0.jpg", "1.jpg", "2.jpg", "3.jpg"]. However many of those that you want, I'll leave that for const Carousel. Lowercase carousel = render.
[00:02:56] And we're gonna make a Carousel with images = images. This one we do not have to wrap in our static router, because there is no React Router stuff inside of Carousel. So this is fine as is. Const hero = await carousel.findByTestId, ('Hero'), and the first thing, we're gonna put one expectation here of (hero.src).toContain(images
[00:03:41] So whatever the first one is that we put in here, we expect by default, that's should be the first thing that we show the user, right? So that's one. Then I'm just gonna write a for loop here to just test each one of them individually. So for (let i = 0, i < images.length; and i++).
[00:04:05] Const image = images[i]. Const thumb = await carousel.findByTestId(thumbnail), (i), so I'm gonna go ahead and put TestIds on all of these here in just a second. And then we'll set await thumb.click. So I'm goina have the browser simulate clicking on that. You have to await it because that's a user interaction.
[00:04:42] It's not instantaneous, okay? And then I'm gonna say, expect(hero.src).toContain(image). And then I'm gonna say, expect, (Array.from(thumb.classList).toContain("active"). So what have I done here? What's happening is,where is this? Let me move that. It's going through, and it's gonna start clicking on every one of these. So the very first one is gonna click on this one, right?
[00:05:33] So that's a valid test to make sure that if I click on the active one, nothing happens, right? Then it's gonna click on this one, make sure that this changes. And it's also gonna make sure, you see how this is grayed out, right? It has the active class on it, if we inspect the image here.
[00:05:47] You can see here it has active. The other ones don't have active on it, right? So it's checking to see, does it also get the act of class as well? Now what I'm not doing here, which I probably could write a test, I mean, just as valid, you could check to make sure that all the other thumbnails do not have the active class on it, right?
[00:06:07] Cuz you can imagine some bug where I click this one, then I click this one, but this one doesn't get the active class removed, right? We're not going to do that, but feel free to go and explore that if you want to. And then it's gonna do that.
[00:06:19] So we're gonna end up with these four assertions. So this is two assertions for that, plus this one, so we'll end up with nine assertions in one test, right? It's pretty cool. Here I didn't do it cuz I don't think we need to, cuz we don't have multiple tests running here, but you could say carousel.unmount as well if you wanted to.
[00:06:44] Okay, so let's go and put our TestIds in here. So we're gonna go to Carousel.jsx to the hero image here, we're gonna add a data-testid="hero". And to each one of these images, we are going to add data-testid=("thumbnail"). And then we need to get that index. So we have the index, so thumbnail index.
[00:07:24] Okay, and now we should be able to run, npm run test. More assertions, all working. Very happy about that. I don't know about you, but every time I read a test, I was trying to make sure, it's like, what happens if this breaks, does this fail? So what happens if I forget to put the act of class on their?
[00:07:43] Test fails, right? Put the actual class back, test passes. I don't know, this is a pretty compelling test to me, right? It's actually going and testing user expectations to make sure that the user expectations are being met. A plus, high fives all round, right?