Hey all,

Im having trouble setting up simpletest for testing one of my custom CCK content types. At the moment my Setup function is like this:

public function setUp() {
		$args = func_get_args();
	    $modules = array_merge(array('content', 'content copy', 'content permissions', 'option widgets', 'text', 'node reference' , 'user reference', 'schema'), $args);
   		call_user_func_array(array('parent','setUp'), $modules);
    
   		// Create and log in our user
	    $permissions = array(
        'access content',
        'administer content types',
        'administer nodes',
        'administer filters',
      	);
      	
   		$privileged_user = $this->drupalCreateUser($permissions);
	    $this->drupalLogin($privileged_user);
	  
	    $project = 'project';
    	$project_name = 'Project';
	    $edit = array(
	      'type' => $project,
	      'name' => $project_name,
	      'body_label' => 'Project Description',
	      'description' => 'PwnBug Project'
	    );
	    $this->drupalPost('admin/content/types/add', $edit, 'Save content type');
	    $admin_project_url = 'admin/content/node-type/'. $project;
	    
		// Setting up the projectmanager field!
	    $projectmanager = array(
	      '_add_new_field[label]' => 'testfield',
	   	  '_add_new_field[weight]' => '-99',
	      '_add_new_field[field_name]' => 'testfield',
	      //'_add_new_field[type]' => 'text',
          //'_add_new_field[widget_type]' => 'text_textfield',
	      '_add_new_field[type]' => 'userreference',
	      '_add_new_field[widget_type]' => 'userreference_select',
	    );
	    $this->drupalPost($admin_project_url .'/fields', $projectmanager, 'Save');
	}

Things all go bad when with the user reference field. When i use a text field (currently commented in the code) it works fine. All the modules required are added in the Setup function. The form it tries to POST on is the correct form. Ive debugged through CCK abit so im sure the names of the type and widget type are correct.

Ive tried to check the output with the outputScreenContents function as used in the Simpletest tutorial on http://drupal.org/node/395012. If just shows the form so thats not of much help either.

UPDATE: Ive also tried too add my own module to load in the setup. My own module programatically adds a CCK content type. This solution is suggested in topic: http://drupal.org/node/316260. However it seems like that install is not getting hit when i debug it :S

Im kinda stuck at the moment, any help on this matter will be greatly appreciated!

Cheers

Comments

Anonymous’s picture

Hey all,

Still struggling with the same issue....tried a different approach, adding my CCK content type with the import form (admin/content/types/import). My Simpletest Setup() function looks like this:


		$args = func_get_args();
	    $modules = array_merge(array('content', 'content copy','fieldgroup','content permissions', 'option widgets', 'text', 'node reference' , 'user reference', 'schema', 'pwnbug project'), $args);
   		call_user_func_array(array('parent','setUp'), $modules);
    
   		// Create and log in our user
	    $permissions = array(
        'access content',
        'administer content types',
        'administer nodes',
        'administer filters',
      	);
      	
   		$privileged_user = $this->drupalCreateUser($permissions);
	    $this->drupalLogin($privileged_user);
	    
		// Create the content type from file: getting the CCK data
		$filename = drupal_get_path('module','pwnbug_project').'/project.cck';
		$content = implode ('', file ($filename));
		 
		 // Create the Project content type from file: importing data into CCK
		$form_state = array(
	         'values' => array(
	           'type_name' => '<create>',
	           'macro' => $content,
	   	   'op' => 'Import'
	         ),
	        );
		   
		$this->drupalPost('admin/content/types/import', $form, 'Import');

Still no luck...when i run my tests i get errors like:

Failed to set field type_name to (fail1)
Failed to set field macro to $content[type]....huge array (fail2)
Found the Import button (fail3)
Found the requested form fields at admin/content/types/import (fail 4)

It seems like somehow the form doesn't accept the data i feed it... again any on this matter will be greatly appreciated!

UPDATE: ive used the simpletest automater to simulate what i want to do. I've copied the exact code which it produced...which worked when i clicked through the test and still....the test fails with the same four fails as described above...i honestly don't get how it is possible to that the simulated tests works and when it with the same code it fails :S

There must be some way to get simpletest working with custom CCK types....anywone willing to share how they did it..?? It would make me a happy camper!

UPDATE: I have put this matter up for discussion in the Drupal Groups, check it out for more info: http://groups.drupal.org/node/26251

Cheers!

icstars’s picture

this took two days to get right. this procedure assumes you have this http://drupal.org/files/issues/297972_drupal_execute_batch_api.patch installed in your forms.inc. otherwise the drupal_execute function will not work for form submission using the content_copy module.

