Unit Testing with Fat-Free-Framework - phpunit

Is there a way to use PHPUnit where I have a test folder with indexTest.php inside that tests routes in my index.php file?
The fat-free guide gives snippets of code for mocking route requests and POSTS. I have only managed to get such a test to work if I generate the route directly in my test file with whatever functionality in it.
What I would like is to mock a route with tokens, allow it to run from a route in index.php and through the controller and test f3 variables that should be set by running the route.
<?php
class indexTest extends \PHPUnit_Framework_TestCase
{
public function test()
{
$f3 = Base::instance();
// Don't write to STDOUT
$f3->set('QUIET', true);
$f3->route('GET /path', function(){ echo 'TEXT'; });
$this->assertNull($f3->mock('GET /path'));
$this->assertSame('TEXT', $f3->get('RESPONSE'));
$f3->route('GET /verify/#answer/#value',
function($f3, $params){
$errors = array();
$answer = $params['answer'];
$value = $params['value'];
$prefix = substr($answer, 0, 3); //pre, ans, pos
$id = (int)substr($answer, 3); //question id number (1, 2, 3, 4)
//$value is the input value from user
$result = check_id($prefix, $id, $value);
if($result !== true){
$errors[] = $result;
}
$f3->set('errors', $errors);
return $errors;
});
function check_id($prefix, $id, $value)
{
if($prefix == 'pre' || $prefix == 'pos'){
if($value <= 0 || $value > 180 || $value === NULL){
echo 'The input value of ' . $prefix . $id . ' question was out of bounds';
return 'The input value of ' . $prefix . $id . ' question was out of bounds';
}else{
return true;
}
}else if($prefix == 'ans'){
if($value < 0 || $value > 10 || $value === NULL){
echo 'The value of quiz ans' + $id + ' was out of bounds';
return 'The value of quiz ans' + $id + ' was out of bounds';
}else{
return true;
}
}else {
return 'The prefix does not match';
}
}
$this->assertNotNull($f3->mock('GET /verify/ans1/8'));
$this->assertEmpty($f3->get('RESPONSE')[0]);
$this->assertNotNull($f3->mock('GET /verify/dsk4/6'));
$this->assertSame('6', $f3->get('PARAMS.value'));
$this->assertSame('dsk4', $f3->get('PARAMS.answer'));
$this->assertEmpty($f3->get('RESPONSE')[0]);
$this->assertNotNull($f3->mock('GET /verify/pre4/250'));
$this->assertSame('The input value of pre4 question was out of bounds', $f3->get('errors')[0]);
$this->assertNotSame('pre4', $f3->get('PARAMS.answer'));
$f3->set('QUIET',FALSE); // allow test results to be shown later
$f3->clear('ERROR'); // clear any errors
}
}
I'd prefer not to declare the entire route like this, maybe I am entirely wrong and this is not possible? The above code works running vendor/bin/phpunit. Relative examples and tutorials are difficult to find on this.

