Move all tests under api/src into api/tests

This commit is contained in:
nathangray
2017-10-23 10:14:14 +02:00
parent 3c096b3466
commit b8f8a014fa
14 changed files with 14 additions and 16 deletions

View File

@ -1,443 +0,0 @@
<?php
/**
* EGroupware - Setup - db-schema-processor - unit tests
*
* Written by Ralf Becker <RalfBecker@outdoor-training.de>
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage db
* @version $Id$
*/
namespace Egroupware\Api;
use EGroupware\Api\Db;
use EGroupware\Api\Db\Schema;
// test base providing Egw environment
require_once realpath(__DIR__.'/../../../tests/LoggedInTest.php');
// For security reasons we exit by default if called via the webserver
if (php_sapi_name() !== 'cli')
{
die ('Access denied !!!');
}
/**
* Testing the Schema processor
*
*/
class SchemaTest extends LoggedInTest {
protected static $adodb;
protected static $db;
protected static $schema_proc;
protected static $test_app = 'login';
// define a test-table to create
protected static $test_tables = array(
'schema_proc_test' => array(
'fd' => array(
'test_auto' => array('type' => 'auto'),
'test_int4' => array('type' => 'int','precision' => '4'),
'test_varchar' => array('type' => 'varchar','precision' => '128'),
'test_char' => array('type' => 'char','precision' => '10'),
'test_timestamp' => array('type' => 'timestamp','default'=>'current_timestamp'),
'test_text' => array('type' => 'text'),
'test_blob' => array('type' => 'blob'),
),
'pk' => array('test_auto'),
'fk' => array(),
'ix' => array(array('test_char','test_varchar'),'test_varchar',array('test_text','options'=>array('mysql'=>'FULLTEXT','sapdb'=>false,'maxdb'=>false,'pgsql'=>false,'mssql'=>false))),
'uc' => array('test_char')
),
);
/**
* Get a database connection
*/
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
// now we should have a valid db-connection
self::$adodb = $GLOBALS['egw']->ADOdb;
self::$db = $GLOBALS['egw']->db;
// Show lots of debug
//self::$db->query_log = 'php://stdout';
Db::set_table_definitions(self::$test_app, 'schema_proc_test', self::$test_tables['schema_proc_test']);
// dropping test-tables, if they are there from a previous failed run
self::$schema_proc = new Schema(self::$db->Type);
foreach(self::$adodb->MetaTables() as $table)
{
$table = strtolower($table);
if (strstr($table,'schema_proc'))
{
$aSql = self::$schema_proc->dict->DropTableSQL($table);
self::$schema_proc->ExecuteSqlArray($aSql,1,"DropTableSQL('%1') = %2",$table,$aSql);
}
}
}
/**
* Try to create test tables, check to see if it worked
*/
public function testCreateTable()
{
foreach(self::$test_tables as $name => $definition)
{
self::$schema_proc->CreateTable($name,$definition);
$columns = self::$adodb->MetaColumns($name);
$this->assertNotFalse($columns);
$this->assertGreaterThan(0, count($columns));
// check if all columns are there
foreach($definition['fd'] as $column => $data)
{
$this->check_column($column,$columns);
}
// check if all indexes are there
$indexes = self::$adodb->MetaIndexes($name,true);
$this->assertNotFalse($indexes);
if ($indexes !== False)
{
foreach(array('ix','uc') as $kind)
{
foreach($definition[$kind] as $key => $idx)
{
$this->check_index($idx,$kind=='uc',$indexes);
}
}
if (count($definition['pk'])) $this->check_index($definition['pk'],True,$indexes);
}
}
}
/**
* Try to insert some content into the created table(s)
*
* @depends testCreateTable
*/
public function testInsertContent()
{
self::$adodb->Execute("INSERT INTO schema_proc_test (test_int4,test_varchar,test_char) VALUES (1,'Hallo Ralf','0123456789')");
self::$db->insert('schema_proc_test',array(
'test_int4' => 2,
'test_varchar' => 'Hallo wer noch?',
'test_char' => '9876543210',
'test_text' => 'This is a test-value for the text-column, insert-value',
'test_blob' => 'This is a test-value for the blob-column, insert-value',
), False, __LINE__, __FILE__, self::$test_app);
$this->check_content(
self::$adodb->GetAll("SELECT * FROM schema_proc_test"), array(
array(
'test_auto' => 1, 'test_int4' => 1, 'test_varchar' => 'Hallo Ralf','test_char' => '0123456789',
),
array(
'test_auto' => 2, 'test_int4' => 2, 'test_varchar' => 'Hallo wer noch?','test_char' => '9876543210',
'test_text' => 'This is a test-value for the text-column, insert-value',
'test_blob' => 'This is a test-value for the blob-column, insert-value',
),
)
);
}
/**
* Try to update existing content
*
* @depends testInsertContent
*/
public function testUpdateContent()
{
// updating blob's and other columns
self::$db->update('schema_proc_test', array(
'test_int4' => 99,
'test_char' => 'abcdefghij',
'test_text' => 'This is a test-value for the text-column',
'test_blob' => 'This is a test-value for the blob-column',
), array('test_auto'=>1), __LINE__, __FILE__, self::$test_app);
// updating only the blob's
self::$db->update('schema_proc_test',array(
'test_text' => 'This is a test-value for the text-column, 2.row',
'test_blob' => 'This is a test-value for the blob-column, 2.row',
), array('test_auto'=>2), __LINE__, __FILE__, self::$test_app);
// db::update uses UpdateBlob only for MaxDB at the moment, it works for MySql too, but fails for postgres with text / CLOB's
// $adodb->UpdateBlob('schema_proc_test','test_text','This is a test-value for the text-column, 2.row','test_auto=2','CLOB');
// $adodb->UpdateBlob('schema_proc_test','test_blob','This is a test-value for the blob-column, 2.row','test_auto=2','BLOB');
$this->check_content(self::$adodb->GetAll("SELECT * FROM schema_proc_test"),array(
array(
'test_auto' => 1, 'test_int4' => 99, 'test_varchar' => 'Hallo Ralf','test_char' => 'abcdefghij',
'test_text' => 'This is a test-value for the text-column',
'test_blob'=>'This is a test-value for the blob-column',
),
array(
'test_auto' => 2, 'test_int4' => 2, 'test_varchar' => 'Hallo wer noch?','test_char' => '9876543210',
'test_text' => 'This is a test-value for the text-column, 2.row',
'test_blob'=>'This is a test-value for the blob-column, 2.row',
),
));
}
/**
* Drop the test_blob column
*
* @depends testCreateTable
*/
public function testDropColumn()
{
$new_table_def = $test_tables['schema_proc_test'];
unset($new_table_def['fd']['test_blob']);
self::$schema_proc->DropColumn('schema_proc_test',$new_table_def,'test_blob');
$this->check_column('test_blob',self::$adodb->MetaColumns('schema_proc_test'),False);
}
/**
* Alter the test_char column
*
* @depends testCreateTable
*/
public function testAlterColumn()
{
self::$schema_proc->AlterColumn('schema_proc_test','test_char',array('type' => 'varchar','precision' => 32));
$this->check_column_type('test_char','varchar',32,self::$adodb->MetaColumns('schema_proc_test'));
}
/**
* Add a column
*
* @depends testCreateTable
*/
public function testAddColumn()
{
self::$schema_proc->AddColumn('schema_proc_test','test_bool',array('type' => 'bool'));
$this->check_column('test_bool',self::$adodb->MetaColumns('schema_proc_test'));
}
/**
* Rename a column
*
* @depends testCreateTable
*/
public function testRenameColumn()
{
self::$schema_proc->RenameColumn('schema_proc_test','test_timestamp','test_time');
$this->check_column('test_timestamp',self::$adodb->MetaColumns('schema_proc_test'),false);
$this->check_column('test_time',self::$adodb->MetaColumns('schema_proc_test'));
}
/**
* Rename a table
*
* @depends testCreateTable
*/
public function testRenameTable()
{
self::$schema_proc->RenameTable('schema_proc_test','schema_proc_renamed');
$tables = self::$adodb->MetaTables();
$this->check_table('schema_proc_test',$tables,False);
$this->check_table('schema_proc_renamed',$tables);
}
/**
* @depends testRenameTable
*/
public function testRenameColumnWithIndex()
{
self::$schema_proc->RenameColumn('schema_proc_renamed','test_varchar','test_varchar_renamed');
$columns = self::$adodb->MetaColumns('schema_proc_renamed');
$this->check_column('test_varchar',$columns,False);
$this->check_column('test_varchar_renamed',$columns);
$indexes = self::$adodb->MetaIndexes('schema_proc_renamed');
if ($indexes !== False)
{
$this->check_index('test_varchar',False,$indexes,False);
$this->check_index('test_varchar_renamed',False,$indexes);
}
else
{
$this->markTestIncomplete();
}
}
/**
* @depends testRenameColumnWithIndex
*/
public function testDropIndex()
{
self::$schema_proc->DropIndex('schema_proc_renamed',array('test_char','test_varchar_renamed'));
$indexes = self::$adodb->MetaIndexes('schema_proc_renamed');
if ($indexes !== False)
{
$this->check_index(array('test_char','test_varchar_renamed'),False,$indexes,False);
}
else
{
$this->markTestIncomplete();
}
}
/**
* @depends testDropIndex
*/
public function testInsertMoreContent()
{
self::$db->query("INSERT INTO schema_proc_renamed (test_int4,test_varchar_renamed,test_char) VALUES (10,'Hallo Hallo Hallo ...','12345678901234567890123456789012')");
$this->check_content(self::$adodb->GetAll("SELECT * FROM schema_proc_renamed"),array(
array('test_auto' => 1, 'test_int4' => 99, 'test_varchar_renamed' => 'Hallo Ralf','test_char' => 'abcdefghij'),
array('test_auto' => 2, 'test_int4' => 2, 'test_varchar_renamed' => 'Hallo wer noch?','test_char' => '9876543210'),
array('test_auto' => 3, 'test_int4' => 10, 'test_varchar_renamed' => 'Hallo Hallo Hallo ...','test_char' => '12345678901234567890123456789012'),
));
}
/**
* @depends testInsertMoreContent
*/
public function testDropTable()
{
foreach(self::$adodb->MetaTables() as $table)
{
$table = strtolower($table);
if (strstr($table,'schema_proc'))
{
$aSql = self::$schema_proc->dict->DropTableSQL($table);
self::$schema_proc->ExecuteSqlArray($aSql,1,"DropTableSQL('%1') = %2",$table,$aSql);
}
}
}
/**
* Checks if table $table exists or not
*
* @param string $table table-name
* @param array $tables array of table-names from call to MetaTables()
* @param boolean $existence =true should we check for existence or non-existence, default existence
*/
protected function check_table($table,$tables,$existence=True)
{
$exist = in_array($table,$tables) || in_array(strtoupper($table),$tables);
$this->assertEquals($existence, $exist, "Checking for $table");
}
/**
* Checks if $column exists or not
*
* @param string $column column-name
* @param array $columns array of adodb field objects from MetaColumns($table)
* @param boolean $existence =true should we check for existence or non-existence, default existence
*/
protected function check_column($column,$columns,$existence=True)
{
$exist = isset($columns[$column]) || isset($columns[strtoupper($column)]);
$this->assertEquals($existence, $exist, "Checking for $column");
}
/**
* Checks the type of a column
*
* @param string $column column-name
* @param string $type column-type as the DB uses it, no eGW type !!!
* @param int $precision precision
* @param array $columns array of adodb field objects from MetaColumns($table)
*/
protected function check_column_type($column,$type,$precision,$columns)
{
static $alternate_types = array(
'varchar' => array('C'),
'int' => array('I'),
);
$data = isset($columns[$column]) ? $columns[$column] : $columns[strtoupper($column)];
$this->assertInstanceOf('ADOFieldObject', $data, "Column '$column' does not exist.");
$data->type = strtolower($data->type);
$this->assertFalse($data->type != $type && !in_array($data->type,$alternate_types[$type]),
"Column '$column' is NOT of type '$type', but '$data->type'"
);
if ($precision)
{
$this->assertEquals($precision, $data->max_length,
"Precision of column '$column' is NOT $precision, but $data->precision"
);
}
}
/**
* Checks if $idx exists or not
*
* @param array $columns array of strings with column-names of that index
* @param boolean $unique unique index or not
* @param array $indexes array of index-describtions from call to MetaIndexes($table)
* @param boolean $_existence =true should we check for existence or none-existence, default existence
*/
protected function check_index($columns,$unique,$indexes,$_existence=True)
{
if (!is_array($columns)) $columns = array($columns);
$existence = $_existence && $columns['options'][$GLOBALS['db']->Type] !== False;
unset($columns['options']);
$exist = False;
$idx_data = array();
foreach($indexes as $idx_data)
{
if (implode(':',$columns) == strtolower(implode(':',$idx_data['columns'])))
{
$exist = true;
break;
}
}
$this->assertEquals($existence, $exist,
"Index (".implode(', ',$columns).") is ".($existence ? 'missing' : 'still there')
);
if ($existence)
{
$this->assertEquals($unique, !!$idx_data['unique'],
"Index (".implode(', ',$columns).") is ".($unique ? 'NOT ' : '')."unique"
);
}
}
/**
* Checks the content written to the table
*
* @param array $is content read from the database via GetAll()
* @param array $should content against which we test
*/
protected function check_content($is,$should)
{
foreach($should as $key => $val)
{
if (!isset($is[$key]) && isset($is[strtoupper($key)]))
{
$key = strtoupper($key);
}
if (!is_array($val))
{
$this->assertEquals($val, $is[$key], 'Content read back from table is not as expected');
}
if (is_array($val) && !$this->check_content($is[$key],$val,True) || !is_array($val) && $is[$key] != $val)
{
$this->fail('Content read back from table is not as expected');
return False;
}
}
return True;
}
}

