Defects: Building a custom defect plugin

TestRail's defect plugins are useful to integrate TestRail with third-party bug and issue tracking tools. TestRail comes with ready-to-use plugin scripts to integrate with many popular tools. That said, the defect plugins that ship with TestRail were designed to work with standard configurations of defect tracking tools and you can customize the defect plugins for your needs.

If you have customized your defect tracking tool (for example, by adding new required custom fields) or you would like to change the behavior of the defect integration, you can customize the defect plugins or even build new plugins. Customizing the defect plugins or building new plugins can be useful for the following scenarios:

  • You've configured your defect tracking tool to require additional custom fields and you would like to add those fields to the Push Defect dialog
  • You want to change the behavior of the default defect plugins (e.g. adding user mapping, adding additional information to the submitted bug reports etc.)
  • You are using a custom/not-yet-supported tool and you would like to build an integration for that tool

This article explains the basic architecture of defect plugins and how to customize them. We also have concrete examples on how to add new fields to the push defect dialog and on how to add user mapping capabilities to a defect script.

Getting started

Defect plugins are developed in PHP and you should have a basic understanding of PHP when you customize or build your own defect plugin. You can also contact us if you prefer us to build your customizations and we would be happy to take a look at your requirements.

There are two directories where TestRail looks for defect plugins when the application is initialized. The first directory is where TestRail also stores its built-in default plugins. You should never add or modify plugin scripts in this directory, as those files will be overridden when you update TestRail:

<TestRail>/app/plugins/defects

Modified or new plugin scripts should always be placed into the custom defect plugin directory:

<TestRail>/custom/defects

Plugin files

A defect plugin is a simple PHP script that implements a class that is used by TestRail to communicate with the defect tracking tool. To customize or create your own defect plugin, you can copy a ready-to-use script from the above mentioned application directory to the custom defect plugin directory and rename the file. You can alternatively start with the script skeleton below.

For example, if you would like to customize TestRail's Jira defect plugin, copy the file from <TestRail>/app/plugins/defects to <TestRail>/custom/defects. You should also rename the file, as TestRail uses the filename of the defect plugin as its unique (class) identifier. For example, you could rename the file to Jira_ExampleInc.php so that it includes your organization's name or the name of the custom tool you would like to integrate TestRail with. Please see the next section on how to adjust the defect class name to match the filename.

Plugin basics

A defect plugin is a simple PHP class that implements specific methods that TestRail calls to communicate with the defect tracking tool. TestRail uses the file name of the script as unique identifier for the script. The plugin class name must be based on the file name. For example, if the plugin script file is named Jira.php, the plugin class name must be called Jira_defect_plugin.

Every defect plugin class must extend TestRail's Defect_plugin base class. The following class skeleton implements all base methods and explains the basic methods and arguments.

<?php
// MyPlugin.php
class MyPlugin_defect_plugin extends Defect_plugin
{
	// Get Meta
	//
	// Expected to return meta data for this plugin such as Author,
	// Version, Description and supported plugin capabilities.
	public function get_meta()
	{
	}
 
	// Validate Config
	//
	// Validates the plugin configuration that is entered in the site
	// or project settings. Expected to throw a ValidationException
	// in case the passed configuration does not validate.
	public function validate_config($config)
	{
	}
 
	// Configure
	//
	// Passes the configuration for the plugin as specified in the
	// site or project settings (as string).
	public function configure($config)
	{
	}
 
	// Prepare Push
	//
	// Signals if the plugin requires/supports a form to submit the
	// defect. Returns 'false' to signal that no form is required.
	// Otherwise the plugin should return the form description that
	// is used to render the form and display it to the user.
	public function prepare_push($context)
	{
	}
 
	// Prepare Field
	//
	// Called for each field of the push form to gather the field
	// data for the form. The plugin can return default values or
	// available options for the field (in case of a dropdown
	// box, for example), among other values.
	public function prepare_field($context, $input, $field)
	{
	}
 
