Improvements for entry widget for robustness, includes automatic server side tests.

This commit is contained in:
nathangray 2018-11-15 11:10:05 -07:00
parent 88b7c91b42
commit d0b57cfccd
7 changed files with 353 additions and 10 deletions

View File

@ -138,7 +138,12 @@ var et2_entry = (function(){ "use strict"; return et2_valueWidget.extend(
// If value was set, find the record explicitly.
if(typeof this.options.value == 'string')
{
widget.options.value = this.getRoot().getArrayMgr('content').getEntry(this.prefix+this.options.value + '['+this.options.field+']');
widget.options.value = this.getArrayMgr('content').getEntry(this.id+'['+this.options.field+']') ||
this.getRoot().getArrayMgr('content').getEntry(this.prefix+this.options.value + '['+this.options.field+']');
}
else if (this.options.field && this.options.value && this.options.value[this.options.field])
{
widget.options.value = this.options.value[this.options.field];
}
if(this.options.compare)
{

View File

@ -530,6 +530,7 @@ class Etemplate extends Etemplate\Widget\Template
public static function reset_request()
{
self::$request = Etemplate\Request::read();
self::$cache = array();
}
/**
* Get template data as array

View File

@ -70,7 +70,8 @@ abstract class Entry extends Transformer
$attrs['id'] = $this->id;
$form_name = self::form_name($cname, $this->id);
$data_id = $attrs['value'] ? self::form_name($cname, $attrs['value']) : self::form_name($cname, self::ID_PREFIX . $this->id);
$prefixed_id = (substr($this->id, 0, 1) == self::ID_PREFIX ? $this->id : self::ID_PREFIX . $this->id);
$data_id = $attrs['value'] ? self::form_name($cname, $attrs['value']) : self::form_name($cname, $prefixed_id);
// No need to proceed
if(!$data_id) return;
@ -94,8 +95,8 @@ abstract class Entry extends Transformer
}
// Set the new value so transformer can find it. Use prefix to avoid changing the original value
$new_value =& self::get_array(self::$request->content, self::ID_PREFIX .$this->id, true, false);
if (true) $new_value = $data;
$new_value =& self::get_array(self::$request->content, $prefixed_id, true, false);
if ($data) $new_value = $data;
// Check for missing field
if(!$attrs['field'] && !$attrs['alternate_fields'])
@ -109,10 +110,15 @@ abstract class Entry extends Transformer
$this->regex($attrs, $new_value);
// Change this before parent so client gets what it needs
// Client is expecting to find data with the prefix
if(substr($this->id, 0, 1) !== self::ID_PREFIX)
{
$this->id = self::ID_PREFIX . $this->id . "[{$attrs['field']}]";
}
parent::beforeSendToClient($cname, $expand);
// Change this after parent::beforeSendToClient or it adds another layer
$this->id = self::ID_PREFIX . $this->id . "[{$attrs['field']}]";
}
@ -173,7 +179,8 @@ abstract class Entry extends Transformer
protected function customfield($attrs, &$data)
{
list($app, $type) = explode('-',$attrs['type']);
$id = is_array($data) ? static::get_array($data, $this->id) : $data;
$data_id = $attrs['value'] ?: $attrs['id'];
$id = is_array($data) ? static::get_array($data, $data_id) : $data;
if(!$app || !$type || !$GLOBALS['egw_info']['apps'][$app] || !$id ||
// Simple CF, already there
$data[$attrs['field']]
@ -207,9 +214,10 @@ abstract class Entry extends Transformer
*/
protected function regex($attrs, &$data)
{
$id = is_array($data) ? static::get_array($data, $this->id) : $data;
$data_id = $attrs['value'] ?: $attrs['id'];
$id = is_array($data) ? static::get_array($data, $data_id) : $data;
$value =& $this->get_data_field($attrs, $data);
if(!$attrs['regex'] || !$id || !$value)
if(!$attrs['regex'] || !$value)
{
return;
}
@ -221,6 +229,6 @@ abstract class Entry extends Transformer
$replace = array_pop($regex);
$regex = implode(',', $regex);
}
$value = preg_replace($regex, $replace, $value);
$data[$attrs['field']] = preg_replace($regex, $replace, $value);
}
}

View File

@ -0,0 +1,25 @@
<?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
Most of these need the client side for full functionality, so this is also a
list of patterns to test.
-->
<overlay>
<template id="api.entry_test" template="" lang="" group="0" version="19.1">
<!-- Nice, unique ID with value and field specified -->
<entry id="widget" value="entry_id" field="entry_field_1"/>
<!--No value attribute, will fallback to ID and succeed-->
<entry id="entry_id" field="entry_field_2"/>
<!--No value attribute, ID does not match anything in content, will fallback to ID and fail-->
<entry id="no_value" field="entry_field_3"/>
<!-- Checking compare - should render as X -->
<entry id="compare" value="entry_id" field="entry_num_2" compare="2"/>
</template>
</overlay>

View File

@ -0,0 +1,27 @@
<?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
Most of these need the client side for full functionality, so this is also a
list of patterns to test.
-->
<overlay>
<template id="api.entry_test_contact" template="" lang="" group="0" version="19.1">
<!-- Nice, unique ID with value and field specified -->
<contact-value id="widget" value="entry_id" field="email"/>
<!--No value attribute, will fallback to ID and succeed-->
<contact-value id="entry_id" field="n_fn"/>
<!--No value attribute, ID does not match anything in content, will fallback to ID and fail-->
<contact-value id="no_value" field="email"/>
<!-- These are all the same -->
<contact-value id="info_contact_email" value="info_contact" options="email"/>
<contact-value id="info_contact_email" value="info_contact" field="email"/>
<contact-value id="info_contact" options="email"/>
</template>
</overlay>

View File

@ -0,0 +1,139 @@
<?php
/**
* Test for contact entry widget, which displays the value of a particular field
* (specified by field attribute) from a particular contact (specified
* by value attribute).
*
* This is just the server side, client has it's own issues (but no automatic test)
* see infolog/templates/test/entry_test.xet for a test template to use for
* manual client side verification.
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @copyright (c) 2019 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate\Widget;
require_once realpath(__DIR__.'/../WidgetBaseTest.php');
use EGroupware\Api\Etemplate;
class EntryTest extends \EGroupware\Api\Etemplate\WidgetBaseTest {
const TEST_TEMPLATE = 'api.entry_test_contact';
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
}
public function tearDown()
{
// Delete all elements
foreach($this->elements as $id)
{
list($app, $id) = explode(':',$id);
$bo_class = "{$app}_bo";
$bo = new $bo_class();
$bo->delete($id, true, false, true);
}
parent::tearDown();
}
/**
* Test that the correct value is extracted, based on the value attribute,
* or if it is missing try the ID. This goes to contact, rather than
* abstract test class.
*/
public function testContact()
{
// Create a test contact
$test_contact = $this->make_contact();
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - IDs are important, they should match valid contacts
$content = array(
'entry_id' => $GLOBALS['egw_info']['user']['person_id'],
'info_contact' => $test_contact
);
$result = $this->mockedExec($etemplate, $content);
$data = array();
foreach($result as $response)
{
if($response['type'] == 'et2_load')
{
$data = $response['data']['data'];
break;
}
}
// Check that the entry was found
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'widget']);
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'entry_id']);
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'info_contact_email']);
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'info_contact']);
$this->assertEmpty($data['content'][Entry::ID_PREFIX.'no_value']);
// Check that the value is present - exact value is pulled client side
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'widget']['email']);
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'entry_id']['n_fn']);
$this->assertEquals($GLOBALS['egw_info']['user']['account_email'], $data['content'][Entry::ID_PREFIX.'entry_id']['email']);
$this->assertEquals($GLOBALS['egw_info']['user']['account_fullname'], $data['content'][Entry::ID_PREFIX.'entry_id']['n_fn']);
// Values are to be done as labels
$this->assertEquals('label', $data['modifications'][Entry::ID_PREFIX.'widget[email]']['type'], 'Unexpected widget type');
$this->assertEquals('label', $data['modifications'][Entry::ID_PREFIX.'entry_id[n_fn]']['type'], 'Unexpected widget type');
$this->assertEquals('label', $data['modifications'][Entry::ID_PREFIX.'info_contact_email[email]']['type'], 'Unexpected widget type');
$this->assertEquals('label', $data['modifications'][Entry::ID_PREFIX.'info_contact[email]']['type'], 'Unexpected widget type');
$this->assertEquals('label', $data['modifications'][Entry::ID_PREFIX.'no_value[email]']['type'], 'Unexpected widget type');
// No errors
$this->assertEmpty($data['validation_errors']);
}
/**
* Make a contact so we can test
*/
protected function make_contact()
{
$bo = new \addressbook_bo();
$element = array(
'n_fn' => "Mr Test Guy",
'n_family' => 'Guy',
'n_fileas' => 'Test Organisation: Guy, Test',
'n_given' => 'Test',
'n_prefix' => 'Mr',
'org_name' => 'Test Organisation',
'tel_cell' => '555-4321',
'tel_home' => '66 12 34 56',
'bday' => "1995-07-28T10:52:54Z"
);
$element_id = $bo->save($element, true, true, true, true);
$this->elements[] = 'addressbook:'.$element_id;
return $element_id;
}
}
/**
* Testable entry widget
*/
class EntryTestWidget extends \EGroupware\Api\Etemplate\Widget\Entry
{
public function get_entry($value, array $attrs)
{
$entry = EntryTest::getEntry($value, $attrs);
return $entry;
}
}

