mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-08-15 19:24:21 +02:00
Move all tests under api/src into api/tests
This commit is contained in:
@ -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;
|
||||
}
|
||||
}
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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'));
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
@ -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./'),
|
||||
);
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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');
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user