How to test file download in Behat - bdd

There is this new Export functionality developed on this application and I'm trying to test it using Behat/Mink.
The issue here is when I click on the export link, the data on the page gets exported in to a CSV and gets saved under /Downloads but I don't see any response code or anything on the page.
Is there a way I can export the CSV and navigate to the /Downloads folder to verify the file?

Assuming you are using the Selenium driver you could "click" on the link and $this->getSession()->wait(30) until the download is finished and then check the Downloads folder for the file.
That would be the simplest solution. Alternatively you can use a proxy, like BrowserMob, to watch all requests and then verify the response code. But that would be a really painful path for that alone.
The simplest way to check that the file is downloaded would be to define another step with a basic assertion.
/**
* #Then /^the file ".+" should be downloaded$/
*/
public function assertFileDownloaded($filename)
{
if (!file_exists('/download/dir/' . $filename)) {
throw new Exception();
}
}
This might be problematic in situations when you download a file with the same name and the browser saves it under a different name. As a solution you can add a #BeforeScenario hook to clear the list of the know files.
Another issue would be the download dir itself – it might be different for other users / machines. To fix that you could pass the download dir in your behat.yml as a argument to the context constructor, read the docs for that.
But the best approach would be to pass the configuration to the Selenium specifying the download dir to ensure it's always clear and you know exactly where to search. I am not certain how to do that, but from the quick googling it seems to be possible.

Checkout this blog: https://www.jverdeyen.be/php/behat-file-downloads/
The basic idea is to copy the current session and do the request with Guzzle. After that you can check the response any way you like.
class FeatureContext extends \Behat\Behat\Context\BehatContext {
/**
* #When /^I try to download "([^"]*)"$/
*/
public function iTryToDownload($url)
{
$cookies = $this->getSession()->getDriver()->getWebDriverSession()->getCookie('PHPSESSID');
$cookie = new \Guzzle\Plugin\Cookie\Cookie();
$cookie->setName($cookies[0]['name']);
$cookie->setValue($cookies[0]['value']);
$cookie->setDomain($cookies[0]['domain']);
$jar = new \Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar();
$jar->add($cookie);
$client = new \Guzzle\Http\Client($this->getSession()->getCurrentUrl());
$client->addSubscriber(new \Guzzle\Plugin\Cookie\CookiePlugin($jar));
$request = $client->get($url);
$this->response = $request->send();
}
/**
* #Then /^I should see response status code "([^"]*)"$/
*/
public function iShouldSeeResponseStatusCode($statusCode)
{
$responseStatusCode = $this->response->getStatusCode();
if (!$responseStatusCode == intval($statusCode)) {
throw new \Exception(sprintf("Did not see response status code %s, but %s.", $statusCode, $responseStatusCode));
}
}
/**
* #Then /^I should see in the header "([^"]*)":"([^"]*)"$/
*/
public function iShouldSeeInTheHeader($header, $value)
{
$headers = $this->response->getHeaders();
if ($headers->get($header) != $value) {
throw new \Exception(sprintf("Did not see %s with value %s.", $header, $value));
}
}
}

Little modified iTryToDownload() function with using all cookies:
public function iTryToDownload($link) {
$elt = $this->getSession()->getPage()->findLink($link);
if($elt) {
$value = $elt->getAttribute('href');
$driver = $this->getSession()->getDriver();
if ($driver instanceof \Behat\Mink\Driver\Selenium2Driver) {
$ds = $driver->getWebDriverSession();
$cookies = $ds->getAllCookies();
} else {
throw new \InvalidArgumentException('Not Selenium2Driver');
}
$jar = new \Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar();
for ($i = 0; $i < count($cookies); $i++) {
$cookie = new \Guzzle\Plugin\Cookie\Cookie();
$cookie->setName($cookies[$i]['name']);
$cookie->setValue($cookies[$i]['value']);
$cookie->setDomain($cookies[$i]['domain']);
$jar->add($cookie);
}
$client = new \Guzzle\Http\Client($this->getSession()->getCurrentUrl());
$client->addSubscriber(new \Guzzle\Plugin\Cookie\CookiePlugin($jar));
$request = $client->get($value);
$this->response = $request->send();
} else {
throw new \InvalidArgumentException(sprintf('Could not evaluate: "%s"', $link));
}
}