View File

@ -0,0 +1,138 @@
<?php
/**
* Test for special entry widget, which displays the value of a particular field
* (specified by field attribute) from a particular egroupware entry (specified
* by value attribute).
*
* Just simple stuff here, the overriding classes have their own tests.
*
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @package api
* @copyright (c) 2019 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Etemplate\Widget;
require_once realpath(__DIR__.'/../WidgetBaseTest.php');
use EGroupware\Api\Etemplate;
class EntryTest extends \EGroupware\Api\Etemplate\WidgetBaseTest {
const TEST_TEMPLATE = 'api.entry_test';
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
Etemplate::registerWidget(EntryTestWidget::class, 'entry');
}
public function setUp() {
parent::setUp();
Etemplate::reset_request();
}
public static function getEntry($value, $attrs)
{
$entry = array();
$entry['7'] = array(
'entry_id' => 7,
'entry_field_1' => 'Field 1',
'entry_field_2' => 'Field 2',
'entry_field_3' => 'Field 3',
'entry_num_1' => 1,
'entry_num_2' => 2,
'entry_num_3' => 3,
'entry_date' => '2018-11-12'
);
return $entry[$value];
}
/**
* Test that the correct value is extracted, based on the value attribute,
* or if it is missing try the ID
*/
public function testValueAttr()
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - entry ID is important, it should match what getEntry() gives
$content = array(
'entry_id' => '7',
'not_entry_id' => '123'
);
$result = $this->mockedExec($etemplate, $content);
$data = array();
foreach($result as $response)
{
if($response['type'] == 'et2_load')
{
$data = $response['data']['data'];
break;
}
}
// Check that the entry was found
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'widget']);
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'entry_id']);
// Check that the entry was not found
$this->assertEmpty($data['content'][Entry::ID_PREFIX.'no_value']);
// Check that the value is present - exact value is pulled client side
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'widget']['entry_field_1']);
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'entry_id']['entry_field_2']);
// No errors
$this->assertEmpty($data['validation_errors']);
}
/**
* Check on compare attribute.
* Actual comparison is done client side, but here we check that the value is found.
*/
public function testCompareAttr()
{
// Instanciate the template
$etemplate = new Etemplate();
$etemplate->read(static::TEST_TEMPLATE, 'test');
// Content - entry ID is important, it should match what getEntry() gives
$content = array(
'entry_id' => '7',
);
$result = $this->mockedExec($etemplate, $content);
$data = array();
foreach($result as $response)
{
if($response['type'] == 'et2_load')
{
$data = $response['data']['data'];
break;
}
}
// Check that the value is present - exact value is pulled client side
$this->assertNotEmpty($data['content'][Entry::ID_PREFIX.'compare']['entry_num_1']);
}
}
/**
* Testable entry widget
*/
class EntryTestWidget extends \EGroupware\Api\Etemplate\Widget\Entry
{
public function get_entry($value, array $attrs)
{
$entry = EntryTest::getEntry($value, $attrs);
return $entry;
}
}