Index: modules/simpletest/simpletest.css =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.css,v retrieving revision 1.2 diff -u -r1.2 simpletest.css --- modules/simpletest/simpletest.css 24 Jun 2008 21:51:02 -0000 1.2 +++ modules/simpletest/simpletest.css 31 Jul 2008 00:33:17 -0000 @@ -1,11 +1,9 @@ /* $Id: simpletest.css,v 1.2 2008/06/24 21:51:02 dries Exp $ */ -/* Addon for the simpletest module */ -#simpletest { -} /* Test Table */ th.simpletest_run { width: 50px; + padding-right: 0.5em; } th.simpletest_test { width: 160px; @@ -17,46 +15,55 @@ } table#simpletest-form-table tr td { - background-color: white !important; + background-color: white; } table#simpletest-form-table tr.simpletest-group td { - background-color: #EDF5FA !important; + background-color: #EDF5FA; } -div.simpletest-pass { - color: #33a333; +table#simpletest-form-table thead .form-item { + margin-bottom:0pt; + margin-top:0pt; + white-space:nowrap; } -div.simpletest-fail { - color: #a30000; +div.simpletest-image { + display: inline; } -tr.simpletest-pass.odd { - background: #b6ffb6; +div.simpletest-image, label.simpletest-group-label { + cursor: pointer; } -tr.simpletest-pass.even { - background: #9bff9b; +.simpletest-pass { + color: #316d31; } -tr.simpletest-fail.odd { - background: #ffc9c9; +.simpletest-fail { + color: #981010; } -tr.simpletest-fail.even { - background: #ffacac; +.simpletest-exception { + color: #bda400; } -tr.simpletest-exception.odd { - background: #f4ea71; +div.message > div.item-list { + font-weight: normal; } -tr.simpletest-exception.even { - background: #f5e742; +li.simpletest-important { + font-weight: bold; } -div.simpletest-image { - display: inline; +ul.simpletest-results li div.item-list ul { + padding-left: 20px; +} + +ul.simpletest-results li span.simpletest-overview { cursor: pointer; } + +ul.simpletest-results li span.simpletest-overview:hover { + text-decoration: underline; +} \ No newline at end of file Index: modules/simpletest/simpletest.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.test,v retrieving revision 1.4 diff -u -r1.4 simpletest.test --- modules/simpletest/simpletest.test 18 Jul 2008 07:24:29 -0000 1.4 +++ modules/simpletest/simpletest.test 31 Jul 2008 00:33:17 -0000 @@ -94,11 +94,13 @@ * Confirm that the stub test produced the desired results. */ function confirmStubTestResults() { - $this->assertAssertion($this->pass, 'Other', 'Pass'); - $this->assertAssertion($this->fail, 'Other', 'Fail'); + $info = $this->getInfo(); + $name = $info['name']; + $this->assertAssertion($this->pass, $name); + $this->assertAssertion($this->fail, $name); - $this->assertAssertion(t('Created permissions: @perms', array('@perms' => $this->valid_permission)), 'Role', 'Pass'); - $this->assertAssertion(t('Invalid permission %permission.', array('%permission' => $this->invalid_permission)), 'Role', 'Fail'); + $this->assertAssertion(t('Created permissions: @perms', array('@perms' => $this->valid_permission)), 'Role'); + $this->assertAssertion(t('Invalid permission %permission.', array('%permission' => $this->invalid_permission)), 'Role'); } /** @@ -110,18 +112,17 @@ * @param string $status Assertion status. * @return Assertion result. */ - function assertAssertion($message, $type, $status) { + function assertAssertion($message, $type) { $message = trim(strip_tags($message)); $found = FALSE; - foreach ($this->results['assertions'] as $assertion) { + foreach ($this->results as $assertion) { if ($assertion['message'] == $message && - $assertion['type'] == $type && - $assertion['status'] == $status) { + $assertion['type'] == $type) { $found = TRUE; break; } } - return $this->assertTrue($found, t('Found assertion {"@message", "@type", "@status"}.', array('@message' => $message, '@type' => $type, '@status' => $status))); + return $this->assertTrue($found, t('Found assertion {"@message", "@type"}.', array('@message' => $message, '@type' => $type))); } /** @@ -131,20 +132,13 @@ $results = array(); if ($this->parse()) { - if ($fieldset = $this->getResultFieldSet()) { - // Code assumes this is the only test in group. - $results['summary'] = $this->asText($fieldset->div); - $results['name'] = $this->asText($fieldset->fieldset->legend); - - $results['assertions'] = array(); - $tbody = $fieldset->fieldset->table->tbody; - foreach ($tbody->tr as $row) { + if ($list = $this->getResultList()) { + $results = array(); + foreach ($list->li as $li) { $assertion = array(); - $assertion['message'] = $this->asText($row->td[0]); - $assertion['type'] = $this->asText($row->td[1]); - $ok_url = (url('misc/watchdog-ok.png') == 'misc/watchdog-ok.png') ? 'misc/watchdog-ok.png' : (base_path() . 'misc/watchdog-ok.png'); - $assertion['status'] = ($row->td[5]->img['src'] == $ok_url) ? 'Pass' : 'Fail'; - $results['assertions'][] = $assertion; + $assertion['message'] = $this->asText($li->span); + $assertion['type'] = str_replace('Group: ', '', $this->asText($li->div->ul->li[0])); + $results[] = $assertion; } } } @@ -156,12 +150,11 @@ * * @return fieldset containing the results for group this test is in. */ - function getResultFieldSet() { - $fieldsets = $this->elements->xpath('//fieldset'); - $info = $this->getInfo(); - foreach ($fieldsets as $fieldset) { - if ($fieldset->legend == $info['group']) { - return $fieldset; + function getResultList() { + $lis = $this->elements->xpath('//li'); + foreach ($lis as $li) { + if (strpos($this->asText($li->span), t('SimpleTest: ')) === 0) { + return $li->div->ul->li->div->ul; } } return FALSE; Index: modules/simpletest/drupal_web_test_case.php =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/drupal_web_test_case.php,v retrieving revision 1.27 diff -u -r1.27 drupal_web_test_case.php --- modules/simpletest/drupal_web_test_case.php 18 Jul 2008 07:30:34 -0000 1.27 +++ modules/simpletest/drupal_web_test_case.php 31 Jul 2008 00:33:17 -0000 @@ -62,6 +62,7 @@ if (substr($function['function'], 0, 6) != 'assert' && $function['function'] != 'pass' && $function['function'] != 'fail') { break; } + $previous_function = $function; } } else { @@ -75,8 +76,8 @@ 'message' => $message, 'group' => $group, 'function' => $function['function'], - 'line' => $function['line'], - 'file' => $function['file'], + 'line' => $previous_function['line'], + 'file' => $previous_function['file'], ); $db_prefix = $current_db_prefix; return $status; @@ -304,6 +305,7 @@ 'file' => $file, )); } + drupal_set_message(var_export($severity, TRUE)); return TRUE; } @@ -710,7 +712,6 @@ // Close the CURL handler. $this->curlClose(); - restore_error_handler(); } } @@ -785,7 +786,7 @@ // them. @$htmlDom = DOMDocument::loadHTML($this->_content); if ($htmlDom) { - $this->assertTrue(TRUE, t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser')); + $this->pass(t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser')); // It's much easier to work with simplexml than DOM, luckily enough // we can just simply import our DOM tree. $this->elements = simplexml_import_dom($htmlDom); @@ -1183,7 +1184,7 @@ * TRUE on pass, FALSE on fail. */ function assertText($text, $message = '', $group = 'Other') { - return $this->assertTextHelper($text, $message, $group = 'Other', FALSE); + return $this->assertTextHelper($text, $message, $group, FALSE); } /** Index: modules/simpletest/simpletest.module =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.module,v retrieving revision 1.7 diff -u -r1.7 simpletest.module --- modules/simpletest/simpletest.module 16 Jul 2008 21:59:27 -0000 1.7 +++ modules/simpletest/simpletest.module 31 Jul 2008 00:33:17 -0000 @@ -50,6 +50,9 @@ 'simpletest_result_summary' => array( 'arguments' => array('form' => NULL) ), + 'simpletest_results' => array( + 'arguments' => array('results' => NULL, 'assertions' => NULL), + ), ); } @@ -62,85 +65,39 @@ $uncategorized_tests = simpletest_get_all_tests(); $tests = simpletest_categorize_tests($uncategorized_tests); if (isset($_SESSION['test_id'])) { + $form['#test_id'] = $_SESSION['test_id']; $results = db_query("SELECT * FROM {simpletest} WHERE test_id = %d ORDER BY test_class, message_id", $_SESSION['test_id']); unset($_SESSION['test_id']); - $summary = array( - '#theme' => 'simpletest_result_summary', - '#pass' => 0, - '#fail' => 0, - '#exception' => 0, - '#weight' => -10, - ); - $form['summary'] = $summary; - $form['results'] = array(); - $group_summary = array(); - $map = array( - 'pass' => theme('image', 'misc/watchdog-ok.png'), - 'fail' => theme('image', 'misc/watchdog-error.png'), - 'exception' => theme('image', 'misc/watchdog-warning.png'), - ); $header = array(t('Message'), t('Group'), t('Filename'), t('Line'), t('Function'), array('colspan' => 2, 'data' => t('Status'))); + $test_results = array(); + $test_assertions = array(); while ($result = db_fetch_object($results)) { - $class = $result->test_class; - $info = $uncategorized_tests[$class]->getInfo(); - $group = $info['group']; - if (!isset($group_summary[$group])) { - $group_summary[$group] = $summary; - } - $element = &$form['results'][$group][$class]; - if (!isset($element)) { - $element['summary'] = $summary; - } - $status = $result->status; - // This reporter can only handle pass, fail and exception. - if (isset($map[$status])) { - $element['#title'] = $info['name']; - $status_index = '#'. $status; - $form['summary'][$status_index]++; - $group_summary[$group][$status_index]++; - $element['summary'][$status_index]++; - $element['result_table']['#rows'][] = array( - 'data' => array( - $result->message, - $result->message_group, - basename($result->file), - $result->line, - $result->caller, - $map[$status], - ), - 'class' => "simpletest-$status", - ); - } - unset($element); - } - $all_ok = TRUE; - foreach ($form['results'] as $group => &$elements) { - $group_ok = TRUE; - foreach ($elements as $class => &$element) { - $info = $uncategorized_tests[$class]->getInfo(); - $ok = $element['summary']['#fail'] + $element['summary']['#exception'] == 0; - $element += array( - '#type' => 'fieldset', - '#collapsible' => TRUE, - '#collapsed' => $ok, - '#description' => $info['description'], - ); - $element['result_table']['#markup'] = theme('table', $header, $element['result_table']['#rows']); - $element['summary']['#ok'] = $ok; - $group_ok = $group_ok && $ok; + if (empty($test_results[$result->test_class])) { + $class = $result->test_class; + $test = new $class(); + $info = $test->getInfo(); + $test_results[$class] = array('#fail' => 0, '#pass' => 0, '#exception' => 0); + foreach ($info as $key => $value) { + $test_results[$class]['#' . $key] = $value; + } } - $elements += array( - '#type' => 'fieldset', - '#title' => $group, - '#collapsible' => TRUE, - '#collapsed' => $group_ok, - 'summary' => $group_summary[$group], - ); - $elements['summary']['#ok'] = $group_ok; - $all_ok = $group_ok && $all_ok; - } - $form['summary']['#ok'] = $all_ok; - } + $test_results[$result->test_class]['#' . $result->status]++; + $assertion = (array)$result; + $assertion['group'] = $assertion['message_group']; + unset($assertion['message_group']); + $assertion['function'] = $assertion['caller']; + unset($assertion['caller']); + $test_assertions[$result->test_class][] = $assertion; + $test_results[$class]['#ok'] = !($test_results[$class]['#fail'] + $test_results[$class]['#exception']); + } + $form['#results'] = $test_results; + $form['#assertions'] = $test_assertions; + } + $form['run_all_top'] = array( + '#type' => 'checkbox', + '#default_value' => 0, + '#attributes' => array('class' => 'simpletest-master'), + ); foreach ($tests as $group_name => $test_group) { foreach ($test_group as $test) { $test_info = $test->getInfo(); @@ -154,32 +111,15 @@ } } - $form['run'] = array( - '#type' => 'fieldset', - '#collapsible' => FALSE, - '#collapsed' => FALSE, - '#title' => t('Run tests'), - ); - $form['run']['running_options'] = array( - '#type' => 'radios', - '#default_value' => 'selected_tests', - '#options' => array( - 'all_tests' => t('Run all tests (WARNING, this may take a long time)'), - 'selected_tests' => t('Run selected tests'), - ), + $form['submit'] = array( + '#prefix' => '
', + '#suffix' => '
', ); - $form['run']['op'] = array( + $form['submit']['run'] = array( '#type' => 'submit', '#value' => t('Run tests'), ); - $form['reset'] = array( - '#type' => 'fieldset', - '#collapsible' => FALSE, - '#collapsed' => FALSE, - '#title' => t('Clean test environment'), - '#description' => t('Remove tables with the prefix "simpletest" and temporary directories that are left over from tests that crashed.') - ); - $form['reset']['op'] = array( + $form['submit']['reset'] = array( '#type' => 'submit', '#value' => t('Clean environment'), '#submit' => array('simpletest_clean_environment') @@ -195,11 +135,13 @@ function theme_simpletest_test_form($form) { drupal_add_css(drupal_get_path('module', 'simpletest') .'/simpletest.css', 'module'); drupal_add_js(drupal_get_path('module', 'simpletest') .'/simpletest.js', 'module'); + $header = array( - array('data' => t('Run'), 'class' => 'simpletest_run checkbox'), + array('data' => drupal_render($form['run_all_top']), 'class' => 'simpletest_run checkbox'), array('data' => t('Test'), 'class' => 'simpletest_test'), array('data' => t('Description'), 'class' => 'simpletest_description'), ); + unset($form['run_all_top']); $js = array( 'images' => array( theme('image', 'misc/menu-collapsed.png', 'Expand', 'Expand'), @@ -240,12 +182,21 @@ $js['simpletest-test-group-'. $test_class] = $current_js; } unset($form['tests']); + unset($form['run_all_bottom']); drupal_add_js(array('simpleTest' => $js), 'setting'); // Output test groups: $output = ''; - if (isset($form['results'])) { - $output .= drupal_render($form['summary']); - $output .= drupal_render($form['results']); + if (isset($form['#results'])) { + $output .= '