In project we have problem that we have two servers: one with web drivers and browsers and second with selenium hub. As result we decide to use curl request for fetching headers. So I wrote function which would called in step definition. Below you can find a function which use a standard php functions: curl_init()
/**
* #param $request_url
* #param $userToken
* #return bool
* #throws Exception
*/
private function makeCurlRequestForDownloadCSV($request_url, $userToken)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$headers = [
'Content-Type: application/json',
"Authorization: Bearer {$userToken}"
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$output = curl_exec($ch);
$info = curl_getinfo($ch);
$output .= "\n" . curl_error($ch);
curl_close($ch);
if ($output === false || $info['http_code'] != 200 || $info['content_type'] != "text/csv; charset=UTF-8") {
$output = "No cURL data returned for $request_url [" . $info['http_code'] . "]";
throw new Exception($output);
} else {
return true;
}
}
How you can see I have authorization by token. If you want to understand what headers you should use you should download file manual and look request and response in browser's tab network

Related

How to get the page Info.totalResults from the youtube api Search: list

Below is the code i have created so far, my goal is to use the youtube Search: list api to find streams for specific games and then publish how many streams there are for that game, I have a database for the game titles and this is my function below, my api link does work im just not able to get the Info.totalResults from it, any help would be great, Thank you for any help you can provide
function totalgamelist() {
global $wpdb;
$gamelistname = $wpdb->prefix . 'Games';
global $wpdb;
$getgames = $wpdb->get_results( 'SELECT * FROM '.$gamelistname , OBJECT );
if (empty($getgames)) {
//empty array
echo 'This is empty sorry!';
} else {
echo 'We Got Something!';
foreach ( $getgames as $getgame )
{
echo '<div class="gamename">'.$getgame->GameTitle;
$JSON = file_get_contents("https://www.googleapis.com/youtube/v3/search?part=snippet&eventType=live&type=video&videoCategoryId=20&regionCode=US&maxResults=50&q='.$getgame->GameTitle.'&key=[API KEY]");
$json_data = json_decode($JSON, true);
echo $json_data['totalResults'];
echo '</div>';
}
}
}
EDIT:
So with the help from johnh10, i was able to find out that it wasn't my ability to display the results although the echo johnh10 gave is correct :) it was also that my server was blocking access to the url i was asking to view. Below is the curl code i used to access the url, hope it helps others.
$urlgame = 'https://www.googleapis.com/youtube/v3/search?part=snippet&eventType=live&type=video&videoCategoryId=20&regionCode=US&maxResults=1&q='.$getgame->GameTitle.'&key=[API Key]';
$ch = curl_init($urlgame);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
$json_data = json_decode($data, true);
if (!empty($json_data)) {
$streamnumber = $json_data['pageInfo']['totalResults'];
echo ' Streams:'.$streamnumber;
} else {
echo ' Streams: No Streams Found';
}
echo $json_data['pageInfo']['totalResults'];

Can't get Task to Send Email in Symfony 1.4

I created a task to automate emailing a report in Symfony 1.4. Both this task and a related module for viewing in the web share a custom PHP class file. The task is able to pull in the data correctly, but I have not been able to get it to send out the email. I attempted to follow the official Symfony 1.4 documentation as well as a number of examples from a Google search, but none are solving my problem. The terminal isn't displaying any error either.
My Code:
<?php
require_once sfConfig::get('sf_lib_dir').'/vendor/sesame/reports/reports.class.php';
//use reports;
class reportsTask extends sfBaseTask
{
protected function configure()
{
// // add your own arguments here
// $this->addArguments(array(
// new sfCommandArgument('my_arg', sfCommandArgument::REQUIRED, 'My argument'),
// ));
$this->addOptions(array(
new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name'),
new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
new sfCommandOption('type', null, sfCommandOption::PARAMETER_OPTIONAL, 'The output type of the report', 'email')
// add your own options here
));
$this->namespace = '';
$this->name = 'reports';
$this->briefDescription = '';
$this->detailedDescription = <<<EOF
The [reports|INFO] task does things.
Call it with:
[php symfony reports|INFO]
EOF;
}
protected function execute($arguments = array(), $options = array())
{
$databaseManager = new sfDatabaseManager($this->configuration);
$databaseManager->loadConfiguration();
$reports = new reports();
$output = $reports->buildReport($options['type']);
switch($options['type']){
case 'csv':
echo $output;
break;
case 'email':
$message = $this->getMailer()->compose($output['from'], $output['to'], $output['subject']);
$message->setBody($output['body']['content'], $output['body']['type']);
$message->attach(Swift_Attachment::newInstance($output['attachment']['content'], $output['attachment']['name'], $output['attachment']['type']));
$this->getMailer()->sendNextImmediately()->send($message) or die('email failed to deliver');
$output = array('status'=>'success', 'to'=>$output['to']);
default:
$this->logSection('results', json_encode($output));
}
}
}
The terminal command being attempted from the project root:
php symfony reports
Any answers leading to the right path would be most helpful. Please keep in mind that I need to stay with version 1.4. The server is capable of sending off emails and my module version does just that when invoked by a URL. I need it to run on the command line though so I can set up a cron.

