Passed
Push — nested-vendor ( f8843a )
by Sam
09:43
created

testMustBeLoggedInToChangePassword()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 14
nc 1
nop 0
dl 0
loc 19
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\Tests;
4
5
use SilverStripe\Control\Controller;
6
use SilverStripe\Dev\SapphireTest;
7
use SilverStripe\Forms\ConfirmedPasswordField;
8
use SilverStripe\Forms\FieldList;
9
use SilverStripe\Forms\Form;
10
use SilverStripe\Forms\ReadonlyField;
11
use SilverStripe\Forms\RequiredFields;
12
use SilverStripe\Security\Member;
13
use SilverStripe\Security\PasswordValidator;
14
15
class ConfirmedPasswordFieldTest extends SapphireTest
16
{
17
    protected $usesDatabase = true;
18
19
    protected function setUp()
20
    {
21
        parent::setUp();
22
23
        PasswordValidator::singleton()->setMinLength(0);
24
    }
25
26
    public function testSetValue()
27
    {
28
        $field = new ConfirmedPasswordField('Test', 'Testing', 'valueA');
29
        $this->assertEquals('valueA', $field->Value());
30
        $this->assertEquals('valueA', $field->children->fieldByName($field->getName() . '[_Password]')->Value());
31
        $this->assertEquals('valueA', $field->children->fieldByName($field->getName() . '[_ConfirmPassword]')->Value());
32
        $field->setValue('valueB');
33
        $this->assertEquals('valueB', $field->Value());
34
        $this->assertEquals('valueB', $field->children->fieldByName($field->getName() . '[_Password]')->Value());
35
        $this->assertEquals('valueB', $field->children->fieldByName($field->getName() . '[_ConfirmPassword]')->Value());
36
    }
37
38
    /**
39
     * @useDatabase true
40
     */
41
    public function testHashHidden()
42
    {
43
        $field = new ConfirmedPasswordField('Password', 'Password', 'valueA');
44
        $field->setCanBeEmpty(true);
45
46
        $this->assertEquals('valueA', $field->Value());
47
        $this->assertEquals('valueA', $field->children->fieldByName($field->getName() . '[_Password]')->Value());
48
        $this->assertEquals('valueA', $field->children->fieldByName($field->getName() . '[_ConfirmPassword]')->Value());
49
50
        $member = new Member();
51
        $member->Password = "valueB";
52
        $member->write();
53
54
        /** @skipUpgrade */
55
        $form = new Form(Controller::curr(), 'Form', new FieldList($field), new FieldList());
56
        $form->loadDataFrom($member);
57
58
        $this->assertEquals('', $field->Value());
59
        $this->assertEquals('', $field->children->fieldByName($field->getName() . '[_Password]')->Value());
60
        $this->assertEquals('', $field->children->fieldByName($field->getName() . '[_ConfirmPassword]')->Value());
61
    }
62
63
    public function testSetShowOnClick()
64
    {
65
        //hide by default and display show/hide toggle button
66
        $field = new ConfirmedPasswordField('Test', 'Testing', 'valueA', null, true);
67
        $fieldHTML = $field->Field();
68
        $this->assertContains(
69
            "showOnClickContainer",
70
            $fieldHTML,
71
            "Test class for hiding/showing the form contents is set"
72
        );
73
        $this->assertContains(
74
            "showOnClick",
75
            $fieldHTML,
76
            "Test class for hiding/showing the form contents is set"
77
        );
78
79
        //show all by default
80
        $field = new ConfirmedPasswordField('Test', 'Testing', 'valueA', null, false);
81
        $fieldHTML = $field->Field();
82
        $this->assertNotContains(
83
            "showOnClickContainer",
84
            $fieldHTML,
85
            "Test class for hiding/showing the form contents is set"
86
        );
87
        $this->assertNotContains(
88
            "showOnClick",
89
            $fieldHTML,
90
            "Test class for hiding/showing the form contents is set"
91
        );
92
    }
93
94
    public function testValidation()
95
    {
96
        $field = new ConfirmedPasswordField(
97
            'Test',
98
            'Testing',
99
            [
100
                '_Password' => 'abc123',
101
                '_ConfirmPassword' => 'abc123',
102
            ]
103
        );
104
        $validator = new RequiredFields();
105
        $this->assertTrue(
106
            $field->validate($validator),
107
            'Validates when both passwords are the same'
108
        );
109
        $field->setName('TestNew'); //try changing name of field
110
        $this->assertTrue(
111
            $field->validate($validator),
112
            'Validates when field name is changed'
113
        );
114
        //non-matching password should make the field invalid
115
        $field->setValue([
116
            '_Password' => 'abc123',
117
            '_ConfirmPassword' => '123abc',
118
        ]);
119
        $this->assertFalse(
120
            $field->validate($validator),
121
            'Does not validate when passwords differ'
122
        );
123
124
        // Empty passwords should make the field invalid
125
        $field->setCanBeEmpty(false);
126
        $field->setValue([
127
            '_Password' => '',
128
            '_ConfirmPassword' => '',
129
        ]);
130
        $this->assertFalse(
131
            $field->validate($validator),
132
            'Empty passwords should not be allowed when canBeEmpty is false'
133
        );
134
    }
135
136
    public function testFormValidation()
137
    {
138
        /** @skipUpgrade */
139
        $form = new Form(
140
            Controller::curr(),
141
            'Form',
142
            new FieldList($field = new ConfirmedPasswordField('Password')),
143
            new FieldList()
144
        );
145
146
        $form->loadDataFrom([
147
            'Password' => [
148
                '_Password' => '123',
149
                '_ConfirmPassword' => '999',
150
            ],
151
        ]);
152
153
        $this->assertEquals('123', $field->children->first()->Value());
154
        $this->assertEquals('999', $field->children->last()->Value());
155
        $this->assertNotEquals($field->children->first()->Value(), $field->children->last()->Value());
156
    }
157
158
    /**
159
     * @param int|null $minLength
160
     * @param int|null $maxLength
161
     * @param bool $expectValid
162
     * @param string $expectedMessage
163
     * @dataProvider lengthValidationProvider
164
     */
165
    public function testLengthValidation($minLength, $maxLength, $expectValid, $expectedMessage = '')
166
    {
167
        $field = new ConfirmedPasswordField('Test', 'Testing', [
168
            '_Password' => 'abc123',
169
            '_ConfirmPassword' => 'abc123',
170
        ]);
171
        $field->setMinLength($minLength)->setMaxLength($maxLength);
172
173
        $validator = new RequiredFields();
174
        $result = $field->validate($validator);
175
176
        $this->assertSame($expectValid, $result, 'Validate method should return its result');
177
        $this->assertSame($expectValid, $validator->getResult()->isValid());
178
        if ($expectedMessage) {
179
            $this->assertContains($expectedMessage, $validator->getResult()->serialize());
180
        }
181
    }
182
183
    /**
184
     * @return array[]
185
     */
186
    public function lengthValidationProvider()
187
    {
188
        return [
189
            'valid: within min and max' => [3, 8, true],
190
            'invalid: lower than min with max' => [8, 12, false, 'Passwords must be 8 to 12 characters long'],
191
            'valid: greater than min' => [3, null, true],
192
            'invalid: lower than min' => [8, null, false, 'Passwords must be at least 8 characters long'],
193
            'valid: less than max' => [null, 8, true],
194
            'invalid: greater than max' => [null, 4, false, 'Passwords must be at most 4 characters long'],
195
196
        ];
197
    }
198
199
    public function testStrengthValidation()
200
    {
201
        $field = new ConfirmedPasswordField('Test', 'Testing', [
202
            '_Password' => 'abc',
203
            '_ConfirmPassword' => 'abc',
204
        ]);
205
        $field->setRequireStrongPassword(true);
206
207
        $validator = new RequiredFields();
208
        $result = $field->validate($validator);
209
210
        $this->assertFalse($result, 'Validate method should return its result');
211
        $this->assertFalse($validator->getResult()->isValid());
212
        $this->assertContains(
213
            'Passwords must have at least one digit and one alphanumeric character',
214
            $validator->getResult()->serialize()
215
        );
216
    }
217
218
    public function testCurrentPasswordValidation()
219
    {
220
        $field = new ConfirmedPasswordField('Test', 'Testing', [
221
            '_Password' => 'abc',
222
            '_ConfirmPassword' => 'abc',
223
        ]);
224
        $field->setRequireExistingPassword(true);
225
226
        $validator = new RequiredFields();
227
        $result = $field->validate($validator);
228
229
        $this->assertFalse($result, 'Validate method should return its result');
230
        $this->assertFalse($validator->getResult()->isValid());
231
        $this->assertContains(
232
            'You must enter your current password',
233
            $validator->getResult()->serialize()
234
        );
235
    }
236
237
    public function testMustBeLoggedInToChangePassword()
238
    {
239
        $field = new ConfirmedPasswordField('Test', 'Testing');
240
        $field->setRequireExistingPassword(true);
241
        $field->setValue([
242
            '_CurrentPassword' => 'foo',
243
            '_Password' => 'abc',
244
            '_ConfirmPassword' => 'abc',
245
        ]);
246
247
        $validator = new RequiredFields();
248
        $this->logOut();
249
        $result = $field->validate($validator);
250
251
        $this->assertFalse($result, 'Validate method should return its result');
252
        $this->assertFalse($validator->getResult()->isValid());
253
        $this->assertContains(
254
            'You must be logged in to change your password',
255
            $validator->getResult()->serialize()
256
        );
257
    }
258
259
    /**
260
     * @useDatabase true
261
     */
262
    public function testValidateCorrectPassword()
263
    {
264
        $this->logInWithPermission('ADMIN');
265
266
        $field = new ConfirmedPasswordField('Test', 'Testing');
267
        $field->setRequireExistingPassword(true);
268
        $field->setValue([
269
            '_CurrentPassword' => 'foo-not-going-to-be-the-correct-password',
270
            '_Password' => 'abc',
271
            '_ConfirmPassword' => 'abc',
272
        ]);
273
274
        $validator = new RequiredFields();
275
        $result = $field->validate($validator);
276
277
        $this->assertFalse($result, 'Validate method should return its result');
278
        $this->assertFalse($validator->getResult()->isValid());
279
        $this->assertContains(
280
            'The current password you have entered is not correct',
281
            $validator->getResult()->serialize()
282
        );
283
    }
284
285
    public function testTitle()
286
    {
287
        $this->assertNull(ConfirmedPasswordField::create('Test')->Title(), 'Should not have it\'s own title');
288
    }
289
290
    public function testSetTitlePropagatesToPasswordField()
291
    {
292
        /** @var ConfirmedPasswordField $field */
293
        $field = ConfirmedPasswordField::create('Test')
294
            ->setTitle('My password');
295
296
        $this->assertSame('My password', $field->getPasswordField()->Title());
297
    }
298
299
    public function testSetRightTitlePropagatesToChildren()
300
    {
301
        $field = new ConfirmedPasswordField('Test');
302
303
        $this->assertCount(2, $field->getChildren());
304
        foreach ($field->getChildren() as $child) {
305
            $this->assertEmpty($child->RightTitle());
306
        }
307
308
        $field->setRightTitle('Please confirm');
309
        foreach ($field->getChildren() as $child) {
310
            $this->assertSame('Please confirm', $child->RightTitle());
311
        }
312
    }
313
314
    public function testSetChildrenTitles()
315
    {
316
        $field = new ConfirmedPasswordField('Test');
317
        $field->setRequireExistingPassword(true);
318
        $field->setChildrenTitles([
319
            'Current Password',
320
            'Password',
321
            'Confirm Password',
322
        ]);
323
324
        $this->assertSame('Current Password', $field->getChildren()->shift()->Title());
325
        $this->assertSame('Password', $field->getChildren()->shift()->Title());
326
        $this->assertSame('Confirm Password', $field->getChildren()->shift()->Title());
327
    }
328
329
    public function testPerformReadonlyTransformation()
330
    {
331
        $field = new ConfirmedPasswordField('Test', 'Change it');
332
        $result = $field->performReadonlyTransformation();
333
334
        $this->assertInstanceOf(ReadonlyField::class, $result);
335
        $this->assertSame('Change it', $result->Title());
336
        $this->assertContains('***', $result->Value());
337
    }
338
339
    public function testPerformDisabledTransformation()
340
    {
341
        $field = new ConfirmedPasswordField('Test', 'Change it');
342
        $result = $field->performDisabledTransformation();
343
344
        $this->assertInstanceOf(ReadonlyField::class, $result);
345
    }
346
347
    public function testSetRequireExistingPasswordOnlyRunsOnce()
348
    {
349
        $field = new ConfirmedPasswordField('Test', 'Change it');
350
351
        $this->assertCount(2, $field->getChildren());
352
353
        $field->setRequireExistingPassword(true);
354
        $this->assertCount(3, $field->getChildren(), 'Current password field was not pushed');
355
356
        $field->setRequireExistingPassword(true);
357
        $this->assertCount(3, $field->getChildren(), 'Current password field should not be pushed again');
358
359
        $field->setRequireExistingPassword(false);
360
        $this->assertCount(2, $field->getChildren(), 'Current password field should not be removed');
361
    }
362
}
363