' . t('The assertions listed in bold text are those made by the test itself, rather than the implicit framework tests.') .'

'; + $output .= drupal_get_form('simpletest_filter_form'); + $output .= theme('simpletest_results', $form['#results'], $form['#assertions']); + $output .= drupal_get_form('simpletest_rerun_form', $form['#test_id']); + return $output; + } + else { + // This will forward us to the batch api if necessary. + drupal_get_form('simpletest_rerun_form', NULL); + $output .= '

' . t('Select the "Clean environment" button below to remove any database tables or files that might be left over from interrupted tests. Otherwise, select the tests you would like to run, and click "Run tests".') . '

'; } if (count($rows)) { $output .= theme('table', $header, $rows, array('id' => 'simpletest-form-table')); @@ -256,12 +207,8 @@ return $output; } -function theme_simpletest_result_summary($form, $text = NULL) { - return '
' . _simpletest_format_summary_line($form) . '
'; -} - function _simpletest_format_summary_line($summary) { - return t('@pass, @fail, @exception', array( + return t('@pass, @fail, and @exception', array( '@pass' => format_plural(isset($summary['#pass']) ? $summary['#pass'] : 0, '1 pass', '@count passes'), '@fail' => format_plural(isset($summary['#fail']) ? $summary['#fail'] : 0, '1 fail', '@count fails'), '@exception' => format_plural(isset($summary['#exception']) ? $summary['#exception'] : 0, '1 exception', '@count exceptions'), @@ -275,10 +222,9 @@ $output = ''; $batch_mode = !preg_match("/^simpletest\d+$/", $_SERVER['HTTP_USER_AGENT']); $tests_list = array(); - $run_all_tests = $form_state['values']['running_options'] == 'all_tests'; simpletest_get_all_tests(); foreach ($form_state['values'] as $class_name => $value) { - if (class_exists($class_name) && ($value === 1 || $run_all_tests)) { + if (class_exists($class_name) && $value === 1) { $tests_list[] = $class_name; } } @@ -307,6 +253,10 @@ $test_id = db_last_insert_id('simpletest_test_id', 'test_id'); if ($batch_mode) { + $first_test = array_shift($test_list); + $first_instance = new $first_test(); + array_unshift($test_list, $first_test); + $info = $first_instance->getInfo(); $batch = array( 'title' => t('Running SimpleTests'), 'operations' => array( @@ -314,9 +264,10 @@ ), 'finished' => '_simpletest_batch_finished', 'redirect' => 'admin/build/testing', - 'progress_message' => t('Processing tests.'), + 'progress_message' => '', 'css' => array(drupal_get_path('module', 'simpletest') .'/simpletest.css'), - 'init_message' => t('SimpleTest is initializing...') . ' ' . format_plural(count($test_list), "one test case will run.", "@count test cases will run."), + 'js' => array(drupal_get_path('module', 'simpletest') .'/simpletest.js'), + 'init_message' => t('Processing test @num of @max - %test.', array('%test' => $info['name'], '@num' => '1', '@max' => count($test_list))), ); batch_set($batch); } @@ -348,6 +299,7 @@ // Nth iteration: get the current values where we last stored them. $test_list = $context['sandbox']['tests']; $test_results = $context['sandbox']['test_results']; + $test_assertions = $context['sandbox']['test_assertions']; } $max = $context['sandbox']['max']; @@ -363,19 +315,27 @@ foreach ($test_results[$test_class] as $key => $value) { $test_results[$key] += $value; } - $test_results[$test_class]['#name'] = $info['name']; - $items = array(); - foreach (element_children($test_results) as $class) { - $items[] = '
' . t('@name: @summary', array('@name' => $test_results[$class]['#name'], '@summary' => _simpletest_format_summary_line($test_results[$class]))) . '
'; + $test_assertions[$test_class] = $test->_assertions; + foreach ($info as $key => $value) { + $test_results[$test_class]['#' . $key] = $value; + } + $test_results[$test_class]['#ok'] = !($test_results['#fail'] + $test_results['#exception']); + if (!empty($test_list)) { + $current_test_class = array_shift($test_list); + $instance = new $current_test_class(); + $current_info = $instance->getInfo(); + $context['message'] = t('Processing test @num of @max - %test.', array('%test' => $current_info['name'], '@num' => $max - count($test_list), '@max' => $max)); + array_unshift($test_list, $current_test_class); } - $message = t('Processed test @num of @max - %test.', array('%test' => $info['name'], '@num' => $max - $size, '@max' => $max)); - $message .= theme('item_list', $items); - $context['message'] = $message; - // TODO: Do we want a summary of all? + else { + $context['message'] = t('Finished processing all tests.'); + } + $context['message'] .= theme_simpletest_results($test_results, $test_assertions); // Save working values for the next iteration. $context['sandbox']['tests'] = $test_list; $context['sandbox']['test_results'] = $test_results; + $context['sandbox']['test_assertions'] = $test_assertions; // The test_id is the only thing we need to save for the report page. $context['results']['test_id'] = $test_id; @@ -384,7 +344,9 @@ } function _simpletest_batch_finished($success, $results, $operations) { - $_SESSION['test_id'] = $results['test_id']; + if (isset($results['test_id'])) { + $_SESSION['test_id'] = $results['test_id']; + } if ($success) { drupal_set_message(t('The tests have finished running.')); } @@ -461,6 +423,134 @@ } /** + * Theme simpletest results. + * + * @param $results + * A nested array of simpletest results. + * @param $assertions + * An array of assertions for each test. + * @return + * A nested list of simpletest items. + */ +function theme_simpletest_results($results, $assertions) { + $items = array(); + $group_data = array(); + $totals = array( + '#fail' => 0, + '#pass' => 0, + '#exception' => 0, + ); + foreach (element_children($results) as $class) { + $totals['#fail'] += $results[$class]['#fail']; + $totals['#pass'] += $results[$class]['#pass']; + $totals['#exception'] += $results[$class]['#exception']; + $item = array(); + foreach ($results[$class] as $key => $value) { + if ($key == '#ok') { + $group_data[$results[$class]['#group']][$key] = isset($group_data[$results[$class]['#group']][$key]) ? $group_data[$results[$class]['#group']][$key] && $value : $value; + } + else { + $group_data[$results[$class]['#group']][$key] = isset($group_data[$results[$class]['#group']][$key]) ? $group_data[$results[$class]['#group']][$key] + $value : $value; + } + } + $item['class'] = 'simpletest-container ' . ($results[$class]['#fail'] ? 'simpletest-fail expanded' : ($results[$class]['#exception'] ? 'simpletest-exception expanded' : 'simpletest-pass collapsed')); + $item['data'] = '' . t('@name: @summary', array('@name' => $results[$class]['#name'], '@summary' => _simpletest_format_summary_line($results[$class]))) . ''; + $assertion_items = array(); + foreach ($assertions[$class] as $assertion) { + // The idea of importancy rests upon the fact that the testing framework + // makes many assertions in its normal process of making GET and POST + // requests, as well as many other normal procedures. We will treat + // assertions actually initiated by the test itself as far more likely to + // be important, and thus highlight it in bold text. + $important = FALSE; + if ($assertion['group'] == 'Other') { + $assertion['group'] = $results[$class]['#name']; + $important = TRUE; + } + + $css_class = simpletest_get_assertion_css_class($assertion, $important); + $assertion_items[] = array( + 'data' => t('[@status] !message', + array('@status' => simpletest_get_status_message($assertion), + '!message' => $assertion['message'])), + 'class' => 'collapsed ' . $css_class, + 'children' => array( + array( + 'data' => t('Group: @group', array('@group' => $assertion['group'])), + 'class' => 'leaf ' . $css_class, + ), + array( + 'data' => t('File: @file', array('@file' => basename($assertion['file']))), + 'class' => 'leaf ' . $css_class, + ), + array( + 'data' => t('Function: @function', array('@function' => $assertion['function'])), + 'class' => 'leaf ' . $css_class, + ), + array( + 'data' => t('Line: @line', array('@line' => $assertion['line'])), + 'class' => 'leaf ' . $css_class, + ), + ), + ); + } + $item['children'] = $assertion_items; + $items[$results[$class]['#group']]['children'][] = $item; + $items[$results[$class]['#group']]['data'] = '' . t('@name: @summary', array('@name' => $results[$class]['#group'], '@summary' => _simpletest_format_summary_line($group_data[$results[$class]['#group']]))) . ''; + $items[$results[$class]['#group']]['class'] = 'simpletest-container ' . ($group_data[$results[$class]['#group']]['#fail'] ? 'simpletest-fail expanded' : ($group_data[$results[$class]['#group']]['#exception'] ? 'simpletest-exception expanded' : 'simpletest-pass collapsed')); + } + $output = ''; + $output .= '

