Completed
Push — master ( 622a07...7c2344 )
by Daniel
108:51 queued 72:30
created

FormTest::testRemoveManyExtraClasses()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 0
dl 0
loc 22
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\Tests;
4
5
use SilverStripe\Forms\Tests\FormTest\TestController;
6
use SilverStripe\Forms\Tests\FormTest\ControllerWithSecurityToken;
7
use SilverStripe\Forms\Tests\FormTest\ControllerWithStrictPostCheck;
8
use SilverStripe\Forms\Tests\FormTest\Player;
9
use SilverStripe\Forms\Tests\FormTest\Team;
10
use SilverStripe\ORM\DataModel;
11
use SilverStripe\Security\SecurityToken;
12
use SilverStripe\Security\RandomGenerator;
13
use SilverStripe\Dev\CSSContentParser;
14
use SilverStripe\Dev\FunctionalTest;
15
use SilverStripe\Control\Controller;
16
use SilverStripe\Control\HTTPRequest;
17
use SilverStripe\Forms\TextField;
18
use SilverStripe\Forms\FieldList;
19
use SilverStripe\Forms\Form;
20
use SilverStripe\Forms\HeaderField;
21
use SilverStripe\Forms\TextareaField;
22
use SilverStripe\Forms\DateField;
23
use SilverStripe\Forms\NumericField;
24
use SilverStripe\Forms\LookupField;
25
use SilverStripe\Forms\FileField;
26
use SilverStripe\Forms\FormAction;
27
use SilverStripe\View\SSViewer;
28
29
30
/**
31
 * @skipUpgrade
32
 */