most of my errors were from incorrect setup. to help diagnose i put the following code in my setUp right after the modules:

$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer nodes', 'access administration pages', 'administer site configuration', 'access site reports', 'administer permissions', 'administer users'));
$this->drupalLogin($admin_user);
$this->outputAdminPage('modules', 'modules', 'admin/build/modules');
$this->outputAdminPage('watchdog', 'watchdog', 'admin/reports/dblog');
$this->outputAdminPage('content types', 'content types', 'admin/content/types');
$this->outputAdminPage('permissions', 'permissions', 'admin/user/permissions');

where outputAdminPage was defined in the class as:

  private function outputAdminPage($description, $basename, $url) {
    // This is a hack to get a directory that won't be cleaned up by simpletest
    $file_dir = file_directory_path();
    $file_dir .= '/../simpletest_output_pages';

    if (!is_dir($file_dir)) {
      mkdir($file_dir, 0777, TRUE);
    }
    $output_path = "$file_dir/$basename." . $this->randomName(10) . '.html';
    $this->drupalGet($url);
    $rv = file_put_contents($output_path, $this->drupalGetContent());
    $this->pass("$description: Contents of result page are ".l('here',$output_path));
  }  

this gives you the ability to literally "see" what's going on in the simpletest drupal instance.

the core challenge with getting a cck installed is that its using drupal_execute and a form submission which is instrumented to report form errors, not watchdog errors, and since the forms are being submitted via code, you never see any failures, just downstream damage.

the first step was to export my custom cck content type to a file and save it in a new cck folder under my module folder.

then i created the following mymodule.install file:

function zbpm_install() {
  drupal_set_message('Installing Zbpm.');
  zbpm_install_content_types();
}

function zbpm_import_content_type($type = '<create>', $macro = '', $file = '') {
  if(!module_exists("content_copy")){
    drupal_set_message('Programmatically creating CCK fields requires the Content Copy module. Exiting.');
    watchdog ('zbpm install', 'content copy module not installed');
    return;
  }
  
  include_once( $_SERVER['DOCUMENT_ROOT']. base_path() . drupal_get_path('module', 'content') .'/includes/content.admin.inc');
  include_once( $_SERVER['DOCUMENT_ROOT']. base_path() . drupal_get_path('module', 'node') .'/content_types.inc');
  
  drupal_set_message('Installing:' . $file);
  $values = array();
  $values['type_name'] = $type;
  if($file){
    if(file_exists($file)){
      $values['macro'] = file_get_contents($file);
    }else{
      drupal_set_message('Unable to read input file for import. Exiting.');
	  watchdog ('zbpm install', "Unable to read input file for import. Exiting.");
      return;
    }
  }elseif($macro){
    $values['macro'] = $macro;
  }
  $form_state = array();
  $form_state['values'] = $values;
  //drupal_set_message('<pre>DEBUG: '.print_r($values['macro'],1).'</pre>');
  
  drupal_execute("content_copy_import_form", $form_state);
  drupal_set_message("Installed $type content type and cck fields");
  watchdog ('zbpm install', "Installed $type content type and cck fields");
  //watchdog ('zbpm install', '<pre>DEBUG: '.print_r($form,1).'</pre>');
  content_clear_type_cache();
}

function zbpm_install_content_types(){
	  		
  if( ! user_access( 'administer content types' )){
    drupal_set_message( 'Sorry, You don\'t have permission to do that' );
    watchdog ('zbpm install', 'insufficient privileges to create content types');
    return drupal_access_denied();
  }
  if( ! variable_get( 'zbpm_content_types_installed', 0 ) ){
    drupal_set_message('Installing content types and cck fields');
    $pathtocck = $_SERVER['DOCUMENT_ROOT']. base_path() . drupal_get_path('module', 'zbpm') . '/cck/';
  
    zbpm_import_content_type($type = '<create>', $macro = '', $file = $pathtocck . 'zbpm_proc.cck');
    
    variable_set( 'zbpm_content_types_installed', 1 );
  }else{
    drupal_set_message( 'Zbpm content types are already installed.');
    watchdog ('zbpm install', 'content types installed variable already set to true');
  }
}

in addition, i modified the content_copy_import_form_submit function within the content_copy module of cck to use watchdog on errors:

function content_copy_import_form_submit($form, &$form_state) {
  $form_values = $form_state['values'];
  // Get the content type we are importing into.
  $type_name = $form_values['type_name'];
  $type_label = node_get_types('name', $type_name);

  $content = NULL;
  // Convert the import formatted text back into a $content array.
  // Return if errors generated or not an array.
  // Use '@' to suppress errors about undefined constants in the macro.
  @eval($form_values['macro']);

  // Preliminary error trapping, must have valid arrays to work with.
  if (!isset($content) || !isset($content['type']) || !is_array($content) || !is_array($content['type'])) {
    form_set_error('macro', t('The import data is not valid import text.'));
	watchdog('cck', 'The import data is not valid import text.');
    return;
  }

  module_load_include('inc', 'content', 'includes/content.crud');

  // Get all type and field info for this database.
  $content_info = _content_type_info();

  $imported_type = $content['type'];
  $imported_type_name = $imported_type['type'];
  $imported_type_label = $imported_type['name'];

  // It is allowed to import a type with no fields,
  // so the fields array could be empty and must be cast as an array.
  $imported_fields = isset($content['fields']) ? $content['fields'] : array();

  // Perform more pre-import error trapping.
  // If there are potential problems, exit without doing the import.
  $not_enabled = array();

  // The groups array could be empty and still valid, make sure to cast it as an array.
  // If there are groups in the import, make sure the fieldgroup module is enabled.
  $imported_groups = array();
  if (isset($content['groups']) && module_exists('fieldgroup')) {
    $imported_groups = (array) $content['groups'];
  }
  elseif (isset($content['groups']) && is_array($content['groups'])) {
    $not_enabled[] = 'fieldgroup';
  }

  // Make sure that all the field and widget modules in the import are enabled in this database.
  foreach ($imported_fields as $import) {
    $field = content_field_instance_collapse($import);
    if (empty($field['module']) || empty($field['widget_module'])) {
      $not_enabled[] = $field['field_name'];
    }
    else {
      if (!module_exists($field['module'])) {
        $not_enabled[] = $field['module'];
      }
      if (!module_exists($field['widget_module'])) {
        $not_enabled[] = $field['widget_module'];
      }
    }
  }

  // If any required module is not enabled, set an error message and exit.
  if ($not_enabled) {
    form_set_error('macro', t('The following modules must be enabled for this import to work: %modules.', array(
        '%modules' => implode(', ', array_unique($not_enabled))
        )));
	watchdog('cck', t('The %modules modules must be enabled for this import to work.', array(
        '%modules' => implode(', ', array_unique($not_enabled))
        )));
  }

  // Make sure the imported content type doesn't already exist in the database.
  if ($form_values['type_name'] == '<create>') {
    if (in_array($imported_type_name, array_keys($content_info['content types']))) {
      form_set_error('macro', t('The content type %type already exists in this database.', array(
            '%type' => $imported_type_name
            )));
    }
  }

  if (form_get_errors()) {
    drupal_set_message(t('Exiting. No import performed.'), 'error');
    return;
  }

  // Create the content type, if requested.
  if ($form_values['type_name'] == '<create>') {

    $type = (object) $imported_type;
    $values = $imported_type;
    // Prevent a warning in node/content_types.inc
    $type->has_title = TRUE;
    $type_form_state = array('values' => $values);

    // There's no API for creating node types, we still have to use drupal_execute().
    drupal_execute('node_type_form', $type_form_state, $type);

    // Reset type and database values once new type has been added.
    $type_name  = $imported_type_name;
    $type_label = node_get_types('name', $type_name);
    content_clear_type_cache();
    $content_info = _content_type_info();

    if (form_get_errors() || !isset($content_info['content types']) || !is_array($content_info['content types'][$type_name])) {
       drupal_set_message(t('An error has occurred adding the content type %type.<br/>Please check the errors displayed for more details.', array(
            '%type' => $imported_type_name
            )));
		watchdog('cck', t('An error has occurred adding the content type %type.<br/>Please check the errors displayed for more details.', array(
            '%type' => $imported_type_name
            )));
            
       return;
    }
  }

  // Create the groups for this type, if they don't already exist.
  if (module_exists('fieldgroup') && $imported_groups) {
    foreach ($imported_groups as $group) {
      $group_name = $group['group_name'];
      fieldgroup_save_group($type_name, $group);
    }
    // Reset the static variable in fieldgroup_groups() with new data.
    fieldgroup_groups('', FALSE, TRUE);
  }

  // Iterate through the field forms in the import and execute each.
  $rebuild = FALSE;
  foreach ($imported_fields as $field) {

    // Make sure the field doesn't already exist in the type.
    // If so, do nothing, fields can't be duplicated within a content type.
    $field_name   = $field['field_name'];

    // Might need to overwrite the content type name if a new type was created.
    $field['type_name'] = $type_name;

    if (!empty($field['field_name']) && isset($content_info['content types'][$type_name]['fields'][$field_name])) {
      drupal_set_message(t('The imported field %field_label (%field_name) was not added to %type because that field already exists in %type.', array(
        '%field_label' => $field['label'], '%field_name' => $field_name, '%type' => $type_label)));
    }
    else {
      $field = content_field_instance_create($field, FALSE);
      $rebuild = TRUE;
      drupal_set_message(t('The field %field_label (%field_name) was added to the content type %type.', array(
        '%field_label' => $field['widget']['label'], '%field_name' => $field_name, '%type' => $type_label)));
    }

    // Fieldgroup module erases all group related data when a module that
    // provides a content type is disabled, but CCK does not remove the fields.
    // In this case, we should ensure group data related to fields is properly
    // restored. Hence, we need to update field group data for newly imported
    // field, but also for fields that already exist.
    if (module_exists('fieldgroup') && isset($imported_groups)) {
      fieldgroup_update_fields($field);
    }
  }

  // Clear caches and rebuild menu only if any field has been created.
  if ($rebuild) {
    content_clear_type_cache(TRUE);
    menu_rebuild();
  }

  // Import weights of non-CCK fields.
  if (isset($content['extra'])) {
    variable_set('content_extra_weights_'. $type_name, $content['extra']);
  }
}