Short answer
Separate your controller code from bootstrapping and routing code
Reuse the routing configuration in your environments, e.g. website, CLI and testing environment
Use Base->mock() in your tests to mock the previously defined routes
Don't execute Base->run() in the testing environment
Long answer
I'm planning for a long time to write an article about testing F3 routes but due the lack of time I will just give some points here instead:
Create a reusable file which defines the routes (e.g. a routes.php file or a INI file with route defintions)
Load the routes before running test code. This could be easily done with a custom bootstrap file for PHPUnit (--bootstrap <FILE> or use the according directive in PHPUnit's configuration).
Write the PHPUnit tests
Example
The following example is an adaption of my GitHub Gist:
bootstrap-website.php
<?php
$f3 = Base::instance();
require 'bootstrap-shared.php';
// [Custom rules only for the website here]
require 'routes.php';
$f3->run();
bootstrap-test.php
<?php
$f3 = Base::instance();
require 'bootstrap-shared.php';
// [Custom rules only for testing environment here]
$f3->set('QUIET', true);
$f3->set('APP.TEST', true);
require 'routes.php';
routes.php
<?php
/**
* #var $f3 Base
*/
$f3->route('GET /path', function(){ echo 'TEXT'; });
ExampleTest.php
class ExampleTest extends PHPUnit_Framework_TestCase {
public function test() {
// Could also be provided by a custom base TestCase.
$f3 = Base::instance();
$this->assertNull($f3->mock('GET /path'));
$this->assertSame('TEXT', $f3->get('RESPONSE'));
}
}
Some notes:
bootstrap-test.php is the custom bootstrapping file for PHPUnit
bootstrap-website.php is the bootstrapping file for the website
bootstrap-shared.php contains information shared by all environments. The file could include routing information. I separated the routing information in the example: routes.php
ExampleTest.php is a regular PHPUnit test
The $f3->set('QUIET', true); snippet should be added to the custom bootstrap file. It's also a good idea to introduce a variable showing that the application is running in a test mode, for instance $f3->set('APP.TEST', true)
F3 doesn't clean up your variables between tests/mocks. You could store the original state before running tests and then restore the state in PHPUnit's setUp() method
Instead of rendering pages it could also be sufficient to collect only the data which should be available for rendering. In this case use the introduced APP.TEST variable in your view to skip rendering
Notes for later answer updates
ini_set('error_log','./phpunit/error.log')
$f3->set('ONERROR',function(){});

Related

Insert PHP file in functions.php then echo variables from that file in Wordpress

After trying to find a solution to this, I've decided I'm not bright enough. So, please help.
Here the idea: have a file (ip.php) detect the visitors' country/city, call it once in functions.php (or header.php), then echo/print variables from this ip.php file into the other Wordpress theme files (for example footer.php).
This is the ip.php content:
<?php
function getUserIP()
{
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
$_SERVER['HTTP_CLIENT_IP'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
}
$client = #$_SERVER['HTTP_CLIENT_IP'];
$forward = #$_SERVER['HTTP_X_FORWARDED_FOR'];
$remote = $_SERVER['REMOTE_ADDR'];
if(filter_var($client, FILTER_VALIDATE_IP))
{
$ip = $client;
}
elseif(filter_var($forward, FILTER_VALIDATE_IP))
{
$ip = $forward;
}
else
{
$ip = $remote;
}
return $ip;
}
$user_ip = getUserIP();
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;
$cityDbReader = new Reader('/html/nocache-ip/GeoLite2-City.mmdb');
$record = $cityDbReader->city($user_ip);
$country = $record->country->name;
?>
Using the following in the header.php works fine for the header:
require_once( ABSPATH . '/nocache-ip/ip.php' );
echo "\nYou came from $country\n";
The country, city names are displayed. But when trying to use the same code again in the footer.php, it doesn't work (Undefined variable: country).
So this is the issue. How can I make echo/print the visitor's country in other parts of the theme, while calling the ip.php only once?
I've read about having to make all this into a function, then add it to functions.php somehow and then calling the variables with country(); unfortunately this is above my head.
You can use include so that your function is available, example: <?php include 'yourfunctionpage.php';?>

Drupal HOOK_views_pre_render not working for simple change of $views->result

well I haven't been able to find the problem with my little custom module for a couple of days, I'm hoping some clever and kind person might spot my issue ;)
So I am creating a simple module that runs on a certain view type, this view contains images, the images have alt tags, I want to amend these alt tags by using the url alias and sticking this on the end of the alt tag..
I have successfully found the data in the $view array and I have looped through all image instances and done as said above, I know it has worked because I have printed them out onto the page.
However it is not updating the view on the actual page, the alt tags remain the same... Please excuse my ignorance here as I am learning PHP and multidimensional keyed arrays are taking some time to sink in.
So what is the variable called for the alt tag, an object? I think that's wrong but anyway.. This variable which is ..
$view->result[0]->_field_data['nid']['entity']->field_image_front_menu['und'][0]['alt']
... is being changed, everything seems fine inside the function and I have tried
return $view
but to no avail.
Here is my code in entirety
<?php
function image_alt_tag_alter_views_pre_render(&$view) {
if ($view->name == "we_print_for_menu") {
$path = arg(0) . "/" . arg(1);
$alias = drupal_get_path_alias($path);
// loops through index in array to change each alt tag
for ($i = 0; $i < count($view->result); ++$i) {
$view->result[$i]->_field_data['nid']['entity']->field_image_front_menu['und'][0]['alt'] = $view->result[$i]->_field_data['nid']['entity']->field_image_front_menu['und'][0]['alt'] . $alias;
}
return $view;
?>
<?php
function image_alt_tag_alter_views_post_execute(&$view) {
global $user;
if ($view->name == "we_print_for_menu") {
path = arg(0) . "/" . arg(1);
$alias = drupal_get_path_alias($path);
dsm($view);
for ($i = 0; $i < count($view->result); ++$i) {
$amended_alt = $view->result[$i]->_field_data['nid']['entity']->field_image_front_menu['und'][0]['alt'] . " " . $alias;
$view->result[$i]->_field_data['nid']['entity']->field_image_front_menu['und'][0]['alt'] = $amended_alt;
}
}
}

Drupal Page Template based on url alias

I want to create an drupal page Template depending on the url alias.
Her my current situation:
I create a page named test, the url alias is test, too.
The page template, based on this docu - http://drupal.org/node/1089656 is: page--test.tpl.php.
I cleaned the drupal them cache, but there is still the default page template shown for this page.
What could be the error?
page--test.tpl.php doesn't work because Drupal is using the real path of page--node--#.tpl.php. To get Drupal to recognize aliased paths, you have to add the aliased path as part of the theme suggestions like so:
function MYMODULE_preprocess_page(&$vars, $hook) {
// only do this for page-type nodes and only if Path module exists
if (module_exists('path') && isset($vars['node']) && $vars['node']->type == 'page') {
// look up the alias from the url_alias table
$source = 'node/' .$vars['node']->nid;
$alias = db_query("SELECT alias FROM {url_alias} WHERE source = '$source'")->fetchField();
if ($alias != '') {
// build a suggestion for every possibility
$parts = explode('/', $alias);
$suggestion = '';
foreach ($parts as $part) {
if ($suggestion == '') {
// first suggestion gets prefaced with 'page--'
$suggestion .= "page--$part";
} else {
// subsequent suggestions get appended
$suggestion .= "__$part";
}
// add the suggestion to the array
$vars['theme_hook_suggestions'][] = $suggestion;
}
}
}
}
Source: http://groups.drupal.org/node/130944#comment-425189

drupal module, check if node type

As a more specific take on this question:
drupal jQuery 1.4 on specific pages
How do I check, inside a module, whether or not a node is a certain type to be able to do certain things to the node.
Thanks
The context:
I'm trying to adapt this code so that rather than working on 'my_page' it works on a node type.
function MYMODULE_preprocess_page(&$variables, $arg = 'my_page', $delta=0) {
// I needed a one hit wonder. Can be altered to use function arguments
// to increase it's flexibility.
if(arg($delta) == $arg) {
$scripts = drupal_add_js();
$css = drupal_add_css();
// Only do this for pages that have JavaScript on them.
if (!empty($variables['scripts'])) {
$path = drupal_get_path('module', 'admin_menu');
unset($scripts['module'][$path . '/admin_menu.js']);
$variables['scripts'] = drupal_get_js('header', $scripts);
}
// Similar process for CSS but there are 2 Css realted variables.
// $variables['css'] and $variables['styles'] are both used.
if (!empty($variables['css'])) {
$path = drupal_get_path('module', 'admin_menu');
unset($css['all']['module'][$path . '/admin_menu.css']);
unset($css['all']['module'][$path . '/admin_menu.color.css']);
$variables['styles'] = drupal_get_css($css);
}
}
}
Thanks.
Inside of a module, you can do this:
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) != 'edit') {
if (!($node)) {
$node = node_load(arg(1));
}
if ($node->type == 'page') {
// some code here
}
}
That will load a node object given the current node page (if not available). Since I don't know the context of code you are working with, this is kind of a rough example, but you can always see properties of a node by doing node_load(node_id). But, depending on the Drupal API function, it may already be loaded for you.
For example, hook_nodeapi.
http://api.drupal.org/api/function/hook_nodeapi
You could do:
function mymodule_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
switch ($op) {
case 'view':
// some code here
}
}
Try this:-
function MyModule_preprocess_node(&$vars) {
if ($vars['type'] == 'this_type') {
// do some stuff
}
}
Try this:-
$node = node_load(arg(1));
$node =$node->type;
if($node == 'node_type'){
//do something
}