	// Validate Push
	//
	// Called before the actual push attempt is processed (in case
	// the plugin uses a custom form). Useful for adding custom
	// validation functionality for the form that is not covered by
	// TestRail itself. Expected to throw a ValidationException in
	// case the passed input of the custom form does not validate.
	public function validate_push($context, $input)
	{
	}
 
	// Push
	//
	// Executes the actual push request by adding a new case to the
	// defect tracker. Expected to return the new defect ID (as
	// string or integer).
	public function push($context, $input)
	{
	}
 
	// Lookup
	//
	// Looks up the defect/issue/case with the given ID and returns
	// its properties as key/value array. The following return values
	// are required:
	// 
	// id:           The ID of the defect (usually the same as the
	//               passed ID)
	// title:        The title/summary of the defect
	// status_id:    The status ID of the defect. Possible values
	//               are:
	//               - GI_DEFECTS_STATUS_OPEN
	//               - GI_DEFECTS_STATUS_RESOLVED
	//               - GI_DEFECTS_STATUS_CLOSED
	// status:       The status as text
	// 	             
	//
	// The following properties are optional:
	//
	// url:          The full HTTP url to view the defect
	// description:  The description of the defect (supports HTML,
	//               the plugin is responsible for providing well-
	//               formed and valid HTML and must escape data
	//               that is not expected to be rendered as HTML).
	// attributes:   A key/value list of additional attributes.
	//               The value also supports HTML with the same
	//               implications as for the description.
	public function lookup($defect_id)
	{
	}
}

It's not always necessary to fully implement all methods though. Defect plugins (currently) provide two capabilities: pushing defects to an external application and looking up defect information. Depending on the plugin you plan to write and depending on the context, it can make sense to only implement one of these capabilities.

For example, if you planned to implement a plugin that allows testers to send an email via the Push dialog, there wouldn't be a need to implement the lookup functionality. Likewise, if your defect tracking tool allows you to retrieve defect information but you prefer to use the defect tracker's bug report form, you wouldn't have to implement the push functionality.

Defect plugins are quite flexible in general. For example, while all defect plugins that currently ship with TestRail display a dialog to let testers customize the entered bug report, having a dialog is not always necessary. The defect plugin could alternatively automatically submit a bug report in the background without any user interaction.

Building your own plugin

This section walks you through creating your first defect plugin and explains the various plugin methods and idioms. Even if you don't plan to create your own plugin from scratch but just want to customize an existing plugin, this is the best way to learn about the inner workings of the plugins. We will be creating a new defect plugin for the fictional bug tracking tool Bugs and the following sections explain the steps to create a new plugin.

Before we begin with with our new plugin, it's important to note that TestRail expects (and provides) all strings and text in UTF-8 encoding. This means that all strings and texts that you return to (or receive from) TestRail have to be encoded in UTF-8. This is especially import if you are processing non-western characters, but you also need to take this into account for non-ASCII special characters etc.

Creating the file

The first step to create a new defect plugin is to create the actual script file. We will call the file Bugs.php for this example and place it into TestRail's custom defect plugin directory (<TestRail>/custom/defects). If you plan to use an existing defect plugin (all default defect plugins come with full source code and can be found in the <TestRail>/app/plugins/defects directory), make sure to copy it to the custom defect plugin directory and rename it. Our basic defect plugin for Bugz looks like this (note that we don't close the <?php opening tag at the end of the file; this is a trick to prevent empty lines at the end of the file that may break PHP's header output):

<?php
 
class Bugs_defect_plugin extends Defect_plugin
{
}

Returning meta data

The first thing we need to implement is the get_meta method. This method tells TestRail a few things about the plugin such as the author's name, the version of the plugin, the supported capabilities and configuration details. To do this, the method simply returns an array with these details like this:

