Complex classes like Node often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Node, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | class Node { |
||
14 | |||
15 | const DEFAULT_WAIT_TIME = 2000; |
||
16 | |||
17 | /** |
||
18 | * The driver for traversing this node and its children |
||
19 | * @var Driver |
||
20 | */ |
||
21 | protected $_driver; |
||
22 | |||
23 | /** |
||
24 | * The parent node, NULL if root |
||
25 | * @var Node |
||
26 | */ |
||
27 | protected $_parent; |
||
28 | |||
29 | /** |
||
30 | * The id for the current node, NULL if root |
||
31 | * @var string |
||
32 | */ |
||
33 | protected $_id = NULL; |
||
34 | |||
35 | /** |
||
36 | * The time find operation will wait for the node to appear, in milliseconds |
||
37 | * @var integer |
||
38 | */ |
||
39 | protected $_next_wait_time; |
||
40 | |||
41 | /** |
||
42 | * All methods not for this node will be proxied through this |
||
43 | * @var object |
||
44 | */ |
||
45 | protected $_extension; |
||
46 | |||
47 | 38 | function __construct(Driver $driver = NULL, Node $parent = NULL, $id = NULL) |
|
|
|||
48 | { |
||
49 | 38 | $this->_driver = $driver; |
|
50 | 38 | $this->_parent = $parent; |
|
51 | 38 | $this->_id = $id; |
|
52 | |||
53 | 38 | if ($parent AND $parent->_extension) |
|
54 | { |
||
55 | 1 | $this->_extension = $parent->_extension; |
|
56 | } |
||
57 | 38 | } |
|
58 | |||
59 | /** |
||
60 | * Getter, get the current driver object |
||
61 | * @return Driver |
||
62 | */ |
||
63 | 22 | public function driver() |
|
67 | |||
68 | /** |
||
69 | * Getter / Setter for the extension object |
||
70 | * @param mixed $extension |
||
71 | * @return mixed|$this |
||
72 | */ |
||
73 | 2 | public function extension($extension = NULL) |
|
74 | { |
||
75 | 2 | if ($extension !== NULL) |
|
76 | { |
||
77 | 1 | $this->_extension = $extension; |
|
78 | 1 | return $this; |
|
79 | } |
||
80 | 2 | return $this->_extension; |
|
81 | } |
||
82 | |||
83 | /** |
||
84 | * Getter - get the parent node |
||
85 | * @return Node |
||
86 | */ |
||
87 | 1 | public function parent() |
|
91 | |||
92 | /** |
||
93 | * Setter this method is used to populate a node with a new id |
||
94 | * @param string $id |
||
95 | * @return Node $this |
||
96 | */ |
||
97 | 14 | public function load_new_id($id) |
|
102 | |||
103 | /** |
||
104 | * Setter / Getter of the next wait time |
||
105 | * @param integer $next_wait_time milliseconds |
||
106 | * @return integer|Node |
||
107 | */ |
||
108 | 12 | public function next_wait_time($next_wait_time = NULL) |
|
109 | { |
||
110 | 12 | if ($next_wait_time !== NULL) |
|
111 | { |
||
112 | 1 | $this->_next_wait_time = $next_wait_time; |
|
113 | 1 | return $this; |
|
114 | } |
||
115 | |||
116 | 12 | if ($this->_next_wait_time === NULL AND $this->_driver) |
|
117 | { |
||
118 | 12 | $this->_next_wait_time = $this->_driver->default_wait_time; |
|
119 | } |
||
120 | |||
121 | 12 | return $this->_next_wait_time; |
|
122 | } |
||
123 | |||
124 | /** |
||
125 | * Wait milliseconds |
||
126 | * @param integer $milliseconds |
||
127 | * @return Node $this |
||
128 | */ |
||
129 | 1 | public function wait($milliseconds = 1000) |
|
135 | |||
136 | /** |
||
137 | * The DOMDocument or DOMElement representation of the current tag |
||
138 | * @return DOMDocument|DOMElement |
||
139 | */ |
||
140 | 9 | public function dom() |
|
144 | |||
145 | /** |
||
146 | * GETTERS |
||
147 | * =========================================== |
||
148 | */ |
||
149 | |||
150 | /** |
||
151 | * is this the main html page? |
||
152 | * @return boolean |
||
153 | */ |
||
154 | 1 | public function is_root() |
|
158 | |||
159 | /** |
||
160 | * The current internal ID, unique to this page |
||
161 | * @return mixed |
||
162 | */ |
||
163 | 15 | public function id() |
|
167 | |||
168 | /** |
||
169 | * The html source of the current tag |
||
170 | * @return string |
||
171 | */ |
||
172 | 2 | public function html() |
|
176 | |||
177 | /** |
||
178 | * The html source of the current tag |
||
179 | * @return string |
||
180 | */ |
||
181 | 1 | public function __toString() |
|
185 | |||
186 | /** |
||
187 | * The tag name of the current tag (body, div, input) |
||
188 | * @return string |
||
189 | */ |
||
190 | 2 | public function tag_name() |
|
194 | |||
195 | /** |
||
196 | * Attribute of the current tag |
||
197 | * @param string $name the name of the attribute |
||
198 | * @return string |
||
199 | */ |
||
200 | 3 | public function attribute($name) |
|
204 | |||
205 | /** |
||
206 | * The text content of the current tag (similar to javascript's innerText) |
||
207 | * @return string |
||
208 | */ |
||
209 | 2 | public function text() |
|
213 | |||
214 | /** |
||
215 | * Is this element visible? |
||
216 | * @return boolean |
||
217 | */ |
||
218 | 1 | public function is_visible() |
|
222 | |||
223 | /** |
||
224 | * Is this option element selected? |
||
225 | * @return boolean |
||
226 | */ |
||
227 | 1 | public function is_selected() |
|
231 | |||
232 | /** |
||
233 | * Is this checkbox checked? |
||
234 | * @return boolean |
||
235 | */ |
||
236 | 1 | public function is_checked() |
|
240 | |||
241 | /** |
||
242 | * Get the value of the current form field |
||
243 | * @return string |
||
244 | */ |
||
245 | 3 | public function value() |
|
249 | |||
250 | |||
251 | /** |
||
252 | * SETTERS |
||
253 | * =========================================== |
||
254 | */ |
||
255 | |||
256 | /** |
||
257 | * Set the value for the current form field |
||
258 | * @param mixed $value |
||
259 | * @return Node $this |
||
260 | */ |
||
261 | 3 | public function set($value) |
|
266 | |||
267 | /** |
||
268 | * Append to the current value - useful for textarea / input fields |
||
269 | * @param string $value |
||
270 | * @return Node $this |
||
271 | */ |
||
272 | 1 | public function append($value) |
|
280 | |||
281 | /** |
||
282 | * Click on the current html tag, either a button or a link |
||
283 | * @return Node $this |
||
284 | */ |
||
285 | 2 | public function click() |
|
290 | |||
291 | /** |
||
292 | * Select an option for the current select tag |
||
293 | * @return Node $this |
||
294 | */ |
||
295 | 3 | public function select_option() |
|
300 | |||
301 | /** |
||
302 | * Unselect an option for the current select tag |
||
303 | * @return Node $this |
||
304 | */ |
||
305 | 2 | public function unselect_option() |
|
310 | |||
311 | /** |
||
312 | * Hover over the current tag with the mouse |
||
313 | * @param integer $x offset inside the tag |
||
314 | * @param integer $y offset inside the tag |
||
315 | * @return Node $this |
||
316 | */ |
||
317 | 1 | public function hover($x = NULL, $y = NULL) |
|
322 | |||
323 | /** |
||
324 | * Simulate drop file events on the current element |
||
325 | * @param array|string $files local file filename or an array of filenames |
||
326 | * @return Node $this |
||
327 | */ |
||
328 | 1 | public function drop_files($files) |
|
333 | |||
334 | |||
335 | /** |
||
336 | * ACTIONS |
||
337 | * ======================================= |
||
338 | */ |
||
339 | |||
340 | /** |
||
341 | * Click on a specifc tag child of the current tag |
||
342 | * @param string|array $selector |
||
343 | * @param array $filters |
||
344 | * @return Node $this |
||
345 | */ |
||
346 | 1 | public function click_on($selector, array $filters = array()) |
|
351 | |||
352 | /** |
||
353 | * Click on a specifc link child of the current tag |
||
354 | * @param string|array $selector |
||
355 | * @param array $filters |
||
356 | * @return Node $this |
||
357 | */ |
||
358 | 1 | public function click_link($selector, array $filters = array()) |
|
363 | |||
364 | /** |
||
365 | * Click on a specifc button child of the current tag |
||
366 | * @param string|array $selector |
||
367 | * @param array $filters |
||
368 | * @return Node $this |
||
369 | */ |
||
370 | 1 | public function click_button($selector, array $filters = array()) |
|
375 | |||
376 | /** |
||
377 | * Set the value of the specific form field inside the current tag |
||
378 | * @param string|array $selector |
||
379 | * @param mixed $with the value to be set |
||
380 | * @param array $filters |
||
381 | * @return Node this |
||
382 | */ |
||
383 | 1 | public function fill_in($selector, $with, array $filters = array()) |
|
384 | { |
||
385 | 1 | $field = $this->find_field($selector, $filters); |
|
386 | |||
387 | 1 | if ( ! in_array($field->tag_name(), array('input', 'textarea'))) |
|
388 | throw new Exception('Element of type ":type" cannot be filled in! Only input and textarea elements can.'); |
||
389 | |||
390 | 1 | $field->set($with); |
|
391 | |||
392 | 1 | return $this; |
|
393 | } |
||
394 | |||
395 | /** |
||
396 | * Choose a spesific radio tag inside the current tag |
||
397 | * @param string|array $selector |
||
398 | * @param array $filters |
||
399 | * @return Node $this |
||
400 | */ |
||
401 | 1 | public function choose($selector, array $filters = array()) |
|
406 | |||
407 | /** |
||
408 | * Check a spesific checkbox input tag inside the current tag |
||
409 | * @param string|array $selector |
||
410 | * @param array $filters |
||
411 | * @return Node $this |
||
412 | */ |
||
413 | 1 | public function check($selector, array $filters = array()) |
|
418 | |||
419 | /** |
||
420 | * Uncheck a spesific checkbox input tag inside the current tag |
||
421 | * @param string|array $selector |
||
422 | * @param array $filters |
||
423 | * @return Node $this |
||
424 | */ |
||
425 | 1 | public function uncheck($selector, array $filters = array()) |
|
430 | |||
431 | /** |
||
432 | * Attach a file to a spesific file input tag inside the current tag |
||
433 | * @param string|array $selector |
||
434 | * @param string $file the filename for the file |
||
435 | * @param array $filters |
||
436 | * @return Node $this |
||
437 | */ |
||
438 | 1 | public function attach_file($selector, $file, array $filters = array()) |
|
443 | |||
444 | /** |
||
445 | * Select an option of a spesific select tag inside the current tag |
||
446 | * |
||
447 | * To select the option the second parameter can be either a string of the option text |
||
448 | * or a filter to be applied on the options e.g. array('value' => 10) |
||
449 | * |
||
450 | * @param string|array $selector |
||
451 | * @param array $filters |
||
452 | * @param array|string $option_filters |
||
453 | * @return Node $this |
||
454 | */ |
||
455 | 1 | public function select($selector, $option_filters, array $filters = array()) |
|
456 | { |
||
457 | 1 | if ( ! is_array($option_filters)) |
|
458 | { |
||
459 | 1 | $option_filters = array('text' => $option_filters); |
|
460 | } |
||
461 | |||
462 | $this |
||
463 | 1 | ->find_field($selector, $filters) |
|
464 | 1 | ->find('option', $option_filters) |
|
465 | 1 | ->select_option(); |
|
466 | |||
467 | 1 | return $this; |
|
468 | } |
||
469 | |||
470 | /** |
||
471 | * Unselect an option of a spesific select tag inside the current tag |
||
472 | * |
||
473 | * To select the option the second parameter can be either a string of the option text |
||
474 | * or a filter to be applied on the options e.g. array('value' => 10) |
||
475 | * |
||
476 | * @param string|array $selector |
||
477 | * @param array $filters |
||
478 | * @param array|string $option_filters |
||
479 | * @return Node $this |
||
480 | */ |
||
481 | 1 | public function unselect($selector, $option_filters, array $filters = array()) |
|
482 | { |
||
483 | 1 | if ( ! is_array($option_filters)) |
|
484 | { |
||
485 | 1 | $option_filters = array('value' => $option_filters); |
|
486 | } |
||
487 | |||
488 | $this |
||
489 | 1 | ->find_field($selector) |
|
490 | 1 | ->find('option', $option_filters) |
|
491 | 1 | ->unselect_option(); |
|
492 | 1 | return $this; |
|
493 | } |
||
494 | |||
495 | /** |
||
496 | * Confirm a javascript alert/confirm dialog box |
||
497 | * |
||
498 | * @param boolean|string $confirm alert/confirm - use boolean for inputs use string |
||
499 | * @return Node $this |
||
500 | */ |
||
501 | 1 | public function confirm($confirm) |
|
506 | |||
507 | /** |
||
508 | * Execute arbitrary javascript on the page and get the result |
||
509 | * |
||
510 | * @param string $script |
||
511 | * @return mixed |
||
512 | */ |
||
513 | 1 | public function execute($script, $callback = NULL) |
|
526 | |||
527 | /** |
||
528 | * Perform a screenshot of the current into the given file |
||
529 | * @param string $file |
||
530 | * @return Node $this |
||
531 | */ |
||
532 | 1 | public function screenshot($file) |
|
537 | |||
538 | /** |
||
539 | * Hover the mouse over a specific tag child of the current tag |
||
540 | * @param string|array $selector |
||
541 | * @param array $filters |
||
542 | * @return Node $this |
||
543 | */ |
||
544 | 1 | public function hover_on($selector, array $filters = array()) |
|
549 | |||
550 | /** |
||
551 | * Hover the mouse over a specific link child of the current tag |
||
552 | * @param string|array $selector |
||
553 | * @param array $filters |
||
554 | * @return Node $this |
||
555 | */ |
||
556 | 1 | public function hover_link($selector, array $filters = array()) |
|
561 | |||
562 | /** |
||
563 | * Hover the mouse over a specific field child of the current tag |
||
564 | * @param string|array $selector |
||
565 | * @param array $filters |
||
566 | * @return Node $this |
||
567 | */ |
||
568 | 1 | public function hover_field($selector, array $filters = array()) |
|
573 | |||
574 | /** |
||
575 | * Hover the mouse over a specific button child of the current tag |
||
576 | * @param string|array $selector |
||
577 | * @param array $filters |
||
578 | * @return Node $this |
||
579 | */ |
||
580 | 1 | public function hover_button($selector, array $filters = array()) |
|
585 | |||
586 | /** |
||
587 | * FINDERS |
||
588 | * ===================================================== |
||
589 | */ |
||
590 | |||
591 | /** |
||
592 | * Find an html form field child of the current tag |
||
593 | * @param string|array $selector |
||
594 | * @param array $filters |
||
595 | * @return Node $this |
||
596 | */ |
||
597 | 5 | public function find_field($selector, array $filters = array()) |
|
601 | |||
602 | /** |
||
603 | * Find an html form field child of the current tag |
||
604 | * @param string|array $selector |
||
605 | * @param array $filters |
||
606 | * @return Node $this |
||
607 | */ |
||
608 | 4 | public function find_link($selector, array $filters = array()) |
|
612 | |||
613 | /** |
||
614 | * Find an html button tag child of the current tag |
||
615 | * @param string|array $selector |
||
616 | * @param array $filters |
||
617 | * @return Node $this |
||
618 | */ |
||
619 | 4 | public function find_button($selector, array $filters = array()) |
|
623 | |||
624 | /** |
||
625 | * Find an html tag child of the current tag |
||
626 | * This is the basic find method that is used by all the other finders. |
||
627 | * To work with ajax requests it waits a bit (defualt 2 seconds) for the content to appear on the page |
||
628 | * before throwing an Functest_Exception_Notfound exception |
||
629 | * |
||
630 | * @param string|array $selector |
||
631 | * @param array $filters |
||
632 | * @throws Functest_Exception_Notfound If element not found |
||
633 | * @return Node $this |
||
634 | */ |
||
635 | 10 | public function find($selector, array $filters = array()) |
|
651 | |||
652 | /** |
||
653 | * Oposite to the find method() |
||
654 | * |
||
655 | * @param string|array $selector |
||
656 | * @param array $filters |
||
657 | * @throws Functest_Exception_Found If element is found on the page |
||
658 | * @return Node $this |
||
659 | */ |
||
660 | 1 | public function not_present($selector, array $filters = array()) |
|
661 | { |
||
662 | 1 | $locator = self::get_locator($selector, $filters); |
|
663 | 1 | $self = $this; |
|
664 | |||
665 | $not_found = Attempt::make(function() use ($self, $locator){ |
||
666 | 1 | return ! $self->all($locator)->first(); |
|
667 | 1 | }, $this->next_wait_time()); |
|
668 | |||
669 | 1 | $this->_next_wait_time = NULL; |
|
670 | |||
671 | 1 | if ( ! $not_found) |
|
672 | 1 | throw new Exception_Found($locator, $this->driver()); |
|
673 | |||
674 | 1 | return TRUE; |
|
675 | } |
||
676 | |||
677 | /** |
||
678 | * Returns the parent element |
||
679 | * |
||
680 | * @return Node parent |
||
681 | */ |
||
682 | 1 | public function end() |
|
686 | |||
687 | /** |
||
688 | * Find a list of elements represented by the selector / filter |
||
689 | * |
||
690 | * @param string|array $selector |
||
691 | * @param array $filters |
||
692 | * @return Nodelist |
||
693 | */ |
||
694 | 18 | public function all($selector, array $filters = array()) |
|
700 | |||
701 | /** |
||
702 | * Shortcuts for creating locators (from arrays or nested arrays) |
||
703 | * @param string|Locator|array $selector |
||
704 | * @param array $filters |
||
705 | * @return Locator |
||
706 | */ |
||
707 | 27 | public static function get_locator($selector, array $filters = array()) |
|
729 | |||
730 | /** |
||
731 | * Pass all other methods to the extension if it is set. That way you can add additional methods |
||
732 | * @param string $method |
||
733 | * @param array $arguments |
||
734 | * @return Node $this |
||
735 | */ |
||
736 | 1 | public function __call($method, $arguments) |
|
747 | } |
||
748 |
Adding explicit visibility (
private
,protected
, orpublic
) is generally recommend to communicate to other developers how, and from where this method is intended to be used.