View File

@ -1,278 +0,0 @@
<?php
/**
* Tests for Date widget
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @subpackage etemplate
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate\Widget;
require_once realpath(__DIR__.'/../../tests/WidgetBaseTest.php');
use EGroupware\Api\Etemplate;
use EGroupware\Api\DateTime;
class DateTest extends \EGroupware\Api\Etemplate\WidgetBaseTest
{
const TEST_TEMPLATE = 'api.date_test';
protected static $usertime;
protected static $server_tz;
/**
* Work in server time, so tests match expectations
*/
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
static::$usertime = DateTime::$user_timezone;
static::$server_tz = date_default_timezone_get();
// Set time to UTC time for consistency
DateTime::setUserPrefs('UTC');
date_default_timezone_set('UTC');
DateTime::$server_timezone = new \DateTimeZone('UTC');
}
public static function tearDownAfterClass()
{
// Reset
DateTime::setUserPrefs(static::$usertime->getName());
date_default_timezone_set(static::$server_tz);
unset($GLOBALS['egw']);
parent::tearDownAfterClass();
}
/**
* Test the widget's basic functionality - we put data in, it comes back
* unchanged.
*
* @dataProvider basicProvider
*/
public function testBasic($content, $expected)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Send it around
$result = $this->mockedRoundTrip($etemplate, $content);
// Test it
$this->validateTest($result, $expected ? $expected : $content);
}
public function basicProvider()
{
// Reset server timezone here, as it tends to go back to php.ini value
DateTime::$server_timezone = new \DateTimeZone('UTC');
$now = new DateTime(time(),DateTime::$server_timezone);
$now->setTime(22, 13, 20); // Just because 80000 seconds after epoch is 22:13:20
$today = clone $now;
$today->setTime(0,0);
$time = new DateTime('1970-01-01',new \DateTimeZone('UTC'));
$time->setTime(22, 13, 20); // Just because 80000 seconds after epoch is 22:13:20
$data = array(
array(
array('date' => $today->getTimestamp(), 'date_time' => $today->getTimestamp()),
false // Expect what went in
),
array(
// Timeonly is epoch
array('date' => $now->getTimestamp(), 'date_time' => $now->getTimestamp(), 'date_timeonly' => $now->getTimestamp()),
array('date' => $now->getTimestamp(), 'date_time' => $now->getTimestamp(), 'date_timeonly' => $time->getTimestamp())
)
);
return $data;
}
/**
* Check some basic validation stuff
*
* @param type $content
* @param type $validation_errors
*
* @dataProvider validationProvider
*/
public function testValidation($content)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
$this->validateRoundTrip($etemplate, Array(), $content, Array(), array_flip(array_keys($content)));
}
public function validationProvider()
{
// All these are invalid, and should not give a value back
return array(
array(array('date' => 'Invalid')),
array(array('date_time' => 'Invalid')),
array(array('date_timeonly' => 'Invalid')),
);
}
/**
* Test for minimum attribute
*
* @param String|numeric $value
* @param float $min Minimum allowed value
* @param boolean $error
*
* @dataProvider minProvider
*/
public function testMin($value, $min, $error)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - doesn't really matter, we're changing it
$content = array();
// Need to exec the template so the widget is there to modify
$result = $this->mockedExec($etemplate, $content, array(), array(), array());
// Set limits
$etemplate->getElementById('date')->attrs['min'] = $min;
$etemplate->getElementById('date')->attrs['max'] = null;
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
// 'Edit' the data client side
$data['data']['content'] = array('date' => $value);
// Let it validate
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
$this->validateTest($content,
$error ? array() : array('date' => is_string($value) ? strtotime($value) : $value),
$error ? array('date' => $error) : array()
);
}
public function minProvider()
{
return Array(
// User value, Min, Error
array('', 0, FALSE),
array('2018-01-01', '2017-12-31', FALSE),
array('2018-01-01', '2018-01-01', FALSE),
array('2017-12-01', '2017-12-31', TRUE),
// Relative days
array('two days from now', 2, FALSE),
array(time(), 2, TRUE),
array(time(), -1, FALSE),
array('yesterday', 'today', TRUE),
// Different periods
array('yesterday', '+2d', TRUE),
array('yesterday', '-2d', FALSE),
array('yesterday', '-1m', FALSE),
array('yesterday', '-1y +1m', FALSE),
array(time(), '+1d', TRUE),
array(time(), '+1m', TRUE),
array(time(), '+1y -1m', TRUE),
);
}
/**
* Test for maximum attribute
*
* @param String|numeric $value
* @param float $max Maximum allowed value
* @param boolean $error
*
* @dataProvider maxProvider
*/
public function testMax($value, $max, $error)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - doesn't really matter, we're changing it
$content = array();
// Need to exec the template so the widget is there to modify
$result = $this->mockedExec($etemplate, $content, array(), array(), array());
// Set limits
$etemplate->getElementById('date')->attrs['min'] = null;
$etemplate->getElementById('date')->attrs['max'] = $max;
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
// 'Edit' the data client side
$data['data']['content'] = array('date' => $value);
// Let it validate
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
return $this->validateTest($content,
$error ? array() : array('date' => is_string($value) ? strtotime($value) : $value),
$error ? array('date' => $error) : array()
);
}
public function maxProvider()
{
return Array(
// User value, Max, Error
array('', 0, FALSE),
array('2017-12-31', '2018-01-01', FALSE),
array('2018-01-01', '2018-01-01', FALSE),
array('2017-12-31', '2017-12-01', TRUE),
// Relative days
array('two days from now', 2, FALSE),
array(time(), 2, FALSE),
array(time(), -1, TRUE),
array('yesterday', 0, FALSE),
// Different periods
array('yesterday', '+2d', FALSE),
array('yesterday', '-2d', TRUE),
array('yesterday', '+1m', FALSE),
array('yesterday', '+1y -1m', FALSE),
array(time(), '-1d', TRUE),
array(time(), '-1m', TRUE),
array(time(), '-1y -1m', TRUE),
);
}
}