public function get_meta()
{
	return array(
		'author' => 'Example Inc.',
		'version' => '1.0',
		'description' => 'Bugs defect plugin for TestRail',
		'can_push' => false,
		'can_lookup' => false,
		'default_config' => 
			"; Please configure your Bugs connection below\n" .
			"[connection]\n" .
			"address=http://<your-server>/\n" .
			"user=testrail\n" .
			"password=secret"
	);
}

Especially important are the can_push and can_lookup properties. These properties tell TestRail which capabilities are implemented by the defect plugin. While there are only the Push and Lookup capabilities at the moment, it's possible that new capabilities will get added in future TestRail versions.

The default_config property is also interesting here. Defect plugins can be configured globally in TestRail under Administration > Site Settings or per project under Administration > Projects. When an administrator configures a plugin, the default configuration as specified in the default_config is loaded into the configuration text field.

Configuration

A defect plugin usually requires some configuration settings such as the address of the defect tracker, a username and password, and similar options. When an administrator configures a defect plugin in TestRail, a text field is used to enter configuration settings. This allows plugins to use their own configuration format, as a text field supports many different notations (such as INI options, XML etc.).

TestRail's default defect plugins use the INI file notation for configuration settings, so [sections] and name=value pairs are used to specify settings. As TestRail comes with methods to read such configuration options, it's usually a good idea to use the INI notation.

When an administrator configures a plugin, TestRail asks the plugin to verify the entered configuration settings. As TestRail has no knowledge about required configuration settings (or even the format of the configuration settings), the defect plugin should verify that all required settings have been specified. To do this, TestRail calls the validate_config method whenever the administrator tries to change the configuration:

public function validate_config($config)
{
	$ini = ini::parse($config);
 
	// Check if the [connection] section exists
	if (!isset($ini['connection']))
	{
		throw new ValidationException('Missing [connection] group');
	}
 
	// Check if the required values exist
	$keys = array('address', 'user', 'password');
	foreach ($keys as $key)
	{
		if (!isset($ini['connection'][$key]) ||
			!$ini['connection'][$key])
		{
			throw new ValidationException(
				"Missing configuration for key '$key'"
			);
		}
	}
 
	$address = $ini['connection']['address'];
 
	// Check whether the address is a valid url (syntax only)
	if (!check::url($address))
	{
		throw new ValidationException('Address is not a valid url');
	}
}

The above method checks that the [connection] section exists and that the section contains the keys address, user and password. It also verifies that the password key contains a valid URL (via the check::url routine). Also notice how TestRail's ini module is used to parse the configuration settings at the beginning of the method. If the method finds a problem with the configuration (such as a missing key or an invalid value), it raises an exception of type ValidationException. TestRail then shows the error message to the administrator.

Once the settings have been configured, the plugin needs a way to access the settings. TestRail automatically calls the configure method every time the defect class is being instantiated. For example, before TestRail asks a defect plugin to look up a defect ID, it calls the configure method to provide the plugin with all necessary configuration settings. The plugin is responsible for holding these settings for as long as the instance is active so that it has access to these settings for the actual method calls. A plugin would usually store the settings in an instance variable to do this:

public function configure($config)
{
	$ini = ini::parse($config);
	$this->_address = str::slash($ini['connection']['address']);
	$this->_user = $ini['connection']['user'];
	$this->_password = $ini['connection']['password'];	
}

The method parses the configuration settings via the ini module again and saves the configuration settings in instance variables/fields. To access the defect tracker address in subsequent method calls, the plugin can thus simply access $this→_address.

Looking up defects

We will first walk through the defect look up functionality as it's easier to implement than the push defect functionality and it's a good way to get more familiar with how defect plugins operate. When TestRail renders an entered defect ID (for example, as part of a test result), it checks if the configured defect plugin (if any) supports looking up defects (by checking the can_lookup option returned by get_meta).

If the plugin supports the look up capability, users can hover their mouse cursors over a defect ID to learn more about the defect's status and description. Whenever a user does this, TestRail asks the defect plugin to retrieve information about the defect so that TestRail can display the data (it's useful to know that TestRail might use this information for other purposes in the future such as calculating opened/closed defects for a report).

