Basic Etemplate testing

Some simple exec / process round-trip tests
This commit is contained in:
nathangray 2017-04-21 12:17:59 -06:00
parent 490a8f6ff4
commit 9d1756e5d9
5 changed files with 257 additions and 33 deletions

View File

@ -368,7 +368,15 @@ class Etemplate extends Etemplate\Widget\Template
//error_log(__METHOD__."(,".array2string($content).')');
//error_log(' validated='.array2string($validated));
$content = ExecMethod(self::$request->method, self::complete_array_merge(self::$request->preserv, $validated));
if(is_callable(self::$request->method))
{
call_user_func(self::$request->method,self::complete_array_merge(self::$request->preserv, $validated));
}
else
{
// Deprecated, but may still be needed
$content = ExecMethod(self::$request->method, self::complete_array_merge(self::$request->preserv, $validated));
}
$tcontent = is_array($content) ? $content :
self::complete_array_merge(self::$request->preserv, $validated);

View File

@ -22,12 +22,53 @@ require_once realpath(__DIR__.'/../../test/WidgetBaseTest.php');
class TemplateTest extends \EGroupware\Api\Etemplate\WidgetBaseTest {
/**
* Test instanciation of a template
* Test instanciation of template from a file
*/
public function testInstance()
public function testSimpleInstance()
{
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
static $name = 'api.prompt';
$template = Template::instance($name);
$this->assertInstanceOf(Template::class, $template);
}
/**
* Test instanciating nested template
*
*/
public function testNestedInstanciation()
{
static $template = 'api.nested';
$template = Template::instance($template, 'test');
$this->assertInstanceOf(Template::class, $template);
// Check for the sub-child to see if the nested template was loaded
$this->assertInstanceOf(\EGroupware\Api\Etemplate\Widget::class, $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(Template::class, $template);
// Check for the sub-child to see if the template was loaded
$this->assertInstanceOf(\EGroupware\Api\Etemplate\Widget::class, $template->getElementById('sub_child'));
// Check that it's not just making things up
$this->assertNull($template->getElementById('not_existing'));
}
}

View File

@ -12,9 +12,14 @@
namespace EGroupware\Api\Etemplate;
use Egroupware\Api\Etemplate;
// test base providing Egw environment, since we need the DB
require_once realpath(__DIR__.'/../../test/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.
@ -24,6 +29,16 @@ require_once realpath(__DIR__.'/../../test/LoggedInTest.php');
*/
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();
@ -34,23 +49,89 @@ abstract class WidgetBaseTest extends \EGroupware\Api\LoggedInTest {
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 String $method
* @param Etemplate $etemplate
* @param array $content
* @param array $sel_options
* @param array $readonlys
* @param array $preserv
*/
protected function mockedExec(\EGroupware\Api\Etemplate $etemplate, $method,array $content,array $sel_options=null,array $readonlys=null,array $preserv=null)
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;
}
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($etemplate);
->getMock();
// Replace protected self reference with mock object
$ref = new \ReflectionProperty('\\EGroupware\\Api\\Json\\Response', 'response');
$ref->setAccessible(true);
@ -62,19 +143,6 @@ abstract class WidgetBaseTest extends \EGroupware\Api\LoggedInTest {
// Don't send headers, like the real one does
return self::$response;
});
ob_start();
// Exec the template
$etemplate->exec($method, $content, $sel_options, $readonlys, $preserv, 4);
$result = $response->returnResult();
// Clean json response
$response->initResponseArray();
$ref->setValue(null, null);
ob_end_clean();
return $result;
return $response;
}
}

View File