View File

@ -1,197 +0,0 @@
<?php
/**
* Test for float textboxes
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate\Widget;
require_once realpath(__DIR__.'/../../tests/WidgetBaseTest.php');
use EGroupware\Api\Etemplate;
class FloatTest extends \EGroupware\Api\Etemplate\WidgetBaseTest {
const TEST_TEMPLATE = 'api.float_test';
/**
* Test for validation - floats
*
*
* @dataProvider floatProvider
*/
public function testFloat($value, $expected, $error)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - doesn't really matter, we're changing it
$content = array(
'widget' => 'Hello'
);
$this->validateRoundTrip($etemplate, $content, array('widget' => $value),
$error ? array() : array('widget' => $expected),
$error ? array('widget' => $error) : array()
);
}
/**
* Data provider for float tests
*/
public function floatProvider()
{
return array(
// User value, Expected Error
array('', '', false),
array(1, 1, false),
array(0, 0, false),
array(-1, -1, false),
array(1.5, 1.5, false),
array('1,5', 1.5, false), // Comma as separator is handled
array('one', '', true)
);
}
/**
* Test for float minimum attribute
*
* @param String|numeric $value
* @param float $min Minimum allowed value
* @param boolean $error
*
* @dataProvider minProvider
*/
public function testMin($value, $min, $error)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - doesn't really matter, we're changing it
$content = array(
'widget' => 'Hello',
'widget_readonly' => 'World'
);
$result = $this->mockedExec($etemplate, $content, array(), array(), array());
$etemplate->getElementById('widget')->attrs['min'] = $min;
$etemplate->getElementById('widget')->attrs['max'] = null;
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
// 'Edit' the data client side
$data['data']['content'] = array('widget' => $value);
// Let it validate
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
return $this->validateTest($content,
$error ? array() : array('widget' => $value),
$error ? array('widget' => $error) : array()
);
}
public function minProvider()
{
return Array(
// User value, Min, Error
array('', 0, FALSE),
array(1.0, 0, FALSE),
array(0.0, 0, FALSE),
array(-1.0, 0, TRUE),
array(1.5, 0, FALSE),
array(1, 10, TRUE),
array(10, 10, FALSE),
array(1.5, 1.5, FALSE),
array(0.5, 1.5, TRUE),
);
}
/**
* Test for float maximum attribute
*
* @param String|numeric $value
* @param float $max Maximum allowed value
* @param boolean $error
*
* @dataProvider maxProvider
*/
public function testMax($value, $max, $error)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - doesn't really matter, we're changing it
$content = array(
'widget' => 'Hello',
'widget_readonly' => 'World'
);
$result = $this->mockedExec($etemplate, $content, array(), array(), array());
$etemplate->getElementById('widget')->attrs['min'] = null;
$etemplate->getElementById('widget')->attrs['max'] = $max;
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
// 'Edit' the data client side
$data['data']['content'] = array('widget' => $value);
// Let it validate
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
return $this->validateTest($content,
$error ? array() : array('widget' => $value),
$error ? array('widget' => $error) : array()
);
}
public function maxProvider()
{
return Array(
// User value, Max, Error
array('', 0, FALSE),
array(1.0, 0, TRUE),
array(0, 0, FALSE),
array(-1.0, 0, FALSE),
array(1.5, 2, FALSE),
array(1, 10, FALSE),
array(10, 10, FALSE),
array(2.5, 2, TRUE),
array(1.5, 2.5, FALSE),
array(3, 2.5, TRUE),
);
}
}

View File