Requesting information about a defect is done via the lookup method. Before this method is called, TestRail always calls the configure method so that the plugin has all the necessary configuration settings. The lookup method for our fictional defect tracker Bugs looks like this:

public function lookup($defect_id)
{
	// Get the defect information via the Bugs API
	$defect = $this->_bugs_get_defect($defect_id);
 
	// Build the status_id based on the status property
	if ($defect['status'] == 'Open')
	{
		$status_id = GI_DEFECTS_STATUS_OPEN;
	}
	elseif ($defect['status'] == 'Resolved')
	{
		$status_id = GI_DEFECTS_STATUS_RESOLVED;
	}
	else
	{
		$status_id = GI_DEFECTS_STATUS_CLOSED;
	}
 
	// Build the bug URL and project link
	$bug_url = str::format('{0}?bug={1}', 
		$this->_address,
		$defect['id']);
	$project_link = str::format(
		'<a target="_blank" href="{0}project={1}">{2}</a>',
		a($this->_address),
		a($defect['project_id']),
		h($defect['project']));
 
	// Build the description
	$description = str::format(
		'<div class="monospace">{0}</div>',
		nl2br(
			html::link_urls(
				h($defect['description'])
			)
		)
	);	
 
	// Return the defect details
	return array(
		'id' => $defect['id'],
		'title' => $defect['title'],
		'status_id' => $status_id,
		'status' => $defect['status'],
		'url' => $bug_url,
		'description' => $description,
		'attributes' => array(
			'Type' => h($defect['type']),
			'Status' => h($defect['status']),
			'Project' => $project_link
		)
	);
}

This method is a bit longer than the other method implementations we have seen so far, so let's take a look at the separate steps the method performs. We first get the defect details by calling the internal/private _bugs_get_defect method. This or a similar method would usually call the defect tracker's API or look up the defect information in a database. If you download the source code of this sample plugin below, you will notice that the method simply hard codes all the defect details and doesn't actually make any web service calls, but this works well for demonstration purposes.

We then prepare a few values for TestRail such as the status_id or the links and the description. The status_id tells TestRail if a defect is open, resolved or if it has been closed. If a defect tracker doesn't distinguish between resolved and closed (e.g. a bug is either opened or resolved), make sure to specify the GI_DEFECTS_STATUS_CLOSED status. TestRail might use the status_id for reports and other functionality in the future.

Some of the fields that the lookup method returns support HTML. This means that it's critical that you escape your strings and values before returning them to TestRail (in case you didn't receive the data as valid HTML from your defect tracker). If you don't do this, then there's the risk of cross site scripting (XSS) attacks and it's a potential security risk. To escape a string, TestRail offers the h() and a() routines to escape texts and tag attributes, respectively. For example, the returned attributes can contain HTML code (such as links) and if you don't escape values such as project names, TestRail would render them as is and an attacker could inject JavaScript code via the project name. Also notice how the method wraps the bug description in a div tag to format the description with a mono-spaced font.

Last but not least, the method returns the defect details to TestRail. Besides details such as the defect ID, the title, status and so on, it's also possible to return attributes and a description. The attributes can include information such as the project, issue type, component or project area. Just like the attributes, the description supports HTML and can thus be used to add all kinds of additional information. Please make sure that all returned HTML contains valid tags and formatting, as broken HTML tags can break TestRail's layout and functionality. The following screenshot shows how TestRail renders the returned defect details (notice how attributes are rendered above the description with a blue background).

Pushing defects without a dialog

Let's take a look at pushing defect reports from TestRail to a defect tracking tool next. We will implement the push functionality without a push dialog first and we will take a look at the more complex scenario with a push dialog in the next section. There are two methods that are relevant for us to implement the push functionality without a push dialog: prepare_push and push. TestRail calls the prepare_push method to request information about the dialog and available fields it should render. If we don't need a push dialog and just want to push a bug report without user interaction, we simply return false here:

public function prepare_push($context)
{
	return false;
}

To actually push the defect, we also need to implement the push method. Now, to build a useful defect report we of course need to have some information about the test (such as the case title or entered comment) and maybe other details such as the person who is pushing the defect. This is where the $context argument that is passed to all push related methods comes in handy. The $context argument comes with many useful context information such as details about the test case, the test, the person who is pushing the defect and so on. Let's take a look at some of the information that is passed via the $context argument:

Array
(
    [event] => push
    [tests] => Array
        (
            [0] => stdClass Object
                (
                    [id] => 12
                    [run_id] => 1
                    [case_id] => 128
                    [status_id] => 1
                    [url] => http://testrail/index.php?/tests/view/12
                    [case] => stdClass Object
                        (
                            [id] => 128
                            [title] => Verify CSV import with enclosed test data files
                            [url] => http://testrail/index.php?/cases/view/128
                        )

                    [run] => stdClass Object
                        (
                            [id] => 1
                            [name] => File Formats
                            [config] => 
                            [plan_id] => 
                            [project_id] => 1
                            [url] => http://testrail/index.php?/runs/view/1
                        )

                )

        )

    [test_count] => 1
    [test_change] => stdClass Object
        (
            [status_id] => 1
            [assignedto] => 
            [comment] => Importing our standard Unicode CSV file (unitest1.csv) failed with the error message `Could not detect file encoding`.
            [attachments] => 
            [version] => 
            [elapsed] => 
            [defects] => 
        )

    [project] => stdClass Object
        (
            [id] => 1
            [name] => Datahub
            [url] => http://testrail/index.php?/projects/overview/1
        )

    [preferences] => Array
        (
            [type] => 1
            [project] => DH
            [component] => 10010
        )

    [user] => stdClass Object
        (
            [id] => 2
            [name] => Dennis Gurock
            [email] => dg@example.com
        )

)

As we can see, TestRail provides details about the selected test(s), associated test cases, the project, the test change (i.e. the information the user has entered into the Add Test Result dialog) etc. As you can see from the data structure, there can be multiple tests that TestRail passes to the defect plugin. TestRail passes multiple test records to the script if the user selected multiple tests and used the Add Test Result button to add a result to all those tests at once (a mass action button). Pushing the defect and returning the new defect ID could look like this:

public function push($context, $input)
{
	// Build the summary/title
	$test = current($context['tests']);
	$summary = 'Failed test: ' . $test->case->title;
 
	if ($context['test_count'] > 1)
	{
		$summary .= ' (+others)';
	}	
 
	// Build the comment (based on the test result comment
	// and links/URLs of the tests
	if ($context['test_change']->comment)
	{
		$comment = $context['test_change']->comment;
		$comment .= "\n";
	}
	else 
	{
		$comment = '';
	}
 
	$tests = $context['tests'];
	foreach ($tests as $test)
	{
		$comment .= "\nTest: ";
		$comment .= $test->case->title;
		$comment .= "\n";
		$comment .= $test->url;
	}
 
	// We hard code a few other details here for demonstration
	// purposes. We could also select these attributes based
	// on context information such as the test suite or user
	$project = $context['project']->name;
	$type = 'Bug';
	$component = '(default)';
 
	// Push the defect and return the new defect ID
	$defect_id = $this->_bugs_push_defect(
		$summary,
		$comment,
		$project,
		$type,
		$component
	);
	return $defect_id;
}

The push method builds the title/summary and the comment of the bug report based on the entered test result and the context information. It then calls the internal _bugs_push_defect method to submit the actual bug report. This method would implement the actual calling of a web service API or writing the bug report to the database. We simply need to return the new defect ID to TestRail which adds the ID to the Defects field for us.

Pushing defects with a dialog

