Installing Laravel Dusk0:00
Tonight, I'm taking a look at Laravel Dusk, which is in alpha stage at the time of this recording. It's Saturday at 9 o'clock. Somehow, my entire family is asleep, so we're going to knock this out. And yeah, this will be kind of a new video or a potential series where it's much more like a live stream here. I've never used Laravel Dusk beyond a quick glance at the GitHub page. I know basically nothing about it. So maybe come along and learn this with me. There's not going to be any edits. It's not going to be clean and perfectly presented. It's a bit more real life. Okay. So we're going to use it in Laravel 5.4. It looks like we can require Laravel Dusk. So I'm going to create a new project, and I'm going to use the --dev flag here. And that's because right now 5.3 is the current release. So I'm going to pull in the development branch of Laravel, and that'll give me 5.4. All right. Next, we're going to pull in Laravel Dusk,
right now 5.3 is the current release. So I'm going to pull in the development branch of Laravel, and that'll give me 5.4. All right. Next, we're going to pull in Laravel Dusk, and let's see what else I have to do. You should do this. Okay. As always, you want to add the service provider, and it's only going to be valid for local and the testing environment. So what you may find is that often packages will tell you to add it to your config/app.php file right down here, but that would be applied to every single environment. So if you ever have situations where it only should be applicable in a local environment or something like that, you can put this in your AppServiceProvider. Okay. So let's add this right here. And that's still installing. So if you add this before it's done installing, it'll fail because it can't find the DuskServiceProvider just yet. Okay. Uncomment it. All right. What
And that's still installing. So if you add this before it's done installing, it'll fail because it can't find the Dusk service provider just yet. Okay. Uncomment it. All right. What else? Next, we're going to run the php artisan dusk:install command. A browser directory will be created. Let's just go... I'll show you how I usually do these things, especially for commands. I kind of want to know... Whoops. Hold on. Oh, and you know what it is? We haven't imported that. So let's see Dusk service provider. It's under the Laravel\Dusk namespace. Okay. Can we run it now? No. Whoops. Okay. There we go. But I do like to know what happens when I run these commands. So usually, the syntax will be something like php artisan dusk:install or php artisan dusk:install command. There it is. Okay. Yeah. I just want to see what happens when I ran that. So it created a test browser pages folder. Okay. And then I just want to see what happens when I
like clear things. And then here's where you can store the interaction to click on the link to clear all of the tasks, but you isolate that behind a method. Anyways, we'll learn more about that. So we're going to create a pages folder, create a screenshot. So I assume if the tests fail, Dusk is automatically going to take a screenshot for us. And then it's going to copy some stubs. Okay. So we're going to have right here, an example test, a homepage page, and then we have a DuskTestCase. So I assume any of our Dusk tests should extend DuskTestCase. Yeah, they do. Okay. So what is DuskTestCase? That extends Dusk, uses createsApplication. I think the process of booting up Laravel was extracted to a trait here. Yeah. Okay. All right. What else? file_put_contents, .gitignore. Okay. So yeah, it's just adding that. Okay. Yeah. It's not necessary. I just like to do these things to have a better grasp of
All right. What else? file, putContents, getIgnore. Okay. So yeah, it's just adding that. Okay. Yeah. It's not necessary. I just like to do these things to have a better grasp of what exactly is happening when I try to install Dusk. That's something I do. And now I know. Okay. So let's go back to the instructions. To run your tests, use the php artisan dusk command. Okay. So I assume I have an alias, by the way. So instead of doing php artisan, I just have an alias called art that will reference that. That's really useful just as an aside for things like php artisan migrate, which you do all the time. Just create an alias called migrate. You know what? New computer. I haven't even created that yet. So I'm a hypocrite. But yeah, you would create like alias migrate='php artisan migrate', and then throw that into your bashrc or the zshrc file. Or I think what I haven't done this yet. Yeah.
Shell Aliases Detour4:52
But yeah, you would create like alias migrate='php artisan migrate', and then throw that into your bashrc or the zshrc file. Or I think what I haven't done this yet. Yeah. I create an aliases file that I reference. And then like from your zshrc there, there should be a section at the bottom. Yeah. So go to your main bashrc or if you have zsh, zshrc, and then you can just source any file that you create. I called mine aliases, and now it's going to automatically pull from that. So if we open up aliases again, this is where I store some basic aliases. And like I said, it's a new machine, so I haven't even done that yet, but this has nothing to do with Dusk, but maybe this will be how the series is a bit more casual, a bit more just folks talking to each other. Anyways. Yeah. So then you'd have migrate once you source it. What else do I have?
Running First Dusk Tests5:35
but maybe this will be how the series is a bit more casual, a bit more just folks talking to each other. Anyways. Yeah. So then you'd have php artisan migrate once you resource it. What else do I have? php artisan serve. I had one to do php artisan serve. I just run serve. I don't use that quite much anymore because I use Laravel Valet. Anyways, let's get back to work. We're slacking off. php artisan dusk. Okay. It fails. Did not. So what's going on here? It's failing at ExampleTest. So let's close this up. Okay. So here's, yeah, I'm trying to figure out how this works. So you have your unit tests, which it can be anything you want, but generally your tests in isolation or your integration tests, you'd have section for your actual features, like what your application actually does. And then when you need to test through the browser, some people call this acceptance testing. Other people draw more of a distinction as to what
I do when I'm trying to learn something. Of course I check the docs, but the docs are good for a quick overview. But then when you really want to drill down and kind of figure out how things are stuck together, I would just research it directly. So I'm going to look for a browse method. If you're in sublime, you can go to go to simple and project and we can go directly to it. All right. So I think this is basically what you call when you want to open up a browser, create browser four. So we get a callback. So you call browse, you give it a callback. And then within here is basically the, the commands that you want dusk to perform. So in this case, visit the homepage and assert that we see Laravel. So like right now, let's go to dusk.dev. Yeah. So here's the homepage, fresh install of Laravel. So it's saying, if you open a browser and visit the homepage, we expect to see Laravel. Actually, you know what, let's just get some
Configuring APP_URL and Dusk Env8:43
true. Well, one thing is when I run this, it's opening a browser, but it says it closes really quick, but notice how it says localhost. So I'm guessing what it's doing is it's reading from where you set config/app, where you set the URL. Is that what it's doing? Yeah. APP_URL. So let's go to our .env file and update it to dusk.dev. Will that do it? Yay. That's what does it. Okay. That should be in the documentation. This is still like the preliminary documentation. So he'll do that. So to force Dusk to use its own environment file and create a .env.dusk.local file. For example, if you will be initiating the dusk command from your local environment, you should create. Okay. That's good to know. Very useful. I've run into that issue before when I've made my own packages or acceptance testing, where when you open the browser, you don't always want it to use your local environment.
Very useful. I've run into that issue before when, when I've made my own packages or acceptance testing, where when you open the browser, you don't always want it to use your local environment because maybe that would work off your local database, but you want it to work off your test database. So it always gets tricky where you're like, okay, I'm going to hit this in the browser like normal, but I need some way to tell Laravel that in this test, I actually want to use a totally different environment. So I'm glad he has that figured out. It was always a little tricky. Multiple browsers. Yeah, this is, this is something I heard him advertising. So you could open a browser, perform an action, and then you could open another browser and make sure that everything works the way you'd expect. This would be really cool if you're using Laravel Echo or Pusher where you, you know, consider like a basic chat app. Like you can see here, you open one
everything works the way you'd expect. This would be really cool if you're using Laravel Echo or Pusher where you, you know, consider like a basic chat app. Like you can see here, you open one browser, you type a message, and then we're going to open up another tab or another browser, essentially. And we're going to make sure that that message shows up. And as I understand it, the way this works is you call browse. So like, take a look. You call browse and your closure here will accept a Browser instance. And that's what you call your commands on. But if you ever want to open up a new tab, you could just say secondBrowser. And basically what you're doing is you just ask for it. And I think Laravel, before it triggers the function, it determines how many args you have here. And I assume for however many args you specify, it's going to give you that many instances. Anyways, waiting. Page objects. Yeah. Okay. So let's see what he has here.
extent of the documentation at the time of this recording. So if we switch back, everything's passing. So if we were to try, you know, this is exactly how I would learn something new, because I am learning it new. It fails. Did not expect it. Okay. So this is pretty cool. That means, like I could say, visit /tasks and expect to see maybe some task. I'm just trying to see like what the workflow might be for me. So we run it. That fails. So we could go to my routes file. And we could say, when you visit /tasks, I'm going to return a view called tasks. By the way, we're not building anything here. We're just toying around. Oh, this new computer is killing me. I don't have any of my old aliases. Okay. So we're looking for some task. All right. So now if I run that, it passes. Okay, cool. So what if it's not exact type case? Okay. So that does fail. So capitalization is important. Okay, cool. So you fix
some task. All right. So now if I run that, it passes. Okay, cool. So what if it's not exact type case? Okay. So that does fail. So capitalization is important. Okay, cool. So you fix it. Now you're back to green. That's pretty cool. Now, what I'm curious about is, like, why do I have to do php artisan desk? Is there a desk command? Is this it? All right. Here's the artisan command. Yeah, I'm just curious, like, can I just do phpunit? And where's the stored test browser example? Can I do that? Yeah, that worked. Okay. So what I'm curious about is, is php artisan desk just kind of a clean entry point? What is the reason for this? Okay. So yeah, it's basically going to wrap it up, purge screenshots. So here's what I assume. We run it, it fails. And if we open up the screenshot, yeah, this is kind of a cool thing. It's going to take a screenshot of the browser as it existed at
purge screenshots. So here's what I assume. We run it, it fails. And if we open up the screenshot, yeah, this is kind of a cool thing. It's going to take a screenshot of the browser as it existed at the time of the failure, right? So if we do another one, this will fail. But the screenshot remains the same. So I wonder how that works. Let's create another one. Will we get two if we do another thing? Whoops. TestBasicExampleTwo. I'm just, all I'm checking here is, will I get two screenshots? Yeah. So it corresponds to each test. So if we run php artisan desk, among other things, it's going to empty out the screenshots. Yeah. And we saw those disappear. And then you get fresh ones. Okay. Next, with desk environment. So this is what's going to be responsible for setting up your environment file. If file_exists, desk file. If file_exists .env. Okay. So yeah, if the, if you created a environment file for desk, we're going to return that. Otherwise,
setting up your .env file. If file exists, desk file. If file exists .env. Okay. So yeah, if the, if you created a environment file for desk, we're going to return that. Otherwise, it's just going to assume a default. Okay. Anyways, let's go back. So it starts up a process that will call phpunit for you. It specifies a custom phpunit config file. Okay. Set TTY run. Okay. To purchase screenshots, finder. This is completely unrelated, but Symfony's finder component is so cool. I think it's one of the cleanest and the most Laravel-like Symfony components. So you can see here, finder, create a new instance, and give me all of the files within the screenshots directory, and we're going to name them. And then for each one, unlink it. Yeah. Anyways, this is how I personally learn a tool is a little bit of documentation, a little bit of toying around within a fresh project, and then a little bit of digging
Browser Interactions and Forms16:07
unlink it. Yeah. Anyways, this is how I personally learn a tool is a little bit of documentation, a little bit of toying around within a fresh project, and then a little bit of digging behind the scenes to figure out exactly what does happen. So now I know exactly why I do php artisan desk. Cool. All right. So we can get rid of this. What I want to do next is figure out basic things like filling out a form. So can I do things like visit the tasks page and then type... Let's just go to some random page. Go to a page and type foobar into a name input. I need to figure out how to do that. And then maybe press submit and then assert see. You typed... And then what would it be? foobar. Yeah. This is how I would learn something is I wouldn't apply it to a real project. I need to figure out the basic API and the most simplest possible application. This even goes back to when I was a kid learning CSS. I remember going to websites that would show you how to
I would remove each selector. Did that change the sticky footer? Nope. Nope. Nope. Nope. And then finally one property, it would break. And I would know, okay, so this is important to making this work. Yeah, but mostly the idea is breaking everything down to the simplest possible example so that you can understand it. Anyways, I don't know if any of this works. I think it does because this is some of the API that Laravel had before. And I assume he's going to use some of this, but who knows? So what I'm going to do is take a look at assertC and figure out where this exists in the code base. Okay, so makeAssertions. Let's look for that. Where are we? DeskVendor. Okay, so we have a Browser class. So this is Laravel Desk's Browser class, and it has... So basically it's separating all of the different things it can do into traits, and he's just giving those namespace of concerns. It's kind of a common convention from the Rails world, actually.
it's separating all of the different things it can do into traits, and he's just giving those namespace of concerns. It's kind of a common convention from the Rails world, actually. Waits for elements, interacts with mouse. Remember him talking about it interacts with time. I guess that's not there anymore. Anyways, so if we go to makeAssertions, here's a trait, and I assume this is where we have all of, yeah, assertions. assertSee, assertDontSee, assertSeeIn, assert that the given text appears within the given selector. Okay, cool. So if I do assertSee, can I limit that? No, I can't. Okay, so that just allocates to assertSeeIn. Okay, good to know. Quite a bit here, and this is pretty fleshed out. I like it. assertChecked, assertVisible. Pretty cool. Now though, I still want to know, like, how do I do things like this? Press. So I'm going to go back to browser. I assume it interacts with elements. And of course,
assertVisible. Pretty cool. Now though, I still want to know, like, how do I do things like this? Press. So I'm going to go back to browser. I assume it interacts with elements. And of course, remember, like, there's going to be tons of documentation on Laravel's website, so you don't have to do this. But I would read, if I were you, I would read the documentation, and then I would also dig in just a little bit to get your feet wet. Anyways, yeah, here we go. Click. Okay, so you give it a selector. So you would say click, and you wouldn't say the text of whatever you're clicking. You would actually give it a selector, like some element. This resolver findOrFail. Okay, so I assume that's like a wrapper class he has that will make sure it exists or something. And if it doesn't exist, maybe it throws an exception. I don't know. Click link. Okay. Now one thing I have seen
he has that will make sure it exists or something. And if it doesn't exist, maybe it throws an exception. I don't know. Click link. Okay. Now one thing I have seen is that this seems to use jQuery. So like, in order to click a link, you can't really do that with CSS. You can't say with CSS, give me the anchor tag that, you know, contains foo, and then style it. I'd love it if you could do that, but you can't. But you can do it with jQuery, as it turns out. So it looks like what he's doing is just executing jQuery here, or Zepto, or I can't remember the other one that's kind of like a minimal version of jQuery. Anyways, I assume he just injects jQuery if you're not referencing it in your project. Maybe when you run Dusk, it injects it for you. I don't know. But I think there's also a, I know a little bit about this class, WebDriverBy. This is ways you can track down elements with the WebDriver,
it injects it for you. I don't know. But I think there's also a, I know a little bit about this class, WebDriverBy. This is ways you can track down elements with the WebDriver, which is what Dusk uses behind the scenes. I think there is one. Yeah, link text. Anyways, none of that matters. Let's keep going. Text, get the text of the element matching the given selector. Okay, so that's kind of interesting. So if I were to say basically browser text, and then I give it a selector, it's going to give me the text associated with that. I want to see how that works. So before we do any of this, browser visit page, and then get the text for name. Now, what I want to know is like, do I provide my selector here? Will he do that for me behind the scenes? I don't know. But let's save that and then just dd(name). Okay, so of course it's going to fail. No such element. So let's fix that.
behind the scenes? I don't know. But let's save that and then just dd $name. Okay, so of course it's going to fail. No such element. So let's fix that. Go to our routes/web.php file. Let's just update this to, what did we call it, page. And then open up resources/views/page.blade.php. And then we're going to have an h1 with an ID of name. Okay, so will that return some task? It does. Very cool. I like it. So yeah, if you ever needed to figure out the value of something and make sure that it matches, you could do that. Otherwise, don't forget, you could just say assertSee and then give it a value. I guess you would only do that if, I don't know exactly when I would reach for that, but good to know I can. So I can click, I can, what's keys? Send the given keys to the element matching the given selector. Not immediately sure what that, oh, send keys. Isn't that a, isn't that a wrapper for just like
clear the input. I want to see what is resolve for typing. Okay. If isNull element finds by ID. There it is. Okay. So if I give it field, I'm giving it an element by an ID or, or if isNull. Okay. So basically what he's doing is try to find this element. Yeah. So I think what it is, if I were to say typeName, it's going to say, okay, let's see if there's an element on the page that has an ID of name. And if it's not, no, that's what you want. Otherwise we're going to find the input or a textarea that has that name. Okay. So that's what it's doing. Fair enough. So let's go back, close this out. You guys ever, I notice when I'm working and I have like 10 tabs open, I start getting stress and anxiety and I ended up having to close it back to one and like bring it back to the most minimal form or I get this weird coding anxiety. Anyways, browser, visit page, type into the name input. Hello there. And let's see how that works. So if we just run
bring it back to the most minimal form or I get this weird coding anxiety. Anyways, browser, visit page, type into the name input. Hello there. And let's see how that works. So if we just run it, I don't have that, right? No such element, unable to locate elements that matches that. So let's go to page and then we have a form and then we have our input. Okay. So if we run that, it'll open a browser. Now I am curious. Sometimes it doesn't open the browser at all. So what's up with that? I'd like to know how it knows when to do it because it seems like if it can't do that, then I will see a browser open. Yeah. You see that? So I'm curious, is it just happening so fast? I don't see it. I'm not entirely sure. It's cool that I don't have to see a browser, but I'd love to know why I'm not seeing a browser there. Anyways, let's go back and let's not give it a name, but let's give it an ID of name. Will that still work?
add my CSRF field. So now I want to say, let's clear this out. Type name. We'll say, hello world. And then press submit. Well, let's just change it. So I just want to make sure that when it fails, it fails in a way that I can understand. One thing you have to worry about with acceptance testing is because it's outside in, when it does fail, it's not always immediately clear what you should do. And that's why it's useful to have many layers of tests. You have your acceptance test, but then you drill down to your integration. And then you also drill down to the class level or the unit level. And if all goes according to plan, they work together nicely to make everything as clear as possible. Anyways, that's it for this video. I hope you found it helpful. And I'll see you in the next video. Bye-bye. Post request to page. Then I'm just going to return a string. You typed request name. Okay,
Wait Methods and Page Objects29:06
open. But when you do it with code, it tries to open the modal immediately and click okay immediately. But the okay button isn't available to click yet, so the test fails. So you end up having to do these things like you set a timeout and you keep checking. Does the okay button exist? Does it exist? Or there's wait methods you can use. But it's just very tricky. And I do happen to know that Desk has a bunch of wait methods to help with that. Let's see. Like waitUntil... Let's see. Yeah, it looks like it has a bunch here. waitFor an element to exist. waitFor text to be on the page. waitUntil... Yeah, that's useful. Actually, let's do one quick thing. I do want to see how these page objects work. So he includes one out of the box. I wonder if there's any generator for that. Desk. Yeah, we have three. Okay. Desk install is what basically bootstraps all of this stuff here. Desk make creates a new DeskTest class. Okay. So I assume that's going
any generator for that. Desk. Yeah, we have three. Okay. desk install is what basically bootstraps all of this stuff here. desk make creates a new desk test class. Okay. So I assume that's going to create something like this. Let's try that. php artisan desk make tasks test. Yeah. Okay. So that just basically gives me another one of those. Very useful. And then was there... Desk. Was there... This is something I do. If you ever just quickly want to filter something down, you run the command and then you pipe it to a call to grab. So in my case, I only cared about desk references in php artisan. So you pipe the whole output to a search for desk. Anyways, desk page. So that is going to create this. So if I were to say php artisan desk page dashboard, that'll... Okay. Great. Looks good. So let's see. The home page assert that the browser is on the page. So I'm not entirely sure. What does that mean?
page dashboard, that'll... Okay. Great. Looks good. So let's see. The home page assert that the browser is on the page. So I'm not entirely sure. What does that mean? assert that the browser is on the page. Let's go back. Wasn't there a reference? Okay. Dashboard. I don't see anything related to assert here. Yeah. I don't know. I don't know what that is yet. But let's try it out. So we're going to visit a new home page. And is that in here? It's not. Use test browser pages home page. All right. So if I run that php artisan desk, call the undefined method home page URL. Remember, this is alpha. So I'm thinking something has changed here. Or did I change that by any chance? Let's go. Let's do this. stubs. home page. No, that's on there. So call the undefined method pages home page URL. Let's go see. I'm seeing if anything's been merged
Let's go. Let's do this. Stubs. Home page. No, that's on there. So call the undefined method pages homePageURL. Let's go see. I'm seeing if anything's been merged in. Add required URL for browser page. Bobby. Bobby's a Laracasts User. He's cool. Okay. Okay. So does every Page object need a URL method? It would make sense. Like it needs some way to know. What do I do here? Do I just return a path to the page? Let's try that. It needs to know what it's requesting. Yeah. Okay. So maybe this is just a situation where the stub requires a URL thing. Well said, Jeffrey. Yeah. Maybe this just needs a quick PR or an update that will include the URL method. Anyways. Okay. So that's cool. So that means if I were to have foo, it should tell me that foo doesn't exist, but it doesn't. And that's because I haven't performed an assertion. Assert. See. Nice. Did not expect to see text nice within
the next stone in your path. Anyways, if we bring this back and the homepage goes to welcome here, let's just type nice at the bottom and that should make the test pass. Yeah. Okay. Cool. So I think also another thing you could do is say public function performAction. This could be a wrapper. Like imagine you have a task app and you want to call clearTasks. Well, I assume this accepts browser. Let's see. I'm trying to teach you, but I don't even know it. assert. Okay. Oh, okay. I know what assert means now. This is just a placeholder, I assume. Right? Maybe not. I'm thinking that's just a placeholder for any method that you call. Yes. Okay. I get it now. I'm stupid. So maybe if I called assert, that's just going to call this method here, but this can be anything you want it to be. Right? Let's try. So if I were to say assert and then say browser, here's where I could put the assertion. And
this method here, but this can be anything you want it to be. Right? Let's try. So if I were to say assert and then say browser, here's where I could put the assertion. And basically the method is a wrapper around a potentially complex assertion or something you need to do. Yay. Okay. So just reality check. Will that fail? Yeah. Okay. Yeah. Now I get it. I was trying to figure out why did he have assert? That's just a note to you that you can add a method here and then immediately call it. And this can be assert anything. It can be anything you want, whether it has assert on it or not, and it's going to be triggered. Okay. So I think this is pretty cool. This, once again, is how I would go about learning something new and the way it works for Laracasts is I would use this for a while. I'd get really comfortable with it. I'd dog food it a little bit in my own projects. And then once I felt very comfortable with it, I would create a