@ -1,194 +0,0 @@
<?php
/**
* Test for integer textboxes
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate\Widget;
require_once realpath(__DIR__.'/../../tests/WidgetBaseTest.php');
use EGroupware\Api\Etemplate;
class IntegerTest extends \EGroupware\Api\Etemplate\WidgetBaseTest {
const TEST_TEMPLATE = 'api.integer_test';
/**
* Test for validation - integers
*
*
* @dataProvider integerProvider
*/
public function testInteger($value, $error)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - doesn't really matter, we're changing it
$content = array(
'widget' => 'Hello',
'widget_readonly' => 'World'
);
$this->validateRoundTrip($etemplate, $content, array('widget' => $value),
$error ? array() : array('widget' => $value),
$error ? array('widget' => $error) : array()
);
}
/**
* Data provider for integer tests
*/
public function integerProvider()
{
return array(
// User value, Error
array('', false),
array(1, false),
array(0, false),
array(-1, false),
array(1.5, true),
array('one', true)
);
}
/**
* Test for integer minimum attribute
*
* @param String|numeric $value
* @param int $min Minimum allowed value
* @param boolean $error
*
* @dataProvider minProvider
*/
public function testMin($value, $min, $error)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - doesn't really matter, we're changing it
$content = array(
'widget' => 'Hello',
'widget_readonly' => 'World'
);
$result = $this->mockedExec($etemplate, $content, array(), array(), array());
// Set limits
$etemplate->getElementById('widget')->attrs['min'] = $min;
$etemplate->getElementById('widget')->attrs['max'] = null;
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
// 'Edit' the data client side
$data['data']['content'] = array('widget' => $value);
// Let it validate
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
return $this->validateTest($content,
$error ? array() : array('widget' => $value),
$error ? array('widget' => $error) : array()
);
}
public function minProvider()
{
return Array(
// User value, Min, Error
array('', 0, FALSE),
array(1, 0, FALSE),
array(0, 0, FALSE),
array(-1, 0, TRUE),
array(1.5, 0, TRUE), // Errors because it's not an int
array(1, 10, TRUE),
array(10, 10, FALSE),
);
}
/**
* Test for integer maximum attribute
*
* @param String|numeric $value
* @param int $max Maximum allowed value
* @param boolean $error
*
* @dataProvider maxProvider
*/
public function testMax($value, $max, $error)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - doesn't really matter, we're changing it
$content = array(
'widget' => 'Hello',
'widget_readonly' => 'World'
);
$result = $this->mockedExec($etemplate, $content, array(), array(), array());
// Set limits
$etemplate->getElementById('widget')->attrs['min'] = null;
$etemplate->getElementById('widget')->attrs['max'] = $max;
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
// 'Edit' the data client side
$data['data']['content'] = array('widget' => $value);
// Let it validate
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
return $this->validateTest($content,
$error ? array() : array('widget' => $value),
$error ? array('widget' => $error) : array()
);
}
public function maxProvider()
{
return Array(
// User value, Max, Error
array('', 0, FALSE),
array(1, 0, TRUE),
array(0, 0, FALSE),
array(-1, 0, FALSE),
array(1.5, 2, TRUE), // Errors because it's not an int
array(1, 10, FALSE),
array(10, 10, FALSE),
);
}
}

View File

@ -1,246 +0,0 @@
<?php
/**
* App
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate\Widget;
require_once realpath(__DIR__.'/../../tests/WidgetBaseTest.php');
use EGroupware\Api\Etemplate;
class SelectTest extends \EGroupware\Api\Etemplate\WidgetBaseTest
{
const TEST_TEMPLATE = 'api.select_test';
/**
* Test options, used throughout.
* Note that those are the Greek uppercase, not Latin.
* That's not A,B,E,Z,H,I..., and they don't match.
*/
const VALUES = array(
'Α'=> 'α Alpha',
'Β'=> 'β Beta',
'Γ'=> 'γ Gamma',
'Δ'=> 'δ Delta',
'Ε'=> 'ε Epsilon',
'Ζ'=> 'ζ Zeta',
'Η'=> 'η Eta',
'Θ'=> 'θ Theta',
'Ι'=> 'ι Iota',
'Κ'=> 'κ Kappa',
'Λ'=> 'λ Lambda',
'Μ'=> 'μ Mu',
'Ν'=> 'ν Nu',
'Ξ'=> 'ξ Xi',
'Ο'=> 'ο Omicron',
'Π'=> 'π Pi',
'Ρ'=> 'ρ Rho',
'Σ'=> 'σ Sigma',
'Τ'=> 'τ Tau',
'Υ'=> 'υ Upsilon',
'Φ'=> 'φ Phi',
'Χ'=> 'χ Chi',
'Ψ'=> 'ψ Psi',
'Ω'=> 'ω Omega',
);
/**
* Test the widget's basic functionality - we put data in, it comes back
* unchanged.
*
* @dataProvider validProvider
*/
public function testBasic($content)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Send it around
$result = $this->mockedRoundTrip($etemplate, array('widget' => $content), array('widget' => self::VALUES));
// Test it
$this->validateTest($result, array('widget' => $content));
}
/**
* These are all valid
*
*/
public function validProvider()
{
$values = array(array(''));
foreach(self::VALUES as $key => $label)
{
$values[] = array($key);
}
return $values;
}
/**
* Check validation with failing values
*
* @param string $content
*
* @dataProvider validationProvider
*/
public function testValidation($content)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Send it around
$result = $this->mockedRoundTrip($etemplate, array('widget' => $content), array('widget' => self::VALUES));
// Test it
$this->validateTest($result, array(), array('widget'=> true));
}
/**
* These are all invalid
*/
public function validationProvider()
{
// All these are invalid, and should not give a value back
return array(
array('0'),
array('Alpha'),
array('A'), // This is ASCII A, not Alpha
array('Α,Β'),
);
}
/**
* Test to make sure a selectbox that accepts multiple actually does
*
* @param Array $content
* @param Array $expected
*
* @dataProvider multipleProvider
*/
public function testMultiple($content, $expected)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Send it around
$result = $this->mockedRoundTrip($etemplate, $content, array(
'widget' => self::VALUES,
'widget_multiple' => self::VALUES
));
// Test it
$this->validateTest($result, $expected);
}
public function multipleProvider()
{
return array(
// Test
array(
array('widget' => '', 'widget_multiple' => ''), // Content
array('widget' => '', 'widget_multiple' => ''), // Expected
),
array(
array('widget' => 'Α', 'widget_multiple' => 'Α'),
array('widget' => 'Α', 'widget_multiple' => 'Α'),
),
// Check for CSV - should fail
array(
array('widget' => 'Α,Β', 'widget_multiple' => 'Α,Β'),
array('widget' => '', 'widget_multiple' => ''),
),
// Check for array - should work
array(
array('widget' => array('Α','Β'), 'widget_multiple' => array('Α','Β')),
array('widget' => 'Α', 'widget_multiple' => array('Α','Β')),
),
);
}
/**
* Test that the widget does not return a value if readonly
*/
public function testReadonly()
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Exec
$content = array(
'widget' => 'Α',
'widget_readonly' => 'Α',
'widget_multiple' => 'Α'
);
// Set non-readonly widgets to read-only via parameter to Etemplate::exec()
$result = $this->mockedRoundTrip($etemplate, $content,
array('widget' => self::VALUES, 'widget_readonly' => self::VALUES, 'widget_multiple' => self::VALUES),
array('widget' => true, 'widget_multiple' => true)
);
// Check - nothing comes back
$this->assertEquals(array(), $result);
}
/**
* Test that an edited read-only widget does not return a value, even if the
* client side gives one, which should be an unusual occurrence.
*/
public function testEditedReadonly()
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Exec
$content = array(
'widget' => 'Α',
'widget_readonly' => 'Α',
'widget_multiple' => 'Α'
);
$result = $this->mockedExec($etemplate, $content,
array('widget' => self::VALUES, 'widget_readonly' => self::VALUES, 'widget_multiple' => self::VALUES),
array('widget' => true, 'widget_multiple' => true)
);
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
// 'Edit' the data client side
$data['data']['content'] = array(
'widget' => 'Ω',
'widget_readonly' => 'Ω',
'widget_multiple' => 'Ω'
);
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
// Nothing comes back, even though edited since it's readonly
$this->assertEquals(array(), $content);
}
}

View File

@ -1,75 +0,0 @@
<?php
/**
* Test for templates
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @subpackage etemplate
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate\Widget;
require_once realpath(__DIR__.'/../../tests/WidgetBaseTest.php');
/**
* Description of TemplateTest
*
* @author nathan
*/
class TemplateTest extends \EGroupware\Api\Etemplate\WidgetBaseTest {
/**
* Test instanciation of template from a file
*/
public function testSimpleInstance()
{
static $name = 'api.prompt';
$template = Template::instance($name);
$this->assertInstanceOf('EGroupware\Api\Etemplate\Widget\Template', $template);
}
/**
* Test instanciating nested template
*
*/
public function testNestedInstanciation()
{
static $template = 'api.nested';
$template = Template::instance($template, 'test');
$this->assertInstanceOf('EGroupware\Api\Etemplate\Widget\Template', $template);
// Check for the sub-child to see if the nested template was loaded
$this->assertInstanceOf('EGroupware\Api\Etemplate\Widget', $template->getElementById('sub_child'));
// Check that it's not just making things up
$this->assertNull($template->getElementById('not_existing'));
}
/**
* Test that we can instanciate a sub-template from a file, once the file
* is in the cache
*
* @depends testNestedInstanciation
*/
public function testSubTemplate()
{
// No file matches this, but it was loaded and cached in the previous test
static $template = 'api.nested.sub_template';
$template = Template::instance($template, 'test');
$this->assertInstanceOf('EGroupware\Api\Etemplate\Widget\Template', $template);
// Check for the sub-child to see if the template was loaded
$this->assertInstanceOf('EGroupware\Api\Etemplate\Widget', $template->getElementById('sub_child'));
// Check that it's not just making things up
$this->assertNull($template->getElementById('not_existing'));
}
}

