From 71b893b3daa740d7d3016fa6397e798380541cbb Mon Sep 17 00:00:00 2001 From: Lacey-Anne Sanderson Date: Sat, 30 Aug 2025 00:10:26 +0000 Subject: [PATCH 1/8] Start field type test generator. --- .../TestChadoFieldTypeGenerator.php | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 src/Drush/Generators/TestChadoFieldTypeGenerator.php diff --git a/src/Drush/Generators/TestChadoFieldTypeGenerator.php b/src/Drush/Generators/TestChadoFieldTypeGenerator.php new file mode 100644 index 0000000..1ea197d --- /dev/null +++ b/src/Drush/Generators/TestChadoFieldTypeGenerator.php @@ -0,0 +1,139 @@ +prompt = $this->createInterviewer($vars); + + // Module Machine Name. + $vars['machine_name'] = $this->prompt->askMachineName(); + + // Now start building the test information yaml file. + $test_info_yaml = [ + 'chado_version' => '', + 'bundle' => [], + 'fields' => [], + 'scenarios' => [], + ]; + + // Chado Version. + $vars['chado_version'] = $this->prompt->ask('Chado Version', '1.3.3.013'); + $test_info_yaml['chado_version'] = $vars['chado_version']; + + // Bundle Information. + $vars['bundle_id'] = $this->prompt->ask('Existing Tripal Content Type to test fields on', 'organism'); + $this->addBundleInfo($vars['bundle_id'], $test_info_yaml); + + // For each field attached to this bundle, ask if they want to add it to + // the system under test. + // @todo currently we add all without asking, implement asking ;-p. + $this->addFieldInfo($vars['bundle_id'], $test_info_yaml); + + print_r($test_info_yaml); + } + + /** + * Looks-up the bundle and adds information about it to the system under test. + * + * @param string $bundle_id + * The ID of an existing TripalEntityType (e.g. organism). + * @param array $test_info_yaml + * The current test information yaml array from the generate method. + * + * @return TripalEntityType + * The bundle object used to fill out the test info yaml array. + */ + private function addBundleInfo(string $bundle_id, array &$test_info_yaml) { + + // First we need to get the bundle object. + $bundle = \Drupal::entityTypeManager()->getStorage('tripal_entity_type')->load($bundle_id); + if (!is_object($bundle)) { + throw new \UnexpectedValueException('The Tripal Content Type must already exist in the current site. Make sure you have imported the type collection.'); + } + + $test_info_yaml['bundle']['label'] = $bundle->getLabel(); + $test_info_yaml['bundle']['termIdSpace'] = $bundle->getTermIdSpace(); + $test_info_yaml['bundle']['termAccession'] = $bundle->getTermAccession(); + $test_info_yaml['bundle']['id'] = $bundle->getID(); + $test_info_yaml['bundle']['settings'] = $bundle->getThirdPartySettings('tripal'); + + return $bundle; + } + + /** + * Adds field to the system under test after confirmation. + * + * @param string $bundle_id + * The ID of an existing TripalEntityType (e.g. organism). + * @param array $test_info_yaml + * The current test information yaml array from the generate method. + */ + private function addFieldInfo(string $bundle_id, array &$test_info_yaml) { + // Get all the fields for this bundle. + $this->field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('tripal_entity', $bundle_id); + + // For each field, add the details to the system under test. + foreach ($this->field_definitions as $field_name => $field_defn) { + if (get_class($field_defn) == 'Drupal\field\Entity\FieldConfig') { + $field_storage_defn = $field_defn->getFieldStorageDefinition(); + $field_yaml = []; + + // Basic field type info. + $field_yaml['name'] = $field_name; + $field_yaml['type'] = $field_defn->getType(); + // @todo actually set this. + $field_yaml['type_class'] = ''; + $field_yaml['cardinality'] = $field_storage_defn->getCardinality(); + + // Field Widget and formatter information. + // @todo actually set this. + $field_yaml['widget'] = ''; + // @todo actually set this. + $field_yaml['formatter'] = ''; + + // Field settings such as term and storage info. + $field_settings = $field_defn->getSettings(); + $field_yaml['termIdSpace'] = $field_settings['termIdSpace']; + $field_yaml['termAccession'] = $field_settings['termAccession']; + $field_yaml['settings'] = $field_settings; + $test_info_yaml['fields'][] = $field_yaml; + } + } + } + +} From f558a44d758718f482a0d58b2f31aad5e1ffd887 Mon Sep 17 00:00:00 2001 From: Lacey-Anne Sanderson Date: Sat, 6 Dec 2025 01:48:38 +0000 Subject: [PATCH 2/8] Retrieve class and widget info. --- .../TestChadoFieldTypeGenerator.php | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Drush/Generators/TestChadoFieldTypeGenerator.php b/src/Drush/Generators/TestChadoFieldTypeGenerator.php index 1ea197d..75186a5 100644 --- a/src/Drush/Generators/TestChadoFieldTypeGenerator.php +++ b/src/Drush/Generators/TestChadoFieldTypeGenerator.php @@ -1,7 +1,5 @@ getStorage('tripal_entity_type')->load($bundle_id); @@ -103,28 +101,34 @@ private function addBundleInfo(string $bundle_id, array &$test_info_yaml) { * @param array $test_info_yaml * The current test information yaml array from the generate method. */ - private function addFieldInfo(string $bundle_id, array &$test_info_yaml) { + protected function addFieldInfo(string $bundle_id, array &$test_info_yaml) { + // Get all the fields for this bundle. $this->field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('tripal_entity', $bundle_id); + // We need the Field Type Plugin Manager to get the class. + $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); + $type_definitions = $field_type_manager->getDefinitions(); + // For each field, add the details to the system under test. foreach ($this->field_definitions as $field_name => $field_defn) { if (get_class($field_defn) == 'Drupal\field\Entity\FieldConfig') { $field_storage_defn = $field_defn->getFieldStorageDefinition(); $field_yaml = []; + // Get the field type information. + $field_type = $field_defn->getType(); + $field_type_defn = $type_definitions[$field_type]; + // Basic field type info. $field_yaml['name'] = $field_name; $field_yaml['type'] = $field_defn->getType(); - // @todo actually set this. - $field_yaml['type_class'] = ''; + $field_yaml['type_class'] = $field_type_defn['class']; $field_yaml['cardinality'] = $field_storage_defn->getCardinality(); // Field Widget and formatter information. - // @todo actually set this. - $field_yaml['widget'] = ''; - // @todo actually set this. - $field_yaml['formatter'] = ''; + $field_yaml['widget'] = $field_type_defn['default_widget']; + $field_yaml['formatter'] = $field_type_defn['default_formatter']; // Field settings such as term and storage info. $field_settings = $field_defn->getSettings(); From 3bb804ad75ca5b358e0c7892425d3cf380cabe55 Mon Sep 17 00:00:00 2001 From: Lacey-Anne Sanderson Date: Sat, 6 Dec 2025 02:57:26 +0000 Subject: [PATCH 3/8] Create file and prompt for fields to be added to sut. --- .../TestChadoFieldTypeGenerator.php | 114 ++++++++++++------ 1 file changed, 74 insertions(+), 40 deletions(-) diff --git a/src/Drush/Generators/TestChadoFieldTypeGenerator.php b/src/Drush/Generators/TestChadoFieldTypeGenerator.php index 75186a5..a7c75b0 100644 --- a/src/Drush/Generators/TestChadoFieldTypeGenerator.php +++ b/src/Drush/Generators/TestChadoFieldTypeGenerator.php @@ -2,10 +2,13 @@ namespace Drupal\tripal_devtools\Drush\Generators; +use Drupal\field\Entity\FieldConfig; use DrupalCodeGenerator\Asset\AssetCollection as Assets; use DrupalCodeGenerator\Attribute\Generator; use DrupalCodeGenerator\Command\BaseGenerator; use DrupalCodeGenerator\GeneratorType; +use Symfony\Component\Yaml\Yaml; +use Drupal\tripal\Entity\TripalEntityType; /** * Generates test files to test a specific field type. @@ -25,13 +28,30 @@ class TestChadoFieldTypeGenerator extends BaseGenerator { */ protected object $prompt; + /** + * The bundle this test is generated for. + * + * @var \Drupal\tripal\Entity\TripalEntityType + */ + protected TripalEntityType $bundle; + /** * The field definitions added to the system under test. * - * @var array + * @var Drupal\Core\Field\FieldDefinitionInterface[] + * The array of field definitions for the bundle, keyed by field name. */ protected array $field_definitions; + /** + * The field type definitions added to the system under test. + * + * @var array + * The array field type definitions keyed by field type id. The value is + * the array version of the field type definition. + */ + protected array $field_type_definitions; + /** * {@inheritdoc} */ @@ -59,31 +79,53 @@ protected function generate(array &$vars, Assets $assets): void { // For each field attached to this bundle, ask if they want to add it to // the system under test. - // @todo currently we add all without asking, implement asking ;-p. - $this->addFieldInfo($vars['bundle_id'], $test_info_yaml); + foreach ($this->field_definitions as $field_name => $field_defn) { + if (get_class($field_defn) == 'Drupal\field\Entity\FieldConfig') { + if ($this->prompt->confirm("Add $field_name to the system-under-test?")) { + $this->addFieldInfo($vars['bundle_id'], $field_defn, $test_info_yaml); + } + } + } + + // Ask what field type you would like to test. + $vars['chado_field'] = $this->prompt->ask('Existing field type which you would like to test', 'ChadoPropertyType'); - print_r($test_info_yaml); + // We are now done generating the yaml file so lets create that. + $yaml_file = $assets->addFile($vars['chado_field'] . '-' . $vars['bundle_id'] . '-TestInfo.yml'); + $yaml_file->content(Yaml::dump($test_info_yaml, 8, 2, Yaml::DUMP_COMPACT_NESTED_MAPPING)); } /** * Looks-up the bundle and adds information about it to the system under test. * + * NOTE: This also populates the field_definitions and field_type_definitions + * properties to allow field information to be accessed easily from other + * methods. + * * @param string $bundle_id * The ID of an existing TripalEntityType (e.g. organism). * @param array $test_info_yaml * The current test information yaml array from the generate method. * - * @return TripalEntityType + * @return \Drupal\tripal\Entity\TripalEntityType * The bundle object used to fill out the test info yaml array. */ - protected function addBundleInfo(string $bundle_id, array &$test_info_yaml) { + protected function addBundleInfo(string $bundle_id, array &$test_info_yaml): TripalEntityType { // First we need to get the bundle object. $bundle = \Drupal::entityTypeManager()->getStorage('tripal_entity_type')->load($bundle_id); if (!is_object($bundle)) { throw new \UnexpectedValueException('The Tripal Content Type must already exist in the current site. Make sure you have imported the type collection.'); } + $this->bundle = $bundle; + + // Get all the fields for this bundle. + $this->field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('tripal_entity', $bundle_id); + // - We need the Field Type Plugin Manager to get the class. + $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); + $this->field_type_definitions = $field_type_manager->getDefinitions(); + // Add the bundle info to the yaml array. $test_info_yaml['bundle']['label'] = $bundle->getLabel(); $test_info_yaml['bundle']['termIdSpace'] = $bundle->getTermIdSpace(); $test_info_yaml['bundle']['termAccession'] = $bundle->getTermAccession(); @@ -98,46 +140,38 @@ protected function addBundleInfo(string $bundle_id, array &$test_info_yaml) { * * @param string $bundle_id * The ID of an existing TripalEntityType (e.g. organism). + * @param \Drupal\field\Entity\FieldConfig $field_defn + * The definition of the field you would like to add to the config. * @param array $test_info_yaml * The current test information yaml array from the generate method. */ - protected function addFieldInfo(string $bundle_id, array &$test_info_yaml) { + protected function addFieldInfo(string $bundle_id, FieldConfig $field_defn, array &$test_info_yaml) { - // Get all the fields for this bundle. - $this->field_definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('tripal_entity', $bundle_id); + $field_name = $field_defn->getName(); + $field_storage_defn = $field_defn->getFieldStorageDefinition(); + $field_yaml = []; - // We need the Field Type Plugin Manager to get the class. - $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); - $type_definitions = $field_type_manager->getDefinitions(); + // Get the field type information. + $field_type = $field_defn->getType(); + $field_type_defn = $this->field_type_definitions[$field_type]; + + // Basic field type info. + $field_yaml['name'] = $field_name; + $field_yaml['type'] = $field_defn->getType(); + $field_yaml['type_class'] = $field_type_defn['class']; + $field_yaml['cardinality'] = $field_storage_defn->getCardinality(); + + // Field Widget and formatter information. + $field_yaml['widget'] = $field_type_defn['default_widget']; + $field_yaml['formatter'] = $field_type_defn['default_formatter']; + + // Field settings such as term and storage info. + $field_settings = $field_defn->getSettings(); + $field_yaml['termIdSpace'] = $field_settings['termIdSpace']; + $field_yaml['termAccession'] = $field_settings['termAccession']; + $field_yaml['settings'] = $field_settings; + $test_info_yaml['fields'][] = $field_yaml; - // For each field, add the details to the system under test. - foreach ($this->field_definitions as $field_name => $field_defn) { - if (get_class($field_defn) == 'Drupal\field\Entity\FieldConfig') { - $field_storage_defn = $field_defn->getFieldStorageDefinition(); - $field_yaml = []; - - // Get the field type information. - $field_type = $field_defn->getType(); - $field_type_defn = $type_definitions[$field_type]; - - // Basic field type info. - $field_yaml['name'] = $field_name; - $field_yaml['type'] = $field_defn->getType(); - $field_yaml['type_class'] = $field_type_defn['class']; - $field_yaml['cardinality'] = $field_storage_defn->getCardinality(); - - // Field Widget and formatter information. - $field_yaml['widget'] = $field_type_defn['default_widget']; - $field_yaml['formatter'] = $field_type_defn['default_formatter']; - - // Field settings such as term and storage info. - $field_settings = $field_defn->getSettings(); - $field_yaml['termIdSpace'] = $field_settings['termIdSpace']; - $field_yaml['termAccession'] = $field_settings['termAccession']; - $field_yaml['settings'] = $field_settings; - $test_info_yaml['fields'][] = $field_yaml; - } - } } } From 83aadff61fee2ca6cf8833fc5597ba92e36eb81f Mon Sep 17 00:00:00 2001 From: Lacey-Anne Sanderson Date: Mon, 8 Dec 2025 23:06:31 +0000 Subject: [PATCH 4/8] Add basic scenario support and write the test file. --- .../TestChadoFieldTypeGenerator.php | 76 +++++-- .../chado_field/chado-field-type-test.twig | 199 ++++++++++++++++++ 2 files changed, 258 insertions(+), 17 deletions(-) create mode 100644 templates/generator/chado_field/chado-field-type-test.twig diff --git a/src/Drush/Generators/TestChadoFieldTypeGenerator.php b/src/Drush/Generators/TestChadoFieldTypeGenerator.php index a7c75b0..eb3e78e 100644 --- a/src/Drush/Generators/TestChadoFieldTypeGenerator.php +++ b/src/Drush/Generators/TestChadoFieldTypeGenerator.php @@ -16,7 +16,7 @@ #[Generator( name: 'tripal-chado:test-field-type', description: 'Generates a PHPUnit Kernel Test to test a specific Chado field type.', - templatePath: __DIR__ . '/../../../templates/generator', + templatePath: __DIR__ . '/../../../templates/generator/chado_field', type: GeneratorType::MODULE_COMPONENT, )] class TestChadoFieldTypeGenerator extends BaseGenerator { @@ -63,15 +63,17 @@ protected function generate(array &$vars, Assets $assets): void { // Now start building the test information yaml file. $test_info_yaml = [ - 'chado_version' => '', - 'bundle' => [], - 'fields' => [], + 'system-under-test' => [ + 'chado_version' => '', + 'bundle' => [], + 'fields' => [], + ], 'scenarios' => [], ]; // Chado Version. $vars['chado_version'] = $this->prompt->ask('Chado Version', '1.3.3.013'); - $test_info_yaml['chado_version'] = $vars['chado_version']; + $test_info_yaml['system-under-test']['chado_version'] = $vars['chado_version']; // Bundle Information. $vars['bundle_id'] = $this->prompt->ask('Existing Tripal Content Type to test fields on', 'organism'); @@ -81,18 +83,25 @@ protected function generate(array &$vars, Assets $assets): void { // the system under test. foreach ($this->field_definitions as $field_name => $field_defn) { if (get_class($field_defn) == 'Drupal\field\Entity\FieldConfig') { - if ($this->prompt->confirm("Add $field_name to the system-under-test?")) { - $this->addFieldInfo($vars['bundle_id'], $field_defn, $test_info_yaml); - } + //if ($this->prompt->confirm(" - Add $field_name to the system-under-test?")) { + $this->addFieldInfo($vars['bundle_id'], $field_defn, $test_info_yaml); + //} } } - // Ask what field type you would like to test. - $vars['chado_field'] = $this->prompt->ask('Existing field type which you would like to test', 'ChadoPropertyType'); + // Now add a scenario based on the default values. + $this->addDefaultScenario($vars, $test_info_yaml); + + // Ask what file to save the test in. + $vars['test_class'] = $this->prompt->ask('What should be the class name for the generated test (must end with "Test")', 'BaseFieldTest'); + $vars['test_yml'] = trim($vars['test_class'], 'Test') . '-' . $vars['bundle_id'] . '-TestInfo.yml'; + $vars['test_path'] = $this->prompt->ask('Where should the test files be created (relative to module directory)', 'tests/src/Kernel/Plugin/ChadoField/FieldType'); // We are now done generating the yaml file so lets create that. - $yaml_file = $assets->addFile($vars['chado_field'] . '-' . $vars['bundle_id'] . '-TestInfo.yml'); + $yaml_file = $assets->addFile('{test_path}/{test_yml}'); $yaml_file->content(Yaml::dump($test_info_yaml, 8, 2, Yaml::DUMP_COMPACT_NESTED_MAPPING)); + // Then lets create the test file. + $assets->addFile('{test_path}/{test_class}.php', 'chado-field-type-test.twig'); } /** @@ -126,11 +135,11 @@ protected function addBundleInfo(string $bundle_id, array &$test_info_yaml): Tri $this->field_type_definitions = $field_type_manager->getDefinitions(); // Add the bundle info to the yaml array. - $test_info_yaml['bundle']['label'] = $bundle->getLabel(); - $test_info_yaml['bundle']['termIdSpace'] = $bundle->getTermIdSpace(); - $test_info_yaml['bundle']['termAccession'] = $bundle->getTermAccession(); - $test_info_yaml['bundle']['id'] = $bundle->getID(); - $test_info_yaml['bundle']['settings'] = $bundle->getThirdPartySettings('tripal'); + $test_info_yaml['system-under-test']['bundle']['label'] = $bundle->getLabel(); + $test_info_yaml['system-under-test']['bundle']['termIdSpace'] = $bundle->getTermIdSpace(); + $test_info_yaml['system-under-test']['bundle']['termAccession'] = $bundle->getTermAccession(); + $test_info_yaml['system-under-test']['bundle']['id'] = $bundle->getID(); + $test_info_yaml['system-under-test']['bundle']['settings'] = $bundle->getThirdPartySettings('tripal'); return $bundle; } @@ -170,8 +179,41 @@ protected function addFieldInfo(string $bundle_id, FieldConfig $field_defn, arra $field_yaml['termIdSpace'] = $field_settings['termIdSpace']; $field_yaml['termAccession'] = $field_settings['termAccession']; $field_yaml['settings'] = $field_settings; - $test_info_yaml['fields'][] = $field_yaml; + $test_info_yaml['system-under-test']['fields'][] = $field_yaml; + + } + + /** + * Adds the default scenario to the yaml based on the system-under-test. + * + * @param array $vars + * The variables already set by the command. + * @param array $test_info_yaml + * The current test information yaml array from the generate method. + */ + protected function addDefaultScenario(array $vars, array &$test_info_yaml) { + + $scenario = [ + 'label' => 'Default Values Only', + 'description' => 'Creates a page using only default values for each field.', + ]; + + foreach (['create', 'edit'] as $first_lvl) { + $scenario[$first_lvl] = []; + foreach (['user_input', 'expected'] as $second_lvl) { + $scenario[$first_lvl][$second_lvl] = []; + foreach ($test_info_yaml['system-under-test']['fields'] as $field_yml) { + $field_name = $field_yml['name']; + // @todo add these values based on the field definition. + $scenario[$first_lvl][$second_lvl][$field_name] = [ + 'record_id' => 0, + 'value' => '', + ]; + } + } + } + $test_info_yaml['scenarios'][] = $scenario; } } diff --git a/templates/generator/chado_field/chado-field-type-test.twig b/templates/generator/chado_field/chado-field-type-test.twig new file mode 100644 index 0000000..422118a --- /dev/null +++ b/templates/generator/chado_field/chado-field-type-test.twig @@ -0,0 +1,199 @@ +drupal_connection = $this->container->get('database'); + + // First retrieve info from the YAML file for this particular test. + [$this->system_under_test, $this->scenarios] = $this->getTestInfoFromYaml($this->yaml_info_file); + $this->bundle_name = $this->system_under_test['bundle']['id']; + + // Create the test Chado installation we will be using. + if (!array_key_exists('chado_version', $this->system_under_test)) { + $this->system_under_test['chado_version'] = '1.3'; + } + $this->chado_connection = $this->getTestSchema( + ChadoTestKernelBase::PREPARE_TEST_CHADO, + $this->system_under_test['chado_version'] + ); + + // Next setup the environment according to the system under test. + $this->setupChadoEntityFieldTestEnvironment($this->system_under_test); + } + + /** + * Data Provider: works with the YAML to provide scenarios for testing. + * + * @return array + * List of scenarios to test where each one matches a key and label in the + * associated YAML scenarios. + */ + public static function provideScenarios() { + $scenarios = []; + + $scenarios[] = [ + 0, + "Default Values Only", + ]; + + return $scenarios; + } + + /** + * Tests the field through TripalEntity->save(). + * + * @param int $current_scenario_key + * The key of the scenario in the YAML. + * @param string $current_scenario_label + * The label of the scenario in the YAML. + * + * @dataProvider provideScenarios + */ + #[DataProvider('provideScenarios')] + public function testFieldTypeCrud(int $current_scenario_key, string $current_scenario_label) { + + // Retrieve the correct scenario. + $current_scenario = $this->scenarios[$current_scenario_key]; + $this->assertEquals($current_scenario_label, $current_scenario['label'], "We may not have retrieved the expected scenario as the labels did not match."); + + // 1. Create the entity with that value set. + $entity = TripalEntity::create([ + 'title' => $this->randomString(), + 'type' => $this->bundle_name, + ] + $current_scenario['create']['user_input']); + $this->assertInstanceOf(TripalEntity::class, $entity, "We were not able to create a piece of tripal content to test our " . $current_scenario['label'] . " scenario."); + $status = $entity->save(); + $this->assertEquals(SAVED_NEW, $status, "We expected to have saved a new entity for our " . $current_scenario['label'] . " scenario."); + + // @debug print_r($entity->toArray()); + // 2. Load the entity we just created so we can check the values. + $created_entity = TripalEntity::load($entity->id()); + $this->assertFieldValuesMatch($current_scenario['create']['expected'], $created_entity, $current_scenario['label'] . ' CREATE '); + + // 3. Make changes and then save again. + foreach ($current_scenario['edit']['user_input'] as $field_name => $new_values) { + $created_entity->set($field_name, $new_values); + } + // @debug print_r($created_entity->toArray()); + $status = $created_entity->save(); + $this->assertEquals(SAVED_UPDATED, $status, "We expected to have updated the existing entity for our " . $current_scenario['label'] . " scenario."); + + // 4. Load the entity we just updated so we can check the values. + $updated_entity = TripalEntity::load($created_entity->id()); + // @debug print_r($updated_entity->toArray()); + $this->assertFieldValuesMatch($current_scenario['edit']['expected'], $updated_entity, $current_scenario['label'] . ' EDIT '); + } + +} From a2f34701763bf1a5ac11eadb6bf4db407669cae8 Mon Sep 17 00:00:00 2001 From: Lacey-Anne Sanderson Date: Tue, 9 Dec 2025 18:00:32 +0000 Subject: [PATCH 5/8] Fix field prompt and default scenario record_id. --- .../TestChadoFieldTypeGenerator.php | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/Drush/Generators/TestChadoFieldTypeGenerator.php b/src/Drush/Generators/TestChadoFieldTypeGenerator.php index eb3e78e..9fb7212 100644 --- a/src/Drush/Generators/TestChadoFieldTypeGenerator.php +++ b/src/Drush/Generators/TestChadoFieldTypeGenerator.php @@ -81,11 +81,13 @@ protected function generate(array &$vars, Assets $assets): void { // For each field attached to this bundle, ask if they want to add it to // the system under test. + $dont_prompt_fields = $this->prompt->confirm('Do you want to exclude some fields for this content type', FALSE); foreach ($this->field_definitions as $field_name => $field_defn) { if (get_class($field_defn) == 'Drupal\field\Entity\FieldConfig') { - //if ($this->prompt->confirm(" - Add $field_name to the system-under-test?")) { - $this->addFieldInfo($vars['bundle_id'], $field_defn, $test_info_yaml); - //} + if (($dont_prompt_fields === FALSE) or $this->prompt->confirm("\tAdd $field_name to the system-under-test?")) { + print "\tAdding $field_name to the system-under-test.\n"; + $this->addFieldInfo($vars['bundle_id'], $field_defn, $test_info_yaml); + } } } @@ -199,21 +201,47 @@ protected function addDefaultScenario(array $vars, array &$test_info_yaml) { ]; foreach (['create', 'edit'] as $first_lvl) { - $scenario[$first_lvl] = []; - foreach (['user_input', 'expected'] as $second_lvl) { - $scenario[$first_lvl][$second_lvl] = []; - foreach ($test_info_yaml['system-under-test']['fields'] as $field_yml) { - $field_name = $field_yml['name']; - // @todo add these values based on the field definition. - $scenario[$first_lvl][$second_lvl][$field_name] = [ - 'record_id' => 0, - 'value' => '', - ]; + $scenario[$first_lvl] = [ + 'user_input' => [], + 'expected' => [], + ]; + foreach ($test_info_yaml['system-under-test']['fields'] as $field_yml) { + $field_name = $field_yml['name']; + $field_class = $field_yml['type_class']; + // Get each field to generate some sample values for testing. + $sample_value = $field_class::generateSampleValue($this->field_definitions[$field_name]); + if ($sample_value === NULL) { + $sample_value = []; + } + // Now set the sample value for both our input and expected. + $scenario[$first_lvl]['user_input'][$field_name] = $sample_value; + $scenario[$first_lvl]['expected'][$field_name] = $sample_value; + // We also need to update the record_id to match expectations. + if ($first_lvl == 'create') { + $user_input_record_id = 0; + $expected_record_id = 1; + } + else { + $user_input_record_id = 1; + $expected_record_id = 1; + } + // If single value field then set record_id directly. + if (array_key_exists('record_id', $scenario[$first_lvl]['user_input'][$field_name])) { + $scenario[$first_lvl]['user_input'][$field_name]['record_id'] = $user_input_record_id; + $scenario[$first_lvl]['expected'][$field_name]['record_id'] = $expected_record_id; + } + // If multi-value field then set record_id for each item. + elseif (array_key_exists(0, $scenario[$first_lvl]['user_input'][$field_name]) and array_key_exists('record_id', $scenario[$first_lvl]['user_input'][$field_name][0])) { + foreach (array_keys($scenario[$first_lvl]['user_input'][$field_name]) as $delta) { + $scenario[$first_lvl]['user_input'][$field_name][$delta]['record_id'] = $user_input_record_id; + $scenario[$first_lvl]['expected'][$field_name][$delta]['record_id'] = $expected_record_id; + } } } } $test_info_yaml['scenarios'][] = $scenario; + } } From 7558d6d662d5e9fad19b79dd3d0e887625dfa64e Mon Sep 17 00:00:00 2001 From: Lacey-Anne Sanderson Date: Tue, 9 Dec 2025 22:18:03 +0000 Subject: [PATCH 6/8] Add an extra test for other field type methods. --- .../chado_field/chado-field-type-test.twig | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/templates/generator/chado_field/chado-field-type-test.twig b/templates/generator/chado_field/chado-field-type-test.twig index 422118a..fbf73b9 100644 --- a/templates/generator/chado_field/chado-field-type-test.twig +++ b/templates/generator/chado_field/chado-field-type-test.twig @@ -131,6 +131,7 @@ class {{ test_class }} extends ChadoTestKernelBase { // Next setup the environment according to the system under test. $this->setupChadoEntityFieldTestEnvironment($this->system_under_test); + $this->installSchema('tripal_chado', ['tripal_custom_tables', 'tripal_mviews']); } /** @@ -196,4 +197,59 @@ class {{ test_class }} extends ChadoTestKernelBase { $this->assertFieldValuesMatch($current_scenario['edit']['expected'], $updated_entity, $current_scenario['label'] . ' EDIT '); } + /** + * Tests non-CRUD methods for the field types. + * + * Specifically, + * - generateSampleValue() + * - isCompatible() + * - getConstraints() + */ + public function testFieldTypeHelperMethods() { + // Retrieve the first scenario. + $current_scenario = $this->getYamlScenario(0, 'Default Values Only'); + + // Create the entity with that value set. + $entity = TripalEntity::create([ + 'title' => $this->randomString(), + 'type' => $this->bundle_name, + ] + $current_scenario['create']['user_input']); + $this->assertInstanceOf(TripalEntity::class, $entity, "We were not able to create a piece of tripal content to test our " . $current_scenario['label'] . " scenario."); + $status = $entity->save(); + $this->assertEquals(SAVED_NEW, $status, "We expected to have saved a new entity for our " . $current_scenario['label'] . " scenario."); + + // Create another content type with a chado_table not compatible with + // any fields. + $sad_bundle = $this->createTripalContentType(); + $this->chado_connection->query('CREATE TABLE IF NOT EXISTS {1:emptytable} (amount real)'); + $sad_bundle->setThirdPartySetting('tripal', 'chado_base_table', 'emptytable'); + $sad_bundle->save(); + + // For each of the fields in the system under test... + foreach ($this->system_under_test['fields'] as $field_info) { + $field_name = $field_info['name']; + $field_class = $field_info['type_class']; + $field_defn = $this->fieldConfig[$field_name]; + $field_item = $entity->get($field_name)->first(); + + // Check that we can generate a sample value. + $generated_value = $field_class::generateSampleValue($field_defn); + $this->assertIsArray($generated_value, "We expected $field_name::generateSampleValue() to generate an array."); + $this->assertArrayHasKey('record_id', $generated_value[0], "We expected the $field_name generated value to have a record_id."); + + // Check that we can confirm it is compatible with the current bundle. + $compatible = $field_item->isCompatible($entity->getBundle()); + $this->assertTrue($compatible, "We expect the $field_name field to be compatible with the current bundle (i.e. " . $this->bundle_name . ")."); + + // Check that it is not compatible with a bundle that has no compatible + // columns for any field. + $compatible = $field_item->isCompatible($sad_bundle); + $this->assertFalse($compatible, "We don't expect the $field_name field to be compatible with a bundle that has no chado table set."); + + // Check that we can retrieve constraints. + $constraints = $field_item->getConstraints(); + $this->assertIsArray($constraints, "We expected to at least be given an empty array when trying to retrieve the constraints for $field_name."); + } + } + } From b13eaec9aaca30c4a94f794c54c3e87e943feaa3 Mon Sep 17 00:00:00 2001 From: Lacey-Anne Sanderson Date: Wed, 17 Dec 2025 02:40:26 +0000 Subject: [PATCH 7/8] Reorganize the files. --- .../ChadoFieldTypeGenerator.php} | 8 ++++---- .../{chado_field => tests}/chado-field-type-test.twig | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename src/Drush/Generators/{TestChadoFieldTypeGenerator.php => Tests/ChadoFieldTypeGenerator.php} (97%) rename templates/generator/{chado_field => tests}/chado-field-type-test.twig (100%) diff --git a/src/Drush/Generators/TestChadoFieldTypeGenerator.php b/src/Drush/Generators/Tests/ChadoFieldTypeGenerator.php similarity index 97% rename from src/Drush/Generators/TestChadoFieldTypeGenerator.php rename to src/Drush/Generators/Tests/ChadoFieldTypeGenerator.php index 9fb7212..985b4a9 100644 --- a/src/Drush/Generators/TestChadoFieldTypeGenerator.php +++ b/src/Drush/Generators/Tests/ChadoFieldTypeGenerator.php @@ -1,6 +1,6 @@ Date: Wed, 17 Dec 2025 03:15:28 +0000 Subject: [PATCH 8/8] Separate into CRUD and Methods tests. --- .../Tests/ChadoFieldTypeGenerator.php | 77 ++++++- .../tests/chado-field-type-crud-test.twig | 200 ++++++++++++++++++ ...wig => chado-field-type-methods-test.twig} | 52 +---- 3 files changed, 268 insertions(+), 61 deletions(-) create mode 100644 templates/generator/tests/chado-field-type-crud-test.twig rename templates/generator/tests/{chado-field-type-test.twig => chado-field-type-methods-test.twig} (75%) diff --git a/src/Drush/Generators/Tests/ChadoFieldTypeGenerator.php b/src/Drush/Generators/Tests/ChadoFieldTypeGenerator.php index 985b4a9..979452b 100644 --- a/src/Drush/Generators/Tests/ChadoFieldTypeGenerator.php +++ b/src/Drush/Generators/Tests/ChadoFieldTypeGenerator.php @@ -61,20 +61,47 @@ protected function generate(array &$vars, Assets $assets): void { // Module Machine Name. $vars['machine_name'] = $this->prompt->askMachineName(); + // Chado Version. + $vars['chado_version'] = $this->prompt->ask('Chado Version', '1.3.3.013'); + + // Ask what file to save the test in. + $vars['test_class_prefix'] = $this->prompt->ask('What should be the prefix used to name the test classes generated?', 'BaseField'); + $vars['test_CRUD_class'] = $vars['test_class_prefix'] . 'CRUDTest'; + $vars['test_CRUD_yml'] = $vars['test_class_prefix'] . 'CRUD-TestInfo.yml'; + $vars['test_methods_class'] = $vars['test_class_prefix'] . 'MethodsTest'; + $vars['test_methods_yml'] = $vars['test_class_prefix'] . 'Methods-TestInfo.yml'; + $vars['test_path'] = 'tests/src/Kernel/Plugin/ChadoField/FieldType'; + + $this->generateCrudTest($vars, $assets); + $this->generateMethodsTest($vars, $assets); + } + + /** + * Generates the field test focused on testing create-update-delete (CRUD). + * + * @param array $vars + * The variables collected from the user. + * @see self::generate() + * @param \DrupalCodeGenerator\Asset\AssetCollection $assets + * The assets generated by the generator as a whole. + * + * @return void + * No need to return anything as all params are passed by reference. + */ + protected function generateCrudTest(array &$vars, Assets &$assets): void { + + print "\nThe following questions are specific to testing the create-update-delete (CRUD) of the indicated field.\n"; + // Now start building the test information yaml file. $test_info_yaml = [ 'system-under-test' => [ - 'chado_version' => '', + 'chado_version' => $vars['chado_version'], 'bundle' => [], 'fields' => [], ], 'scenarios' => [], ]; - // Chado Version. - $vars['chado_version'] = $this->prompt->ask('Chado Version', '1.3.3.013'); - $test_info_yaml['system-under-test']['chado_version'] = $vars['chado_version']; - // Bundle Information. $vars['bundle_id'] = $this->prompt->ask('Existing Tripal Content Type to test fields on', 'organism'); $this->addBundleInfo($vars['bundle_id'], $test_info_yaml); @@ -94,16 +121,44 @@ protected function generate(array &$vars, Assets $assets): void { // Now add a scenario based on the default values. $this->addDefaultScenario($vars, $test_info_yaml); - // Ask what file to save the test in. - $vars['test_class'] = $this->prompt->ask('What should be the class name for the generated test (must end with "Test")', 'BaseFieldTest'); - $vars['test_yml'] = trim($vars['test_class'], 'Test') . '-' . $vars['bundle_id'] . '-TestInfo.yml'; - $vars['test_path'] = $this->prompt->ask('Where should the test files be created (relative to module directory)', 'tests/src/Kernel/Plugin/ChadoField/FieldType'); + // We are now done generating the yaml file so lets create that. + $yaml_file = $assets->addFile('{test_path}/{test_CRUD_yml}'); + $yaml_file->content(Yaml::dump($test_info_yaml, 8, 2, Yaml::DUMP_COMPACT_NESTED_MAPPING)); + + // Then lets create the test file. + $assets->addFile('{test_path}/{test_CRUD_class}.php', 'chado-field-type-crud-test.twig'); + } + + /** + * Generates the field test focused on testing methods directly. + * + * @param array $vars + * The variables collected from the user. + * @see self::generate() + * @param \DrupalCodeGenerator\Asset\AssetCollection $assets + * The assets generated by the generator as a whole. + * + * @return void + * No need to return anything as all params are passed by reference. + */ + protected function generateMethodsTest(array &$vars, Assets &$assets): void { + + print "\nThe following questions are specific to testing various methods of the indicated field.\n"; + + // Now start building the test information yaml file. + $test_info_yaml = [ + 'system-under-test' => [ + 'chado_version' => $vars['chado_version'], + ], + 'scenarios' => [], + ]; // We are now done generating the yaml file so lets create that. - $yaml_file = $assets->addFile('{test_path}/{test_yml}'); + $yaml_file = $assets->addFile('{test_path}/{test_methods_yml}'); $yaml_file->content(Yaml::dump($test_info_yaml, 8, 2, Yaml::DUMP_COMPACT_NESTED_MAPPING)); + // Then lets create the test file. - $assets->addFile('{test_path}/{test_class}.php', 'chado-field-type-test.twig'); + $assets->addFile('{test_path}/{test_methods_class}.php', 'chado-field-type-methods-test.twig'); } /** diff --git a/templates/generator/tests/chado-field-type-crud-test.twig b/templates/generator/tests/chado-field-type-crud-test.twig new file mode 100644 index 0000000..351bf49 --- /dev/null +++ b/templates/generator/tests/chado-field-type-crud-test.twig @@ -0,0 +1,200 @@ +drupal_connection = $this->container->get('database'); + + // First retrieve info from the YAML file for this particular test. + [$this->system_under_test, $this->scenarios] = $this->getTestInfoFromYaml($this->yaml_info_file); + $this->bundle_name = $this->system_under_test['bundle']['id']; + + // Create the test Chado installation we will be using. + if (!array_key_exists('chado_version', $this->system_under_test)) { + $this->system_under_test['chado_version'] = '1.3'; + } + $this->chado_connection = $this->getTestSchema( + ChadoTestKernelBase::PREPARE_TEST_CHADO, + $this->system_under_test['chado_version'] + ); + + // Next setup the environment according to the system under test. + $this->setupChadoEntityFieldTestEnvironment($this->system_under_test); + $this->installSchema('tripal_chado', ['tripal_custom_tables', 'tripal_mviews']); + } + + /** + * Data Provider: works with the YAML to provide scenarios for testing. + * + * @return array + * List of scenarios to test where each one matches a key and label in the + * associated YAML scenarios. + */ + public static function provideScenarios() { + $scenarios = []; + + $scenarios[] = [ + 0, + "Default Values Only", + ]; + + return $scenarios; + } + + /** + * Tests the field through TripalEntity->save(). + * + * @param int $current_scenario_key + * The key of the scenario in the YAML. + * @param string $current_scenario_label + * The label of the scenario in the YAML. + * + * @dataProvider provideScenarios + */ + #[DataProvider('provideScenarios')] + public function testFieldTypeCrud(int $current_scenario_key, string $current_scenario_label) { + + // Retrieve the correct scenario. + $current_scenario = $this->scenarios[$current_scenario_key]; + $this->assertEquals($current_scenario_label, $current_scenario['label'], "We may not have retrieved the expected scenario as the labels did not match."); + + // 1. Create the entity with that value set. + $entity = TripalEntity::create([ + 'title' => $this->randomString(), + 'type' => $this->bundle_name, + ] + $current_scenario['create']['user_input']); + $this->assertInstanceOf(TripalEntity::class, $entity, "We were not able to create a piece of tripal content to test our " . $current_scenario['label'] . " scenario."); + $status = $entity->save(); + $this->assertEquals(SAVED_NEW, $status, "We expected to have saved a new entity for our " . $current_scenario['label'] . " scenario."); + + // @debug print_r($entity->toArray()); + // 2. Load the entity we just created so we can check the values. + $created_entity = TripalEntity::load($entity->id()); + $this->assertFieldValuesMatch($current_scenario['create']['expected'], $created_entity, $current_scenario['label'] . ' CREATE '); + + // 3. Make changes and then save again. + foreach ($current_scenario['edit']['user_input'] as $field_name => $new_values) { + $created_entity->set($field_name, $new_values); + } + // @debug print_r($created_entity->toArray()); + $status = $created_entity->save(); + $this->assertEquals(SAVED_UPDATED, $status, "We expected to have updated the existing entity for our " . $current_scenario['label'] . " scenario."); + + // 4. Load the entity we just updated so we can check the values. + $updated_entity = TripalEntity::load($created_entity->id()); + // @debug print_r($updated_entity->toArray()); + $this->assertFieldValuesMatch($current_scenario['edit']['expected'], $updated_entity, $current_scenario['label'] . ' EDIT '); + } + +} diff --git a/templates/generator/tests/chado-field-type-test.twig b/templates/generator/tests/chado-field-type-methods-test.twig similarity index 75% rename from templates/generator/tests/chado-field-type-test.twig rename to templates/generator/tests/chado-field-type-methods-test.twig index fbf73b9..e72a757 100644 --- a/templates/generator/tests/chado-field-type-test.twig +++ b/templates/generator/tests/chado-field-type-methods-test.twig @@ -14,14 +14,11 @@ use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; * * Specifically focused on create + update actions performed on the entity * directly. Both TripalEntity, ChadoStorage and the field will be covered. - * - * @group TripalField - * @group ChadoField */ #[Group('tripal-field')] #[Group('chado-field')] #[RunTestsInSeparateProcesses] -class {{ test_class }} extends ChadoTestKernelBase { +class {{ test_methods_class }} extends ChadoTestKernelBase { use ChadoFieldTestTrait; @@ -61,7 +58,7 @@ class {{ test_class }} extends ChadoTestKernelBase { * * @var string */ - protected string $yaml_info_file = __DIR__ . '/{{ test_yml }}'; + protected string $yaml_info_file = __DIR__ . '/{{ test_methods_yml }}'; /** * Describes the environment to setup for this test. @@ -152,51 +149,6 @@ class {{ test_class }} extends ChadoTestKernelBase { return $scenarios; } - /** - * Tests the field through TripalEntity->save(). - * - * @param int $current_scenario_key - * The key of the scenario in the YAML. - * @param string $current_scenario_label - * The label of the scenario in the YAML. - * - * @dataProvider provideScenarios - */ - #[DataProvider('provideScenarios')] - public function testFieldTypeCrud(int $current_scenario_key, string $current_scenario_label) { - - // Retrieve the correct scenario. - $current_scenario = $this->scenarios[$current_scenario_key]; - $this->assertEquals($current_scenario_label, $current_scenario['label'], "We may not have retrieved the expected scenario as the labels did not match."); - - // 1. Create the entity with that value set. - $entity = TripalEntity::create([ - 'title' => $this->randomString(), - 'type' => $this->bundle_name, - ] + $current_scenario['create']['user_input']); - $this->assertInstanceOf(TripalEntity::class, $entity, "We were not able to create a piece of tripal content to test our " . $current_scenario['label'] . " scenario."); - $status = $entity->save(); - $this->assertEquals(SAVED_NEW, $status, "We expected to have saved a new entity for our " . $current_scenario['label'] . " scenario."); - - // @debug print_r($entity->toArray()); - // 2. Load the entity we just created so we can check the values. - $created_entity = TripalEntity::load($entity->id()); - $this->assertFieldValuesMatch($current_scenario['create']['expected'], $created_entity, $current_scenario['label'] . ' CREATE '); - - // 3. Make changes and then save again. - foreach ($current_scenario['edit']['user_input'] as $field_name => $new_values) { - $created_entity->set($field_name, $new_values); - } - // @debug print_r($created_entity->toArray()); - $status = $created_entity->save(); - $this->assertEquals(SAVED_UPDATED, $status, "We expected to have updated the existing entity for our " . $current_scenario['label'] . " scenario."); - - // 4. Load the entity we just updated so we can check the values. - $updated_entity = TripalEntity::load($created_entity->id()); - // @debug print_r($updated_entity->toArray()); - $this->assertFieldValuesMatch($current_scenario['edit']['expected'], $updated_entity, $current_scenario['label'] . ' EDIT '); - } - /** * Tests non-CRUD methods for the field types. *