00:09
be used outside of AdonisJS. In that context, it's called FlyDrive, and the documentation can be found at flydrive.dev.
00:16
Now, like Mail and other first party AdonisJS packages, Drive also comes with fakes and other helpful assertions that aid us with our tests as well.
00:26
In this project, I already have Drive installed and configured, and indeed, inside of our post's controller, we already have, if we scroll down a little bit, an Upload Thumbnail
00:34
endpoint to test file uploads. This is going to expect a field called Thumbnail to be sent up with the request that is a maximum
00:42
size of 5MB and a JPEG or PNG file type. If it doesn't get an image at all, it will throw an ImageNotProvidedException with a
00:51
400 status code, and if the validation of our file throws any errors, then we'll return back a 422 with those errors.
00:59
Otherwise, the file name will be added with a CUID, followed by the file's extension name, and moved into our storage. Once that's done, we will get back a message with the image successfully uploaded, the
01:09
final file name of the image with that CUID added in, as well as the URL itself. Again, since this is still within our post controller, we're going to keep this within
01:17
our post specification, and for our test, we'll say that it should allow a valid thumbnail to be uploaded.
01:25
We'll grab a sync, and we'll use assert, our API client, our route, and since we're dealing with fakes, we'll also go ahead and grab our cleanup method.
01:33
To start, let's go ahead and get our fake setup, so we'll do const disk equals, and we can import drive. It looks like the import for that is not coming through, so let me go ahead and scroll on
01:43
up to the top of the file, and we will import drive from at AdonisJS slash drive slash,
01:51
and there should be a services slash main export from that. All right, that'll give us our drive, so we can scroll back down to our test then,
01:59
and then we should have .fake at our disposal. Similar to mail, as we saw previously, this disk then will give us additional assertions
02:08
and methods to be able to test exactly what has happened with that file upload.
02:13
Additionally, we also then want to clean up our fake after our test has completed, so we'll
02:19
call our cleanup callback and do drive.restore to reset it back to its original state. Now, to aid us further with testing, we can add in a utility package, so let's jump
02:28
into an empty terminal tab here, and we'll be npm i as a dev dependency at poppins with
02:34
two p's and two s's at the end, slash file generator. This is going to allow us and help us make actual files to test with without needing
02:43
to actually have files within our project itself. So we can go ahead and install this, great, and we can clear that out and jump back over
02:51
into our watch tab, and I'm going to go ahead and scroll back up and just add this import in.
02:56
So import file generator from at poppins slash file generator, just like so, and scroll on back down to our test then.
03:04
What this will allow us to do then is define a file using await file generator, and then it gives us various types that we can generate a file for.
03:12
So, for example, here we can generate a png using generate png, just like so, and pass a file size in that we want, and you can also specify a file name then as the second argument.
03:21
This can either be a string of bytes, this can either be a number of bytes, or you can use a human readable string, so something like three megabytes there to generate that file size.
03:30
Once we have our file, we can then go ahead and send our API request, so we'll do const
03:35
response equals await, use our client to send out a post request to the route posts, and I have this called post.thumbnail.
03:44
Since we're sending a post, we're going to want this to be with a CSRF token, and then we can attach a file to this request, and this will then automatically switch this to
03:53
a multi-part request as well on our behalf. Within the file here, we do the file name. As we saw, our validation is going to expect this to be called the thumbnail.
04:01
The second argument here then is the file contents, which is our file dot, and you can see this file generator has given us actual contents of the file, the MIME type, the name,
04:10
and the size of the file as well, so we do file contents there. The third argument then is additional optional options like the file name and actual content
04:18
type, so we can specify those as well, so we do file name, which our file generator has given us as file.name, and we also have the content type via file.mime.
04:27
Okay, now we're ready to actually assert against our response. Let's just start simple, and then we'll actually work on asserting that our file was there.
04:34
So we'll start with our response dot assert, okay, let's make sure that everything went
04:38
okay, and then we can do response dot assert body contains to ensure that our message of
04:45
image successfully uploaded comes back with that JSON response. If we give that a save, see that runs through A-okay, and our test works A-okay as well.
04:55
Okay, let's go ahead and grab the actual body that we're getting back. So const body equals response body, and let's just go ahead and console log what that body is.
05:05
Remember, you can also do response dot dump body as well, so we can see that we're getting back our actual JSON object with that message, the file name, which is our cuid, followed
05:13
by the file extension, and then the actual URL of that file, so we can see it's trying to place it within drive, fakes, that cuid, followed by the file extension as the name.
05:22
Now, that location is actually within our project because we're using a local store, and that's gonna go within this temp folder. You might not have had this previously, but now that we've run a test, the file actually
05:32
went inside of this temp folder, and there is our drive fakes folder as well, which is where drive is placing these faked files and then cleaning it up whenever drive is restored.
05:42
So as I noted in a previous lesson, this restore step is very vital. If we were to actually omit it, so here I'll comment it out, and we rerun our test, you'll
05:51
see that we actually have a file now within here that has not been cleaned up. So as we continue to run our tests over and over and over again, those files will just
06:01
continuously keep accumulating, which is why the restore step is vitally important, especially with something like using drive here, dealing with physical files on our file system, as
06:11
it is the portion involved with that cleanup. So whenever we run that once more with restore uncommented, you'll see that that then cleans
06:17
up all of those fakes, and it doesn't happen until that restore step, giving us the availability then to make assertions against the actual files that were generated.
06:26
So we can use that disk variable to say that we want to assert that the file actually exists
06:32
at the body.filename location, because the fake disk drive is already going to know to look at slash drive, and then it's fake disk directory.
06:42
So we just need to pass it the file name there for that. Additionally, if we wanted to, we could also add in a assertion for the actual properties
06:48
that we get back within the body to make sure that we get back a URL and a file name itself. You can also assert that within the body directly as well.
06:57
But since the file name is a CUID, we're not actually going to know what either of these two properties are, which makes the property assertion here perfect.
07:05
So we'll give that a save, and that should go through A-OK once more. We're done with our console log, so we can get rid of that. Now, before we move onward, I do want to note that if we scroll up here within this fake,
07:15
if you have multiple stores that you're using with Drive, for example, you might be going to R2, S3, you can specify which service it is that you want to fake by passing an argument
07:25
into the fake for that. So for example, here, we're going to fake specifically the file system drive. Whenever this is omitted, it will use whatever's default within your configuration.
07:34
That is equal to doing drive.usefs, for example, to specify a specific drive to use outside of the test environment.
07:42
All right, now that we know that, we're ready to move on to our sad path then, our file upload. And we saw that we have a couple. First, let's start with the validation.
07:49
So we'll test that it should not allow an invalid thumbnail to be uploaded.
07:56
Do async, grab our client routes, and clean up. And for this one, we're just going to be asserting against the response that we get back from our client. So we can just plop drive within its fake mode.
08:06
And then, of course, we want to clean that up. We don't want any files left behind, so drive.restore. And then we'll go ahead and grab a file that is invalid.
08:15
So we'll use our file generator 2, and we'll test 2 in this one go. So we'll generate a PDF, which is not supported by our file upload as a valid type.
08:24
And we can also make this 6 megabytes, which is over our file size limit. So we should get back two errors with this.
08:29
So we can grab our response then, await, use our client to send out a post request to the route,
08:36
posts, thumbnail, and do our with CSRF token, and then attach our file in as thumbnail,
08:42
pass the file contents in with the file name of file.name and the content type of file.mime.
08:50
Within our posts controller then, whenever we have an invalid response or the validation has any errors here, we return back an unprocessable entity.
08:58
If you're using Vine.js validation for your file, that would be similar there as well. So we want to verify that we get back an unprocessable entity response.
09:07
So we'll do response.assert unprocessable entity to assert that that is a 422 status code.
09:14
And we can also do response.assert body contains as this returns back the actual errors as well.
09:21
So we'll make sure that we have an array with an object of field name that is thumbnail, as that is the actual field being validated for the file.
09:30
The validation type that has failed, which should be size because we're passing six megabytes, and this only allows five megabytes there.
09:39
And the message for that, which we could omit, but I'll go ahead and pass in here, file size should be less than five MB. All right.
09:47
In addition to that, we should have a second validation error, again, for the field name thumbnail. This time the type is going to be the ext name or extension name,
09:56
and the message should be something like invalid file extension PDF, only JPEG, JPEG and PNG are allowed.
10:06
If we give that a save, we should then see that go through and that works a okay. If you want to see exactly what that response is, we could do response.dump body just like so,
10:15
and it should show our exact errors, which we see right there. Perfect. We can go ahead and get rid of that because we now also need to test our other sad path
10:24
to ensure that this gracefully handles when an uploaded file is missing or misnamed. So this should gracefully handle when an uploaded file
10:34
is missing slash misnamed. Do async, grab our client routes and clean up once more,
10:42
drive.fake, cleanup, drive restore. And if you're heavily testing or doing a specification specific to drive as well, you could always do this within hooks
10:51
rather than directly inside of the test to simplify there as well. All right. Then we'll grab our file await file generator dot, go ahead and generate a PNG.
11:01
So we'll do a valid type and the file size doesn't really matter here. We'll do six megabytes just to make sure that this does not get tripped up on the validation, but rather it's just, it cannot be found.
11:11
Then we'll do const response equals await clients. And we'll send a post out to again, our posts thumbnail endpoint with the file. And now it expects thumbnail.
11:21
So we'll do thumbnails, which might be a common misnaming, and then we'll pass along our file contents,
11:27
the file name, file.name and content type of file.mime. So again, since our posts controller here is specifically looking for thumbnail for the field name,
11:37
since we're passing thumbnails, the controller is not gonna be able to find the file within this validation step, meaning that we're going to get back a bad request
11:46
with our image not provided error. Okay. So for that, we can go ahead and assert with our response that we get back a bad request, which is a 400.
11:56
And we can also assert that the body that we get back, and this is all that our body is. So we can just do assert body
12:03
rather than assert body contains an error image not provided, just like so. Give that a save. And, uh-oh, what did we get back for our response?
12:12
We got back a 200, even though we expected a 400. Okay. Oh, yep. If we scroll up a little bit more here, I think this might be the culprit right there,
12:22
our invalid or expired CSRF token. I think I missed putting that on. It's also why our formatting here got a little wonky. So, okay.
12:29
We do want to do with CSRF token there as well on our request. So let's give that a save and perfect. Okay, great.
12:37
So now we've asserted that this will gracefully handle when an uploaded file is missing or misnamed. Now, as always, if you feel that uploading your thumbnail needs more tests in addition to this,
12:47
maybe for the validation, or maybe you want to make sure that all of the file types work appropriately, then feel free to create those tests to assert that.
12:56
They would be rather similar to what we already have here. So we'll go ahead and call this sufficient for our purposes.