View File

@ -1,172 +0,0 @@
<?php
/**
* Test for textbox
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @subpackage etemplate
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate\Widget;
require_once realpath(__DIR__.'/../../tests/WidgetBaseTest.php');
use EGroupware\Api\Etemplate;
class TextboxTest extends \EGroupware\Api\Etemplate\WidgetBaseTest
{
const TEST_TEMPLATE = 'api.textbox_test';
/**
* Test the widget's basic functionallity - we put data in, it comes back
* unchanged.
*/
public function testBasic()
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Exec
$content = array(
'widget' => '',
'widget_readonly' => ''
);
$result = $this->mockedRoundTrip($etemplate, $content, array(), array());
// Check
$this->assertEquals(array('widget' => ''), $result);
$content = array(
'widget' => 'Hello',
'widget_readonly' => 'World'
);
$result = $this->mockedRoundTrip($etemplate, $content, array(), array());
// Check only the editable widget gives a value
$this->assertEquals(array('widget' => 'Hello'), $result);
}
/**
* Test that the widget does not return a value if readonly
*/
public function testReadonly()
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Exec
$content = array(
'widget' => 'Hello',
'widget_readonly' => 'World'
);
$result = $this->mockedRoundTrip($etemplate, $content, array(), array('widget' => true));
// Check
$this->assertEquals(array(), $result);
}
/**
* Test that an edited read-only widget does not return a value, even if the
* client side gives one, which should be an unusual occurrence.
*/
public function testEditedReadonly()
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Exec
$content = array(
'widget' => 'Hello',
'widget_readonly' => 'World'
);
$result = $this->mockedExec($etemplate, $content, array(), array('widget' => true), array());
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
// 'Edit' the data client side
$data['data']['content'] = array(
'widget' => 'Goodnight',
'widget_readonly' => 'Moon'
);
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
$this->assertEquals(array(), $content);
}
/**
* Test regex validation
*
* @dataProvider regexProvider
*/
public function testRegex($value, $error)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - doesn't really matter, we're changing it
$content = array(
'widget' => 'Hello',
'widget_readonly' => 'World'
);
$result = $this->mockedExec($etemplate, $content, array(), array(), array());
// Only lowercase
$etemplate->getElementById('widget')->attrs['validator'] = '/^[a-z]*$/';
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
// 'Edit' the data client side
$data['data']['content'] = array('widget' => $value);
// Let it validate
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
return $this->validateTest($content, $error ? array() : array('widget' => $value), $error ? array('widget' => $error) : array());
}
public function regexProvider()
{
return array(
// Value Errors
array('', FALSE),
array('Hello', TRUE),
array('hello', FALSE),
array(1234, TRUE),
array('hi1234',TRUE)
);
}
}

View File

@ -1,125 +0,0 @@
<?php
/**
* Tests for URL-email widget
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @subpackage etemplate
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate\Widget;
require_once realpath(__DIR__.'/../../tests/WidgetBaseTest.php');
use EGroupware\Api\Etemplate;
class UrlEmailTest extends \EGroupware\Api\Etemplate\WidgetBaseTest
{
const TEST_TEMPLATE = 'api.email_test';
/**
* Test the widget's basic functionality - we put data in, it comes back
* unchanged.
*
* @dataProvider validProvider
*/
public function testBasic($content)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Send it around
$result = $this->mockedRoundTrip($etemplate, array('widget' => $content));
// Test it
$this->validateTest($result, array('widget' => $content));
}
/**
* These are all valid, most from https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/
*
*/
public function validProvider()
{
return array(
array(''),
array("Ralf Becker <rb@stylite.de>"),
array("Ralf Becker (Stylite AG) <rb@stylite.de>"),
array("<rb@stylite.de>"),
array("rb@stylite.de"),
array('"Becker), Ralf" <rb@stylite.de>'),
array("'Becker), Ralf' <rb@stylite.de>"),
array('umlaut-in@domäin.com'), // We allow umlauts in domain
array('email@domain.com'), // Valid email
array('firstname.lastname@domain.com'), // Email contains dot in the address field
array('email@subdomain.domain.com'), // Email contains dot with subdomain
array('firstname+lastname@domain.com'), // Plus sign is considered valid character
array('1234567890@domain.com'), // Digits in address are valid
array('email@domain-one.com'), // Dash in domain name is valid
array('_______@domain.com'), // Underscore in the address field is valid
array('email@domain.name'), // .name is valid Top Level Domain name
array('email@domain.co.jp'), // Dot in Top Level Domain name also considered valid (use co.jp as example here)
array('firstname-lastname@domain.com'), // Dash in address field is valid
// Supposedly valid, but we don't
// array('"email"@domain.com'), // Quotes around email is considered valid
// array('email@123.123.123.123'), // Domain is valid IP address
// array('email@[123.123.123.123]'), // Square bracket around IP address is considered valid
);
}
/**
* Check validation with failing strings
*
* @param type $content
* @param type $validation_errors
*
* @dataProvider validationProvider
*/
public function testValidation($content)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
$content = array('widget' => $content);
$this->validateRoundTrip($etemplate, Array(), $content, Array(), array_flip(array_keys($content)));
}
/*
* These are all invalid, most from https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/
*/
public function validationProvider()
{
// All these are invalid, and should not give a value back
return array(
array("Becker, Ralf <rb@stylite.de>"), // Contains comma outside " or ' enclosed block
array("Becker < Ralf <rb@stylite.de>"), // Contains < ----------- " ---------------
array('plainaddress'), // Missing @ sign and domain
array('#@%^%#$@#$@#.com'), // Garbage
array('@domain.com'), // Missing username
array('email.domain.com'), // Missing @
array('email@domain@domain.com'), // Two @ sign
array('me@home.com, me@work.com'), // Two addresses
//array('.email@domain.com'), // Leading dot in address is not allowed
array('email.@domain.com'), // Trailing dot in address is not allowed
//array('email..email@domain.com'), // Multiple dots
//array('あいうえお@domain.com'), // Unicode char as address
array('email@domain.com (Joe Smith)'), // Text followed email is not allowed
array('email@domain'), // Missing top level domain (.com/.net/.org/etc)
array('email@-domain.com'), // Leading dash in front of domain is invalid
//array('email@domain.web'), // .web is not a valid top level domain, but we don't care
array('email@111.222.333.44444'), // Invalid IP format
array('email@domain..com'), // Multiple dot in the domain portion is invalid
);
}
}

View File

