Switch to Package Codebase0:00
Okay, the tinkering phase is officially over. Let's get to work on the package. So I'm going to switch over to a new project. Ta-da! Here's Cypress, and this itself is the Composer package. You can see here's the service provider, our tests, the stubs that we copy over, the underlying Cypress controller that takes care of all the backend logic, like getting a CSRF token, logging in a User, all of that stuff. Okay, so I'm going to start, we can start with the controller. We need to add the functionality for getting the routes.
Add Routes Endpoint Test0:29
Okay, so I'm going to start, we can start with the controller. We need to add the functionality for getting the routes. So I'm going to start with a test, and you'll notice all of the features here do have tests. So it logs a new User in with the given attributes. It logs an existing User in with the given attributes. It logs a User out. So let's do, we can just do it right up here for now. Test, and what does it do? It fetches a collection of named routes. Okay, so I'm going to copy this.
It fetches a collection of named routes. Okay, so I'm going to copy this. Let's make a POST request to cypress.routes, and that will give us our response. And yeah, just to start, why don't we dd the response.json and give this a run. Okay, it immediately fails because that route does not exist. Okay. So we have those stored here, and actually notice real quick how I have this set up. I'm using a tool called Orchestra Test Bench, which makes it really easy to test Composer packages.
Configure Testbench Provider1:26
I'm using a tool called Orchestra Testbench, which makes it really easy to test Composer packages. So notice things like this. They offer a method called getPackageProviders, where we can manually add a service provider for the test. So we are simulating an actual application that uses this service provider. Now that service provider will add the routes here. So those routes from this file will be added as part of the test. Okay. Make a GET request to cypress.routes, and we've already done this a number of episodes.
Okay. Make a GET request to cypress.routes, and we've already done this a number of episodes ago. Oh, and it reformats. I don't want that. Eh, I can clean that up later. That's a case where the auto-formatting really doesn't look good. But anyways, if we run it again... Okay, the POST method is not supported for this route. Did I do a POST?
Okay, the POST method is not supported for this route. Did I do a POST? No. I did a GET. I must have... Let's come back to our test. I must have... Here we go. I must have done a POST request. Yeah.
I must have done a POST request. Yeah. Okay. Let's make a GET request. Or maybe that should be a POST request. I don't think it really matters, but yeah, maybe let's make it a POST request. So let's come on back. Yeah, this looks bad now. Run it. And let's see.
Run it. And let's see. Okay. We have an error called undefined method cypress-controller-routes. It doesn't exist. All right. We have our next step. So we've written a test, but it's already telling me what I need to do first. So we run it. And now invalid JSON was returned from the route.
So we run it. And now invalid JSON was returned from the route. Okay. Let's come back to our test. And again, I'm not entirely positive what I'm testing here. Let's say... And this is where it's really helpful to write it out. I do this all the time. Something like, given I have a named route called home, when I fetch the list of named routes, it should include one for home.
Something like, given I have a named route called home, when I fetch the list of named routes, it should include one for home. You know, something kind of like that. So our given... Why don't we just write it out like we normally would? Route::get(foo, function() { return view(home); })->name(home); Next, when I fetch the list of named routes, well, that's what we're doing here. And then this would ultimately be our assertion. Notice how it kind of helps you gather your thoughts a bit better. Even though right now I'm still not sure how I perform that assertion.
Implement Named Routes Response3:33
Notice how it kind of helps you gather your thoughts a bit better. Even though right now I'm still not sure how I perform that assertion. So let's just keep going. Oh, yeah. Invalid JSON was returned. So let's get started. Return. And we learned how to do this before. Route, get routes. And then we call it twice, right?
Route, get routes. And then we call it twice, right? Run it again. Okay. So now this is what we are getting. But notice, once again, we have that situation where it returns so much data that we don't need. So we can map over it and return only what we require. Luckily now, if I switch back to our test project, we've already done that. So I'm not going to do it two times.
Luckily now, if I switch back to our test project, we've already done that. So I'm not going to do it two times. Let's just grab. In fact, we can just get the whole thing. We've done all the work. Okay. Switch back. I know that's kind of confusing. But we're back in our package project. And I will get the routes and then paste that in.
Finalize JSON Assertions4:41
here. Okay. Let's see. Response. assert. And there's actually an assert. Honestly, I often get confused by these because there's so many different assertJson ones. If I remember correctly, there's one that should really be all you need. I can't remember if it's assertJsonStructure or assertJson. Let's have a look here.
I can't remember if it's assertJsonStructure or assertJson. Let's have a look here. Assert that the response is a superset of the given JSON. Okay. So let's see. Could I just say... What is one thing we know it will return? What about the URI? That would be foo, right? So let's see if that would work.
That would be foo, right? So let's see if that would work. Unable to find URI foo within the response. But it is there, URI foo. See, I thought that would work. Hmm. Okay. Maybe we want assertJsonFragment. Let's see what that one does. Assert that the response contains the given.
Okay. Sounds like this is actually what I want here. Because I don't need it to match exactly what I expect. I just want to ensure that the things I require are there. All right. So we can keep going. The name. We called it home. Excuse me. URI method is currently the array, which would be the get type, and it also accepts head.
Let's see. Hmm. At the very end... Action. It just says closure in this case. And that would be fine. So will that pass? Yeah. What else? URI name method action.
All right. So now this is under test. If we were to change anything, it's all going to fail. And that's what we want. But actually, we should make sure that we can access it according to the key, right? So last thing here. So response, assert. Hmm. Actually, maybe we can just do a standard assert. Let's see.
Actually, maybe we can just do a standard assert. Let's see. array_has_key. I think that's right. Let's make sure that we have a home key with $response json. Is that right? Yeah. Because response... I hate when it reorders that. If we were to dd $response json, that's going to turn into an associative array.
I hate when it reorders that. If we were to dyn up response json, that's going to turn into an associative array. See? Array. And then each item in the array will have the key, which is the named route, and then the value, which is the data for that named route. So I think we're pretty good on this front. Clean that up. Not too bad. Next, I'm going to switch over to Cypress test, and this is where I have a general test
Verify Production Route Lockdown7:27
Not too bad. Next, I'm going to switch over to Cypress test, and this is where I have a general test for the package itself, like really important things. We can't ever allow this package to be exposed in production mode. So notice we say, okay, if we set up our environment for production, then let's just filter through or cycle through all of these routes and make sure they are not available. It's really important these are never available in your production environment. So I'm going to add to that cypress.routes. And now run that, and that will pass as well. But if we're not in production, then it will work.
And now run that, and that will pass as well. But if we're not in production, then it will work. And if you're curious about that, we can use this environment setup tag. And then notice this actually corresponds to a method, setupAcceptance. And all I'm doing here is this is a way to override the current environment detection in Laravel. And we're just telling Laravel, just return acceptance. Don't do any of your dynamic checking, just return acceptance. And that'll work. Okay, next, some of the things like the stubs I don't really have under test, it's kind
Refresh Stubs and Commands8:27
And that'll work. Okay, next, some of the things like the stubs I don't really have under test, it's kind of tricky to do that. So I will manually refresh some of this based upon what we worked on in the last episodes. For example, if I switch over to Cypress work, one thing we added was Laravel routes that we then imported into our support/index file. So let's switch it over. We can do that here. But I'm still not entirely sure I want another file there. Or maybe we should have a stub support Laravel directory.
But I'm still not entirely sure I want another file there. Or maybe we should have a stub support Laravel directory. I'm not sure. This is okay to get us started. Laravel commands, Laravel routes. Yeah, I don't love that. It's okay for the videos though. Okay, let's switch back. Back in our testing project, Laravel commands was updated. And I think we only added two.
Back in our testing project, Laravel commands was updated. And I think we only added two. And this is now a POST request. One to fetch and refresh the routes, and then another to handle that duck punching of the cypress.visit method. So I'm going to copy that, switch on back to our package, and then update Laravel commands. And let's see, if I go to the top, why don't we put it right after CSRF token? How about right here? So let's add some documentation here. Visit the given URL or route.
So let's add some documentation here. Visit the given URL or route. And an example would be cypress, in this case, visit-foo-path. Or another example would be the route. So route-home. And then let's do one more, team. And then we would include parameters for that team. So that would be the team parameter, maybe, would be one. But yeah, like I said, we might decide, and this might happen after the videos here, but we may decide to simplify this a little bit.
But yeah, like I said, we might decide, and this might happen after the videos here, but we may decide to simplify this a little bit. I haven't made up my mind. But if I could say cypress.visit route, and then what if that was an array like this, where I could say something like team.dashboard, and then here's a wildcard, here's another, and it would just go through the URI. And for each wildcard it finds, it would sequentially swap those out. I'm just trying to make it as easy as possible to write. Sometimes when you make it a little more complicated to write, people just don't do it. So those are things to think about.
Auto-Refresh Routes on Startup10:45
Sometimes when you make it a little more complicated to write, people just don't do it. So those are things to think about. So finally, I think the only remaining step is to, in our index.js file, before the suite runs, we activate your cypress environment file. We clear the cache. And then let's also one time refresh the routes. And you'll remember what that does, is it will make the POST request, get those routes. We're going to optionally write it to a file, although we may decide you never do anything with that file. So maybe there's no reason to do it.
with that file. So maybe there's no reason to do it. The only reason would be as a quick reference. But yeah, you may decide that you can remove that entirely. Then we update our routes and memory, and then I can get rid of that part. We don't need that. Okay, so now let's switch over to, I think we're about done. Let's go to the cypress boilerplate command. This is that command you run right after you install laracasts/cypress, and you'll see this is what we do.
Update Install Boilerplate Command11:40
This is that command you run right after you install laravel-cast slash cypress, and you'll see this is what we do. We just copy all of this stuff over to your project routes so that you have full control over it. And if you want to change any of this stuff or the endpoints, you have full control. Now the only thing, at least in this current video, it may not say that way, but at least in this current video, we are now adding this Laravel routes file, which means, and I'm just doing this manually, we're going to create Laravel routes just as a bit of feedback. And then it reformats.
feedback. And then it reformats. And okay, I think we're pretty good to go here. If we switch over and look at the diff, this is everything we've been working on in the last handful of videos. We set up some tests for the server-side functionality. We update that controller. We add a new routes file that gets imported here. We then refresh routes using a new command we created here. We did duck punching on cypress.visit to allow for a route to be passed.
We then refresh routes using a new command we created here. We did duck punching on cypress.visit to allow for a route to be passed. We're in pretty good shape. So before I commit and push this, I do want to make sure some of this stuff that isn't really under tests where we're copying over a stub, I do want to make sure that works locally. So I'll show you how to test that out in the next lesson.