for each element with behat or codeception

I want to test a website which has a dynamic menustructure. I want to loop through all menuitems and run the same series of test on every page. We're talking about 100+ pages that change reguraly.
I would like to do this with either behat or codeception.
Does anybody have an idea about how to do this?
When using Behat with Mink, you can grab your menu items with findAll() and then iterate over it:
/**
* #When /^I run my test series for all menu items$/
*/
public function iRunMyTestSeriesForAllMenuItems() {
$result = TRUE;
$this->getSession()->visit('http://www.example.com/');
$links = $this->getSession()->getPage()->findAll('css', '#menu ul li a');
foreach ($links as $link) {
$url = $link->getAttribute('href');
if (FALSE === $this->yourTestHere($url)) {
$result = FALSE;
}
}
return $result;
}
I had a similar use case where I wanted to visit all pages of a given site map making sure there isn't any dead links. I had the approach to dynamically generate an array of steps which is then returned and processed by Behat. I had to add an artificial step "I print out page" to make sure I can see on the console what page is currently tested.
/**
* #Then /^I should access all pages of site map "([^"]*)"$/
*/
public function iShouldAccessAllPagesOfSiteMap($selector) {
$page = $this->getSession()->getPage();
$locator = sprintf('#%s a', $selector);
$elements = $page->findAll('css', $locator);
$steps = array();
foreach ($elements as $element) {
/** #var \Behat\Mink\Element\NodeElement $element */
$steps[] = new Behat\Behat\Context\Step\When(sprintf('I print out page "%s"', $element->getAttribute('href')));
$steps[] = new Behat\Behat\Context\Step\When(sprintf('I go to "%s"', $element->getAttribute('href')));
$steps[] = new Behat\Behat\Context\Step\Then('the response status code should be 200');
}
return $steps;
}
/**
* #When /^I print out page "([^"]*)"$/
*/
public function iPrintOutThePage($page) {
$string = sprintf('Visiting page ' . $page);
echo "\033[36m -> " . strtr($string, array("\n" => "\n| ")) . "\033[0m\n";
}
Then my scenario looks as follows:
Scenario: my website has no "dead" pages
Given I am on "/examples/site-map/"
Then I should access all pages of site map "c118"
The whole Gist is here https://gist.github.com/fudriot/6028678

CakePHP requestAction catch error when url not exists

I try to catch error when the requested url does not exist in my application when I use requestAction($url);
with try-catch block, requested wrongs url continue to be loaded;
Please tell me if there is an other solution to test if the url exist, or if the file exist in cakephp dir (my url can be composed of a plugin name).
my actual code:
$url = $appliUrl . $cron['Cron']['plugin'] . '/'. $cron['Cron']['controller'] . '/' . $cron['Cron']['action'];
if (!empty($cron['Cron']['params'])) {
$params = explode(',', $cron['Cron']['params']);
foreach ($params as $param)
$url .= '/' . $param;
}
try{
$output = $this->requestAction($url);
}catch (Exception $e){
$output = "error in the url : ".$url;
}
You can use a variety of methods to accomplish this. In this example, use cURL.
function url_exists($url) {
if (!$fp = curl_init($url)) return false;
return true;
}
Then you can wrap your $output in an if block checking for the url.
if (url_exists($url)) {
$output = $this->requestAction($url);
}
EDIT
Make sure you put the function in a lib or your bootstrap for access. Don't just stick it in your controller like that :)
ANOTHER EDIT
<?php
$ch = curl_init('http://yoururl/');
curl_setopt($ch, CURLOPT_NOBODY, 1);
$c = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE);

