1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SilverStripe\Forms\Tests; |
4
|
|
|
|
5
|
|
|
use SilverStripe\Control\Controller; |
6
|
|
|
use SilverStripe\ORM\ArrayList; |
7
|
|
|
use SilverStripe\Dev\CSSContentParser; |
8
|
|
|
use SilverStripe\Dev\SapphireTest; |
9
|
|
|
use SilverStripe\Forms\DropdownField; |
10
|
|
|
use SilverStripe\Forms\RequiredFields; |
11
|
|
|
use SilverStripe\Forms\FieldList; |
12
|
|
|
use SilverStripe\Forms\Form; |
13
|
|
|
use SilverStripe\View\ArrayData; |
14
|
|
|
use SilverStripe\ORM\Map; |
15
|
|
|
|
16
|
|
|
class DropdownFieldTest extends SapphireTest |
17
|
|
|
{ |
18
|
|
|
|
19
|
|
|
public function testGetSource() |
20
|
|
|
{ |
21
|
|
|
$source = array(1=>'one', 2 => 'two'); |
22
|
|
|
$field = new DropdownField('Field', null, $source); |
23
|
|
|
$this->assertEquals( |
24
|
|
|
$source, |
25
|
|
|
$field->getSource() |
26
|
|
|
); |
27
|
|
|
$this->assertEquals( |
28
|
|
|
$source, |
29
|
|
|
$field->getSource() |
30
|
|
|
); |
31
|
|
|
|
32
|
|
|
$items = new ArrayList( |
33
|
|
|
array( |
34
|
|
|
array( 'ID' => 1, 'Title' => 'ichi', 'OtherField' => 'notone' ), |
35
|
|
|
array( 'ID' => 2, 'Title' => 'ni', 'OtherField' => 'nottwo' ), |
36
|
|
|
) |
37
|
|
|
); |
38
|
|
|
$field->setSource($items); |
39
|
|
|
$this->assertEquals( |
40
|
|
|
$field->getSource(), |
41
|
|
|
array( |
42
|
|
|
1 => 'ichi', |
43
|
|
|
2 => 'ni', |
44
|
|
|
) |
45
|
|
|
); |
46
|
|
|
|
47
|
|
|
$map = new Map($items, 'ID', 'OtherField'); |
48
|
|
|
$field->setSource($map); |
49
|
|
|
$this->assertEquals( |
50
|
|
|
$field->getSource(), |
51
|
|
|
array( |
52
|
|
|
1 => 'notone', |
53
|
|
|
2 => 'nottwo', |
54
|
|
|
) |
55
|
|
|
); |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Test different data sources |
60
|
|
|
*/ |
61
|
|
View Code Duplication |
public function testSources() |
|
|
|
|
62
|
|
|
{ |
63
|
|
|
// Array |
64
|
|
|
$items = array('a' => 'Apple', 'b' => 'Banana', 'c' => 'Cranberry'); |
65
|
|
|
$field = new DropdownField('Field', null, $items); |
66
|
|
|
$this->assertEquals($items, $field->getSource()); |
67
|
|
|
|
68
|
|
|
// SS_List |
69
|
|
|
$list = new ArrayList( |
70
|
|
|
array( |
71
|
|
|
new ArrayData( |
72
|
|
|
array( |
73
|
|
|
'ID' => 'a', |
74
|
|
|
'Title' => 'Apple' |
75
|
|
|
) |
76
|
|
|
), |
77
|
|
|
new ArrayData( |
78
|
|
|
array( |
79
|
|
|
'ID' => 'b', |
80
|
|
|
'Title' => 'Banana' |
81
|
|
|
) |
82
|
|
|
), |
83
|
|
|
new ArrayData( |
84
|
|
|
array( |
85
|
|
|
'ID' => 'c', |
86
|
|
|
'Title' => 'Cranberry' |
87
|
|
|
) |
88
|
|
|
) |
89
|
|
|
) |
90
|
|
|
); |
91
|
|
|
$field2 = new DropdownField('Field', null, $list); |
92
|
|
|
$this->assertEquals($items, $field2->getSource()); |
93
|
|
|
|
94
|
|
|
$field3 = new DropdownField('Field', null, $list->map()); |
95
|
|
|
$this->assertEquals($items, $field3->getSource()); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
View Code Duplication |
public function testReadonlyField() |
|
|
|
|
99
|
|
|
{ |
100
|
|
|
$field = new DropdownField('FeelingOk', 'Are you feeling ok?', array(0 => 'No', 1 => 'Yes')); |
101
|
|
|
$field->setEmptyString('(Select one)'); |
102
|
|
|
$field->setValue(1); |
103
|
|
|
$readonlyField = $field->performReadonlyTransformation(); |
|
|
|
|
104
|
|
|
preg_match('/Yes/', $field->Field(), $matches); |
105
|
|
|
$this->assertEquals($matches[0], 'Yes'); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
public function testHasEmptyDefault() |
109
|
|
|
{ |
110
|
|
|
$source = array(1 => 'one'); |
111
|
|
|
|
112
|
|
|
// Test getSource with empty |
113
|
|
|
$field = new DropdownField('Field', null, $source); |
114
|
|
|
$field->setHasEmptyDefault(true); |
115
|
|
|
|
116
|
|
|
$this->assertEquals( |
117
|
|
|
$field->getSource(), |
118
|
|
|
array( |
119
|
|
|
1 => 'one' |
120
|
|
|
) |
121
|
|
|
); |
122
|
|
|
|
123
|
|
|
// Test that an empty option comes through in the markup however |
124
|
|
|
$options = $this->findOptionElements($field->Field()); |
125
|
|
|
|
126
|
|
|
$this->assertEquals( |
127
|
|
|
2, |
128
|
|
|
count($options), |
129
|
|
|
'Two options exist in the markup, one for the source, one for empty' |
130
|
|
|
); |
131
|
|
|
|
132
|
|
|
// the default value should be first |
133
|
|
|
$first = array_shift($options); |
134
|
|
|
$attrs = $first->attributes(); |
135
|
|
|
|
136
|
|
|
$this->assertNotEquals( |
137
|
|
|
1, |
138
|
|
|
$attrs['value'], |
139
|
|
|
'First value is the not value (not the source value)' |
140
|
|
|
); |
141
|
|
|
|
142
|
|
|
// Test Field Without Empty |
143
|
|
|
$FieldWithoutEmpty = new DropdownField('Field', null, $source); |
144
|
|
|
$this->assertEquals( |
145
|
|
|
$FieldWithoutEmpty->getSource(), |
146
|
|
|
array( |
147
|
|
|
1 => 'one' |
148
|
|
|
) |
149
|
|
|
); |
150
|
|
|
|
151
|
|
|
$this->assertEquals( |
152
|
|
|
1, |
153
|
|
|
count($options), |
154
|
|
|
'As hasEmptyDefault is not provided, then no default option.' |
155
|
|
|
); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
public function testZeroArraySourceNotOverwrittenByEmptyString() |
159
|
|
|
{ |
160
|
|
|
$source = array(0=>'zero'); |
161
|
|
|
$field = new DropdownField('Field', null, $source); |
162
|
|
|
$field->setEmptyString('select...'); |
163
|
|
|
$this->assertEquals( |
164
|
|
|
$field->getSource(), |
165
|
|
|
array( |
166
|
|
|
0 => 'zero' |
167
|
|
|
) |
168
|
|
|
); |
169
|
|
|
|
170
|
|
|
$options = $this->findOptionElements($field->Field()); |
171
|
|
|
|
172
|
|
|
$this->assertEquals( |
173
|
|
|
2, |
174
|
|
|
count($options), |
175
|
|
|
'Two options exist in the markup, one for the source, one for empty' |
176
|
|
|
); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
View Code Duplication |
public function testStringZeroValueSelectedOptionBehaviour() |
|
|
|
|
180
|
|
|
{ |
181
|
|
|
$field = new DropdownField( |
182
|
|
|
'Field', |
183
|
|
|
null, |
184
|
|
|
array( |
185
|
|
|
'-1' => 'some negative', |
186
|
|
|
'0' => 'none', |
187
|
|
|
'1' => 'one', |
188
|
|
|
'2+' => 'two or more' |
189
|
|
|
), |
190
|
|
|
'0' |
191
|
|
|
); |
192
|
|
|
|
193
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
194
|
|
|
$this->assertEquals((string) $selectedOptions[0], 'none', 'The selected option is "none"'); |
195
|
|
|
|
196
|
|
|
$field = new DropdownField( |
197
|
|
|
'Field', |
198
|
|
|
null, |
199
|
|
|
array( |
200
|
|
|
'-1' => 'some negative', |
201
|
|
|
'0' => 'none', |
202
|
|
|
'1' => 'one', |
203
|
|
|
'2+' => 'two or more' |
204
|
|
|
), |
205
|
|
|
0 |
206
|
|
|
); |
207
|
|
|
|
208
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
209
|
|
|
$this->assertEquals((string) $selectedOptions[0], 'none', 'The selected option is "none"'); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
View Code Duplication |
public function testStringOneValueSelectedOptionBehaviour() |
|
|
|
|
213
|
|
|
{ |
214
|
|
|
$field = new DropdownField( |
215
|
|
|
'Field', |
216
|
|
|
null, |
217
|
|
|
array( |
218
|
|
|
'-1' => 'some negative', |
219
|
|
|
'0' => 'none', |
220
|
|
|
'1' => 'one', |
221
|
|
|
'2+' => 'two or more' |
222
|
|
|
), |
223
|
|
|
'1' |
224
|
|
|
); |
225
|
|
|
|
226
|
|
|
|
227
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
228
|
|
|
$this->assertEquals((string) $selectedOptions[0], 'one', 'The selected option is "one"'); |
229
|
|
|
|
230
|
|
|
$field = new DropdownField( |
231
|
|
|
'Field', |
232
|
|
|
null, |
233
|
|
|
array( |
234
|
|
|
'-1' => 'some negative', |
235
|
|
|
'0' => 'none', |
236
|
|
|
'1' => 'one', |
237
|
|
|
'2+' => 'two or more' |
238
|
|
|
), |
239
|
|
|
1 |
240
|
|
|
); |
241
|
|
|
|
242
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
243
|
|
|
$this->assertEquals((string) $selectedOptions[0], 'one', 'The selected option is "one"'); |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
public function testNumberOfSelectOptionsAvailable() |
247
|
|
|
{ |
248
|
|
|
/* Create a field with a blank value */ |
249
|
|
|
$field = $this->createDropdownField('(Any)'); |
250
|
|
|
|
251
|
|
|
/* 3 options are available */ |
252
|
|
|
$this->assertEquals(count($this->findOptionElements($field->Field())), 3, '3 options are available'); |
253
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
254
|
|
|
$this->assertEquals( |
255
|
|
|
count($selectedOptions), |
256
|
|
|
1, |
257
|
|
|
'We only have 1 selected option, since a dropdown can only possibly have one!' |
258
|
|
|
); |
259
|
|
|
|
260
|
|
|
/* Create a field without a blank value */ |
261
|
|
|
$field = $this->createDropdownField(); |
262
|
|
|
|
263
|
|
|
/* 2 options are available */ |
264
|
|
|
$this->assertEquals(count($this->findOptionElements($field->Field())), 2, '2 options are available'); |
265
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
266
|
|
|
$this->assertEquals(count($selectedOptions), 0, 'There are no selected options'); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
public function testIntegerZeroValueSeelctedOptionBehaviour() |
270
|
|
|
{ |
271
|
|
|
$field = $this->createDropdownField('(Any)', 0); |
272
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
273
|
|
|
$this->assertEquals((string) $selectedOptions[0], 'No', 'The selected option is "No"'); |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
public function testBlankStringValueSelectedOptionBehaviour() |
277
|
|
|
{ |
278
|
|
|
$field = $this->createDropdownField('(Any)'); |
279
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
280
|
|
|
$this->assertEquals((string) $selectedOptions[0], '(Any)', 'The selected option is "(Any)"'); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
public function testNullValueSelectedOptionBehaviour() |
284
|
|
|
{ |
285
|
|
|
$field = $this->createDropdownField('(Any)', null); |
286
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
287
|
|
|
$this->assertEquals((string) $selectedOptions[0], '(Any)', 'The selected option is "(Any)"'); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
public function testStringValueSelectedOptionBehaviour() |
291
|
|
|
{ |
292
|
|
|
$field = $this->createDropdownField('(Any)', '1'); |
293
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
294
|
|
|
$this->assertEquals((string) $selectedOptions[0], 'Yes', 'The selected option is "Yes"'); |
295
|
|
|
$field->setSource( |
296
|
|
|
array( |
297
|
|
|
'Cats' => 'Cats and Kittens', |
298
|
|
|
'Dogs' => 'Dogs and Puppies' |
299
|
|
|
) |
300
|
|
|
); |
301
|
|
|
$field->setValue('Cats'); |
302
|
|
|
$selectedOptions = $this->findSelectedOptionElements($field->Field()); |
303
|
|
|
$this->assertEquals( |
304
|
|
|
(string) $selectedOptions[0], |
305
|
|
|
'Cats and Kittens', |
306
|
|
|
'The selected option is "Cats and Kittens"' |
307
|
|
|
); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
public function testNumberOfDisabledOptions() |
311
|
|
|
{ |
312
|
|
|
/* Create a field with a blank value & set 0 & 1 to disabled */ |
313
|
|
|
$field = $this->createDropdownField('(Any)'); |
314
|
|
|
$field->setDisabledItems(array(0,1)); |
315
|
|
|
|
316
|
|
|
/* 3 options are available */ |
317
|
|
|
$this->assertEquals(count($this->findOptionElements($field->Field())), 3, '3 options are available'); |
318
|
|
|
|
319
|
|
|
/* There are 2 disabled options */ |
320
|
|
|
$disabledOptions = $this->findDisabledOptionElements($field->Field()); |
321
|
|
|
$this->assertEquals(count($disabledOptions), 2, 'We have 2 disabled options'); |
322
|
|
|
|
323
|
|
|
/* Create a field without a blank value & set 1 to disabled, then set none to disabled (unset) */ |
324
|
|
|
$field = $this->createDropdownField(); |
325
|
|
|
$field->setDisabledItems(array(1)); |
326
|
|
|
|
327
|
|
|
/* 2 options are available */ |
328
|
|
|
$this->assertEquals(count($this->findOptionElements($field->Field())), 2, '2 options are available'); |
329
|
|
|
|
330
|
|
|
/* get disabled items returns an array of one */ |
331
|
|
|
$this->assertEquals( |
332
|
|
|
$field->getDisabledItems(), |
333
|
|
|
array( 1 ) |
334
|
|
|
); |
335
|
|
|
|
336
|
|
|
/* unset disabled items */ |
337
|
|
|
$field->setDisabledItems(array()); |
338
|
|
|
|
339
|
|
|
/* There are no disabled options anymore */ |
340
|
|
|
$disabledOptions = $this->findDisabledOptionElements($field->Field()); |
341
|
|
|
$this->assertEquals(count($disabledOptions), 0, 'There are no disabled options'); |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* The Field() method should be able to handle arrays as values in an edge case. If it couldn't handle it then |
346
|
|
|
* this test would trigger an array to string conversion PHP notice |
347
|
|
|
* |
348
|
|
|
* @dataProvider arrayValueProvider |
349
|
|
|
*/ |
350
|
|
|
public function testDropdownWithArrayValues($value) |
351
|
|
|
{ |
352
|
|
|
$field = $this->createDropdownField(); |
353
|
|
|
$field->setValue($value); |
354
|
|
|
$this->assertInstanceOf('SilverStripe\\ORM\\FieldType\\DBHTMLText', $field->Field()); |
355
|
|
|
$this->assertSame($value, $field->Value()); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* @return array |
360
|
|
|
*/ |
361
|
|
|
public function arrayValueProvider() |
362
|
|
|
{ |
363
|
|
|
return array( |
364
|
|
|
array(array()), |
365
|
|
|
array(array(0)), |
366
|
|
|
array(array(123)), |
367
|
|
|
array(array('string')), |
368
|
|
|
array('Regression-ish test.') |
369
|
|
|
); |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* Create a test dropdown field, with the option to |
374
|
|
|
* set what source and blank value it should contain |
375
|
|
|
* as optional parameters. |
376
|
|
|
* |
377
|
|
|
* @param string|null $emptyString The text to display for the empty value |
378
|
|
|
* @param string|integer $value The default value of the field |
379
|
|
|
* @return DropdownField object |
380
|
|
|
*/ |
381
|
|
|
public function createDropdownField($emptyString = null, $value = '') |
382
|
|
|
{ |
383
|
|
|
/* Set up source, with 0 and 1 integers as the values */ |
384
|
|
|
$source = array( |
385
|
|
|
0 => 'No', |
386
|
|
|
1 => 'Yes' |
387
|
|
|
); |
388
|
|
|
|
389
|
|
|
$field = new DropdownField('Field', null, $source, $value); |
390
|
|
|
|
391
|
|
|
if ($emptyString !== null) { |
392
|
|
|
$field->setEmptyString($emptyString); |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
return $field; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
/** |
399
|
|
|
* Find all the <OPTION> elements from a |
400
|
|
|
* string of HTML. |
401
|
|
|
* |
402
|
|
|
* @param string $html HTML to scan for elements |
403
|
|
|
* @return SimpleXMLElement |
404
|
|
|
*/ |
405
|
|
|
public function findOptionElements($html) |
406
|
|
|
{ |
407
|
|
|
$parser = new CSSContentParser($html); |
408
|
|
|
return $parser->getBySelector('option'); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* Find all the <OPTION> elements from a |
413
|
|
|
* string of HTML that have the "selected" |
414
|
|
|
* attribute. |
415
|
|
|
* |
416
|
|
|
* @param string $html HTML to parse for elements |
417
|
|
|
* @return array of SimpleXMLElement objects |
418
|
|
|
*/ |
419
|
|
View Code Duplication |
public function findSelectedOptionElements($html) |
|
|
|
|
420
|
|
|
{ |
421
|
|
|
$options = $this->findOptionElements($html); |
422
|
|
|
|
423
|
|
|
/* Find any elements that have the "selected" attribute and put them into a list */ |
424
|
|
|
$foundSelected = array(); |
425
|
|
|
foreach ($options as $option) { |
426
|
|
|
$attributes = $option->attributes(); |
427
|
|
|
if ($attributes) { |
428
|
|
|
foreach ($attributes as $attribute => $value) { |
429
|
|
|
if ($attribute == 'selected') { |
430
|
|
|
$foundSelected[] = $option; |
431
|
|
|
} |
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
return $foundSelected; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
/** |
440
|
|
|
* Find all the <OPTION> elements from a |
441
|
|
|
* string of HTML that have the "disabled" |
442
|
|
|
* attribute. |
443
|
|
|
* |
444
|
|
|
* @param string $html HTML to parse for elements |
445
|
|
|
* @return array of SimpleXMLElement objects |
446
|
|
|
*/ |
447
|
|
View Code Duplication |
public function findDisabledOptionElements($html) |
|
|
|
|
448
|
|
|
{ |
449
|
|
|
$options = $this->findOptionElements($html); |
450
|
|
|
|
451
|
|
|
/* Find any elements that have the "disabled" attribute and put them into a list */ |
452
|
|
|
$foundDisabled = array(); |
453
|
|
|
foreach ($options as $option) { |
454
|
|
|
$attributes = $option->attributes(); |
455
|
|
|
if ($attributes) { |
456
|
|
|
foreach ($attributes as $attribute => $value) { |
457
|
|
|
if ($attribute == 'disabled') { |
458
|
|
|
$foundDisabled[] = $option; |
459
|
|
|
} |
460
|
|
|
} |
461
|
|
|
} |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
return $foundDisabled; |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
/** |
468
|
|
|
* @skipUpgrade |
469
|
|
|
*/ |
470
|
|
|
public function testValidation() |
471
|
|
|
{ |
472
|
|
|
$field = DropdownField::create( |
473
|
|
|
'Test', |
474
|
|
|
'Testing', |
475
|
|
|
array( |
476
|
|
|
"One" => "One", |
477
|
|
|
"Two" => "Two", |
478
|
|
|
"Five" => "Five" |
479
|
|
|
) |
480
|
|
|
); |
481
|
|
|
$validator = new RequiredFields(); |
482
|
|
|
$form = new Form(null, 'Form', new FieldList($field), new FieldList(), $validator); |
|
|
|
|
483
|
|
|
$field->setValue("One"); |
484
|
|
|
$this->assertTrue($field->validate($validator)); |
485
|
|
|
$field->setName("TestNew"); //try changing name of field |
486
|
|
|
$this->assertTrue($field->validate($validator)); |
487
|
|
|
//non-existent value should make the field invalid |
488
|
|
|
$field->setValue("Three"); |
489
|
|
|
$this->assertFalse($field->validate($validator)); |
490
|
|
|
//empty string shouldn't validate |
491
|
|
|
$field->setValue(''); |
492
|
|
|
$this->assertFalse($field->validate($validator)); |
493
|
|
|
//empty field should validate after being set |
494
|
|
|
$field->setEmptyString('Empty String'); |
495
|
|
|
$field->setValue(''); |
496
|
|
|
$this->assertTrue($field->validate($validator)); |
497
|
|
|
//disabled items shouldn't validate |
498
|
|
|
$field->setDisabledItems(array('Five')); |
499
|
|
|
$field->setValue('Five'); |
500
|
|
|
$this->assertFalse($field->validate($validator)); |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
/** |
504
|
|
|
* #2939 DropdownField creates invalid HTML when required |
505
|
|
|
*/ |
506
|
|
|
public function testRequiredDropdownHasEmptyDefault() |
507
|
|
|
{ |
508
|
|
|
$field = new \SilverStripe\Forms\DropdownField("RequiredField", "dropdown", ["item 1", "item 2"]); |
509
|
|
|
|
510
|
|
|
$form = new Form( |
|
|
|
|
511
|
|
|
Controller::curr(), |
512
|
|
|
"form", |
513
|
|
|
new FieldList($field), |
514
|
|
|
new FieldList(), |
515
|
|
|
new RequiredFields(["RequiredField"]) |
516
|
|
|
); |
517
|
|
|
|
518
|
|
|
$this->assertTrue($field->getHasEmptyDefault()); |
519
|
|
|
} |
520
|
|
|
} |
521
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.