تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Storing Routes in Memory0:00

All right, let's get back to work. We've added our refresh routes command that ultimately writes the routes to a file. But maybe we should also store this in memory. So here's what I'm thinking, at least as a first step, in my support/index.php file. We can put it right up here, at least for now, and then we'll extract it. What if we store something like cypress.laravel as an object? And then here we could store all of our routes. That way, they're always available in memory. Let's play around with that. So we'll come on back.

Let's play around with that. So we'll come on back. We'll still write to a file just in case. But I can also say cypress.laravel.routes equals routes. So now, if we were to console.log and just figure out what we stored there, let's have a look. We give it a run, and it fails because we haven't yet implemented that feature from the last episode. But here's what we log to the console. And sure enough, all of our named routes are in memory. So think about it.

And sure enough, all of our named routes are in memory. So think about it. If I wanted to get the URI to the home route, we could access the home key and then URI. Like this, routes, home, URI. Give it a refresh, and there we go. In this case, the URI is the home page. But it works. But now, this is a little cumbersome to write. So maybe instead, I could just say cypress.log cypress.laravel.routes, maybe. And then we give it the name.

So maybe instead, I could just say cypress.log cypress.laravel.routes, maybe. And then we give it the name. And we can even add a helper function on top of that if we wanted to. We're just playing around for now. We're not sure. But if we took that approach, then we would need a helper function called routes that accepts the name. And to start, it would just return that, where we make that dynamic. All right, give it a refresh, and we still get the same thing. So now, one thing to consider.

All right, give it a refresh, and we still get the same thing. So now, one thing to consider. We'll tackle this soon. But some of these URIs are going to include wildcards, aren't they? So let's see if I can find one. Yeah, even something like this, ignition.scripts. So in some cases, the URI will include the wildcard, which means we need to offer some functionality to replace that wildcard with a parameter. We can't forget to do that. But we'll get to it at the end.

Duck Punching Cypress Visit2:13

We can't forget to do that. But we'll get to it at the end. Okay, so let's come back to our test spec. And now here's the next step. Right now, Cypress doesn't offer support for anything like this, of course, because it would be Laravel-specific. You have to give it a path. But yeah, if we now want it to be Laravel-aware so that I can pass route, how can we overload the visit method? Well, we can use a concept known as duck punching.

the visit method? Well, we can use a concept known as duck punching. And this is kind of a fun idea. So if you're not familiar with duck typing, the basic idea is you're not using actual enforced types. Instead, if the thing you pass to the method can do the job that the method expects, then you're good to go. And that's where the idea of if it looks like a duck and it quacks like a duck, then it must be a duck. So if it conforms to the API that the method requires, then it should be fine.

Then the subject you pass to it, and then any of the original options. I think that's right. Okay, so the general idea here is at some point we defer to the original implementation. But before that, add a hook to branch off and add new functionality. So something like this. If some kind of conditional, then add your hook and return. Otherwise defer to the original implementation. So this would be return originalFunction, and we'll pass the subject and the options to it. Okay, so in our case, what should the hook be?

to it. Okay, so in our case, what should the hook be? Let's think about it. We want to pass a route option or parameter here. So why don't we check to see, and the subject, by the way, would be this here. So why don't we check to see, well, if there is a route parameter, then we want to use a Laravel named route in this case. Okay. If subject.route, we have our hook. So we'll effectively do kind of like this, where you're calling cypress.visit again,

If subject.route, we have our hook. So we'll effectively do kind of like this, where you're calling cypress.visit again, but we're being explicit. Or we can defer to, of course, the original implementation, and then just be explicit about what the URL should be, and then as well as what the method should be. So this ultimately still calls cypress.visit. We're just translating route.home into the appropriate URL. So how do we get the URL? Well, subject.route will be our named route, and we know this is stored in memory. So I should be able to say cypress.laravel.route for subject.route, right?

Well, subject.route will be our named route, and we know this is stored in memory. So I should be able to say cypress.laravel.route for subject.route, right? So in the case of home, that would return this. That's correct. And then the method, how do we access that? We may have to go through here. So we get our named route, and then methods, and we can probably just get the first item there. I think that would be fine. Or we can add a helper for this too, cypress.laravel.route, subject.route.method, and then get the first

I think that would be fine. Or we can add a helper for this too, cypress.laravel.route, subject.route.method, and then get the first item there. Okay. And that's a basic example for duck punching. So if we did everything correctly, when we run our test, we should see the default Laravel splash page. Give it a run, and it works. So let's come on back and see if this one works as well. And it should do the exact same thing.

Validating Route Names6:05

So let's come on back and see if this one works as well. And it should do the exact same thing. Give it a refresh, and it works. Now let's think about some edge cases. For example, what if I provide a route, but it's not correct? All right. Let's see what happens. We give it a refresh. Cannot read property URI of undefined. So you can probably figure out what the problem is, but maybe we should have a quick expectation.

Cannot read property URI of undefined. So you can probably figure out what the problem is, but maybe we should have a quick expectation that the named route actually exists. Let's see how we could do that. We could do it here. But we have this helper function route, so maybe it's better to place that here. And we could just do something like if cypress.laravel.routes.hasOwnProperty. Let's just make sure there is a property name there. Then fail some kind of assertion, which means, yeah, this is our assertion. So let's change it up.

Then fail some kind of assertion, which means, yeah, this is our assertion. So let's change it up. It's not going to be a conditional. It's going to be assert. And we get that out of the box. assert that the named route exists. So if I come back and run it, as we'd expect, the assertion fails, but it's not overly clear. So let's add a message. We can do that as the second argument. Laravel route with name name should exist.