How do you POST to a page using the PHP header() function?

I found the following code on here that I think does what I want, but it doesn't work:
$host = "www.example.com";
$path = "/path/to/script.php";
$data = "data1=value1&data2=value2";
$data = urlencode($data);
header("POST $path HTTP/1.1\r\n");
header("Host: $host\r\n");
header("Content-type: application/x-www-form-urlencoded\r\n");
header("Content-length: " . strlen($data) . "\r\n");
header("Connection: close\r\n\r\n");
header($data);
I'm looking to post form data without sending users to a middle page and then using JavaScript to redirect them. I also don't want to use GET so it isn't as easy to use the back button.
Is there something wrong with this code? Or is there a better method?
Edit I was thinking of what the header function would do. I was thinking I could get the browser to post back to the server with the data, but this isn't what it's meant to do. Instead, I found a way in my code to avoid the need for a post at all (not breaking and just continuing onto the next case within the switch).
The header function is used to send HTTP response headers back to the user (i.e. you cannot use it to create request headers.
May I ask why are you doing this? Why simulate a POST request when you can just right there and then act on the data someway? I'm assuming of course script.php resides on your server.
To create a POST request, open a up a TCP connection to the host using fsockopen(), then use fwrite() on the handler returned from fsockopen() with the same values you used in the header functions in the OP. Alternatively, you can use cURL.
The answer to this is very needed today because not everyone wants to use cURL to consume web services. Also PHP does allow for this using the following code
function get_info()
{
$post_data = array(
'test' => 'foobar',
'okay' => 'yes',
'number' => 2
);
// Send a request to example.com
$result = $this->post_request('http://www.example.com/', $post_data);
if ($result['status'] == 'ok'){
// Print headers
echo $result['header'];
echo '<hr />';
// print the result of the whole request:
echo $result['content'];
}
else {
echo 'A error occured: ' . $result['error'];
}
}
function post_request($url, $data, $referer='') {
// Convert the data array into URL Parameters like a=b&foo=bar etc.
$data = http_build_query($data);
// parse the given URL
$url = parse_url($url);
if ($url['scheme'] != 'http') {
die('Error: Only HTTP request are supported !');
}
// extract host and path:
$host = $url['host'];
$path = $url['path'];
// open a socket connection on port 80 - timeout: 30 sec
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if ($fp){
// send the request headers:
fputs($fp, "POST $path HTTP/1.1\r\n");
fputs($fp, "Host: $host\r\n");
if ($referer != '')
fputs($fp, "Referer: $referer\r\n");
fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: ". strlen($data) ."\r\n");
fputs($fp, "Connection: close\r\n\r\n");
fputs($fp, $data);
$result = '';
while(!feof($fp)) {
// receive the results of the request
$result .= fgets($fp, 128);
}
}
else {
return array(
'status' => 'err',
'error' => "$errstr ($errno)"
);
}
// close the socket connection:
fclose($fp);
// split the result header from the content
$result = explode("\r\n\r\n", $result, 2);
$header = isset($result[0]) ? $result[0] : '';
$content = isset($result[1]) ? $result[1] : '';
// return as structured array:
return array(
'status' => 'ok',
'header' => $header,
'content' => $content);
}
In addition to what Salaryman said, take a look at the classes in PEAR, there are HTTP request classes there that you can use even if you do not have the cURL extension installed in your PHP distribution.
There is a good class that does what you want. It can be downloaded at: http://sourceforge.net/projects/snoopy/
private function sendHttpRequest($host, $path, $query, $port=80){
header("POST $path HTTP/1.1\r\n" );
header("Host: $host\r\n" );
header("Content-type: application/x-www-form-urlencoded\r\n" );
header("Content-length: " . strlen($query) . "\r\n" );
header("Connection: close\r\n\r\n" );
header($query);
}
This will get you right away

Resources