@ -31,6 +31,10 @@ class EtemplateTest extends Etemplate\WidgetBaseTest {
*/
const TEST_TEMPLATE = 'api.prompt';
protected $content = array('value' => 'test content');
protected $sel_options = array(array('value' => 0, 'label' => 'label'));
protected $readonlys = array('value' => true);
/**
* Test reading xml files
*
@ -48,7 +52,7 @@ class EtemplateTest extends Etemplate\WidgetBaseTest {
$this->assertEquals(true, $etemplate->read(static::TEST_TEMPLATE));
// This loads and parses
$result = $this->mockedExec($etemplate, '',array());
$result = $this->mockedExec($etemplate, array());
// Look for the load and match the template name
foreach($result as $command)
@ -74,7 +78,7 @@ class EtemplateTest extends Etemplate\WidgetBaseTest {
// Change the target DOM ID
$etemplate->set_dom_id('test_id');
$result = $this->mockedExec($etemplate, '',array());
$result = $this->mockedExec($etemplate, array());
// Check for the load
foreach($result as $command)
@ -92,10 +96,6 @@ class EtemplateTest extends Etemplate\WidgetBaseTest {
*/
public function testExec()
{
$content = array('id' => 'value');
$sel_options = array(array('value' => 0, 'label' => 'label'));
$readonlys = array('id' => true);
// Templates must be in the correct templates directory - use one from API
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE);
@ -103,7 +103,7 @@ class EtemplateTest extends Etemplate\WidgetBaseTest {
// Change the target DOM ID
$etemplate->set_dom_id('test_id');
$result = $this->mockedExec($etemplate, '',$content, $sel_options, $readonlys);
$result = $this->mockedExec($etemplate, $this->content, $this->sel_options, $this->readonlys);
// Check for the load
$data = array();
@ -116,8 +116,103 @@ class EtemplateTest extends Etemplate\WidgetBaseTest {
}
}
$this->assertArraySubset($content, $data['data']['content'], false, 'Content does not match');
$this->assertArraySubset($sel_options, $data['data']['sel_options'], false, 'Select options do not match');
$this->assertArraySubset($readonlys, $data['data']['readonlys'], false, 'Readonlys does not match');
$this->assertArraySubset($this->content, $data['data']['content'], false, 'Content does not match');
$this->assertArraySubset($this->sel_options, $data['data']['sel_options'], false, 'Select options do not match');
$this->assertArraySubset($this->readonlys, $data['data']['readonlys'], false, 'Readonlys does not match');
}
/**
* Test that data passed in is passed back
*
* In this case, since there's one input widget and we're passing it's value, and
* we're not passing anything extra and no preserve, it should be the same.
*
* @depends testExec
*/
public function testRoundTrip()
{
// Templates must be in the correct templates directory - use one from API
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE);
$this->readonlys['value'] = false;
$result = $this->mockedRoundTrip($etemplate, $this->content, $this->sel_options, $this->readonlys);
$this->assertEquals($this->content, $result);
}
/**
* Simple test of a read-only widget
*
* The value is passed in, but does not come back
*
* @depends testExec
*/
public function testSimpleReadonly()
{
// Templates must be in the correct templates directory - use one from API
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE);
$this->readonlys['value'] = true;
$result = $this->mockedRoundTrip($etemplate, $this->content, $this->sel_options, $this->readonlys);
// The only input widget is readonly, expect an empty array
$this->assertEquals(array(), $result);
}
/**
* Simple test of preserve
*
* The value is passed in, and comes back, even if the widget is readonly,
* or if there is no matching widget.
*
* @depends testExec
*/
public function testArbitraryPreserve()
{
// Templates must be in the correct templates directory - use one from API
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE);
$this->readonlys['value'] = true;
$preserve = array('arbitrary' => 'value');
$result = $this->mockedRoundTrip($etemplate, $this->content, $this->sel_options, $this->readonlys, $preserve);
// The only input widget is readonly, expect preserve back
$this->assertEquals($preserve, $result);
// Now try with widget
$this->readonlys['value'] = false;
$result = $this->mockedRoundTrip($etemplate, $this->content, $this->sel_options, $this->readonlys, $preserve);
// The only input widget is readonly, expect preserve + content back
$this->assertArraySubset($this->content, $result);
$this->assertArraySubset($preserve, $result);
}
public function testReadonlyPreserve()
{
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE);
$this->readonlys['value'] = true;
$preserve['value'] = 'preserved_value';
$result = $this->mockedRoundTrip($etemplate, $this->content, $this->sel_options, $this->readonlys, $preserve);
// The only input widget is readonly, expect preserve back, not content
$this->assertEquals($preserve['value'], $result['value']);
$this->readonlys['value'] = false;
$result = $this->mockedRoundTrip($etemplate, $this->content, $this->sel_options, $this->readonlys, $preserve);
// The only input widget is editable, expect content back, not preserve
$this->assertEquals($this->content['value'], $result['value']);
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE overlay PUBLIC "-//EGroupware GmbH//eTemplate 2//EN" "http://www.egroupware.org/etemplate2.dtd">
<!-- This template is used in automated testing -->
<overlay>
<template id="api.nested.sub_template" template="" lang="" group="0" version="16.1">
<widget id="sub_child"/>
</template>
<template id="api.nested" template="" lang="" group="0" version="16.1">
<widget id="only_child"/>
<template id="api.nested.sub_template"/>
</template>
</overlay>