'; + $output .= t('Overall results: @summary', array('@summary' => _simpletest_format_summary_line($totals))); + $output .= '

'; + $output .= theme('item_list', $items, NULL, 'ul', array('class' => 'simpletest-results')); + return $output; +} + +/** + * Get the appropriate css class based on the assertions status and importance. + * + * @param $assertion Assertion item aray. + * @param $important Importance of assertion. + * @return CSS class. + */ +function simpletest_get_assertion_css_class($assertion, $important) { + switch ($assertion['status']) { + case 'pass': + $css_class = 'simpletest-pass'; + break; + case 'fail': + $css_class = 'simpletest-fail'; + break; + case 'exception': + $css_class = 'simpletest-exception'; + break; + } + if ($important) { + $css_class .= ' simpletest-important'; + } + return $css_class; +} + +/** + * Get the status message text + * + * @param $assertion Assertion item aray. + * @return Status message text. + */ +function simpletest_get_status_message($assertion) { + switch ($assertion['status']) { + case 'pass': + return t('PASS'); + case 'fail': + return t('FAIL'); + case 'exception': + return t('EXCEPTION'); + } + return t('UNKNOWN'); +} + +/** * Remove all temporary database tables and directories. */ function simpletest_clean_environment() { @@ -537,6 +627,64 @@ } /** + * FAPI callback for the filtering form. + */ +function simpletest_filter_form() { + $form = array(); + $form['outcome'] = array( + '#type' => 'checkboxes', + '#title' => t('Filter by outcome'), + '#options' => array(), + '#attributes' => array('class' => 'container-inline'), + '#default_value' => array(), + ); + $categories = array('pass' => t('Passes'), 'fail' => t('Fails'), 'exception' => t('Exceptions')); + foreach ($categories as $outcome => $readable) { + $form['outcome'][$outcome] = array( + '#type' => 'checkbox', + '#title' => $readable, + '#attributes' => array('class' => 'simpletest-filter'), + '#default_value' => TRUE, + ); + } + return $form; +} + +/** + * FAPI callback for the re-run form. + */ +function simpletest_rerun_form($form_state, $test_id) { + $form = array(); + $form['test_id'] = array( + '#type' => 'hidden', + '#default_value' => $test_id, + ); + $form['actions'] = array( + '#prefix' => '
', + '#suffix' => '
', + ); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => t('Re-run tests'), + ); + $form['actions']['cancel'] = array( + '#markup' => l(t('Return to testing overview page'), 'admin/build/testing'), + ); + return $form; +} + +/** + * FAPI submit handler. + */ +function simpletest_rerun_form_submit($form, &$form_state) { + $result = db_query('SELECT DISTINCT(test_class) FROM {simpletest} WHERE test_id = %d', $form_state['values']['test_id']); + while ($class = db_result($result)) { + $form_state['values'][$class] = 1; + } + simpletest_test_form_submit($form, $form_state); +} + +/** * Remove all files from specified firectory and then remove directory. * * @param string $path Directory path. Index: modules/simpletest/simpletest.js =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.js,v retrieving revision 1.2 diff -u -r1.2 simpletest.js --- modules/simpletest/simpletest.js 23 Apr 2008 18:50:49 -0000 1.2 +++ modules/simpletest/simpletest.js 31 Jul 2008 00:33:17 -0000 @@ -1,12 +1,15 @@ // $Id: simpletest.js,v 1.2 2008/04/23 18:50:49 dries Exp $ +/** + * Add the cool table collapsing on the testing overview page. + */ Drupal.behaviors.simpleTestMenuCollapse = function() { // Adds expand-collapse functionality. $('div.simpletest-image').click(function() { // Toggle all of the trs. if (!Drupal.settings.simpleTest[$(this).attr('id')].clickActive) { Drupal.settings.simpleTest[$(this).attr('id')].clickActive = true; - var trs = $(this).parents('tbody').children().filter('.'+ Drupal.settings.simpleTest[$(this).attr('id')].testClass), trs_formatted = [], direction = Drupal.settings.simpleTest[$(this).attr('id')].imageDirection, self = $(this); + var trs = $(this).parents('tbody').children().filter('.' + Drupal.settings.simpleTest[$(this).attr('id')].testClass), trs_formatted = [], direction = Drupal.settings.simpleTest[$(this).attr('id')].imageDirection, self = $(this); for (var i = 0; i < trs.length; i++) { trs_formatted.push(trs[i]); } @@ -21,12 +24,21 @@ Drupal.settings.simpleTest[self.attr('id')].clickActive = false; } } - toggleTrs(trs_formatted, (direction? 'pop' : 'shift'), (direction? 'fadeOut' : 'fadeIn')); + toggleTrs(trs_formatted, (direction ? 'pop' : 'shift'), (direction ? 'fadeOut' : 'fadeIn')); Drupal.settings.simpleTest[$(this).attr('id')].imageDirection = !direction; $(this).html(Drupal.settings.simpleTest.images[(direction? 0 : 1)]); } }); + $('label.simpletest-group-label').click(function() { + $(this).siblings('div.simpletest-image').click(); + return false; + }); } + +/** + * Select/deselect all the inner checkboxes when the outer checkboxes are + * selected/deselected. + */ Drupal.behaviors.simpleTestSelectAll = function() { $('td.simpletest-select-all').each(function() { var checkboxes = Drupal.settings.simpleTest['simpletest-test-group-'+ $(this).attr('id')].testNames, totalCheckboxes = 0, @@ -35,11 +47,11 @@ for (var i = 0; i < checkboxes.length; i++) { $('#'+ checkboxes[i]).attr('checked', checked); } - self.data('simpletest-checked-tests', (checked? checkboxes.length : 0)); + self.data('simpletest-checked-tests', (checked ? checkboxes.length : 0)); }).data('simpletest-checked-tests', 0); var self = $(this); for (var i = 0; i < checkboxes.length; i++) { - if ($('#'+ checkboxes[i]).change(function() { + if ($('#' + checkboxes[i]).change(function() { if (checkbox.attr('checked') == 'checked') { checkbox.attr('checked', ''); } @@ -49,7 +61,7 @@ checkbox.attr('checked', 'checked'); } else { - checkbox.attr('checked', ''); + checkbox.removeAttr('checked'); } }).attr('checked') == 'checked') { totalCheckboxes++; @@ -60,4 +72,69 @@ } $(this).append(checkbox); }); + $('input.simpletest-master:checkbox').click(function() { + if ($(this).is(':checked')) { + $('#simpletest-form-table input:checkbox,input.simpletest-master:checkbox').attr('checked', 'checked'); + } + else { + $('#simpletest-form-table input:checkbox,input.simpletest-master:checkbox').removeAttr('checked'); + } + $('#simpletest-form-table input:checkbox:not(.simpletest-master)').change(function() { + if ($('#simpletest-form-table input:checkbox:not(:checked):not(.simpletest-master)').length) { + $('input.simpletest-master:checkbox').removeAttr('checked'); + } + else if ($('#simpletest-form-table input:checkbox:not(.simpletest-master)').length == $('#simpletest-form-table input:checkbox:checked:not(.simpletest-master)').length) { + $('input.simpletest-master:checkbox').attr('checked', 'checked'); + } + }); + }); +}; + +/** + * Filter the results based on their outcome. + */ +Drupal.behaviors.simpleTestFilterForm = function() { + var updateResults = function(type, checked) { + $('li.simpletest-' + type + ':not(.simpletest-container)')[checked ? 'show' : 'hide'](); + $('li.simpletest-container:not(:has(li.simpletest-container))').each(function() { + var children = $(this).find('li:visible:not(.simpletest-container)').is('.simpletest-pass,.simpletest-fail,.simpletest-exception'); + $(this)[children ? 'show' : 'hide'](); + }); + } + $('#edit-outcome-pass').change(function() { + updateResults('pass', $(this).is(':checked')); + }); + $('#edit-outcome-fail').change(function() { + updateResults('fail', $(this).is(':checked')); + }); + $('#edit-outcome-exception').change(function() { + updateResults('exception', $(this).is(':checked')); + }); + updateResults('pass', $('#edit-outcome-pass').is(':checked')); + updateResults('fail', $('#edit-outcome-fail').is(':checked')); + updateResults('exception', $('#edit-outcome-exception').is(':checked')); +}; + +/** + * The results should collapse when you click on them, of course. + */ +Drupal.behaviors.simpleTestResultsCollapse = function() { + $('li > span.simpletest-overview').click(function() { + $(this).siblings().children('ul').toggle().toggleClass('simpletest-hidden').parent().parent().toggleClass('collapsed').toggleClass('expanded'); + }); + $('li.collapsed > span.simpletest-overview').each(function() { + $(this).siblings().children('ul').hide().addClass('simpletest-hidden'); + }); +}; + +/** + * Batch update function - re-attaches javascript behaviors to the new elements + * now injected into the HTML. + */ +Drupal.batchUpdate.simpleTestResultsCollapse = function() { + $('li > span.simpletest-overview').click(function() { + $(this).siblings().children('ul').toggle().toggleClass('simpletest-hidden').parent().parent().toggleClass('collapsed').toggleClass('expanded'); + }).each(function() { + $(this).siblings().children('ul').hide().addClass('simpletest-hidden').parent().parent().removeClass('expanded').addClass('collapsed'); + }); }; \ No newline at end of file Index: includes/batch.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/batch.inc,v retrieving revision 1.20 diff -u -r1.20 batch.inc --- includes/batch.inc 24 Jun 2008 21:51:02 -0000 1.20 +++ includes/batch.inc 31 Jul 2008 00:33:15 -0000 @@ -22,11 +22,14 @@ // Register database update for end of processing. register_shutdown_function('_batch_shutdown'); - // Add batch-specific css. + // Add batch-specific css and javascript. foreach ($batch['sets'] as $batch_set) { foreach ($batch_set['css'] as $css) { drupal_add_css($css); } + foreach ($batch_set['js'] as $js) { + drupal_add_js($js); + } } $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; @@ -251,8 +254,11 @@ '@total' => $total, '@current' => floor($current), '@percentage' => $percentage, - ); - $message = strtr($progress_message, $values) . '
'; + ); + $message = strtr($progress_message, $values); + if (!empty($message)) { + $message .= '
'; + } $message .= $task_message ? $task_message : ' '; return array($percentage, $message); Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.277 diff -u -r1.277 form.inc --- includes/form.inc 18 Jul 2008 07:06:24 -0000 1.277 +++ includes/form.inc 31 Jul 2008 00:33:16 -0000 @@ -2421,6 +2421,7 @@ 'progress_message' => $t('Remaining @remaining of @total.'), 'error_message' => $t('An error has occurred.'), 'css' => array(), + 'js' => array(), ); $batch_set = $init + $batch_definition + $defaults; Index: modules/filter/filter.test =================================================================== RCS file: /cvs/drupal/drupal/modules/filter/filter.test,v retrieving revision 1.5 diff -u -r1.5 filter.test --- modules/filter/filter.test 6 Jun 2008 10:36:43 -0000 1.5 +++ modules/filter/filter.test 31 Jul 2008 00:33:16 -0000 @@ -292,15 +292,15 @@ $this->assertRaw('href="http://www.test17.com"', t('Parse multiple www-strings inside same paragraph.')); $this->assertRaw('href="http://www.test18.com"', t('Parse multiple www-strings inside same paragraph.')); $this->assertRaw('href="http://www.test19.com"', t('Parse multiple www-strings inside same paragraph.')); - $this->assertRaw('href="http://www.test20.com"', t('Parse multiple www-strings inside same paragraph limited with
.')); - $this->assertRaw('href="http://www.test21.com"', t('Parse multiple www-strings inside same paragraph limited with
.')); - $this->assertRaw('href="mailto:person@test22.com"', t('Parse email string with multiple www-strings inside same paragraph limited with
.')); - $this->assertRaw('href="http://www.test23.com"', t('Parse multiple www-strings inside same paragraph limited with
.')); - $this->assertRaw('href="http://www.test24.com"', t('Parse multiple www-strings inside same paragraph limited with
and .')); - $this->assertRaw('href="http://www.test25.com"', t('Parse multiple www-strings inside same paragraph limited with
and .')); - $this->assertRaw('href="http://www.test26.com"', t('Parse multiple www-strings inside same paragraph limited with
and .')); - $this->assertRaw('href="mailto:person@test27.com"', t('Parse email string with multiple www-strings inside same paragraph limited with
and .')); - $this->assertRaw('href="http://www.test28.com"', t('Parse multiple www-strings inside same paragraph limited with
and .')); + $this->assertRaw('href="http://www.test20.com"', t('Parse multiple www-strings inside same paragraph limited with <br>.')); + $this->assertRaw('href="http://www.test21.com"', t('Parse multiple www-strings inside same paragraph limited with <br>.')); + $this->assertRaw('href="mailto:person@test22.com"', t('Parse email string with multiple www-strings inside same paragraph limited with <br>.')); + $this->assertRaw('href="http://www.test23.com"', t('Parse multiple www-strings inside same paragraph limited with <br>.')); + $this->assertRaw('href="http://www.test24.com"', t('Parse multiple www-strings inside same paragraph limited with <br> and <img>.')); + $this->assertRaw('href="http://www.test25.com"', t('Parse multiple www-strings inside same paragraph limited with <br> and <img>.')); + $this->assertRaw('href="http://www.test26.com"', t('Parse multiple www-strings inside same paragraph limited with <br> and <img>.')); + $this->assertRaw('href="mailto:person@test27.com"', t('Parse email string with multiple www-strings inside same paragraph limited with <br> and <img>.')); + $this->assertRaw('href="http://www.test28.com"', t('Parse multiple www-strings inside same paragraph limited with <br> and <img>.')); $this->assertNoRaw('href="http://www.test29.com"', t('Do not parse URL inside a script element (part of javascript code).')); $this->assertNoRaw('href="http://www.test30.com"', t('Do not parse URL inside an a element.')); $this->assertRaw('href="http://www.test31.com"', t('Parse URL inside strong tag.')); Index: misc/drupal.js =================================================================== RCS file: /cvs/drupal/drupal/misc/drupal.js,v retrieving revision 1.45 diff -u -r1.45 drupal.js --- misc/drupal.js 25 Jun 2008 07:45:03 -0000 1.45 +++ misc/drupal.js 31 Jul 2008 00:33:16 -0000 @@ -1,6 +1,6 @@ // $Id: drupal.js,v 1.45 2008/06/25 07:45:03 dries Exp $ -var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'themes': {}, 'locale': {} }; +var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'themes': {}, 'locale': {}, 'batchUpdate': {} }; /** * Set the variable that indicates if JavaScript behaviors should be applied Index: misc/batch.js =================================================================== RCS file: /cvs/drupal/drupal/misc/batch.js,v retrieving revision 1.4 diff -u -r1.4 batch.js --- misc/batch.js 21 Oct 2007 18:59:01 -0000 1.4 +++ misc/batch.js 31 Jul 2008 00:33:16 -0000 @@ -16,6 +16,9 @@ // Success: redirect to the summary. var updateCallback = function (progress, status, pb) { + for (behavior in Drupal.batchUpdate) { + Drupal.batchUpdate[behavior](progress, status, pb); + } if (progress == 100) { pb.stopMonitoring(); window.location = uri+'&op=finished'; @@ -35,4 +38,4 @@ $(holder).append(progress.element); progress.startMonitoring(uri+'&op=do', 10); }); -}; +}; \ No newline at end of file Index: modules/contact/contact.test =================================================================== RCS file: /cvs/drupal/drupal/modules/contact/contact.test,v retrieving revision 1.7 diff -u -r1.7 contact.test --- modules/contact/contact.test 22 Jul 2008 20:08:44 -0000 1.7 +++ modules/contact/contact.test 31 Jul 2008 00:33:16 -0000 @@ -191,7 +191,7 @@ // Get role id (rid) for specified role. $rid = db_result(db_query("SELECT rid FROM {role} WHERE name = '%s'", array($role))); if ($rid === FALSE) { - $this->fail(t(' [permission] Role "' . $role . '" not found.')); + $this->fail(t('Role "@role" not found.', array('@role' => $role)), t('Permission')); } // Create edit array from permission. @@ -201,7 +201,7 @@ } $this->drupalPost('admin/user/permissions', $edit, t('Save permissions')); - $this->assertText(t('The changes have been saved.'), t(' [permission] Saved changes.')); + $this->assertText(t('The changes have been saved.'), t('Saved changes.'), t('Permission')); } }