We can do that as the second argument. Laravel route with name name should exist. All right. Try again. And there we go. So our assertion fails, and we have a clear message. The Laravel route with the name xhome should exist. But it doesn't. And in fact, real quick, why don't we put that in quotes? OK, so now we know to fix the error and run it again.

Replacing Wildcard Parameters7:56

So why don't we say something like teams and then a specific teamId. And then maybe you can have multiple dashesboards or something like that. You know, some kind of route here with two wildcards. And then let's just return a string. Some dashboard for the team. All right, teams.dashboard. Let's see what happens when we try to access this URI in a test. teams.dashboard. Like so. Think about it.

Like so. Think about it. That would generate a route. Teams.dashboard. Here it is. That would give you this URI here exactly. So we need to replace those wildcards. Now, of course, Laravel will do this automatically for you. But on this end, we need to add that functionality. So maybe a route could be an array or it could be an object itself.

But on this end, we need to add that functionality. So maybe a route could be an array or it could be an object itself. Or maybe we could just accept the route parameters here. So for example, the team parameter and a dashboard parameter. Team might be one. Dashboard might be two. You know, something like that. And then ultimately, we would want to turn that into this, right? Or an option is just pass a simple array and then sequentially replace those wildcards. I'm not sure yet.

Or an option is just pass a simple array and then sequentially replace those wildcards. I'm not sure yet. We'll start with this. If we want to allow for this, let's go back to our helper method here, route. First, let's assert that the named route exists. And then, let's see, we're going to get that URI and ultimately return it. But first, we have to replace the wildcards. So why don't we, yeah, route, hmm, how do we do this? We need access to the parameters. So maybe name parameters, something like that.

We need access to the parameters. So maybe name parameters, something like that. And then when you call it, yeah, here, subject.parameters, maybe, maybe that. So you see what we're doing here? We're calling visit, we're giving it a route, and optionally, a set of parameters. We then pass that to our route helper function. We give it the route you want, as well as any wildcards, but that's not required. So let's default to an empty object. Then the route helper function will, again, have currently something like this, where we should then say, let's see, Object.keys, maybe, for parameters.

Then the route helper function will, again, have currently something like this, where we should then say, let's see, Object.keys, maybe, for parameters. So that would give us an array of team and dashboard, and then we'll say for each parameter, for each name, or maybe no, for each parameter. For each one of those, we need to update the URI. So we can do that by saying, update URI by replacing, and we're going to need some regular expression that, if we were to write this verbatim, this isn't right, but kind of something like this, where you say, replace team or dashboard with parameters, parameter, with the associated value. So we're saying, replace team with 1.

the associated value. So we're saying, replace team with one. Replace dashboard with two. So this just needs to be dynamic. You kind of want to say, like, the variable parameter, something like this, you know, again, but we want this to be a variable. And right now, this would be treated as the letter P, the letter A, the letter R. So instead, why don't we set up a regex. This would be the way to do it, if you need it to be a variable. And think about it.

This would be the way to do it, if you need it to be a variable. And think about it. You want surrounding {}, and then within it, it should be the variable parameter. So why don't we do this? Why don't we switch to backticks? This might be the way to go. So surround it, and then insert the value for parameter within it. And yeah, I think that should do it. We'll have to test it out. And then we would replace this with our regex, or, short enough, why don't we inline that.

We'll have to test it out. And then we would replace this with our regex, or, short enough, why don't we inline that directly? And this is what we get. So iterate over the parameters, and for each one, replace that URI by hunting down the associated param, and replacing it with the value. Then when you're done, ultimately return the updated URI. Okay, so if we were to give this a run, but all that talk and it failed, cannot convert undefined or null to an object. So parameters is undefined or null.

undefined or null to an object. So parameters is undefined or null. All right, let's see what we did wrong here. Cypress.visit, we have it here. We called the route method, and we accepted an optional object. Here, route. Let's again make that default to an empty object. Will that fix it? Almost. We did the substitution, but it looks like there's a lingering curly brace.

Almost. We did the substitution, but it looks like there's a lingering curly brace. So let's see right here. Yep. Weird. I thought I did that. Guess not. All right. That should do it, and that is what we want. Okay, so now, if you want, you could extract all of this, import Laravel routes, or this.

Extracting Route Helper Logic12:38

That should do it, and that is what we want. Okay, so now, if you want, you could extract all of this, import Laravel routes, or this could all really be under a single namespace. But we could start with this, Laravel routes, paste it all in there. And then, this is kind of a good use case for tap, but I don't have it. So we can kind of simulate tap by saying return the result of this function call, but we're going to trigger it immediately. And we'll pass to it cypress.laravel.routes.name.uri. So that will accept your URI. That gets rid of this, that gets rid of that, and then this moves up, like so.

So that will accept your URI. That gets rid of this, that gets rid of that, and then this moves up, like so. And that should make sense, right? Here we are saying return a function that immediately gets invoked. The technical term is something like self-invoking anonymous function or self-invoking function expression. It's just an anonymous function that immediately gets called. So we call the function, we pass the value we need, that function then accepts it, it works with it, and then, not quite tap, but we'll have to explicitly return the URI. And I think we're good.

Preparing for Package Extraction14:05

Okay, so I think we've done more than enough tinkering at this point. But this is important, and it's supposed to simulate often before you extract new features and concepts to a package, first test them out within an existing project. And then you extract from the existing project, in this case, a demo application, but you extract from the existing project to your package. And that's exactly what we're going to do in the next episode.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