NEW Add PHPUnit test for getElementProperties() (#39550)

getElementProperties() is the central registry used across the
codebase (fetchObjectByElement(), generic links/extrafields, document
generation...) to resolve an element type string into its module,
classpath, classfile, classname, table and parent element, but it had
no test coverage despite being a ~480 line function with about 60
special-case branches plus several generic fallback paths.

Covers the representative paths rather than every branch:
- the generic completion of classfile/classname from subelement
  (project)
- the 'myobject@mymodule' external-module syntax, including the
  surprising fact that table_element keeps the raw '@'-string
- the 'myobject_mysubobject' syntax combined with a dedicated case
  branch overriding module (project_task)
- the generic '...det' object-line fallback for an unknown module,
  including the non-capitalized classname it produces (myobjectdet)
- a real '...det' case where the generic fallback runs first and a
  dedicated branch only adds parent_element on top (contratdet)
- a real '...det' case where the dedicated branch instead overrides
  classpath and classname set by the generic fallback (facturedet)
- the action/actioncomm special case, where table_element differs
  from the raw input element
This commit is contained in:
Frédéric FRANCE 2026-08-17 04:44:13 +02:00 committed by GitHub
parent 585afa14de
commit c8aa61198b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2320,4 +2320,93 @@ class FunctionsLibTest extends CommonClassTest
$this->assertEquals('2020-07-01 00:00:01', dol_print_date($timestamp, 'standard', false));
date_default_timezone_set($savtz);
}
/**
* testGetElementProperties
*
* @return void
*/
public function testGetElementProperties()
{
// Generic path: no '@'/'_' parsing, no special case branch, so classfile/classname are completed
// from subelement (classfile=strtolower(subelement), classname=ucfirst(subelement))
$properties = getElementProperties('project');
$this->assertEquals('project', $properties['element']);
$this->assertEquals('projet', $properties['module']);
$this->assertEquals('project', $properties['subelement']);
$this->assertEquals('projet', $properties['table_element']);
$this->assertEquals('projet/class', $properties['classpath']);
$this->assertEquals('project', $properties['classfile']);
$this->assertEquals('Project', $properties['classname']);
// 'myobject@mymodule' syntax to ask a resource from an external module: note that table_element
// keeps the full original string (it is never cleaned of the '@mymodule' part)
$properties = getElementProperties('myobject@mymodule');
$this->assertEquals('myobject', $properties['element']);
$this->assertEquals('mymodule', $properties['module']);
$this->assertEquals('myobject', $properties['subelement']);
$this->assertEquals('myobject@mymodule', $properties['table_element']);
$this->assertEquals('mymodule/class', $properties['classpath']);
$this->assertEquals('myobject', $properties['classfile']);
$this->assertEquals('Myobject', $properties['classname']);
// 'myobject_mysubobject' syntax: element/module resolved from the string, but the specific
// 'project_task' case branch then overrides module (projet, not project) and table_element
$properties = getElementProperties('project_task');
$this->assertEquals('project', $properties['element']);
$this->assertEquals('projet', $properties['module']);
$this->assertEquals('task', $properties['subelement']);
$this->assertEquals('projet_task', $properties['table_element']);
$this->assertEquals('projet/class', $properties['classpath']);
$this->assertEquals('task', $properties['classfile']);
$this->assertEquals('Task', $properties['classname']);
// Generic '...det' fallback (no dedicated case branch for this fictional module): module and
// subelement are stripped of the 'det' suffix, but element is not, and classname keeps the raw
// (non-capitalized) result of the suffix replacement since it is not empty afterwards
$properties = getElementProperties('myobjectdet');
$this->assertEquals('myobjectdet', $properties['element']);
$this->assertEquals('myobject', $properties['module']);
$this->assertEquals('myobject', $properties['subelement']);
$this->assertEquals('myobjectdet', $properties['table_element']);
$this->assertEquals('myobject/class', $properties['classpath']);
$this->assertEquals('myobject', $properties['classfile']);
$this->assertEquals('myobjectLine', $properties['classname']);
// 'contratdet': the generic '...det' fallback runs first (module=contrat is in the list of
// modules using "Ligne" instead of "Line", so classname=contratLigne), then the dedicated
// 'contratdet' case branch only adds parent_element on top, it does not touch classname/classfile
$properties = getElementProperties('contratdet');
$this->assertEquals('contrat', $properties['module']);
$this->assertEquals('contrat', $properties['subelement']);
$this->assertEquals('contratdet', $properties['table_element']);
$this->assertEquals('contrat', $properties['parent_element']);
$this->assertEquals('contrat/class', $properties['classpath']);
$this->assertEquals('contrat', $properties['classfile']);
$this->assertEquals('contratLigne', $properties['classname']);
// 'facturedet': same generic fallback runs first, but this time the dedicated 'facturedet' case
// branch overrides classpath (fuller 'compta/facture/class' path) and classname (capitalized
// 'FactureLigne' instead of the generic fallback's lowercase 'factureLigne')
$properties = getElementProperties('facturedet');
$this->assertEquals('facture', $properties['module']);
$this->assertEquals('facturedet', $properties['table_element']);
$this->assertEquals('facture', $properties['parent_element']);
$this->assertEquals('compta/facture/class', $properties['classpath']);
$this->assertEquals('facture', $properties['classfile']);
$this->assertEquals('FactureLigne', $properties['classname']);
// facture is a core module, always configured with an output directory
$this->assertNotEmpty($properties['dir_output']);
// 'action'/'actioncomm' special case: note how table_element is corrected to 'actioncomm' even
// though the input 'action' is kept as-is in element, and subelement is capitalized 'Actioncomm'
$properties = getElementProperties('action');
$this->assertEquals('action', $properties['element']);
$this->assertEquals('agenda', $properties['module']);
$this->assertEquals('Actioncomm', $properties['subelement']);
$this->assertEquals('actioncomm', $properties['table_element']);
$this->assertEquals('comm/action/class', $properties['classpath']);
$this->assertEquals('actioncomm', $properties['classfile']);
$this->assertEquals('Actioncomm', $properties['classname']);
}
}