0b704f90c54310afed47bde78ddef7c924995d38
[yaffs-website] / web / core / modules / simpletest / src / Form / SimpletestTestForm.php
1 <?php
2
3 namespace Drupal\simpletest\Form;
4
5 use Drupal\Core\Form\FormBase;
6 use Drupal\Core\Form\FormStateInterface;
7 use Drupal\Core\Render\RendererInterface;
8 use Drupal\simpletest\TestDiscovery;
9 use Symfony\Component\DependencyInjection\ContainerInterface;
10
11 /**
12  * List tests arranged in groups that can be selected and run.
13  *
14  * @internal
15  */
16 class SimpletestTestForm extends FormBase {
17
18   /**
19    * The renderer.
20    *
21    * @var \Drupal\Core\Render\RendererInterface
22    */
23   protected $renderer;
24
25   /**
26    * The test discovery service.
27    *
28    * @var \Drupal\simpletest\TestDiscovery
29    */
30   protected $testDiscovery;
31
32   /**
33    * {@inheritdoc}
34    */
35   public static function create(ContainerInterface $container) {
36     return new static(
37       $container->get('renderer'),
38       $container->get('test_discovery')
39     );
40   }
41
42   /**
43    * Constructs a new SimpletestTestForm.
44    *
45    * @param \Drupal\Core\Render\RendererInterface $renderer
46    *   The renderer.
47    * @param \Drupal\simpletest\TestDiscovery $test_discovery
48    *   The test discovery service.
49    */
50   public function __construct(RendererInterface $renderer, TestDiscovery $test_discovery) {
51     $this->renderer = $renderer;
52     $this->testDiscovery = $test_discovery;
53   }
54
55   /**
56    * {@inheritdoc}
57    */
58   public function getFormId() {
59     return 'simpletest_test_form';
60   }
61
62   /**
63    * {@inheritdoc}
64    */
65   public function buildForm(array $form, FormStateInterface $form_state) {
66     $form['actions'] = ['#type' => 'actions'];
67     $form['actions']['submit'] = [
68       '#type' => 'submit',
69       '#value' => $this->t('Run tests'),
70       '#tableselect' => TRUE,
71       '#button_type' => 'primary',
72     ];
73     $form['clean'] = [
74       '#type' => 'fieldset',
75       '#title' => $this->t('Clean test environment'),
76       '#description' => $this->t('Remove tables with the prefix "test" followed by digits and temporary directories that are left over from tests that crashed. This is intended for developers when creating tests.'),
77       '#weight' => 200,
78     ];
79     $form['clean']['op'] = [
80       '#type' => 'submit',
81       '#value' => $this->t('Clean environment'),
82       '#submit' => ['simpletest_clean_environment'],
83     ];
84
85     // Do not needlessly re-execute a full test discovery if the user input
86     // already contains an explicit list of test classes to run.
87     $user_input = $form_state->getUserInput();
88     if (!empty($user_input['tests'])) {
89       return $form;
90     }
91
92     // JavaScript-only table filters.
93     $form['filters'] = [
94       '#type' => 'container',
95       '#attributes' => [
96         'class' => ['table-filter', 'js-show'],
97       ],
98     ];
99     $form['filters']['text'] = [
100       '#type' => 'search',
101       '#title' => $this->t('Search'),
102       '#size' => 30,
103       '#placeholder' => $this->t('Enter test nameā€¦'),
104       '#attributes' => [
105         'class' => ['table-filter-text'],
106         'data-table' => '#simpletest-test-form',
107         'autocomplete' => 'off',
108         'title' => $this->t('Enter at least 3 characters of the test name or description to filter by.'),
109       ],
110     ];
111
112     $form['tests'] = [
113       '#type' => 'table',
114       '#id' => 'simpletest-form-table',
115       '#tableselect' => TRUE,
116       '#header' => [
117         ['data' => $this->t('Test'), 'class' => ['simpletest-test-label']],
118         ['data' => $this->t('Description'), 'class' => ['simpletest-test-description']],
119       ],
120       '#empty' => $this->t('No tests to display.'),
121       '#attached' => [
122         'library' => [
123           'simpletest/drupal.simpletest',
124         ],
125       ],
126     ];
127
128     // Define the images used to expand/collapse the test groups.
129     $image_collapsed = [
130       '#theme' => 'image',
131       '#uri' => 'core/misc/menu-collapsed.png',
132       '#width' => '7',
133       '#height' => '7',
134       '#alt' => $this->t('Expand'),
135       '#title' => $this->t('Expand'),
136       '#suffix' => '<a href="#" class="simpletest-collapse">(' . $this->t('Expand') . ')</a>',
137     ];
138     $image_extended = [
139       '#theme' => 'image',
140       '#uri' => 'core/misc/menu-expanded.png',
141       '#width' => '7',
142       '#height' => '7',
143       '#alt' => $this->t('Collapse'),
144       '#title' => $this->t('Collapse'),
145       '#suffix' => '<a href="#" class="simpletest-collapse">(' . $this->t('Collapse') . ')</a>',
146     ];
147     $form['tests']['#attached']['drupalSettings']['simpleTest']['images'] = [
148       (string) $this->renderer->renderPlain($image_collapsed),
149       (string) $this->renderer->renderPlain($image_extended),
150     ];
151
152     // Generate the list of tests arranged by group.
153     $groups = $this->testDiscovery->getTestClasses();
154     foreach ($groups as $group => $tests) {
155       $form['tests'][$group] = [
156         '#attributes' => ['class' => ['simpletest-group']],
157       ];
158
159       // Make the class name safe for output on the page by replacing all
160       // non-word/decimal characters with a dash (-).
161       $group_class = 'module-' . strtolower(trim(preg_replace("/[^\w\d]/", "-", $group)));
162
163       // Override tableselect column with custom selector for this group.
164       // This group-select-all checkbox is injected via JavaScript.
165       $form['tests'][$group]['select'] = [
166         '#wrapper_attributes' => [
167           'id' => $group_class,
168           'class' => ['simpletest-group-select-all'],
169         ],
170       ];
171       $form['tests'][$group]['title'] = [
172         // Expand/collapse image.
173         '#prefix' => '<div class="simpletest-image" id="simpletest-test-group-' . $group_class . '"></div>',
174         '#markup' => '<label for="' . $group_class . '-group-select-all">' . $group . '</label>',
175         '#wrapper_attributes' => [
176           'class' => ['simpletest-group-label'],
177         ],
178       ];
179       $form['tests'][$group]['description'] = [
180         '#markup' => '&nbsp;',
181         '#wrapper_attributes' => [
182           'class' => ['simpletest-group-description'],
183         ],
184       ];
185
186       // Cycle through each test within the current group.
187       foreach ($tests as $class => $info) {
188         $form['tests'][$class] = [
189           '#attributes' => ['class' => [$group_class . '-test', 'js-hide']],
190         ];
191         $form['tests'][$class]['title'] = [
192           '#type' => 'label',
193           '#title' => '\\' . $info['name'],
194           '#wrapper_attributes' => [
195             'class' => ['simpletest-test-label', 'table-filter-text-source'],
196           ],
197         ];
198         $form['tests'][$class]['description'] = [
199           '#prefix' => '<div class="description">',
200           '#plain_text' => $info['description'],
201           '#suffix' => '</div>',
202           '#wrapper_attributes' => [
203             'class' => ['simpletest-test-description', 'table-filter-text-source'],
204           ],
205         ];
206       }
207     }
208
209     return $form;
210   }
211
212   /**
213    * {@inheritdoc}
214    */
215   public function submitForm(array &$form, FormStateInterface $form_state) {
216     // Test discovery does not run upon form submission.
217     $this->testDiscovery->registerTestNamespaces();
218
219     // This form accepts arbitrary user input for 'tests'.
220     // An invalid value will cause the $class_name lookup below to die with a
221     // fatal error. Regular user access mechanisms to this form are intact.
222     // The only validation effectively being skipped is the validation of
223     // available checkboxes vs. submitted checkboxes.
224     // @todo Refactor Form API to allow to POST values without constructing the
225     //   entire form more easily, BUT retaining routing access security and
226     //   retaining Form API CSRF #token security validation, and without having
227     //   to rely on form caching.
228     $user_input = $form_state->getUserInput();
229     if ($form_state->isValueEmpty('tests') && !empty($user_input['tests'])) {
230       $form_state->setValue('tests', $user_input['tests']);
231     }
232
233     $tests_list = array_filter($form_state->getValue('tests'));
234     if (!empty($tests_list)) {
235       $test_id = simpletest_run_tests($tests_list, 'drupal');
236       $form_state->setRedirect(
237         'simpletest.result_form',
238         ['test_id' => $test_id]
239       );
240     }
241   }
242
243 }