How to override form action in Drupal?

I originally started this question in another thread, but that thread was sorta, kinda answered, and now I primarily want to know how to specify another form action... I tried using the code below, but the form action, when output, remains unchanged, although looking at the print_r($form), it's correctly changed... Why isn't it picking up?
function mytheme_user_profile_form($form) {
global $user;
$uid = $user->uid;
//print '<pre>'; print_r($form); print '</pre>';
$category = $form['_category']['#value'];
switch($category) {
case 'account':
$form['#action'] = '/user/'.$uid.'/edit?destination=user/'.$uid;
break;
case 'education':
$form['#action'] = '/user/'.$uid.'/edit/education?destination=user/'.$uid;
break;
case 'experience':
$form['#action'] = '/user/'.$uid.'/edit/experience?destination=user/'.$uid;
break;
case 'publications':
$form['#action'] = '/user/'.$uid.'/edit/publications?destination=user/'.$uid;
break;
case 'conflicts':
$form['#action'] = '/user/'.$uid.'/edit/conflicts?destination=user/'.$uid;
break;
}
//print '<pre>'; print_r($form); print '</pre>';
//print $form['#action'];
$output .= drupal_render($form);
return $output;
hook_form_alter() is likely the way to go.
Here are some hopefully helpful links:
Form Theming: How do I set $form['action']?
Modifying Forms in Drupal 5 and 6
hook_form_alter
EDIT: reply to comment #1 below:
How to implement hook_form_alter():
You must create a module (you cannot use template.php). It's easier than it looks.
For a module named "formstuff", you would create formstuff.info and formstuff.module and put them in either sites/all/modules or sites/yoursitename/modules. Set up the .info and .module files per the instructions, then just create the following function in your .module file:
function formstuff_form_alter(&$form, $form_state, $form_id) {
// do stuff
}
This function is a hook because it is named properly (i.e. replace the word 'hook' with the name of your module), and it matches hook_form_alter's function signature (i.e. it takes the same parameters).
Then just enable your module in your site's admin and the hook should do it's magic.
Note that hook_form_alter takes a reference to the form; this allows you to modify it in-place.
You need to put the form_alter function in a module and then use either if or switch to check the form ID. If the form ID is the one you want to alter then give the form an action property
$form['someID'] = array(
'#action' => 'path/you/want',
);

Resources