After we looked at building a defect plugin that can push bug reports without any user interaction, we are going to update the plugin to use a Push Defect dialog. Testers can change the bug description or select a different project when pushing defects to the defect tracker using this dialog. The first thing we have to change is updating the prepare_push method. Instead of returning false to signal that we don't need a dialog, we can return a form schema like this:

public function prepare_push($context)
{
	// Return a form with the following fields/properties
	return array(
		'fields' => array(
			'summary' => array(
				'type' => 'string',
				'label' => 'Summary',
				'required' => true,
				'size' => 'full'
			),
			'type' => array(
				'type' => 'dropdown',
				'label' => 'Issue Type',
				'required' => true,
				'remember' => true,
				'size' => 'compact'
			),
			'project' => array(
				'type' => 'dropdown',
				'label' => 'Project',
				'required' => true,
				'remember' => true,
				'cascading' => true,
				'size' => 'compact'
			),
			'component' => array(
				'type' => 'dropdown',
				'label' => 'Component',
				'required' => true,
				'remember' => true,
				'depends_on' => 'project',
				'size' => 'compact'
			),
			'description' => array(
				'type' => 'text',
				'label' => 'Description'
			)
		)
	);
}

The form schema tells TestRail which form fields we need. The defect script doesn't actually have to render the dialog itself, as TestRail automatically renders the push dialog for us based on the form schema we return in our prepare_push method. Most of the form schema returned by the above example should be self-explanatory. We return a list of form fields that TestRail should display.

The array key (e.g. 'description') of a field is the name TestRail will use to pass the input data to our script. The type of the field can currently be one of text, dropdown or string and tells TestRail what kind of field we need. A string is a single line text input element, whereas text can accept multiple input lines. The required option specifies if the field must be entered by a user or if it's optional and the label specifies how the field is called in the dialog.

The size option specifies the width of the field, where full results in a field that fills the full horizontal width of a dialog. If you specify compact as the field size, TestRail will try to display multiple compact fields in the same line (this is only true for compact fields that were specified in the form schema together). If you specify the remember option, TestRail will save the last entered value of a value for the current TestRail project. This makes it easy to remember previously entered form values (such as a project area or sub component) so that a user doesn't have to enter the same data again and again. We will take a look at how this works later in this section.

Some fields might depend on other fields. For example, if you have a Project dropdown field, the values of a related Project Area field depends on the selected project. TestRail supports field dependencies for this. If a user selects a different project, TestRail asks the defect script for valid project areas for the newly selected project and clears the value of the Project Area field. Field dependencies are specified by the depends_on option and are currently only supported for dropdown fields. It's also required to mark fields that other fields can depend on as cascading (via the respective option).

Now that we have specified the form schema, we also need to tell TestRail the valid dropdown values and the default texts for the dialog fields. This can be done via the prepare_field method. Whenever TestRail initializes the dialog or if any cascading fields change, TestRail asks the defect plugin to provide information about the relevant fields. TestRail calls the prepare_field method for every field TestRail needs information for.

public function prepare_field($context, $input, $field)
{
	$data = array();
 
	// Take the preferences of the user into account, but only
	// for the initial form rendering (not for cascading loads).
	if ($context['event'] == 'prepare')
	{
		$prefs = arr::get($context, 'preferences');
	}
	else
	{
		$prefs = null;
	}
 
	// And then build the options and default values for the
	// form fields and return them.
	switch ($field)
	{
		case 'summary':
			$data['default'] = 
				$this->_get_summary_default($context);
			break;
 
		case 'description':
			$data['default'] = 
				$this->_get_description_default($context);
			break;
 
		case 'type':
			$data['options'] = $this->_bugs_get_types();
 
			// Select the stored preference or the first item in
			// the list otherwise.
			$default = arr::get($prefs, 'type');
			if ($default)
			{
				$data['default'] = $default;
			}
			else
			{
				if ($data['options'])
				{
					$data['default'] = key($data['options']);
				}
			}				
			break;
 
		case 'project':
			$data['default'] = arr::get($prefs, 'project');
			$data['options'] = $this->_bugs_get_projects();
			break;
 
		case 'component':
			if (isset($input['project']))
			{
				$data['default'] = arr::get($prefs, 'component');
				$data['options'] = $this->_bugs_get_components(
					$input['project']);
			}
			break;
	}
 
	return $data;
}