@ -1,171 +0,0 @@
<?php
/**
* Test for URL widget
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @subpackage etemplate
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate\Widget;
require_once realpath(__DIR__.'/../../tests/WidgetBaseTest.php');
use EGroupware\Api\Etemplate;
class UrlTest extends \EGroupware\Api\Etemplate\WidgetBaseTest
{
const TEST_TEMPLATE = 'api.url_test';
/**
* Test the widget's basic functionality - we put data in, it comes back
* unchanged.
*
* @dataProvider validProvider
*/
public function testBasic($content)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Send it around
$result = $this->mockedRoundTrip($etemplate, array('widget' => $content));
// Test it
$this->validateTest($result, array('widget' => $content));
}
/**
* Test that the widget does not return a value if readonly
*/
public function testReadonly()
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Exec
$content = array(
'widget' => 'google.com',
);
$result = $this->mockedRoundTrip($etemplate, $content, array(), array('widget' => true));
// Check
$this->assertEquals(array(), $result);
}
/**
* Check validation with failing strings
*
* @param type $content
* @param type $validation_errors
*
* @dataProvider invalidProvider
*/
public function testValidation($content)
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
$content = array('widget' => $content);
$this->validateRoundTrip($etemplate, Array(), $content, Array(), array_flip(array_keys($content)));
}
/**
* URL samples from https://mathiasbynens.be/demo/url-regex
*/
public function validProvider()
{
return array(
array('http://foo.com/blah_blah'),
array('http://foo.com/blah_blah/'),
array('http://foo.com/blah_blah_(wikipedia)'),
array('http://foo.com/blah_blah_(wikipedia)_(again)'),
array('http://www.example.com/wpstyle/?p=364'),
array('https://www.example.com/foo/?bar=baz&inga=42&quux'),
array('http://✪df.ws/123'),
array('http://userid:password@example.com:8080'),
array('http://userid:password@example.com:8080/'),
array('http://userid@example.com'),
array('http://userid@example.com/'),
array('http://userid@example.com:8080'),
array('http://userid@example.com:8080/'),
array('http://userid:password@example.com'),
array('http://userid:password@example.com/'),
array('http://142.42.1.1/'),
array('http://142.42.1.1:8080/'),
array('foo.com'), // We prepend http in this case
// We use filter_var, and it can't handle these
/*
array('http://➡.ws/䨹'),
array('http://⌘.ws'),
array('http://⌘.ws/'),
array('http://foo.com/blah_(wikipedia)#cite-1'),
array('http://foo.com/blah_(wikipedia)_blah#cite-1'),
array('http://foo.com/unicode_(✪)_in_parens'),
array('http://foo.com/(something)?after=parens'),
array('http://☺.damowmow.com/'),
array('http://code.google.com/events/#&product=browser'),
array('http://j.mp'),
array('ftp://foo.bar/baz'),
array('http://foo.bar/?q=Test%20URL-encoded%20stuff'),
array('http://مثال.إختبار '),
array('http://例子.测试'),
array('http://उदाहरण.परीक्षा'),
array("http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com"),
array('http://1337.net'),
array('http://a.b-c.de'),
array('http://223.255.255.254'),
*
*/
);
}
/**
* URL samples from https://mathiasbynens.be/demo/url-regex
*/
public function invalidProvider()
{
return array(
array('http://'),
array('http://.'),
array('http://..'),
array('http://../'),
array('http://?'),
array('http://??'),
array('http://??/'),
array('http://#'),
array('http://##'),
array('http://##/'),
array('http://foo.bar?q=Spaces should be encoded'),
array('//'),
array('//a'),
array('///a'),
array('///'),
array('http:///a'),
// We don't check protocol
//array('rdar://1234'),
//array('h://test'),
//array('ftps://foo.bar/'),
array('http:// shouldfail.com'),
array(':// should fail'),
array('http://foo.bar/foo(bar)baz quux'),
array('http://-error-.invalid/'),
array('http://-a.b.co'),
array('http://a.b-.co'),
array('http://3628126748'),
array('http://.www.foo.bar/'),
array('http://www.foo.bar./'),
array('http://.www.foo.bar./'),
);
}
}

View File

@ -1,238 +0,0 @@
<?php
/**
* Base test file for all widget tests
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate;
use Egroupware\Api\Etemplate;
// test base providing Egw environment, since we need the DB
require_once realpath(__DIR__.'/../../../tests/LoggedInTest.php');
// Store request in the session, file access probably won't work due to permissions
\EGroupware\Api\Etemplate\Request::$request_class = 'EGroupware\Api\Etemplate\Request\Session';
/**
* Base class for all widget tests doing needed setup so the tests can run, and
* providing common utilities to make testing a little easier.
*
* Widget scans the apps for widgets, which needs the app list, pulled from the
* database, so we need to log in.
*/
abstract class WidgetBaseTest extends \EGroupware\Api\LoggedInTest {
/**
* We use our own callback for results of executing, here we store the results
* until returned
*
* @var Array
*/
protected static $mocked_exec_result = array();
protected $ajax_response = null;
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
// Call Etemplate constructor once to make sure etemplate::$request is set,
// otherwise some tests will fail.
// In normal usage, this is not needed as Etemplate constructor does it.
new \EGroupware\Api\Etemplate();
}
public function setUp()
{
// Mock AJAX response
$this->ajax_response = $this->mock_ajax_response();
}
public function tearDown()
{
// Clean up AJAX response
$this->ajax_response->initResponseArray();
$ref = new \ReflectionProperty('\\EGroupware\\Api\\Json\\Response', 'response');
$ref->setAccessible(true);
$ref->setValue(null, null);
}
/**
* Use a known, public callback so we can hook into it, if needed
*/
public static function public_callback($content)
{
static::$mocked_exec_result = $content;
}
/**
* Mocks what is needed to fake a call to exec, and catch its output.
* The resulting array of information, which would normally be sent to the
* client as JSON, is returned for evaluation.
*
* @param Etemplate $etemplate
* @param array $content
* @param array $sel_options
* @param array $readonlys
* @param array $preserv
*/
protected function mockedExec(Etemplate $etemplate, array $content,array $sel_options=null,array $readonlys=null,array $preserv=null)
{
ob_start();
// Exec the template
$etemplate->exec(__CLASS__ . '::public_callback', $content, $sel_options, $readonlys, $preserv, 4);
$result = $this->ajax_response->returnResult();
// Store & clean the request
//$etemplate->destroy_request();
ob_end_clean();
return $result;
}
/**
* Mocks what is needed to fake a call to Etemplate->exec(), and catch its output.
* The resulting array of information, which would normally be sent to the
* client as JSON, is then processed and validated by the server as if it had
* been sent from the client.
*
* @param \EGroupware\Api\Etemplate $etemplate
* @param array $content
* @param array $sel_options
* @param array $readonlys
* @param array $preserv
* @return type
*/
protected function mockedRoundTrip(\EGroupware\Api\Etemplate $etemplate, array $content,array $sel_options=null,array $readonlys=null,array $preserv=null)
{
$result = $this->mockedExec($etemplate, $content, $sel_options, $readonlys, $preserv);
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
return $content;
}
/**
* Mock the ajax response and override it so it doesn't try to send headers,
* which generates errors since PHPUnit has already done that
*/
protected function mock_ajax_response()
{
$response = $this->getMockBuilder('\\EGroupware\\Api\\Json\\Response')
->disableOriginalConstructor()
->setMethods(['get'/*,'generic'*/])
->getMock();
// Replace protected self reference with mock object
$ref = new \ReflectionProperty('\\EGroupware\\Api\\Json\\Response', 'response');
$ref->setAccessible(true);
$ref->setValue(null, $response);
$response
->method('get')
->with(function() {
// Don't send headers, like the real one does
return self::$response;
});
return $response;
}
/**
* Exec the template with the provided content, change the values according to
* $set_values to simulate client side changes by the user, then validate
* against $expected_values. Optionally, it can check that validation errors
* are created by particular widgets.
*
*
* @param \EGroupware\Api\Etemplate $etemplate
* @param array $content
* @param array $set_values
* @param array $expected_values
* @param array $validation_errors Indexed by widget ID, we just check that an error
* was found, not what that error was.
*/
protected function validateRoundTrip(\EGroupware\Api\Etemplate $etemplate, Array $content, Array $set_values, Array $expected_values = null, Array $validation_errors = array())
{
if(is_null($expected_values))
{
$expected_values = $set_values;
}
$result = $this->mockedExec($etemplate, $content, array(), array(), array());
// Check for the load
$data = array();
foreach($result as $command)
{
if($command['type'] == 'et2_load')
{
$data = $command['data'];
break;
}
}
// 'Edit' the data client side
$data['data']['content'] = $set_values;
// Let it validate
Etemplate::ajax_process_content($data['data']['etemplate_exec_id'], $data['data']['content'], false);
$content = static::$mocked_exec_result;
static::$mocked_exec_result = array();
return $this->validateTest($content, $expected_values, $validation_errors);
}
/**
* Test that the content matches expected_values, and any widgets listed in
* $validation_errors actually did raise a validation error.
*
* Note that in most (all?) cases, a validation error will clear the value.
*
* @param array $content
* @param array $expected_values
* @param array $validation_errors
*/
protected function validateTest(Array $content, Array $expected_values, Array $validation_errors = array())
{
// Make validation errors accessible
$ref = new \ReflectionProperty('\\EGroupware\\Api\\Etemplate\\Widget', 'validation_errors');
$ref->setAccessible(true);
$errors = $ref->getValue();
// Test values
foreach($expected_values as $widget_id => $value)
{
$this->assertEquals($value, $content[$widget_id], 'Widget "' . $widget_id . '" did not get expected value');
}
// Check validation errors
foreach($validation_errors as $widget_id => $errored)
{
$this->assertTrue(array_key_exists($widget_id, $errors), "Widget $widget_id did not cause a validation error");
}
$ref->setValue(array());
}
}

