Transcript
00:00 Fixtures are the first class citizens for extending functionality in Playwright, and the reason why we're talking about them so early into the workshop is because I want you to get comfortable with creating your own fixtures and extending the test experience. For example, by making page navigation in your tests type safe. As I mentioned previously, I'm going to achieve that by replacing the default page go to functionality with my custom fixture called navigate. And I already have the module prepared where I'm going to implement all my custom fixtures called testextend. Ts.
00:31 The naming here doesn't really matter, you can use whatever module you want. Effectively this is part of your test setup. I will start the implementation for my Navigate fixture from the types. I will declare an interface called Fixtures where I will list all my custom fixtures. So I want to create a fixture called Navigate, I will list it here as the key, and the value here will be the type of that fixture, what I'm gonna be calling in tests.
00:54 So here I will use the types generated by React Router to help me out. I will import two things, first the href function which is a URL builder, and then the type called register. This is a generated type that if you inspect it has this pages property that contains all the generated routes that I have in my application. And I am gonna use them to infer what kind of routes are supported in my tests. So heading back to my test extend, here I'm going to declare a type argument called t that extends key of register and its pages property.
01:29 Basically taking all the keys of the pages object which is all these route strings, and storing them in a type called t. Here in the arguments, I will extend this basically, spread this arguments array and provide the type the parameters type of href to make sure that the call signature of my navigate fixture is the same as the call signature of the href function which accepts the path and an optional argument for building my routes. In this case for testing. And I will provide the t as the type argument for the h refs so we are inferring the exact routes that we're using and have the collocation between the route path and the route parameters. The only thing left is to say that it returns a promise that resolves to void.
02:15 Now this is just a type story of this fixture. To actually implement the runtime behavior, I will import from playwright slash test the test function under an alias test base. And here I will declare my own test function that equals test base dot extent with the fixtures that I just created. And I will export it because I will use this one in tests. So here, typescript already errors here saying that we're missing some fixtures.
02:42 So let's implement the navigate fixture. I'll keep the types over here. So I'll provide the navigate key, which will be the implementation for my navigate fixture. We'll turn it into a synchronous function that accepts the test context, the same context that we have in test cases. And the second argument will be the use function to actually provide the value for this fixture.
03:02 I will call it immediately and await it. And here, if you inspect this types, you will see that this is already the types inferred from my fixtures interface above. This is this type signature. So I will have the arguments here, and I will use the regular page go to to still provision the navigation. I don't wanna implement that myself.
03:22 So I'll head to this first argument, the context, and grab the page and await page dot go to. Make this asynchronous so we can await it. And here I will call the href function from React Router and provide it with the args. And this way I will have the type safe URL builder building this end destination route and providing it to page go to to actually trigger the navigation. And this completes the implementation of the fixture, so I have the call signature in typescript and I have the runtime behavior in Playwright.
03:54 Playwright implements this particular way of building fixtures to expose you to the test environment by this test context where you can grab things like page or context or anything else really that Playwright gives you and have a lot of capabilities when it comes to building your fixtures. You also have this closure here, which is smarter than it looks, because everything before the use function is your before hook and everything after is your after hook. So you can provision some resources before you expose the fixture and then use this after part to clean up those resources and have nice and performant tests. So now that I have this fixture, the only remaining part here is to use it in my test. I will head to homepage.test.ts.
04:34 And for now, in order to have this fixture over here on the test context, I will replace this test function. Instead of playwright, I will import it from my tests test extent. And now that I have this test, if I head to the test this context object, I will see that the navigate fixture is here, and that's exactly the function that I have implemented. Before I change anything from now, I wanna take a look at this expect function that it errors because I'm not exporting the expect function anymore. I will jump quickly to my test dot extend, import the expect from typescript, and just export it as is.
05:10 So I am able to use it in tests from the same module without having to switch here and back. So now I will replace the go to function with the navigate fixture. So for the home page, no visual changes appeared. But if I try to provide the argument the path, you can see that now I have this whole list of all the routes existing in my app. And if I make a mistake, TypeScript will let me know immediately your route is not assignable to the keys of pages that you have.
05:37 What's even cooler is that when I want to go to a path that has a path parameter, like here, the provider, the navigate fixture will error because I am forgetting the provider argument. So I will provide the second argument here, the object and the provider string with the exact value so I'm able to build the correct route and then navigate to it under test. So let's clean this up and go to the homepage. Slash. And now that this is ready, I'm gonna run this test to make sure that Playwright is using my fixture correctly and runs the rest of the test as is.
06:10 Awesome. So I can see that a test is passing. And from the logs of my app, I can also see that we are navigating to the home page because that's exactly what navigate here does in the test. So let's take a look at what's happening here. Because we're replacing or extending the test function, we now are able to imbue this default test context with our custom fixtures such as the navigate fixture, and we implement them by describing their types in this fixture's interface and then extending the default test function by calling dot extend and implementing our fixture.
06:40 So in this case, we're using the default page object and its go to method to go to the built URL by React router which remains type safe due to the generated types. Type safe routing is becoming an essential part of any JavaScript framework and is already supported in React router, 10 stack start and Next. Js just to name a few. And I highly encourage you to benefit from those generated types not only while building your apps but also while testing them.