TestRail passes three arguments to our prepare_field method: the $context argument contains the context information that we know from previous methods. The $input array contains the actual data the user has entered in the push dialog (this is especially useful for cascading dropdown calls). The $field argument contains the name of the field the method was called for (as the method is called for our fields separately).

TestRail also passes the preferences of the current user (for the current project) as part of the $context argument. So the first thing our prepare_field method does is to copy the user preferences into a local variable if TestRail is currently initializing the dialog. If this is a subsequent call for a cascading dropdown field, we ignore the preferences.

We then return the appropriate data for the field to TestRail. We do this in a big switch statement which basically executes the relevant code depending on the field TestRail requests data for. for example, when TestRail asks for the possible values for the Project field, the following code is executed:

case 'project':
	$data['default'] = arr::get($prefs, 'project');
	$data['options'] = $this->_bugs_get_projects();
	break;

TestRail basically needs two details about a dropdown field: the possible options that the user can select and the default option that is selected. For text and string fields, only the default value should be specified (there aren't any options the user can select from a text or string field, after all). In our example, the default option is selected based on the user's preference. So if a user previously selected a specific project, we restore this project so that the user doesn't have to specify the project again. Similarly, we generate default texts for the summary/title and the description field based on the test details. Our dialog would thus look like similar to this:

In our example, we don't actually fully implement methods such as _bugs_get_projects. In a fully-functional plugin, this method would use the defect tracker's web service API to get a list of projects (or it would read a list of projects from a database). In our case, we simply return a hard-coded list of projects (this can also be a valid way to implement this if your projects don't change regularly):

private function _bugs_get_projects()
{
	return array(
		'dh' => 'Datahub',
		'pr' => 'Presenter',
		'wr' => 'Writer'
	);
}

Notice how we use IDs as the array keys here. TestRail will pass those IDs as the input value to our push method when the user clicks the submit button. But before we implement the push method, let's briefly look at the validate_push method. This method allows us to validate the user input before accepting the entered values for the bug report. It's not necessary to implement this method if you don't need to do any further validation of the data, but it can be useful to enforce certain input formats or constraints (such as dates etc.). The following example uses the validate_push method to limit the number of characters in the summary field to 100 characters:

public function validate_push($context, $input)
{
	if (isset($input['summary']))
	{
		if (str::len($input['summary']) > 100)
		{
			throw new ValidationException(
				'Field Summary cannot exceed 100 characters.');
		}
	}
}

Once the user has enter the bug report, clicked the submit button and the script has validated the input via the validate_push method, TestRail calls the push method to submit the bug report. Similar to our dialog-less example above, our push method submits the report. Note how we are using the $input argument this time instead of building the bug report from the $context argument ourselves:

public function push($context, $input)
{
	// Push the defect and return the new defect ID
	$defect_id = $this->_bugs_push_defect(
		$input['summary'],
		$input['description'],
		$input['project'],
		$input['type'],
		$input['component']
	);
	return $defect_id;
}

That's it! We have now successfully implemented a full defect plugin that supports the look up and push capabilities, can be configured by the administrator from within TestRail and that supports cascading dropdown for the project list. You can download the full example script below and also view further sample scripts.

Download

You can download the full sample script discussed in this article here:

Bugs.php Download (8KB)

To view further examples of actual defect plugins, it's recommended to take a look at the defect plugins that ship with TestRail. You can find the defect plugins in your TestRail installation directory under app/plugins/defects. If you have any further questions about building or customizing defect plugins, please do not hesitate to contact us.