View File

@ -1,154 +0,0 @@
<?php
/**
* Test file for the base widget class
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate;
require_once realpath(__DIR__.'/WidgetBaseTest.php');
/**
* Tests for the base widget class
*
* Widget scans the apps for widgets, which needs the app list, pulled from the
* database, so we need to log in.
*/
class WidgetTest extends WidgetBaseTest {
/**
* @var Array Used as a common content for expansion
*/
static $expand = array(
'cont' => array(
'expand_me' => 'expanded',
'expand_2' => 'also_expanded',
0 => array(
'id' => 'row_id'
)
),
'row' => 0,
'c' => 0
);
/**
* Test that setting and retrieving widget attributes is working as expected
*/
public function testAttributes()
{
$xml = '<widget id="test" attribute="set" />';
$widget = new Widget($xml);
$this->assertEquals('test', $widget->id, 'ID was not set');
// Set in XML goes into attributes
$this->assertEquals('set', $widget->attrs['attribute'], 'XML attribute missing');
// get/setElementAttribute do not include xml
$this->assertNull($widget->getElementAttribute('test','attribute'));
// Calling setElementAttribute without a request will give an error when
// it tries to set the header.
ob_start();
// XML does not include get/setElementAttribute
$widget->setElementAttribute('test', 'other_attribute', 'set');
$this->assertEquals('set', $widget->getElementAttribute('test','other_attribute'));
$this->assertNull($widget->attrs['other_attribute']);
ob_end_clean();
}
/**
* Check to make sure form name building is still working.
* Uses expansion array
*
* @dataProvider formNameProvider
*
* @param string $base Base or container / parent ID
* @param string $element Element ID
* @param string $expected Expected result
*/
public function testFormName($base, $element, $expected)
{
$this->assertEquals($expected, Widget::form_name($base, $element, self::$expand));
}
/**
* Provides data for testFormName
*
* Each dataset is base (container or parent ID), input ID, expected result
* when using self::$expand to fill expansion variables.
*/
public static function formNameProvider()
{
return array(
// Base name, element name, expected
['', 'input', 'input'],
['', 'del[$cont[expand_me]]', 'del[expanded]'],
['container', 'input', 'container[input]'],
['grid[sub]', 'input', 'grid[sub][input]'],
['grid', 'sub[input]', 'grid[sub][input]'],
['grid[sub]', 'sub[input]', 'grid[sub][sub][input]'],
['', '@expand_me', 'expanded'],
['@expand_me', 'input', '@expand_me[input]'],
['container', '@expand_me', 'container[expanded]'],
// Rows
['', '$row', '0'],
['$row', '', '$row[]'], // Expansion only on element name
['grid', '$row', 'grid[0]'],
['grid', '$cont[$row]', 'grid[Array]'],
['grid', '$row_cont[id]', 'grid[row_id]'],
// Column
['', '$c', '0'],
['$c', '', '$c[]'], // Expansion only on element name
['grid', '$c', 'grid[0]'],
// Maybe not right, but this is what it gives
['container', '@expand_me[input]', 'container[]'],
['container', 'input[@expand_me]', 'container[input][@expand_me]'],
['container', '@expand_2[@expand_me]', 'container[]']
);
}
/**
* Test that the widget loads the xml and gets all children
*/
public function testSimpleLoad()
{
$test_template = <<<EOT
<widget id="main_parent">
<widget id="first_child"/>
<widget id="second_child">
<widget id="sub_widget"/>
</widget>
<widget id="third_child">
<widget id="@expand_me"/>
</widget>
</widget>
EOT;
$widget = new Widget($test_template);
// getElementsByType does not include the widget itself
$this->assertEquals(5, count($widget->getElementsByType('widget')), 'Missing children');
// Check that it can find the sub
$this->assertNotNull($widget->getElementById('sub_widget'), 'Could not find sub_widget');
// Check that it can find the un-expanded - expansion doesn't happen on load
$this->assertNotNull($widget->getElementById('@expand_me'), 'Could not find @expand_me');
// Check failure
$this->assertNull($widget->getElementById('totally_invalid'), 'Found widget that is not there');
}
}

View File

@ -1,105 +0,0 @@
<?php
/**
* EGroupware Api: Mail account credentials tests
*
* @link http://www.stylite.de
* @package api
* @subpackage mail
* @author Ralf Becker <rb-AT-stylite.de>
* @copyright (c) 2016 by Ralf Becker <rb-AT-stylite.de>
* @author Stylite AG <info@stylite.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
namespace EGroupware\Api\Mail;
require_once realpath(__DIR__.'/../../loader/common.php'); // autoloader & check_load_extension
use EGroupware\Api;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use EGroupware\Api\Mail\Credentials;
/**
* Mail account credentials tests
*
* Only testing en&decryption of mail passwords so far.
* Further tests would need database.
*/
class CredentialsTest extends TestCase
{
/**
* Test new 16.1 AES password encryption with OpenSSL
*/
public function testAes()
{
$mail_password = 'RälfÜber12345678sdddfd';
$account_id = $GLOBALS['egw_info']['user']['account_id'] = 1;
Api\Cache::setSession('phpgwapi', 'password', base64_encode('HMqUHxzMBjjvXppV'));
// test encryption with fixed salt
$pw_encrypted = 'IaaBeu6LiIa+iFBnHYroXA==4lp30Z4B20OdUYnFrxM3lo4b+bsf5wQITdyM1eMP6PM=';
$pw_enc = null;
$this->assertEquals($pw_encrypted, self::callProtectedMethod('encrypt_openssl_aes', __NAMESPACE__.'\\Credentials',
array($mail_password, $account_id, &$pw_enc, null, base64_decode(substr($pw_encrypted, 0, Credentials::SALT_LEN64)))),
'AES encrypt with fixed salt');
// test encryption&descryption with random salt
$pw_encrypted_rs = self::callProtectedMethod('encrypt', __NAMESPACE__.'\\Credentials',
array($mail_password, $account_id, &$pw_enc));
$row = array(
'account_id' => $account_id,
'cred_password' => $pw_encrypted_rs,
'cred_pw_enc' => $pw_enc,
);
$this->assertEquals($mail_password, self::callProtectedMethod('decrypt', __NAMESPACE__.'\\Credentials',
array($row)), 'AES decrypt with random salt');
}
/**
* Test old 14.x tripledes password encryption with mcrypt (if available) and openssl
*/
public function testTripledes()
{
$mail_password = 'RälfÜber12345678sdddfd';
$account_id = $GLOBALS['egw_info']['user']['account_id'] = 1;
Api\Cache::setSession('phpgwapi', 'password', base64_encode('HMqUHxzMBjjvXppV'));
$pw_encrypted = 'Y7QwLIqS6MP61hS8/e4i0wCdtpQP6kZ2';
// if mycrypt is available check encrypting too
if (check_load_extension('mcrypt'))
{
$pw_enc = null;
$this->assertEquals($pw_encrypted, self::callProtectedMethod('encrypt_mcrypt_3des', __NAMESPACE__.'\\Credentials',
array($mail_password, $account_id, &$pw_enc)), 'tripledes encryption with mcrypt');
}
else
{
$pw_enc = Credentials::USER;
}
// otherwise only check decrypting with openssl
$row = array(
'account_id' => $account_id,
'cred_password' => $pw_encrypted,
'cred_pw_enc' => $pw_enc,
);
$this->assertEquals($mail_password, self::callProtectedMethod('decrypt', __NAMESPACE__.'\\Credentials',
array($row)), 'tripledes decryption with openssl');
if (check_load_extension('mcrypt'))
{
$this->assertEquals($mail_password, self::callProtectedMethod('decrypt_mcrypt_3des', __NAMESPACE__.'\\Credentials',
array($row)), 'tripledes decryption with mcrypt');
}
}
protected static function callProtectedMethod($name, $classname, $params)
{
$class = new ReflectionClass($classname);
$method = $class->getMethod($name);
$method->setAccessible(true);
$obj = new $classname();
return $method->invokeArgs($obj, $params);
}
}

