Passed
Pull Request — 4 (#9625)
by Nicolaas
07:16
created

TestSession   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 319
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 108
dl 0
loc 319
rs 9.1199
c 1
b 0
f 0
wmc 41

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A get() 0 21 6
A post() 0 21 6
A __destruct() 0 10 4
A sendRequest() 0 25 6
A session() 0 3 1
A submitForm() 0 30 6
A wasRedirected() 0 4 2
A followRedirection() 0 6 2
A lastUrl() 0 3 1
A lastContent() 0 6 2
A lastPage() 0 13 2
A lastResponse() 0 3 1
A cssParser() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like TestSession 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.

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 TestSession, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SilverStripe\Dev;
4
5
use Exception;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Control\Cookie_Backend;
8
use SilverStripe\Control\Director;
9
use SilverStripe\Control\HTTPRequest;
10
use SilverStripe\Control\HTTPResponse;
11
use SilverStripe\Control\HTTPResponse_Exception;
12
use SilverStripe\Control\Session;
13
use SilverStripe\Core\Extensible;
14
use SilverStripe\Core\Injector\Injector;
15
use SimpleByName;
16
use SimplePage;
17
use SimplePageBuilder;
18
19
/**
20
 * Represents a test usage session of a web-app
21
 * It will maintain session-state from request to request
22
 */
23
class TestSession
24
{
25
    use Extensible;
26
27
    /**
28
     * @var Session
29
     */
30
    private $session;
31
32
    /**
33
     * @var Cookie_Backend
34
     */
35
    private $cookies;
36
37
    /**
38
     * @var HTTPResponse
39
     */
40
    private $lastResponse;
41
42
    /**
43
     * Necessary to use the mock session
44
     * created in {@link session} in the normal controller stack,
45
     * e.g. to overwrite Security::getCurrentUser() with custom login data.
46
     *
47
     * @var Controller
48
     */
49
    protected $controller;
50
51
    /**
52
     * Fake HTTP Referer Tracking, set in {@link get()} and {@link post()}.
53
     *
54
     * @var string
55
     */
56
    private $lastUrl;
57
58
    public function __construct()
59
    {
60
        $this->session = Injector::inst()->create(Session::class, []);
61
        $this->cookies = Injector::inst()->create(Cookie_Backend::class);
62
        $request = new HTTPRequest('GET', '/');
63
        $request->setSession($this->session());
64
        $this->controller = new Controller();
65
        $this->controller->setRequest($request);
66
        $this->controller->pushCurrent();
67
        $this->controller->doInit();
68
    }
69
70
    public function __destruct()
71
    {
72
        // Shift off anything else that's on the stack.  This can happen if something throws
73
        // an exception that causes a premature TestSession::__destruct() call
74
        while (Controller::has_curr() && Controller::curr() !== $this->controller) {
75
            Controller::curr()->popCurrent();
76
        }
77
78
        if (Controller::has_curr()) {
79
            $this->controller->popCurrent();
80
        }
81
    }
82
83
    /**
84
     * Submit a get request
85
     *
86
     * @uses Director::test()
87
     * @param string $url
88
     * @param Session $session
89
     * @param array $headers
90
     * @param array $cookies
91
     * @return HTTPResponse
92
     */
93
    public function get($url, $session = null, $headers = null, $cookies = null)
94
    {
95
        $this->extend('updateGetURL', $url, $session, $headers, $cookies);
96
        $headers = (array) $headers;
97
        if ($this->lastUrl && !isset($headers['Referer'])) {
98
            $headers['Referer'] = $this->lastUrl;
99
        }
100
        $this->lastResponse = Director::test(
101
            $url,
102
            null,
103
            $session ?: $this->session,
104
            'GET',
105
            null,
106
            $headers,
107
            $cookies ?: $this->cookies
108
        );
109
        $this->lastUrl = $url;
110
        if (!$this->lastResponse) {
111
            user_error("Director::test(${url}) returned null", E_USER_WARNING);
112
        }
113
        return $this->lastResponse;
114
    }
115
116
    /**
117
     * Submit a post request
118
     *
119
     * @uses Director::test()
120
     * @param string $url
121
     * @param array $data
122
     * @param array $headers
123
     * @param Session $session
124
     * @param string $body
125
     * @param array $cookies
126
     * @return HTTPResponse
127
     * @throws HTTPResponse_Exception
128
     */
129
    public function post($url, $data, $headers = null, $session = null, $body = null, $cookies = null)
130
    {
131
        $this->extend('updatePostURL', $url, $data, $headers, $session, $body, $cookies);
132
        $headers = (array) $headers;
133
        if ($this->lastUrl && !isset($headers['Referer'])) {
134
            $headers['Referer'] = $this->lastUrl;
135
        }
136
        $this->lastResponse = Director::test(
137
            $url,
138
            $data,
139
            $session ?: $this->session,
140
            'POST',
141
            $body,
142
            $headers,
143
            $cookies ?: $this->cookies
144
        );
145
        $this->lastUrl = $url;
146
        if (!$this->lastResponse) {
147
            user_error("Director::test(${url}) returned null", E_USER_WARNING);
148
        }
149
        return $this->lastResponse;
150
    }
151
152
    /**
153
     * Submit a request of any type
154
     *
155
     * @uses Director::test()
156
     * @param string $method
157
     * @param string $url
158
     * @param array $data
159
     * @param array $headers
160
     * @param Session $session
161
     * @param string $body
162
     * @param array $cookies
163
     * @return HTTPResponse
164
     * @throws HTTPResponse_Exception
165
     */
166
    public function sendRequest($method, $url, $data, $headers = null, $session = null, $body = null, $cookies = null)
167
    {
168
        $this->extend('updateRequestURL', $method, $url, $data, $headers, $session, $body, $cookies);
169
170
        $headers = (array) $headers;
171
        if ($this->lastUrl && !isset($headers['Referer'])) {
172
            $headers['Referer'] = $this->lastUrl;
173
        }
174
175
        $this->lastResponse = Director::test(
176
            $url,
177
            $data,
178
            $session ?: $this->session,
179
            $method,
180
            $body,
181
            $headers,
182
            $cookies ?: $this->cookies
183
        );
184
185
        $this->lastUrl = $url;
186
        if (!$this->lastResponse) {
187
            user_error("Director::test(${url}) returned null", E_USER_WARNING);
188
        }
189
190
        return $this->lastResponse;
191
    }
192
193
    /**
194
     * Submit the form with the given HTML ID, filling it out with the given data.
195
     * Acts on the most recent response.
196
     *
197
     * Any data parameters have to be present in the form, with exact form field name
198
     * and values, otherwise they are removed from the submission.
199
     *
200
     * Caution: Parameter names have to be formatted
201
     * as they are in the form submission, not as they are interpreted by PHP.
202
     * Wrong: array('mycheckboxvalues' => array(1 => 'one', 2 => 'two'))
203
     * Right: array('mycheckboxvalues[1]' => 'one', 'mycheckboxvalues[2]' => 'two')
204
     *
205
     * @see http://www.simpletest.org/en/form_testing_documentation.html
206
     *
207
     * @param string $formID HTML 'id' attribute of a form (loaded through a previous response)
208
     * @param string $button HTML 'name' attribute of the button (NOT the 'id' attribute)
209
     * @param array $data Map of GET/POST data.
210
     * @return HTTPResponse
211
     * @throws Exception
212
     */
213
    public function submitForm($formID, $button = null, $data = [])
214
    {
215
        $page = $this->lastPage();
216
        if ($page) {
0 ignored issues
show
introduced by
$page is of type SimplePage, thus it always evaluated to true.
Loading history...
217
            $form = $page->getFormById($formID);
218
            if (!$form) {
0 ignored issues
show
introduced by
$form is of type SimpleForm, thus it always evaluated to true.
Loading history...
219
                user_error("TestSession::submitForm failed to find the form {$formID}");
220
            }
221
222
            foreach ($data as $k => $v) {
223
                $form->setField(new SimpleByName($k), $v);
224
            }
225
226
            if ($button) {
227
                $submission = $form->submitButton(new SimpleByName($button));
228
                if (!$submission) {
0 ignored issues
show
introduced by
$submission is of type SimpleEncoding, thus it always evaluated to true.
Loading history...
229
                    throw new Exception("Can't find button '${button}' to submit as part of test.");
230
                }
231
            } else {
232
                $submission = $form->submit();
233
            }
234
235
            $url = Director::makeRelative($form->getAction()->asString());
236
237
            $postVars = [];
238
            parse_str($submission->_encode(), $postVars);
239
            return $this->post($url, $postVars);
240
        }
241
        user_error('TestSession::submitForm called when there is no form loaded.'
242
                . ' Visit the page with the form first', E_USER_WARNING);
243
    }
244
245
    /**
246
     * If the last request was a 3xx response, then follow the redirection
247
     *
248
     * @return HTTPResponse The response given, or null if no redirect occurred
249
     */
250
    public function followRedirection()
251
    {
252
        if ($this->lastResponse->getHeader('Location')) {
253
            $url = Director::makeRelative($this->lastResponse->getHeader('Location'));
254
            $url = strtok($url, '#');
255
            return $this->get($url);
256
        }
257
    }
258
259
    /**
260
     * Returns true if the last response was a 3xx redirection
261
     *
262
     * @return bool
263
     */
264
    public function wasRedirected()
265
    {
266
        $code = $this->lastResponse->getStatusCode();
267
        return $code >= 300 && $code < 400;
268
    }
269
270
    /**
271
     * Get the most recent response
272
     *
273
     * @return HTTPResponse
274
     */
275
    public function lastResponse()
276
    {
277
        return $this->lastResponse;
278
    }
279
280
    /**
281
     * Return the fake HTTP_REFERER; set each time get() or post() is called.
282
     *
283
     * @return string
284
     */
285
    public function lastUrl()
286
    {
287
        return $this->lastUrl;
288
    }
289
290
    /**
291
     * Get the most recent response's content
292
     *
293
     * @return string
294
     */
295
    public function lastContent()
296
    {
297
        if (is_string($this->lastResponse)) {
0 ignored issues
show
introduced by
The condition is_string($this->lastResponse) is always false.
Loading history...
298
            return $this->lastResponse;
299
        }
300
        return $this->lastResponse->getBody();
301
    }
302
303
    /**
304
     * Return a CSSContentParser containing the most recent response
305
     *
306
     * @return CSSContentParser
307
     */
308
    public function cssParser()
309
    {
310
        return new CSSContentParser($this->lastContent());
311
    }
312
313
    /**
314
     * Get the last response as a SimplePage object
315
     *
316
     * @return SimplePage The response if available
317
     */
318
    public function lastPage()
319
    {
320
        require_once('simpletest/http.php');
321
        require_once('simpletest/page.php');
322
        require_once('simpletest/form.php');
323
324
        $builder = new SimplePageBuilder();
325
        if ($this->lastResponse) {
326
            $page = &$builder->parse(new TestSession_STResponseWrapper($this->lastResponse));
327
            $builder->free();
328
            unset($builder);
329
330
            return $page;
331
        }
332
    }
333
334
    /**
335
     * Get the current session, as a Session object
336
     *
337
     * @return Session
338
     */
339
    public function session()
340
    {
341
        return $this->session;
342
    }
343
}
344