1. Introduction to Cypress Component TDD
Hi, I'm Burat, a staff engineer and test architect at Xtent. Cypress component test-driven design can take our front end engineer to the next level. We'll cover TDD examples, component testing, endpoint testing, and recommended best practices. The application under test is a React version of two reviews from Angular. We have a React application with a JSON server and many Cypress component test examples. Start with a placeholder test, ensure rendering and styles, then write a failing test for a link attribute.
Hi, everyone. My name is Burat. I'm a staff engineer and test architect at Xtent. And I believe Cypress component test-driven design can take our front end engineer to the next level.
We'll talk about a TDD example and go through our component test. We'll talk further about component testing, a second example. We'll cover endpoint testing within the context of TDD. And finally, we'll wrap up with recommended best practices.
The application on the test is two reviews from Angular. You're all familiar with it. It's rewritten in React. And the book, Cypress component test-driven design. You'll be able to find the link to the book right here. And the final app with all the source code, everything in this presentation over here at this link.
It's a React application with a JSON server, some nice tooling. We have many Cypress component test examples here. Each one of them has a React testing library mirror. You have UI integration tests, stubbing the network. You have API end-to-end and UI end-to-end tests. Proportion of them is one to five to 15, which gives you an idea about the test architecture.
Here's our first component. You'll find it in Chapter 3 of the book, our final code right here in this link. Start with a placeholder test. First, you want to make sure that you can render something, you imported the right file, you have the styles. This is a good baseline start. You can just copy-paste this to every starting component test run. So you have that being rendered. Nice. Write a failing test because with TDD, you have fault-finding tests for things that matter. So we want a link, has an attribute that goes to reactjs.org.
2. Component Test Design and Enhancements
We have our first failure. We try to make it work by adding a link with an href. Component tests can aid red-green refactor cycles. We add a failing test to ensure visibility and then add an icon for a better render.
And we have our first failure. Great, one little test, failing, and we try to make that work, adding a link with an href. Once that was something with failing, want to do the minimum to get it to work, and then you just want to try to make it better. Want to use component tests as the design tool to aid your red-green refactor cycles.
We're not seeing anything being rendered, so we think, okay, here's an idea, let's make sure that this is visible. So we add a failing test, now we have a red. Incremental vision enhancements mean you can use the visual feedback that's not up to your expectation as the red indicator. So I'm not seeing it, I want something in there. I'm going to go and add an icon, so I see a decent render. Something being visible doesn't really help, but I want to see the right thing.
3. Writing Incremental Tests and Custom Wrappers
The obvious but hard thing to do with TDD is write very small incremental tests at a time. You want to build the component iteratively using tests. At the end we think, okay, maybe we need an icon by doing this component. If you have TypeScript and ESLint, it's got a tooling, can also aid your red-green refactor cycles. But so we can just add that attribute right there. In order to make the test work, on top of that, we have to wrap it with browser-router because that's how you mount your component when you're using react-router-dump, you just wrap it with browser-router. You have to always remember a component test is a small-scale application. No problem, you can write a custom wrapper function. Back to the test, you wanna add a link that has three spans on there with some strings. So, write a failing test for it.
The obvious but hard thing to do with TDD is write very small incremental tests at a time. You want to build the component iteratively using tests. You want to find out more about it with the test, like you're ad hoc-ing, but as you're ad hoc-ing, you go ahead and add a step.
So we have an attribute, we add a test for it, and then we add that attribute check. We write another step of test, and then we add another attribute to make that work. At the end we think, okay, maybe we need an icon by doing this component. We add that data size selector to the top tag of the component, which I recommend makes the component very easy to find when it's used as a child, or when it's used in end-to-end tests. So we do that, and then under that component, we're expecting an .sdg to render because that's the icon we're looking for.
If you have TypeScript and ESLint, it's got a tooling, can also aid your red-green refactor cycles. Here, the property 2 is missing. NavLink is from react-router-dump. So we don't really have to have a failing test for it. But so we can just add that attribute right there. In order to make the test work, on top of that, we have to wrap it with browser-router because that's how you mount your component when you're using react-router-dump, you just wrap it with browser-router. You have to always remember a component test is a small-scale application. So you have to replicate your application wrappers. You're using browser-router in the main app. And you'll be using browser-router whenever you're mounting your component.
But your application may be looking like this. And your test mounts will be looking like the one on the left. No problem, you can write a custom wrapper function. You'll be able to find the link here and you'll be writing one in the book as well. And it's just a one-time thing. If some of your components don't need to be wrapped with some of these, that's perfectly fine. Extra wrappers do not really change how the component behaves. The component just uses whatever it needs. It's the exact same thing you would do with React testing library. You can also write multiple different kinds of custom wrappers, right? Some of the wrappers for these components, but different wrapper for another set of components. Back to the test, you wanna add a link that has three spans on there with some strings. So, write a failing test for it.
4. Test Failure and Test Design Comparison
We encounter a failure due to a missing nav link data site selector, which is then added along with classes to improve rendering. The test passes and renders as expected. The question arises: should tests be broken down or kept as one flow? The right side, with a single flow, is preferable if the beginning state is common. However, as long as tests are not duplicated and are order independent and stateless, either approach is acceptable. A comparison is made to a React testing library, where the intent remains the same but the API style differs.
And then we get our failure. Because we don't even have that nav link data site selector. So, we go ahead and add that. We add some classes to make the render look nice. There's our spans. Each one of them just has a hard coded text. And we have a nice passing test at the end. Renders the way we want it to.
The question comes up, should we break down the tests or not? On the left and right, it's the same exact test and the same thing. Left side breaks it down, right side just does one flow. What matters is the beginning state of a test. If reaching this state is common, then in my opinion, the right side, an opportunity for a test enhancement is stronger. I prefer the example on the right. So long as test don't duplicate though, they're order independent and stateless, either side is fine. Reason unit testers may prefer the one on the left is because they want to have a smaller blast radius. This is not a problem. If you have a Cyprus test runner, it's very easy to find exactly where the failure happened. You just have to be careful not to do the thing on the left.
So here we have three edge blocks, they overlap with each other. So the first one's covered in the second one and the third one covers the second one. So this is just one test. We can just write one test, most of the third one at the visibility check from the first test, and that's good enough. An example of what not to do and what to do. Here's the comparison to a React testing library. One-on-one, it's the same thing. You can translate each line. The intent is always the same. The difference is the API style. So you have Async, Await, Variable Assignments on one side, and you have a chain syntax, little more flow on the right side. But the intent, your actions, whatever you're doing is the same.
5. Component Testing with Props and Wrappers
Just different ways of doing it. We'll have a second example and more points of component testing, especially about props, wrappers of components, and parent-child relationships. You can manipulate your component either with props or wrappers. You can pass data with props, either by adding a prop to the component or using a wrapper. When adding props, you add the property to the component types, add it to the arguments of the component, and use it in the component. This time, we're adding the value property and a failing test for it. We also write a failing test for a form to be read-only and add the prop accordingly. When encountering failures, prefer adding more tests or refactoring over adding additional source code.
Just different ways of doing it. So that was a walkthrough of a TDD example with component tests. Now we'll have a second example and some more points of component testing, especially about props, wrappers of components, and parent-child relationships.
So this example is from chapter five of the book, and here's the final code. You can use hard coding initially, or you can use console log to make your test pass. Just hard coding, everything's fine there. Then you manipulate your component either with props or wrappers.
So here we have a prop. Instead of a hard-coded test, we're adding that prop right here, the placeholder. And then we add it to the component. You can pass data with props. Like here, we have a prop for heroes, we're passing an empty array. And then here, we're passing a heroes array. On the right side, on the top, we're not passing anything, just the component by itself. But if you wanted to, you could have a wrapper there, for instance, Context API, and thereby provide a value there, the villains array. And that's how we manipulate our components. So either you use a property or wrapper. That's how you customize your component testing in Cypress and React and anywhere else. When you're adding props, you add the property, right here we're adding name property, and then you add the prop to the component types. You add it to the arguments of the component, then you use it in the component. Same flow, we'll be going through it. This time we're adding the value property, a test failing with that, right? And then add the prop, the argument, and use it in the test itself, which is in the component. And we have a nice render at some point, which we'll be using for a form. This form, we want it to have the ability to be a read only also. So we write a failing test for that, go ahead and add the prop. And if we have events, we can just start them. Here we have an event that's two keystrokes, so we want the change event to be called twice. First, the failing tests, and then we add the prop, the arguments, and again, we use it in the component. When I see failures through tests, and when you have green tests, you wanna prefer adding more tests, or refactoring versus adding additional source code. This is very important.
6. Test Design and Parent-Child Example
Sometimes we have a passing test, and then we go and add so much source code, no need to do that. A little bit of a test, make it work, make it better. Here's a parent-child example. Always check if you're duplicating the test effort covered elsewhere. We wanna find opportunities to cover different functionalities. With JSX and template literals, you can be pretty creative about your selectors.
Sometimes we have a passing test, and then we go and add so much source code, no need to do that. A little bit of a test, make it work, make it better. If you have something working, want to prefer adding more tests, you want to have a failure first, or refactoring before you go to the source code.
Here's a parent-child example. So this is a child being used at the parent, two different varieties, a name, and a description. Always check if you're duplicating the test effort covered elsewhere. So we wanna find opportunities to cover different functionalities. On the right, we already have a check that we're typing something, and then that thing is visible. We're duplicating that test over here at the parent, and that's not good. At the parent, you can do something else. We can check that the parent renders two variants of the child. Here is another advice, with JSX and template literals, you can be pretty creative about your selectors. I like to have a data set at the top tag of the component, and if there are any variants, I just exploit JSX and template literals, and it's pretty fun.
7. React Testing Library vs Cypress Component Test
React Testing Library versus Cypress Component Test. The main difference is how we do the onChange event test. Endpoint tests are great when you're finished building your components. You want to aid the test at the lowest level and then move up when you cannot test confidently. This is an example of that. How to do a feature with TDD examples. You always want to remember what you're doing at your child components. The TDD flow is exactly the same with E3. Start with something failing, do the minimum to get it to work, and then if you can make it better, do refactoring. Once things are working, prefer to add more failures.
React Testing Library versus Cypress Component Test. I think for this one, the main difference was how we do the onChange event test. On the right side is CyGet, the change tag right here, and then checking the call count, and on the left side, suggest-fn, instead of a site stub, and we check that it's been called so many times. Doing the exact same thing, every step translates, slightly different style.
We went through TDD example, component example, two component test examples, some best practices over there. Now let's look at TDD with endpoint tests. So endpoint tests are great when you're finished building your components. Now it's time for routing, state management, application flows, components talking to each other, or the back end. That's time for E3. But remember, you want to aid the test at the lowest level, and then move up when you cannot test confidently. This is an example of that. I can't do anything about routing when I'm working with components. I need endpoint test. So first, you look at the child. Can I do this at the child? Nope, and you move up to the parent. If that's not enough, you have a UI integration test, you stub out the network, but your components are talking to each other, and maybe you need the back end. Maybe your component is making a back-end call or something. Maybe you can use endpoint tests then.
How to do a feature with TDD examples. So this is an endpoint test. We're using TDD. It's from chapter 13, and it's the final code of this test. You always want to remember what you're doing at your child components. So here we're visiting the component, we're visiting the application, and an endpoint test, and we're making sure that the header bar component is displayed. So how do we modify our application so that the header bar component is displayed? We go ahead and take a look at the child, the header bar, and there we're using browser router and wrapping the header bar, so our application is going to also have to do that. So you can cheat and always look at the component test that you already built. They'll make your life easier when you're writing your endpoint test, or when you're writing other component tests. You'll be having the selectors already there, data size selectors, and you can always refer back, it's documentation for you, what do I do with this thing? The TDD flow is exactly the same with E3. You wanna start with something failing, you wanna do the minimum to get it to work, so it's minimum at the navbar component, and then if you can make it better, do refactoring. Once things are working, prefer to add more failures.
8. Not Found Route and Best Practices
You want to have a not found route and display a not found component. Add a failing test, then add the not found component and do the route configuration. Seek failures and keep adding tests until there's a failure. In a TDD example using end-to-end tests for routing, visit the hero's path and ensure the hero's component is displayed. Follow recommended best practices for API end-to-end tests, such as using an API test client instead of a UI client, being aware of test suites, avoiding duplication, and using modest amounts of E2E testing.
You want to have an not found route, and that should display a not found component, right? First the failing test, and then we add that not found component, do the route configuration, and we're finding out more about the application through testing in this way. If we have passing tests, we want to add tests or refactor before adding more source code.
We have a passing test. We add that additional check for location, we're still green. So okay, then we can think of another failure. We wanna seek these failures. Failures are great when you're doing TDD. You wanna keep adding tests until there's a failure. So that's very good. Once we have that failing test, then you can add the new feature. So here we are visiting the hero's path, and we want hero's component to be displayed. That's our failure. To make things work, we just add that feature. We say okay, add the heroes out, we should be rendering that certain component. And that's a TDD example using end-to-end tests for routing.
Let's go through recommended best practices. There are a few of these, so we'll go through each. With API end-to-end tests, don't do naive UI end-to-end testing before you have confidence that your back-end works. You wanna use an API test client instead of using your UI client as the test client. This way, you can test earlier in your deployments. You want to be aware of the test suites of your back-end, and you wanna avoid duplication. You wanna use modest amounts of E2E and do a targeted gap filling when you have UI end-to-end test. Now you don't want to have to repeat this UI end-to-end test if you already have confidence in one area. First, this login works. Now try not to do the same login in every test. Maybe you can go through the back door or have another way of logging in. When login doesn't work, only one test is going to fail. And you know it's not working. Not everything has to fail. Here's your API end-to-end test strategy for CRUD.
9. API and UI End-to-End Testing
So it's creation, create and delete, update, create, update, and delete. Update covers every single flow. This is the backend example. We add a hero, make sure it gets added, update it, ensure it's updated correctly, remove it, and verify it's removed from the database. UI end-to-end is similar to API end-to-end, but you can use the API commands and isolate the UI tests. Start with UI create, then clean up with the API. The UI integration test includes canceling, refreshing, and adding a hero to the list.
So it's creation, create and delete, update, create, update, and delete. And to be able to delete, you have to create something. But look at this. Update covers every single flow. If you test update, creation and delete are already covered. So just test update. This is the backend example. You can find it in Chapter 15 and the final code here in this link.
We are adding a hero, a post request. We make sure that thing got added. Make an assertion. Then we go ahead and edit it, we update it. And then we make sure that whatever we updated did get updated the way we wanted it to. Then we make the removal and then make sure that it's removed from the database. This is the example of our API end-to-end test. And you want to have this where the backend code lives closer there if possible.
UI end-to-end on the other side is very similar to API end-to-end. But instead you can use the API commands that you already created and isolate the UI tests. For instance, if you want to test update, you can see the database by the API command that we already wrote. You can do your UI testing. And then at the end, you can delete. We'll go through the simple creation example, but you get the idea. So we'll be first doing UI create and then we'll clean up with the API. Here's that test. We'll go through also the UI integration test. The first ones are canceling and refreshing. And they're not really using the real network. The final one is going ahead and adding a hero. And after adding it, it's making sure that hero is on the list. You can find the full test over here.
10. Final End-to-End Test and Test Design
In chapter 17, we navigate to the hero, ensure everything is settled, create the UI, make assertions, and perform API deletes. Each step requires careful development. Keep end-to-end tests small when doing TDD.
In chapter 17, we go through this in the React query chapter. And here's the final test. In the beginning, we just navigate to that hero. You want everything to settle. We are using the real network, no stubbing here. We are at the right location. We did the UI creation. So we fill out the details, hit the Save button. And then we make our assertions that we're at the right path, that we added this displaying there. And then finally, we do the API deletes, like we saw before. The thing to watch out for is each one of these steps can be lots of development. So with API end-to-end testing where you're doing TDD, you don't want to go even smaller because one line of end-to-end test can cover lots of source code. This is the main idea from here. Keep end-to-end tests even smaller when you're doing TDD.
11. UI Integration Testing and Error Cases
What is UI integration test? Only use real end-to-end tests when necessary. Avoid duplicating tests and leverage the backend's functionality. Convert UI end-to-end tests to UI integration tests. Test refresh and cancel using the real network. Use stuffed data instead of making actual requests. Test error cases and positive flows at the component level. Avoid testing implementation details and focus on black box testing.
What is UI integration test? So you want to always evaluate if you need your backend to gain confidence in your apps functionality. Only use real end-to-end tests when you have to have this confidence. And you shouldn't have to repeat that costly same end-to-end tests everywhere. Be aware of what your backend is already doing and that makes your UI end-to-end concerns even less.
So be careful not to duplicate and try to get them. You can judge people by what they don't do, by the cost of the things that they don't delve into. This is especially true for engineers called buying a chain. Let's convert a UI end-to-end test to a UI integration test. So we have two tests here. One of them testing refresh, the other one testing cancel. Using the real network, we're just waiting for the network to settle before we go ahead and check that refresh button as the refresh or the cancel button does the cancel. You only have an end-to-end test that's adding a hero and checking that the list is getting updated. So we don't really need this get request for real. It can be just stuffed data. So if we use a JSON file, you say any time you hit that route instead of the real thing, use the file and there is your data. To be able to test refresh confidently or cancel, you don't need the backend. It's a perfect use case for the UI integration test.
When you have errors, you want to test your error cases. Any test that's covering the positive flow or side intercept is a good branching spot. Want to try to test at the component level. And if you cannot test something, a negative error case, and are you there, you want to move off the UI integration test. So here, this is our positive scenario. You hit the save button and we expect a post request is going out. There's the perfect spot to do an error test case, a non 200 case. So we start with a 400, you also add a delay, and make sure that there's a spinner at first, and then we get the error. This from chapter 18, when we cover suspense, error boundary, and concurrency. So this test by itself just covers the use case for suspense and error boundary for this particular component. Want to avoid testing implementation details. You want to lean more towards black box testing while you're testing your component. And this is the same idea from react-testing library, which is using max service worker to do the same thing that we're doing with Cypress intercept.
12. Testing Consequences and Visual Feedback
So on the left side, we have implementation detail test. At the end, a network request should be going out to the backend. Cypress intercept can make things a little more succinct, a little more flexible. You want to take advantage of the visual feedback of the component test. It improves your TDD, inspires you to check for more tests and lets you detect these defects before they may even happen. Ad hoc is possible in a component test, so you can just run the component test and then your component is there by itself without reading any of the rest of the application. It's just next level experience for a developer when they're working on a component in isolation. Here's a final test for Navbar, RTL versus Cypress component test. Cypress may be shorter sometimes less code because of all the other packages versus jQuery that it comes with, so it's easier to unselect or get the ones that are not selected versus the other API in React testing library. It's the same way of doing the same thing.
So on the left side, we have implementation detail test. Testing that use state is being called. Or react query is being called. Or our post hook, our custom hook is being called when we click the Save button. Instead, you want to test the consequences of clicking the Save button. At the end, a network request should be going out to the backend. And it's better if you check this because if you change your implementation, this test is still going to work. Doesn't really matter how we do the internals. Max service worker and Cypress intercept are very comparable. Here's my comparison. You can check the links. In my opinion, Cypress intercept can make things a little more succinct, a little more flexible. But you do your own research and let me know what you think.
You want to take advantage of the visual feedback of the component test. So here in chapter eight, NAVVAR, I'm writing the book and I thought this is a good test. But when I start the component, I see that every single link is highlighted. Okay, I click on each, I go to the right link. Fine, but there's a defect. This means visually when you see the component, this quality of the feedback improves your TDD, inspires you to check for more tests and lets you detect these defects before they may even happen. You get that feedback from the CLI tool, but you have to be very careful. For me, it was way easier to see the visual thing and think, okay, when I just click on something, only that guy should be highlighted and the rest should be not highlighted. So I added a test for that. Best part is ad hoc is possible in a component test, so you can just run the component test and then your component is there by itself without reading any of the rest of the application. You can go and click around, look at dev tools, look at the extensions. It's just next level experience for a developer when they're working on a component in isolation. It's just mind blowing, this technology. Here's a final test for Navbar, RTL versus Cypress component test. Again, different styles, same intent. Cypress may be shorter sometimes less code because of all the other packages versus jQuery that it comes with, so it's easier to unselect or get the ones that are not selected versus the other API in React testing library. It's the same way of doing the same thing.
13. Using Wallaby for TDD and Combined Coverage
If you cannot use visual tools, my advice is to use Wallaby. It provides inline feedback for TDD and can be used from a CLI or IDE tool. Combined coverage allows you to ensure the success of your TDD. Once you move beyond components, TDD with endpoint tests becomes more relevant. The key idea of TDD is to start with something failing, do the minimum to make it work, and then improve it. Thank you for listening. Let's start the Q&A by discussing the poll results. Component testing took the lead, but unit and end-to-end testing were also popular. It would be interesting to see if these results change after the talk.
What have we learned? Quality of the feedback in Cypress Component Testing improves TDD. Component engineering, and early defect detection. Once you move up to routing, state management, and other complex things beyond components, TDD with endpoint tests is more relevant. With TDD, the key idea is to start with something failing, want to do the minimum to get it to work, and then you want to try to make it better. Here are the links and references, the book, the final application, and the inspiration that contributed to this content. Thank you very much for listening. You have a nice one, bye.
So, yeah, let's start off the Q&A by discussing the answers to your poll question. And so, I was kind of interested in this in that they seem relatively similar. Obviously, component testing took the lead with 43%, but unit and end to end right behind at 30 and 26%. So what do you think about those results? I'd be curious to see if we did this poll before the talk and after the talk, if these will change. Right, yeah, I think as we went along, we started to really see some of those benefits that you highlighted. So very interesting to see how many people's minds have changed because of that.
14. JS Frameworks and Code Coverage
The differences between JS frameworks for Cypress Component Testing are mainly in the way components are mounted. React Component Testing is similar to regular React app mounting, while Angular and Vue have their own specific configurations. Achieving code coverage with Cypress component tests depends on the framework and bundler used. Cypress allows for combined code coverage between end-to-end and unit tests, including component tests. Migrating from other testing libraries to Cypress Component Testing allows for combining existing tests and maintaining overall coverage. Relying on services for code coverage integration is recommended.
So, we are gonna have some questions here from the audience so we'll go ahead and kick that off. First question is, what are the differences between the different JS frameworks for Cypress Component Testing? I think it branches a little bit, right? You have React, you have Vue, you have Angular, you have Next.js right now, a few others coming up. For instance, between Angular and React, right, the way you mount the component is pretty much the only difference, right. With React Component Testing, it's going to be very similar to how you mount your regular app. So if you have developed React before, you'll be very familiar with Cypress Component Testing in the React flavor. But if you've been an Angular developer, I think you'll like it even better because Karma is notorious for having lots of boilerplates, and mounting would be, of course, different from React, but you're going to like it a lot better than mounting a component and configuring with boilerplate Karma. And the same is similar with Vue. So it's just the way of mounting the components, the rest is exactly the same as an end-to-end test. So if you've used Cypress for end-to-end testing, then you're going to really be in your own game with Cypress component testing. Yeah, so it's nice that you can just apply the same principles, no matter what framework was really used to develop it. Do you have a question here about code coverage as well? How do you achieve code coverage with Cypress component tests? With Jest it comes out of the box, right? But with Cypress, with end-to-end, it's quite easy, a few extensions and a few settings. With component tests, it's going to depend on your framework and it's going to depend on your Bundler. I'm pretty sure we're going to see some recipes, but if you refer to my book, it's a Create React application which uses Webpack, so there's like a good working recipe there. It's going to be a little different for Beat, or it's going to be different for Angular, but hopefully we'll get some recipes from the Cypress team because I'm pretty sure people will be wanting some code coverage in this kind of low-level testing. Yeah, code coverage is definitely always something that people ask about and want to consider. Do you know if you can also combine code coverage from different types of tests with Cypress Component Testing? Absolutely, Cypress has been showcasing combined code coverage between end-to-end and regular unit tests from any framework. You can just add component testing to this mixture, you can triple combine it, so of course, when you have a unit test in JESS, for instance, and a Cypress Component Test, to couple that with end-to-end test, you can expect to see those numbers really shoot up, which also brings up a great use case for migration. So if you have already existing tests, let's say in React Testing Library, you can just keep them there and then if you want to switch to Cypress Component Testing, you can make your new test with Component Test, and just combine the coverage and your overall coverage isn't going to go down. You may have to rely on services, because doing this yourself in the CI can be not very future proof, it's not very easy, but there are examples of doing it online if you prefer to do that. I think that's the way to go, the services.
15. Using Pack.js Cypress Adapter for Test Duplication
To avoid test duplication between the backend and frontend, the Pack.js Cypress Adapter can be used to leverage both contract testing and functional testing. It enables testing integration before deployment, complementing end-to-end API testing with Cypress. This can help reduce API end-to-end tests and create a comprehensive test architecture. The adapter can be used during the transition to Pack.js.
Okay, great. And then we do have a question from Marie in the chat. To avoid test duplication between the backend and frontend, would you recommend the Pack.js Cypress Adapter as a way to leverage both contract testing and functional testing? Good question, I have thought about this a lot, and I love the gap that Pack is filling, because it mainly enables you to test integration before you deploy. So we haven't have this, it's in our books to get this working, but the way I imagine is maybe in the future, if we can get Pack there, it would be a nice complement to some end-to-end API testing with Cypress, and perhaps we can reduce those API end-to-end tests a little bit, if you have confidence with some Pack tests. And then that's going to enable testing free hand with Pack, and then after you deploy, some more spot checks with Cypress end-to-end, and that's going to be nice test architecture. So of course, doing that transition, we can use the adapter, right? We can take advantage of that.