Issues (2882)

src/Dev/TestSession.php (4 issues)

Severity
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, array());
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 = array())
214
    {
215
        $page = $this->lastPage();
216
        if ($page) {
0 ignored issues
show
$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
$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
$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 = array();
238
            parse_str($submission->_encode(), $postVars);
239
            return $this->post($url, $postVars);
240
        } else {
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
    /**
247
     * If the last request was a 3xx response, then follow the redirection
248
     *
249
     * @return HTTPResponse The response given, or null if no redirect occurred
250
     */
251
    public function followRedirection()
252
    {
253
        if ($this->lastResponse->getHeader('Location')) {
254
            $url = Director::makeRelative($this->lastResponse->getHeader('Location'));
255
            $url = strtok($url, '#');
256
            return $this->get($url);
257
        }
258
    }
259
260
    /**
261
     * Returns true if the last response was a 3xx redirection
262
     *
263
     * @return bool
264
     */
265
    public function wasRedirected()
266
    {
267
        $code = $this->lastResponse->getStatusCode();
268
        return $code >= 300 && $code < 400;
269
    }
270
271
    /**
272
     * Get the most recent response
273
     *
274
     * @return HTTPResponse
275
     */
276
    public function lastResponse()
277
    {
278
        return $this->lastResponse;
279
    }
280
281
    /**
282
     * Return the fake HTTP_REFERER; set each time get() or post() is called.
283
     *
284
     * @return string
285
     */
286
    public function lastUrl()
287
    {
288
        return $this->lastUrl;
289
    }
290
291
    /**
292
     * Get the most recent response's content
293
     *
294
     * @return string
295
     */
296
    public function lastContent()
297
    {
298
        if (is_string($this->lastResponse)) {
0 ignored issues
show
The condition is_string($this->lastResponse) is always false.
Loading history...
299
            return $this->lastResponse;
300
        } else {
301
            return $this->lastResponse->getBody();
302
        }
303
    }
304
305
    /**
306
     * Return a CSSContentParser containing the most recent response
307
     *
308
     * @return CSSContentParser
309
     */
310
    public function cssParser()
311
    {
312
        return new CSSContentParser($this->lastContent());
313
    }
314
315
    /**
316
     * Get the last response as a SimplePage object
317
     *
318
     * @return SimplePage The response if available
319
     */
320
    public function lastPage()
321
    {
322
        require_once("simpletest/http.php");
323
        require_once("simpletest/page.php");
324
        require_once("simpletest/form.php");
325
326
        $builder = new SimplePageBuilder();
327
        if ($this->lastResponse) {
328
            $page = &$builder->parse(new TestSession_STResponseWrapper($this->lastResponse));
329
            $builder->free();
330
            unset($builder);
331
332
            return $page;
333
        }
334
    }
335
336
    /**
337
     * Get the current session, as a Session object
338
     *
339
     * @return Session
340
     */
341
    public function session()
342
    {
343
        return $this->session;
344
    }
345
}
346