View File

@ -1,241 +0,0 @@
<?php
/**
* Tests for XSS
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @copyright (c) 2017 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api;
require_once realpath(__DIR__.'/../common.php'); // autoloader & check_load_extension
//
// We're testing security.php
require_once realpath(__DIR__.'/../security.php');
use PHPUnit\Framework\TestCase as TestCase;
class SecurityTest extends TestCase {
public function setUp()
{
// _check_script_tag uses HtmLawed, which calls GLOBALS['egw']->link()
$GLOBALS['egw'] = $this->getMockBuilder('Egw')
->disableOriginalConstructor()
->setMethods(['link', 'setup'])
->getMock();
}
public function tearDown()
{
unset($GLOBALS['egw_inset_vars']);
// Must remember to clear this, or other tests may break
unset($GLOBALS['egw']);
}
/**
* Test some strings for bad stuff
*
* @param String $pattern String to check
* @param boolean $should_fail If we expect this string to fail
*
* @dataProvider patternProvider
*/
public function testPatterns($pattern, $should_fail)
{
$test = array($pattern);
unset($GLOBALS['egw_unset_vars']);
_check_script_tag($test,'test', false);
$this->assertEquals(isset($GLOBALS['egw_unset_vars']), $should_fail);
}
public function patternProvider()
{
return array(
// pattern, true: should fail, false: should not fail
Array('< script >alert(1)< / script >', true),
Array('<span onMouseOver ="alert(1)">blah</span>', true),
Array('<a href= "JaVascript: alert(1)">Click Me</a>', true),
// from https://www.acunetix.com/websitesecurity/cross-site-scripting/
Array('<body onload=alert("XSS")>', true),
Array('<body background="javascript:alert("XSS")">', true),
Array('<iframe src=”http://evil.com/xss.html”>', true),
Array('<input type="image" src="javascript:alert(\'XSS\');">', true),
Array('<link rel="stylesheet" href="javascript:alert(\'XSS\');">', true),
Array('<table background="javascript:alert(\'XSS\')">', true),
Array('<td background="javascript:alert(\'XSS\')">', true),
Array('<div style="background-image: url(javascript:alert(\'XSS\'))">', true),
Array('<div style="width: expression(alert(\'XSS\'));">', true),
Array('<object type="text/x-scriptlet" data="http://hacker.com/xss.html">', true),
// false positiv tests
Array('If 1 < 2, what does that mean for description, if 2 > 1.', false),
Array('If 1 < 2, what does that mean for a script, if 2 > 1.', false),
Array('<div>Script and Javascript: not evil ;-)', false),
Array('<span>style=background-color', false),
Array('<font face="Script MT Bold" size="4"><span style="font-size:16pt;">Hugo Sonstwas</span></font>', false),
Array('<mathias@stylite.de>', false)
);
}
/**
* Test some URLs with bad stuff
*
* @param String $url
* @param Array $vectors
*
* @dataProvider urlProvider
*/
public function testURLs($url, $vectors = FALSE)
{
// no all xss attack vectors from http://ha.ckers.org/xssAttacks.xml are relevant here! (needs interpretation)
if (!$vectors)
{
$this->markTestSkipped("Could not download or parse $url with attack vectors");
return;
}
foreach($vectors as $line => $pattern)
{
$test = array($pattern);
_check_script_tag($test, 'line '.(1+$line), false);
$this->assertTrue(isset($GLOBALS['egw_unset_vars']), $line . ': ' . $pattern);
}
}
public function urlProvider()
{
$urls = array(
// we currently fail 76 of 666 test, thought they seem not to apply to our use case, as we check request data
'https://gist.github.com/JohannesHoppe/5612274' => file(
'https://gist.githubusercontent.com/JohannesHoppe/5612274/raw/60016bccbfe894dcd61a6be658a4469e403527de/666_lines_of_XSS_vectors.html'),
// we currently fail 44 of 140 tests, thought they seem not to apply to our use case, as we check request data
'https://html5sec.org/' => call_user_func(function() {
$payloads = $items = null;
try
{
if (!($items_js = file_get_contents('https://html5sec.org/items.js')) ||
!preg_match_all("|^\s+'data'\s+:\s+'(.*)',$|m", $items_js, $items, PREG_PATTERN_ORDER) ||
!($payload_js = file_get_contents('https://html5sec.org/payloads.js')) ||
!preg_match_all("|^\s+'([^']+)'\s+:\s+'(.*)',$|m", $payload_js, $payloads, PREG_PATTERN_ORDER))
{
return false;
}
}
catch (Exception $e)
{
unset($e);
return false;
}
$replace = array(
"\\'" => "'",
'\\\\'=> '\\,',
'\r' => "\r",
'\n' => "\n",
);
foreach($payloads[1] as $n => $from) {
$replace['%'.$from.'%'] = $payloads[2][$n];
}
return array_map(function($item) use ($replace) {
return strtr($item, $replace);
}, $items[1]);
}),
);
$test_data = array();
foreach($urls as $url => $vectors)
{
$test_data[] = array(
$url, $vectors
);
}
return $test_data;
}
/**
* Test safe unserialization
*
* @param String $str Serialized string to be checked
* @param boolean $result If we expect the string to fail or not
*
* @dataProvider unserializeProvider
* @requires PHP < 7
*/
public function testObjectsCannotBeUnserializedInPhp5($str, $result)
{
$r=@php_safe_unserialize($str);
$this->assertSame($result, (bool)$r, 'Save unserialize failed');
}
/**
* Test safe unserialization
*
* @param String $str Serialized string to be checked
* @param boolean $result If we expect the string to fail or not
*
* @dataProvider unserializeProvider
* @requires PHP 7
*/
public function testObjectsCannotBeUnserializedInPhp7($str, $result)
{
$r=@php_safe_unserialize($str);
if((bool)($r) !== $result)
{
if (!$result)
{
$matches = null;
if (preg_match_all('/([^ ]+) Object\(/', array2string($r), $matches))
{
foreach($matches[1] as $class)
{
if (!preg_match('/^__PHP_Incomplete_Class(#\d+)?$/', $class))
{
$this->fail($str);
}
}
}
}
else
{
$this->fail("false positive: $str");
}
}
// Avoid this test getting reported as no assertions, we do the testing
// in the foreach loop
$this->assertTrue(true);
}
/**
* Data set for unserialize test
*/
public function unserializeProvider()
{
$tests = array(
// Serialized string, expected result
// things unsafe to unserialize
Array("O:34:\"Horde_Kolab_Server_Decorator_Clean\":2:{s:43:\"\x00Horde_Kolab_Server_Decorator_Clean\x00_server\";", false),
Array("O:20:\"Horde_Prefs_Identity\":2:{s:9:\"\x00*\x00_prefs\";O:11:\"Horde_Prefs\":2:{s:8:\"\x00*\x00_opts\";a:1:{s:12:\"sizecallback\";", false),
Array("a:2:{i:0;O:12:\"Horde_Config\":1:{s:13:\"\x00*\x00_oldConfig\";s:#{php_injection.length}:\"#{php_injection}\";}i:1;s:13:\"readXMLConfig\";}}", false),
Array('a:6:{i:0;i:0;i:1;d:2;i:2;s:4:"ABCD";i:3;r:3;i:4;O:8:"my_Class":2:{s:1:"a";r:6;s:1:"b";N;};i:5;C:16:"SplObjectStorage":14:{x:i:0;m:a:0:{}}', false),
Array(serialize(new \stdClass()), false),
Array(serialize(array(new \stdClass(), new \SplObjectStorage())), false),
// string content, safe to unserialize
Array(serialize('O:8:"stdClass"'), true),
Array(serialize('C:16:"SplObjectStorage"'), true),
Array(serialize(array('a', 'O:8:"stdClass"', 'b', 'C:16:"SplObjectStorage"')), true)
);
if (PHP_VERSION >= 7)
{
// Fails our php<7 regular expression, because it has correct delimiter (^|;|{) in front of pattern :-(
$tests[] = Array(serialize('O:8:"stdClass";C:16:"SplObjectStorage"'), true);
}
return $tests;
}
}