33
class FormTest extends FunctionalTest {
34
35
	protected static $fixture_file = 'FormTest.yml';
36
37
	protected $extraDataObjects = array(
38
		Player::class,
39
		Team::class,
40
	);
41
42
	protected $extraControllers = [
43
		TestController::class,
44
		ControllerWithSecurityToken::class,
45
		ControllerWithStrictPostCheck::class,
46
	];
47
48
	public function setUp() {
49
		parent::setUp();
50
51
		// Suppress themes
52
		SSViewer::set_themes([
53
			SSViewer::DEFAULT_THEME
54
		]);
55
	}
56
57
	public function testLoadDataFromRequest() {
58
		$form = new Form(
59
			new Controller(),
60
			'Form',
61
			new FieldList(
62
				new TextField('key1'),
63
				new TextField('namespace[key2]'),
64
				new TextField('namespace[key3][key4]'),
65
				new TextField('othernamespace[key5][key6][key7]')
66
			),
67
			new FieldList()
68
		);
69
70
		// url would be ?key1=val1&namespace[key2]=val2&namespace[key3][key4]=val4&othernamespace[key5][key6][key7]=val7
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
71
		$requestData = array(
72
			'key1' => 'val1',
73
			'namespace' => array(
74
				'key2' => 'val2',
75
				'key3' => array(
76
					'key4' => 'val4',
77
				)
78
			),
79
			'othernamespace' => array(
80
				'key5' => array(
81
					'key6' =>array(
82
						'key7' => 'val7'
83
					)
84
				)
85
			)
86
		);
87
88
		$form->loadDataFrom($requestData);
89
90
		$fields = $form->Fields();
91
		$this->assertEquals($fields->fieldByName('key1')->Value(), 'val1');
92
		$this->assertEquals($fields->fieldByName('namespace[key2]')->Value(), 'val2');
93
		$this->assertEquals($fields->fieldByName('namespace[key3][key4]')->Value(), 'val4');
94
		$this->assertEquals($fields->fieldByName('othernamespace[key5][key6][key7]')->Value(), 'val7');
95
	}
96
97
	public function testSubmitReadonlyFields() {
98
		$this->get('FormTest_Controller');
99
100
		// Submitting a value for a readonly field should be ignored
101
		$response = $this->post(
102
			'FormTest_Controller/Form',
103
			array(
104
				'Email' => 'invalid',
105
				'Number' => '888',
106
				'ReadonlyField' => '<script>alert("hacxzored")</script>'
107
				// leaving out "Required" field
108
			)
109
		);
110
111
		// Number field updates its value
112
		$this->assertContains('<input type="text" name="Number" value="888"', $response->getBody());
113
114
115
		// Readonly field remains
116
		$this->assertContains(
117
			'<input type="text" name="ReadonlyField" value="This value is readonly"',
118
			$response->getBody()
119
		);
120
121
		$this->assertNotContains('hacxzored', $response->getBody());
122
	}
123
124
	public function testLoadDataFromUnchangedHandling() {
125
		$form = new Form(
126
			new Controller(),
127
			'Form',
128
			new FieldList(
129
				new TextField('key1'),
130
				new TextField('key2')
131
			),
132
			new FieldList()
133
		);
134
		$form->loadDataFrom(array(
135
			'key1' => 'save',
136
			'key2' => 'dontsave',
137
			'key2_unchanged' => '1'
138
		));
139
		$this->assertEquals(
140
			$form->getData(),
141
			array(
142
				'key1' => 'save',
143
				'key2' => null,
144
			),
145
			'loadDataFrom() doesnt save a field if a matching "<fieldname>_unchanged" flag is set'
146
		);
147
	}
148
149
	public function testLoadDataFromObject() {
150
		$form = new Form(
151
		new Controller(),
152
			'Form',
153
			new FieldList(
154
				new HeaderField('MyPlayerHeader','My Player'),
155
				new TextField('Name'), // appears in both Player and Team
156
				new TextareaField('Biography'),
157
				new DateField('Birthday'),
158
				new NumericField('BirthdayYear') // dynamic property
159
			),
160
			new FieldList()
161
		);
162
163
		$captainWithDetails = $this->objFromFixture(Player::class, 'captainWithDetails');
164
		$form->loadDataFrom($captainWithDetails);
0 ignored issues
show
Bug introduced by
It seems like $captainWithDetails defined by $this->objFromFixture(\S..., 'captainWithDetails') on line 163 can be null; however, SilverStripe\Forms\Form::loadDataFrom() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
165
		$this->assertEquals(
166
			$form->getData(),
167
			array(
168
				'Name' => 'Captain Details',
169
				'Biography' => 'Bio 1',
170
				'Birthday' => '1982-01-01',
171
				'BirthdayYear' => '1982',
172
			),
173
			'LoadDataFrom() loads simple fields and dynamic getters'
174
		);
175
176
		$captainNoDetails = $this->objFromFixture(Player::class, 'captainNoDetails');
177
		$form->loadDataFrom($captainNoDetails);
0 ignored issues
show
Bug introduced by
It seems like $captainNoDetails defined by $this->objFromFixture(\S...ss, 'captainNoDetails') on line 176 can be null; however, SilverStripe\Forms\Form::loadDataFrom() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
178
		$this->assertEquals(
179
			$form->getData(),
180
			array(
181
				'Name' => 'Captain No Details',
182
				'Biography' => null,
183
				'Birthday' => null,
184
				'BirthdayYear' => 0,
185
			),
186
			'LoadNonBlankDataFrom() loads only fields with values, and doesnt overwrite existing values'
187
		);
188
	}
189
190
	public function testLoadDataFromClearMissingFields() {
191
		$form = new Form(
192
			new Controller(),
193
			'Form',
194
			new FieldList(
195
				new HeaderField('MyPlayerHeader','My Player'),
196
				new TextField('Name'), // appears in both Player and Team
197
				new TextareaField('Biography'),
198
				new DateField('Birthday'),
199
				new NumericField('BirthdayYear'), // dynamic property
200
				$unrelatedField = new TextField('UnrelatedFormField')
201
				//new CheckboxSetField('Teams') // relation editing
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
202
			),
203
			new FieldList()
204
		);
205
		$unrelatedField->setValue("random value");
206
207
		$captainWithDetails = $this->objFromFixture(Player::class, 'captainWithDetails');
208
		$form->loadDataFrom($captainWithDetails);
0 ignored issues
show
Bug introduced by
It seems like $captainWithDetails defined by $this->objFromFixture(\S..., 'captainWithDetails') on line 207 can be null; however, SilverStripe\Forms\Form::loadDataFrom() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
209
		$this->assertEquals(
210
			$form->getData(),
211
			array(
212
				'Name' => 'Captain Details',
213
				'Biography' => 'Bio 1',
214
				'Birthday' => '1982-01-01',
215
				'BirthdayYear' => '1982',
216
				'UnrelatedFormField' => 'random value',
217
			),
218
			'LoadDataFrom() doesnt overwrite fields not found in the object'
219
		);
220
221
		$captainWithDetails = $this->objFromFixture(Player::class, 'captainNoDetails');
222
		$team2 = $this->objFromFixture(Team::class, 'team2');
223
		$form->loadDataFrom($captainWithDetails);
0 ignored issues
show
Bug introduced by
It seems like $captainWithDetails defined by $this->objFromFixture(\S...ss, 'captainNoDetails') on line 221 can be null; however, SilverStripe\Forms\Form::loadDataFrom() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
224
		$form->loadDataFrom($team2, Form::MERGE_CLEAR_MISSING);
0 ignored issues
show
Bug introduced by
It seems like $team2 defined by $this->objFromFixture(\S...t\Team::class, 'team2') on line 222 can be null; however, SilverStripe\Forms\Form::loadDataFrom() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
225
		$this->assertEquals(
226
			$form->getData(),
227
			array(
228
				'Name' => 'Team 2',
229
				'Biography' => '',
230
				'Birthday' => '',
231
				'BirthdayYear' => 0,
232
				'UnrelatedFormField' => null,
233
			),
234
			'LoadDataFrom() overwrites fields not found in the object with $clearMissingFields=true'
235
		);
236
	}
237
238
	public function testLookupFieldDisabledSaving() {
239
		$object = new Team();
240
		$form = new Form(
241
			new Controller(),
242
			'Form',
243
			new FieldList(
244
				new LookupField('Players', 'Players')
245
			),
246
			new FieldList()
247
		);
248
		$form->loadDataFrom(array(
249
			'Players' => array(
250
				14,
251
				18,
252
				22
253
			),
254
		));
255
		$form->saveInto($object);
256
		$playersIds = $object->Players()->getIDList();
257
258
		$this->assertTrue($form->validate());
259
		$this->assertEquals(
260
			$playersIds,
261
			array(),
262
			'saveInto() should not save into the DataObject for the LookupField'
263
		);
264
	}
265
266
	public function testLoadDataFromIgnoreFalseish() {
267
		$form = new Form(
268
			new Controller(),
269
			'Form',
270
			new FieldList(
271
				new TextField('Biography', 'Biography', 'Custom Default')
272
			),
273
			new FieldList()
274
		);
275
276
		$captainNoDetails = $this->objFromFixture(Player::class, 'captainNoDetails');
277
		$captainWithDetails = $this->objFromFixture(Player::class, 'captainWithDetails');
278
279
		$form->loadDataFrom($captainNoDetails, Form::MERGE_IGNORE_FALSEISH);
0 ignored issues
show
Bug introduced by
It seems like $captainNoDetails defined by $this->objFromFixture(\S...ss, 'captainNoDetails') on line 276 can be null; however, SilverStripe\Forms\Form::loadDataFrom() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
280
		$this->assertEquals(
281
			$form->getData(),
282
			array('Biography' => 'Custom Default'),
283
			'LoadDataFrom() doesn\'t overwrite fields when MERGE_IGNORE_FALSEISH set and values are false-ish'
284
		);
285
286
		$form->loadDataFrom($captainWithDetails, Form::MERGE_IGNORE_FALSEISH);
0 ignored issues
show
Bug introduced by
It seems like $captainWithDetails defined by $this->objFromFixture(\S..., 'captainWithDetails') on line 277 can be null; however, SilverStripe\Forms\Form::loadDataFrom() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
287
		$this->assertEquals(
288
			$form->getData(),
289
			array('Biography' => 'Bio 1'),
290
			'LoadDataFrom() does overwrite fields when MERGE_IGNORE_FALSEISH set and values arent false-ish'
291
		);
292
	}
293
294
	public function testFormMethodOverride() {
295
		$form = $this->getStubForm();
296
		$form->setFormMethod('GET');
297
		$this->assertNull($form->Fields()->dataFieldByName('_method'));
298
299
		$form = $this->getStubForm();
300
		$form->setFormMethod('PUT');
301
		$this->assertEquals($form->Fields()->dataFieldByName('_method')->Value(), 'PUT',
302
			'PUT override in forms has PUT in hiddenfield'
303
		);
304
		$this->assertEquals($form->FormMethod(), 'POST',
305
			'PUT override in forms has POST in <form> tag'
306
		);
307
308
		$form = $this->getStubForm();
309
		$form->setFormMethod('DELETE');
310
		$this->assertEquals($form->Fields()->dataFieldByName('_method')->Value(), 'DELETE',
311
			'PUT override in forms has PUT in hiddenfield'
312
		);
313
		$this->assertEquals($form->FormMethod(), 'POST',
314
			'PUT override in forms has POST in <form> tag'
315
		);
316
	}
317
318
	public function testValidationExemptActions() {
319
		$this->get('FormTest_Controller');
320
321
		$this->submitForm(
322
			'Form_Form',
323
			'action_doSubmit',
324
			array(
325
				'Email' => '[email protected]'
326
			)
327
		);
328
329
		// Firstly, assert that required fields still work when not using an exempt action
330
		$this->assertPartialMatchBySelector(
331
			'#Form_Form_SomeRequiredField_Holder .required',
332
			array('"Some Required Field" is required'),
333
			'Required fields show a notification on field when left blank'
0 ignored issues
show
Unused Code introduced by
The call to FormTest::assertPartialMatchBySelector() has too many arguments starting with 'Required fields show a ... field when left blank'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
334
		);
335
336
		// Re-submit the form using validation-exempt button
337
		$this->submitForm(
338
			'Form_Form',
339
			'action_doSubmitValidationExempt',
340
			array(
341
				'Email' => '[email protected]'
342
			)
343
		);
344
345
		// The required message should be empty if validation was skipped
346
		$items = $this->cssParser()->getBySelector('#Form_Form_SomeRequiredField_Holder .required');
347
		$this->assertEmpty($items);
348
349
		// And the session message should show up is submitted successfully
350
		$this->assertPartialMatchBySelector(
351
			'#Form_Form_error',
352
			array(
353
				'Validation skipped'
354
			),
355
			'Form->sessionMessage() shows up after reloading the form'
0 ignored issues
show
Unused Code introduced by
The call to FormTest::assertPartialMatchBySelector() has too many arguments starting with 'Form->sessionMessage() ...ter reloading the form'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
356
		);
357
358
		// Test this same behaviour, but with a form-action exempted via instance
359
		$this->submitForm(
360
			'Form_Form',
361
			'action_doSubmitActionExempt',
362
			array(
363
				'Email' => '[email protected]'
364
			)
365
		);
366
367
		// The required message should be empty if validation was skipped
368
		$items = $this->cssParser()->getBySelector('#Form_Form_SomeRequiredField_Holder .required');
369
		$this->assertEmpty($items);
370
371
		// And the session message should show up is submitted successfully
372
		$this->assertPartialMatchBySelector(
373
			'#Form_Form_error',
374
			array(
375
				'Validation bypassed!'
376
			),
377
			'Form->sessionMessage() shows up after reloading the form'
0 ignored issues
show
Unused Code introduced by
The call to FormTest::assertPartialMatchBySelector() has too many arguments starting with 'Form->sessionMessage() ...ter reloading the form'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
378
		);
379
	}
380
381
	public function testSessionValidationMessage() {
382
		$this->get('FormTest_Controller');
383
384
		$response = $this->post(
385
			'FormTest_Controller/Form',
386
			array(
387
				'Email' => 'invalid',
388
				'Number' => '<a href="http://mysite.com">link</a>' // XSS attempt
389
				// leaving out "Required" field
390
			)
391
		);
392
393
		$this->assertPartialMatchBySelector(
394
			'#Form_Form_Email_Holder span.message',
395
			array(
396
				'Please enter an email address'
397
			),
398
			'Formfield validation shows note on field if invalid'
0 ignored issues
show
Unused Code introduced by
The call to FormTest::assertPartialMatchBySelector() has too many arguments starting with 'Formfield validation sh...te on field if invalid'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
399
		);
400
		$this->assertPartialMatchBySelector(
401
			'#Form_Form_SomeRequiredField_Holder span.required',
402
			array(
403
				'"Some Required Field" is required'
404
			),
405
			'Required fields show a notification on field when left blank'
0 ignored issues
show
Unused Code introduced by
The call to FormTest::assertPartialMatchBySelector() has too many arguments starting with 'Required fields show a ... field when left blank'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
406
		);
407
408
		$this->assertContains(
409
			'&#039;&lt;a href=&quot;http://mysite.com&quot;&gt;link&lt;/a&gt;&#039; is not a number, only numbers can be accepted for this field',
410
			$response->getBody(),
411
			"Validation messages are safely XML encoded"
412
		);
413
		$this->assertNotContains(
414
			'<a href="http://mysite.com">link</a>',
415
			$response->getBody(),
416
			"Unsafe content is not emitted directly inside the response body"
417
		);
418
	}
419
420
	public function testSessionSuccessMessage() {
421
		$this->get('FormTest_Controller');
422
423
		$response = $this->post(
424
			'FormTest_Controller/Form',
425
			array(
426
				'Email' => '[email protected]',
427
				'SomeRequiredField' => 'test',
428
			)
429
		);
430
		$this->assertPartialMatchBySelector(
431
			'#Form_Form_error',
432
			array(
433
				'Test save was successful'
434
			),
435
			'Form->sessionMessage() shows up after reloading the form'
0 ignored issues
show
Unused Code introduced by
The call to FormTest::assertPartialMatchBySelector() has too many arguments starting with 'Form->sessionMessage() ...ter reloading the form'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
436
		);
437
	}
438
439
	public function testGloballyDisabledSecurityTokenInheritsToNewForm() {
440
		SecurityToken::enable();
441
442
		$form1 = $this->getStubForm();
443
		$this->assertInstanceOf('SilverStripe\\Security\\SecurityToken', $form1->getSecurityToken());
444
445
		SecurityToken::disable();
446
447
		$form2 = $this->getStubForm();
448
		$this->assertInstanceOf('SilverStripe\\Security\\NullSecurityToken', $form2->getSecurityToken());
449
450
		SecurityToken::enable();
451
	}
452
453
	public function testDisableSecurityTokenDoesntAddTokenFormField() {
454
		SecurityToken::enable();
455
456
		$formWithToken = $this->getStubForm();
457
		$this->assertInstanceOf(
458
			'SilverStripe\\Forms\\HiddenField',
459
			$formWithToken->Fields()->fieldByName(SecurityToken::get_default_name()),
460
			'Token field added by default'
461
		);
462
463
		$formWithoutToken = $this->getStubForm();
464
		$formWithoutToken->disableSecurityToken();
465
		$this->assertNull(
466
			$formWithoutToken->Fields()->fieldByName(SecurityToken::get_default_name()),
467
			'Token field not added if disableSecurityToken() is set'
468
		);
469
	}
470
471
	public function testDisableSecurityTokenAcceptsSubmissionWithoutToken() {
472
		SecurityToken::enable();
473
		$expectedToken = SecurityToken::inst()->getValue();
474
475
		$response = $this->get('FormTest_ControllerWithSecurityToken');
476
		// can't use submitForm() as it'll automatically insert SecurityID into the POST data
477
		$response = $this->post(
478
			'FormTest_ControllerWithSecurityToken/Form',
479
			array(
480
				'Email' => '[email protected]',
481
				'action_doSubmit' => 1
482
				// leaving out security token
483
			)
484
		);
485
		$this->assertEquals(400, $response->getStatusCode(), 'Submission fails without security token');
486
487
		// Generate a new token which doesn't match the current one
488
		$generator = new RandomGenerator();
489
		$invalidToken = $generator->randomToken('sha1');
490
		$this->assertNotEquals($invalidToken, $expectedToken);
491
492
		// Test token with request
493
		$response = $this->get('FormTest_ControllerWithSecurityToken');
494
		$response = $this->post(
495
			'FormTest_ControllerWithSecurityToken/Form',
496
			array(
497
				'Email' => '[email protected]',
498
				'action_doSubmit' => 1,
499
				'SecurityID' => $invalidToken
500
			)
501
		);
502
		$this->assertEquals(200, $response->getStatusCode(), 'Submission reloads form if security token invalid');
503
		$this->assertTrue(
504
			stripos($response->getBody(), 'name="SecurityID" value="'.$expectedToken.'"') !== false,
505
			'Submission reloads with correct security token after failure'
506
		);
507
		$this->assertTrue(
508
			stripos($response->getBody(), 'name="SecurityID" value="'.$invalidToken.'"') === false,
509
			'Submission reloads without incorrect security token after failure'
510
		);
511
512
		$matched = $this->cssParser()->getBySelector('#Form_Form_Email');
513
		$attrs = $matched[0]->attributes();
514
		$this->assertEquals('[email protected]', (string)$attrs['value'], 'Submitted data is preserved');
515
516
		$response = $this->get('FormTest_ControllerWithSecurityToken');
517
		$tokenEls = $this->cssParser()->getBySelector('#Form_Form_SecurityID');
518
		$this->assertEquals(
519
			1,
520
			count($tokenEls),
521
			'Token form field added for controller without disableSecurityToken()'
522
		);
523
		$token = (string)$tokenEls[0];
524
		$response = $this->submitForm(
525
			'Form_Form',
526
			null,
527
			array(
528
				'Email' => '[email protected]',
529
				'SecurityID' => $token
530
			)
531
		);
532
		$this->assertEquals(200, $response->getStatusCode(), 'Submission suceeds with security token');
533
	}
534
535
	public function testStrictFormMethodChecking() {
536
		$response = $this->get('FormTest_ControllerWithStrictPostCheck');
537
		$response = $this->get(
538
			'FormTest_ControllerWithStrictPostCheck/Form/[email protected]&action_doSubmit=1'
539
		);
540
		$this->assertEquals(405, $response->getStatusCode(), 'Submission fails with wrong method');
541
542
		$response = $this->get('FormTest_ControllerWithStrictPostCheck');
543
		$response = $this->post(
544
			'FormTest_ControllerWithStrictPostCheck/Form',
545
			array(
546
				'Email' => '[email protected]',
547
				'action_doSubmit' => 1
548
			)
549
		);
550
		$this->assertEquals(200, $response->getStatusCode(), 'Submission succeeds with correct method');
551
	}
552
553
	public function testEnableSecurityToken() {
554
		SecurityToken::disable();
555
		$form = $this->getStubForm();
556
		$this->assertFalse($form->getSecurityToken()->isEnabled());
557
		$form->enableSecurityToken();
558
		$this->assertTrue($form->getSecurityToken()->isEnabled());
559
560
		SecurityToken::disable(); // restore original
561
	}
562
563
	public function testDisableSecurityToken() {
564
		SecurityToken::enable();
565
		$form = $this->getStubForm();
566
		$this->assertTrue($form->getSecurityToken()->isEnabled());
567
		$form->disableSecurityToken();
568
		$this->assertFalse($form->getSecurityToken()->isEnabled());
569
570
		SecurityToken::disable(); // restore original
571
	}
572
573
	public function testEncType() {
574
		$form = $this->getStubForm();
575
		$this->assertEquals('application/x-www-form-urlencoded', $form->getEncType());
576
577
		$form->setEncType(Form::ENC_TYPE_MULTIPART);
578
		$this->assertEquals('multipart/form-data', $form->getEncType());
579
580
		$form = $this->getStubForm();
581
		$form->Fields()->push(new FileField(null));
582
		$this->assertEquals('multipart/form-data', $form->getEncType());
583
584
		$form->setEncType(Form::ENC_TYPE_URLENCODED);
585
		$this->assertEquals('application/x-www-form-urlencoded', $form->getEncType());
586
	}
587
588
	public function testAddExtraClass() {
589
		$form = $this->getStubForm();
590
		$form->addExtraClass('class1');
591
		$form->addExtraClass('class2');
592
		$this->assertStringEndsWith('class1 class2', $form->extraClass());
593
	}
594
595
	public function testRemoveExtraClass() {
596
		$form = $this->getStubForm();
597
		$form->addExtraClass('class1');
598
		$form->addExtraClass('class2');
599
		$this->assertStringEndsWith('class1 class2', $form->extraClass());
600
		$form->removeExtraClass('class1');
601
		$this->assertStringEndsWith('class2', $form->extraClass());
602
	}
603
604
	public function testAddManyExtraClasses() {
605
		$form = $this->getStubForm();
606
		//test we can split by a range of spaces and tabs
607
		$form->addExtraClass('class1 class2     class3	class4		class5');
608
		$this->assertStringEndsWith(
609
			'class1 class2 class3 class4 class5',
610
			$form->extraClass()
611
		);
612
		//test that duplicate classes don't get added
613
		$form->addExtraClass('class1 class2');
614
		$this->assertStringEndsWith(
615
			'class1 class2 class3 class4 class5',
616
			$form->extraClass()
617
		);
618
	}
619
620
	public function testRemoveManyExtraClasses() {
621
		$form = $this->getStubForm();
622
		$form->addExtraClass('class1 class2     class3	class4		class5');
623
		//test we can remove a single class we just added
624
		$form->removeExtraClass('class3');
625
		$this->assertStringEndsWith(
626
			'class1 class2 class4 class5',
627
			$form->extraClass()
628
		);
629
		//check we can remove many classes at once
630
		$form->removeExtraClass('class1 class5');
631
		$this->assertStringEndsWith(
632
			'class2 class4',
633
			$form->extraClass()
634
		);
635
		//check that removing a dud class is fine
636
		$form->removeExtraClass('dudClass');
637
		$this->assertStringEndsWith(
638
			'class2 class4',
639
			$form->extraClass()
640
		);
641
	}
642
643
	public function testDefaultClasses() {
644
		Form::config()->update('default_classes', array(
645
			'class1',
646
		));
647
648
		$form = $this->getStubForm();
649
650
		$this->assertContains('class1', $form->extraClass(), 'Class list does not contain expected class');
651
652
		Form::config()->update('default_classes', array(
653
			'class1',
654
			'class2',
655
		));
656
657
		$form = $this->getStubForm();
658
659
		$this->assertContains('class1 class2', $form->extraClass(), 'Class list does not contain expected class');
660
661
		Form::config()->update('default_classes', array(
662
			'class3',
663
		));
664
665
		$form = $this->getStubForm();
666
667
		$this->assertContains('class3', $form->extraClass(), 'Class list does not contain expected class');
668
669
		$form->removeExtraClass('class3');
670
671
		$this->assertNotContains('class3', $form->extraClass(), 'Class list contains unexpected class');
672
	}
673
674
	public function testAttributes() {
675
		$form = $this->getStubForm();
676
		$form->setAttribute('foo', 'bar');
677
		$this->assertEquals('bar', $form->getAttribute('foo'));
678
		$attrs = $form->getAttributes();
679
		$this->assertArrayHasKey('foo', $attrs);
680
		$this->assertEquals('bar', $attrs['foo']);
681
	}
682
683
	public function testButtonClicked() {
684
		$form = $this->getStubForm();
685
		$action = $form->buttonClicked();
686
		$this->assertNull($action);
687
688
		$controller = new FormTest\TestController();
689
		$form = $controller->Form();
690
		$request = new HTTPRequest('POST', 'FormTest_Controller/Form', array(), array(
691
			'Email' => '[email protected]',
692
			'SomeRequiredField' => 1,
693
			'action_doSubmit' => 1
694
		));
695
696
		$form->httpSubmission($request);
697
		$button = $form->buttonClicked();
698
		$this->assertInstanceOf('SilverStripe\\Forms\\FormAction', $button);
699
		$this->assertEquals('doSubmit', $button->actionName());
700
		/** @skipUpgrade */
701
		$form = new Form(
702
			$controller,
703
			'Form',
704
			new FieldList(new FormAction('doSubmit', 'Inline action')),
705
			new FieldList()
706
		);
707
		$form->disableSecurityToken();
708
		$request = new HTTPRequest('POST', 'FormTest_Controller/Form', array(), array(
709
			'action_doSubmit' => 1
710
		));
711
712
		$form->httpSubmission($request);
713
		$button = $form->buttonClicked();
714
		$this->assertInstanceOf('SilverStripe\\Forms\\FormAction', $button);
715
		$this->assertEquals('doSubmit', $button->actionName());
716
	}
717
718
	public function testCheckAccessAction() {
719
		$controller = new FormTest\TestController();
720
		$form = new Form(
721
			$controller,
722
			'Form',
723
			new FieldList(),
724
			new FieldList(new FormAction('actionName', 'Action'))
725
		);
726
		$this->assertTrue($form->checkAccessAction('actionName'));
727
728
		$form = new Form(
729
			$controller,
730
			'Form',
731
			new FieldList(new FormAction('inlineAction', 'Inline action')),
732
			new FieldList()
733
		);
734
		$this->assertTrue($form->checkAccessAction('inlineAction'));
735
	}
736
737
	public function testAttributesHTML() {
738
		$form = $this->getStubForm();
739
740
		$form->setAttribute('foo', 'bar');
741
		$this->assertContains('foo="bar"', $form->getAttributesHTML());
742
743
		$form->setAttribute('foo', null);
744
		$this->assertNotContains('foo="bar"', $form->getAttributesHTML());
745
746
		$form->setAttribute('foo', true);
747
		$this->assertContains('foo="foo"', $form->getAttributesHTML());
748
749
		$form->setAttribute('one', 1);
750
		$form->setAttribute('two', 2);
751
		$form->setAttribute('three', 3);
752
		$this->assertNotContains('one="1"', $form->getAttributesHTML('one', 'two'));
753
		$this->assertNotContains('two="2"', $form->getAttributesHTML('one', 'two'));
754
		$this->assertContains('three="3"', $form->getAttributesHTML('one', 'two'));
755
	}
756
757
	function testMessageEscapeHtml() {
758
		$form = $this->getStubForm();
759
		$form->getController()->handleRequest(new HTTPRequest('GET', '/'), DataModel::inst()); // stub out request
760
		$form->sessionMessage('<em>Escaped HTML</em>', 'good', true);
761
		$parser = new CSSContentParser($form->forTemplate());
762
		$messageEls = $parser->getBySelector('.message');
763
		$this->assertContains(
764
			'&lt;em&gt;Escaped HTML&lt;/em&gt;',
765
			$messageEls[0]->asXML()
766
		);
767
768
		$form = $this->getStubForm();
769
		$form->getController()->handleRequest(new HTTPRequest('GET', '/'), DataModel::inst()); // stub out request
770
		$form->sessionMessage('<em>Unescaped HTML</em>', 'good', false);
771
		$parser = new CSSContentParser($form->forTemplate());
772
		$messageEls = $parser->getBySelector('.message');
773
		$this->assertContains(
774
			'<em>Unescaped HTML</em>',
775
			$messageEls[0]->asXML()
776
		);
777
	}
778
779
	function testFieldMessageEscapeHtml() {
780
		$form = $this->getStubForm();
781
		$form->getController()->handleRequest(new HTTPRequest('GET', '/'), DataModel::inst()); // stub out request
782
		$form->addErrorMessage('key1', '<em>Escaped HTML</em>', 'good', true);
783
		$form->setupFormErrors();
784
		$parser = new CSSContentParser($result = $form->forTemplate());
785
		$messageEls = $parser->getBySelector('#Form_Form_key1_Holder .message');
786
		$this->assertContains(
787
			'&lt;em&gt;Escaped HTML&lt;/em&gt;',
788
			$messageEls[0]->asXML()
789
		);
790
791
		$form = $this->getStubForm();
792
		$form->getController()->handleRequest(new HTTPRequest('GET', '/'), DataModel::inst()); // stub out request
793
		$form->addErrorMessage('key1', '<em>Unescaped HTML</em>', 'good', false);
794
		$form->setupFormErrors();
795
		$parser = new CSSContentParser($form->forTemplate());
796
		$messageEls = $parser->getBySelector('#Form_Form_key1_Holder .message');
797
		$this->assertContains(
798
			'<em>Unescaped HTML</em>',
799
			$messageEls[0]->asXML()
800
		);
801
	}
802
803
    public function testGetExtraFields()
804
    {
805
        $form = new FormTest\ExtraFieldsForm(
806
            new FormTest\TestController(),
807
            'Form',
808
            new FieldList(new TextField('key1')),
809
            new FieldList()
810
        );
811
812
        $data = array(
813
            'key1' => 'test',
814
            'ExtraFieldCheckbox' => false,
815
        );
816
817
        $form->loadDataFrom($data);
818
819
        $formData = $form->getData();
820
        $this->assertEmpty($formData['ExtraFieldCheckbox']);
821
    }
822
823
	protected function getStubForm() {
824
		return new Form(
825
			new FormTest\TestController(),
826
			'Form',
827
			new FieldList(new TextField('key1')),
828
			new FieldList()
829
		);
830
	}
831
832
}
833