Planning completion tests0:00
So another feature which we want to add here is that we want to make sure a User can mark a video as completed or not completed. So this means two new tests here. It marks video as completed. And another one, it marks video as not completed. Okay, because this is something very important for all video players, for all courses, you probably have seen it yourself. It's very good that you can mark a video as completed, and you probably also see this then on the sidebar, so you already know how far you are inside a video course. So this means the first one, it marks video as completed.
Setting up test data0:36
then on the sidebar, so you already know how far you are inside a video course. So this means the first one, it marks video as completed. First, we're going to need a User. And let's start by using the UserFactory to create a new User, because this is the User who is going to watch a video. And then what we're also going to need is a Course, and the Course has a video. And we're also going to give it a name, title, and the title will be just course video. Again, we're going to need something later to check that the User has completed a specific video. So we probably still have to create all of this.
video. So we probably still have to create all of this. Yeah, so we have a User, we have a Course, and the Course has a video with a specific title. So first, I want to make sure that the User has not yet any completed videos. So this means sometimes we're going to need a third before we act, just to make sure that the base state is as we want it to be. So we're going to expect that when we get all the videos of the User, that this will be a relationship between the User and the videos, so the videos that he or she has already watched.
be a relationship between the User and the videos, so the videos that he or she has already watched. And we expect to have a count of zero. And what we also have to make sure is that the Course belongs to the User. So let's do this here manually for now. So the User has courses, and we're going to attach a specific one, the one which we have just created. All right, so we have a course. We're providing the course to the User so that there is a relationship and the User has access to this course.
We're providing the course to the User so that there is a relationship and the User has access to this course. But the User should not have watched any videos yet. So this is why the videos of the User should be empty for now. And then we're going to log in as our User. We can make use of our nice loginAsUser helper method. And then we're going to use Livewire again, test our player components. We're providing the class and the video. And we're getting the video from the course, videos. And let's just take the first one.
And we're getting the video from the course, videos. And let's just take the first one. Okay, so how do we want to act here in order to add a video to a User so that a User can mark a video as watched? So what I want to do here and what we can do here with Livewire test is we can call a specific method of our Livewire component. So let's say we have a method called markVideoAsCompleted. So the thing about this is probably will be a button for the User that the User can click and then to mark this video as being completed or watched. And then down here, we want to assert that something happened now.
and then to mark this video as being completed or watched. And then down here, we want to assert that something happened now. So we're going to expect that, again, we're going to call here the videos on the User. Now we want to make sure that we have a count of one. So now the User should have one watched videos. And then we also want to grab the first one. And the title should equal so we can all chain this with test again. And now the title should be course video. So this way, we want to make sure that the video which we have created here is also the one that the User currently has here.
So this way, we want to make sure that the video which we have created here is also the one that the user currently has here. All right. And now we can call this test the first time. We have already written quite some code here and it will fail for sure. So let's see what the issue is. AssertCount must be a countable or iterable. And we are talking about this here. Yeah. So what we get back here is now because we haven't defined this relationship yet.
Adding user videos relation4:29
Yeah. So what we get back here is now because we haven't defined this relationship yet. And yeah, we cannot call this haveCount method on now. So this means we need to create this new videos relationship on the User. But we have already learned we're going to write now a test first for this relationship. And we have already a UserTest. So a User has courses, but we also now want to make sure that the User has videos. So we want to create now a User and we want to make sure that it also this User now has also a video. And let's say we're going to create two of them.
also a video. And let's say we're going to create two of them. Yep. So one User with two Videos. And then we want to make sure if we call the relationship will be videos on the User to have a count of two. And the instance that we want to check every of those Videos should be of Video. Right? Yes. Okay.
Yes. Okay. So let's run this and let's check the call to undefined method video. And that's not happening because we have provided here video and now there were things that there is a video relationship to create this video for the User. But what we can do here is as a second argument, we can provide the relationship and we want the relationship to be videos. Let's run this again. And it will still fail because also videos method is not given. All right, let's add this to the User.
And it will still fail because also videos method is not given. All right, let's add this to the User. New public function called videos. Actually it will be very similar to this one. So let's maybe just copy this one also belongsToMany relationship. We're just going to call it videos and it's to the Video class. For now I want to keep the name with videos and I think we don't need this for now, but we want to have timestamps. This is always good. All right, let's run the test again and let's see what we have or we get back here.
Creating watched videos table6:32
This is always good. All right, let's run the test again and let's see what we have or we get back here. No such table videos. Okay. And that's true because we don't have this intermediate table yet. So since we're now creating a new table, I think we can also think about how do we name it because we can name it videos, but we can give it a better name like purchase_courses here. So what would be a better name? What we're doing here, we're making sure that the video is completed or that a User has
So what would be a better name? What we're doing here, we're making sure that the video is completed or that a User has watched a given videos. I think a good name would be watchedVideos. watchedVideos. Yeah, I think like that. Let's run the test again. It will now fail because we don't have also this table not yet. Okay. Let's create.
Okay. Let's create. I like to create a model because here we can create a model and the migration together. So the model will be called WatchedVideo. Okay. I know we need some fields, but let's run the test first. Has no column named user_id. So let's add those foreign ids for us. We have one for the User providing the class here and put the namespace and yeah, also want to provide here already the Video.
We have one for the User providing the class here and put the namespace and yeah, also want to provide here already the video. So this is sometimes up to you if you really want to go step by step with everything that you need, or sometimes you already know that you need both these fields here. And since you're already in here, you can also do this as well. So this is some personal preference, but I think it's always a good idea to try to not to do too much and let the tests help you. Okay. So this test is now passing. This was now the User test, right?
So this test is now passing. This was now the UserTest, right? Yes. And let's also regenerate the helper code of the levelId applying because then we will also get rid of those underlines here because now my project knows that I have this relationship. All right. So far so good. So that means we can get back to our VideoPlayerTest and let's run this one again. Method not found exception, unable to call a component method, markVideoAsComplete. Yeah, exactly.
Implementing mark completed8:46
Method not found exception, unable to call a component method, mark video as complete. Yeah, exactly. So that's our Livewire component, which we tried to call here inside our Livewire test. So let's add this one. It will be a public function because we want to call it from the player view file. And I think we don't want to return anything. Let's run the test again. Now we see a different error field asserting that the actual size zero matches the expected size of one. And that's exactly here because here we're getting all the videos and we want to make
size of one. And that's exactly here because here we're getting all the videos and we want to make sure that we get one video back, but we don't have any watched videos yet. So this means now inside our video player, we want to make sure that here we're going to add the video to the User. So how do we do this? We want to get the current authenticated User, can use this by the global auth helper method. Then we want to get the User. Then we want to get the relationship, which is called videos. And here now we want to attach a specific video, which is the current one, which is
Then we want to get the relationship, which is called videos. And here now we want to attach a specific video, which is the current one, which is always this video inside the player component because this is always the current video of the player. Okay, let's try it again. And it's still not working. Let's see what else could this be about? Oh yeah, I got an idea. I think what we're going to need to do here is to refresh the User. That's because we are getting the User up here at this point, there is no relationship.
I think what we're going to need to do here is to refresh the User. That's because we are getting the User up here at this point, there is no relationship yet or we haven't added the videos. We then perform some actions, but the User, which we then check here still hasn't all the new information of the current User and attached videos. So I think if we run this again, yeah, you can see this was it. Now it works. It marks the video as completed. Okay, cool. So this means now we have made sure that we can mark a video as completed.
Implementing mark not completed10:46
Okay, cool. So this means now we have made sure that we can mark a video as completed. And now for a second test, let's copy the code here because I assume that most of it will be very similar. So we have User, we create a Course with Video, then we attach the Course to the User. And I think here we can also already attach now the Video to the User. And we do this by using the courseVideos and getting the first one. All right, now we expect here to get a count of 1 back because the User already got one Video. And now we want to call here different method, markVideoAsNotComplete.
video. And now we want to call here different method, mark video as not complete. So this will be a different button. And then here, down here, we want to make sure now that we get back zero like this. All right, this will fail because we don't have the method yet. MethodNotFoundException for mark video is not completed. All right, insert our player. Let's just copy this one. And again, you might think about, okay, this could also be a kind of toggle. So that we can switch between those.
And again, you might think about, okay, this could also be a kind of toggle. So that we can switch between those. But these are all things that we can still do later. First we want to make this work and then while refactoring, we can think about different solutions as well. So notCompleted, and I think the other one is not called detach. I think so. Let's give it a try. And yes, this means our second test now is also working where we have made sure that it marks video as notCompleted, where we attach a video to the User.
And yes, this means our second test now is also working where we have made sure that it marks video is not completed, where we attach a video to the User. The User has one video, one watchedVideos, and then we're going to run our new method here and then the User doesn't have any videos anymore. And let's run also all of the tests just to make sure that we didn't break any other code. No, we didn't. We're already up to 53 tests so far. Pretty cool. The speed is also still pretty fast, but yeah, we have another test in our application and we have another two features added here.
The speed is also still pretty fast, but yeah, we have another test in our application and we have another two features added here.
