Uploading Images in Behat both at Domain Level and UI Level
The goal here is as follows
@fileCleanup @javascript
Scenario: Uploading File
Given I already made a Segmentation and am editing it
Then I add file and the system will process it
And I can not add another file till it is done
And when the system is done I can add another file
In this case they are large files and I want to get an MVP for the users to interact with so one file at a time is enough for now.
The tricky part is that I am running Selenium on my Mac and running behat in my VM there is no file path connection between the two. This can also be an issue when using Saucelabs and https://crossbrowsertesting.com/.
UI Level
I am using the Flow Angular library so it is JavaScript heavy (some notes below). Otherwise your work is even easier.
My step to set things up Given I already made a Segmentation and am editing it
just adds that resource to the system and then I visit it.
From there I hit Then I add file and the system will process it
and this is where it all begins
/**
* @Then I add file and the system will process it
*/
public function iAddFileAndTheSystemWillProcessIt()
{
$this->visit('/segments/files/' . $this->segment_uuid);
sleep(2); //load up the page
$this->assertPageContainsText('Upload File'); //make sure the button is there
/**
* Here is the meat of it, or tofu, I have to deal with the fact I am running
* Selenium on a different machine than my VM's filesystem
* So I inject this into the WebDriverSession
* Which only exists because this Scenario is marked `@javascript`
*/
$localFile = base_path('features/fixtures/data_columns_not_snaked.xls');
$tempZip = tempnam('', 'WebDriverZip');
$zip = new \ZipArchive();
$zip->open($tempZip, \ZipArchive::CREATE);
$zip->addFile($localFile, basename($localFile));
$zip->close();
$remotePath = $this->getSession()->getDriver()->getWebDriverSession()->file([
'file' => base64_encode(file_get_contents($tempZip))
]);
/** end the hard part **/
$this->attachFileToField('image_upload', $remotePath); //see Flow notes at the bottom of this page
$this->assertPageNotContainsText('Upload File');
unlink($tempZip);
sleep(5); //Later on I will move this to `spin` see notes below
$this->assertPageContainsText('data_columns_not_snaked');
}
Let me show that again but now more simple just a normal input button
/**
* @Then I add file and the system will process it
*/
public function iAddFileAndTheSystemWillProcessIt()
{
$this->visit('/segments/files/' . $this->segment_uuid);
sleep(2); //load up the page
$this->assertPageContainsText('Upload File'); //make sure the button is there
/**
* Here is the meat of it, or tofu, I have to deal with the fact I am running
* Selenium on a different machine than my VM's filesystem
* So I inject this into the WebDriverSession
* Which only exists because this Scenario is marked `@javascript`
*/
$localFile = base_path('features/fixtures/data_columns_not_snaked.xls');
$tempZip = tempnam('', 'WebDriverZip');
$zip = new \ZipArchive();
$zip->open($tempZip, \ZipArchive::CREATE);
$zip->addFile($localFile, basename($localFile));
$zip->close();
$remotePath = $this->getSession()->getDriver()->getWebDriverSession()->file([
'file' => base64_encode(file_get_contents($tempZip))
]);
/** end the hard part **/
$this->attachFileToField('image_upload', $remotePath); //the input field
/** prove the button is gone during this process **/
$this->assertPageNotContainsText('Upload File');
unlink($tempZip);
$this->pressButton('Upload File'); //dealing with normal input button
}
Domain Level
Since my Controller gets the info from the incoming Request
and hands it to the Repository class, I like to test my classes outside the Controller then plug them in (see here for more on that), there is typically no need to pass the full Request
to the Repository. But in this case I went about passing the entire Request
to the Repository
and doing some checking in there as well.
/**
* @Then I should be able to upload an image file
*/
public function iShouldBeAbleToUploadAnImageFile()
{
$request = new \Illuminate\Http\Request();
$file = new \Symfony\Component\HttpFoundation\FileBag();
$path = base_path('features/fixtures/data_columns_not_snaked.xls');
$originalName = 'data_columns_not_snaked.xls';
/** note `true` passed in to `UploadedFile` it defines this as a test request **/
$upload = new \Illuminate\Http\UploadedFile($path, $originalName, null, null, null, true);
$file->set('profile_image', $upload);
$request->files = $file;
$this->repo = new \App\Repositories\ProfileRepository();
$results = $this->repo->uploadUserProfileImage($request);
PHPUnit::assertTrue($results, "Repo did not return true");
PHPUnit::assertTrue(File::exists(public_path('storage/' . $this->user->id . '/data_columns_not_snaked.xls')), "File Not found");
}
And now that is passing as well.
Thats It
I am now testing both the UI and the Domain level part of this Application.
Note
Using Flow Library https://github.com/flowjs/ng-flow makes it easy to process large files in PHP for me.
One thing I had to do was do was set flow-attrs
to the directive so I can get a name
in there to target field.
<div class="btn btn-xs btn-primary" flow-attrs="{name:'image_upload'}" flow-btn ng-if="vm.file_uploading">
<i class="fa fa-cloud-upload"></i> Upload File
</div>
Link to Spin on Behat docs and StackOverflow for a better solution than sleep
http://docs.behat.org/en/v2.5/cookbook/using_spin_functions.html
And a bit more on that http://www.tentacode.net/10-tips-with-behat-and-mink
comments powered by Disqus