and here is the mymodule.test file. to get the exact field names for adding the fake instance of my content type, e.g. whether they have [0] or [value] or [nid] etc. i viewed the html source on the node/add/mytype page and looked at the name attribute of the html field.

class zbpmTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'zbpm',
      'description' => 'Ensure that the business process management module functions properly.',
      'group' => 'Zap',
    );
  }
  public function setUp() {    
    //run this query to get exact module names: select name, filename from system where status = '1' and filename like 'sites/all/%' order by name
    $args = func_get_args();
    $modules = array_merge(array('content', 
    	'schema', 
    	'text', 
    	'content_copy', 
    	'number', 
    	'optionwidgets', 
    	'mcd', 
    	'zbpm'), $args);
    call_user_func_array(array('parent','setUp'), $modules);
    module_load_include('inc', 'content', 'includes/content.crud');

	// Create admin user
	$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer nodes', 'access administration pages', 'administer site configuration', 'access site reports', 'administer permissions', 'administer users'));
	$this->drupalLogin($admin_user);

    $this->outputAdminPage('modules', 'modules', 'admin/build/modules');
    $this->outputAdminPage('watchdog', 'watchdog', 'admin/reports/dblog');
    $this->outputAdminPage('content types', 'content types', 'admin/content/types');
    $this->outputAdminPage('permissions', 'permissions', 'admin/user/permissions');
    
    // Create and log in our user
    $privileged_user = $this->drupalCreateUser(array('create zbpm_proc content'));
    $this->drupalLogin($privileged_user);
  }  
  
  // Create a process node using the node form
  public function testProcessCreate() {
    // Create node to edit.
    $edit = array (
      'title' => $this->randomName(8),
      'body' => $this->randomName(16),
      'field_zap_proc_callback[0][value]' => $this->randomName(16),
      'field_zap_proc_status[value]'=> '1',
      'field_zap_proc_sequential[value]'=>'1',
    );
    
    $this->drupalPost('node/add/zbpm-proc', $edit, t('Save'));
    $this->assertText(t('Process @title has been created.', array('@title' => $edit['title'])));

    // For debugging we can output the page so it can be opened with a browser
    // Remove this line when the test has been debugged
    $this->outputScreenContents('After page creation', 'testProcessCreate');
  }
  // A utility function we can use in debugging our test. This outputs the screen
  // as an HTML file we can view, and uses the pass() method to report it to us
  // in the simpletest web interface
  private function outputScreenContents($description, $basename) {
    // This is a hack to get a directory that won't be cleaned up by simpletest
    $file_dir = file_directory_path();
    $file_dir .= '/../simpletest_output_pages';

    if (!is_dir($file_dir)) {
      mkdir($file_dir, 0777, TRUE);
    }
    $output_path = "$file_dir/$basename." . $this->randomName(10) . '.html';
    $rv = file_put_contents($output_path, $this->drupalGetContent());
    $this->pass("$description: Contents of result page are ".l('here',$output_path));
  }  
  private function outputAdminPage($description, $basename, $url) {
	
    // This is a hack to get a directory that won't be cleaned up by simpletest
    $file_dir = file_directory_path();
    $file_dir .= '/../simpletest_output_pages';

    if (!is_dir($file_dir)) {
      mkdir($file_dir, 0777, TRUE);
    }
    $output_path = "$file_dir/$basename." . $this->randomName(10) . '.html';
    $this->drupalGet($url);
    $rv = file_put_contents($output_path, $this->drupalGetContent());
    $this->pass("$description: Contents of result page are ".l('here',$output_path));
  }  
}