DataObjectAnnotatorTest::setUp()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 1
eloc 6
c 4
b 0
f 0
nc 1
nop 0
dl 0
loc 10
rs 10
1
<?php
2
3
namespace SilverLeague\IDEAnnotator\Tests;
4
5
use Page;
6
use PageController;
7
use PHPUnit_Framework_TestCase;
8
use RootTeam;
9
use SilverLeague\IDEAnnotator\DataObjectAnnotator;
10
use SilverLeague\IDEAnnotator\Extensions\Annotatable;
11
use SilverLeague\IDEAnnotator\Generators\OrmTagGenerator;
12
use SilverLeague\IDEAnnotator\Helpers\AnnotateClassInfo;
13
use SilverLeague\IDEAnnotator\Helpers\AnnotatePermissionChecker;
14
use SilverStripe\Core\Config\Config;
15
use SilverStripe\Core\Injector\Injector;
16
use SilverStripe\Core\Manifest\ModuleManifest;
17
use SilverStripe\Dev\SapphireTest;
18
use SilverStripe\ORM\DataObject;
19
20
/**
21
 * Class DataObjectAnnotatorTest
22
 *
23
 * Several tests to make sure the Annotator does it's job correctly
24
 *
25
 * @mixin PHPUnit_Framework_TestCase
26
 */
27
class DataObjectAnnotatorTest extends SapphireTest
28
{
29
30
    /**
31
     * @var MockDataObjectAnnotator
32
     */
33
    private $annotator;
34
35
    /**
36
     * @var AnnotatePermissionChecker $permissionChecker
37
     */
38
    private $permissionChecker;
39
40
    /**
41
     * Are we enabled?
42
     */
43
    public function testIsEnabled()
44
    {
45
        $this->assertTrue(DataObjectAnnotator::isEnabled());
46
    }
47
48
    /**
49
     * Test the expected classes show up in the Classes for Module
50
     */
51
    public function testGetClassesForModule()
52
    {
53
        $expectedClasses = [
54
            Team::class                          => realpath(__DIR__ . '/../mock/DataObjectAnnotatorTest_Team.php'),
55
            TeamChanged::class                   => realpath(__DIR__ . '/../mock/DataObjectAnnotatorTest_TeamChanged.php'),
56
            TeamComment::class                   => realpath(__DIR__ . '/../mock/DataObjectAnnotatorTest_TeamComment.php'),
57
            DocBlockMockWithDocBlock::class      => realpath(__DIR__ . '/../mock/DocBlockMockWithDocBlock.php'),
58
            OtherDocBlockMockWithDocBlock::class => realpath(__DIR__ . '/../mock/DocBlockMockWithDocBlock.php'),
59
            DoubleDataObjectInOneFile1::class    => realpath(__DIR__ . '/../mock/DoubleDataObjectInOneFile.php'),
60
            DoubleDataObjectInOneFile2::class    => realpath(__DIR__ . '/../mock/DoubleDataObjectInOneFile.php'),
61
            SubTeam::class                       => realpath(__DIR__ . '/../mock/DataObjectAnnotatorTest_SubTeam.php'),
62
            Player::class                        => realpath(__DIR__ . '/../mock/DataObjectAnnotatorTest_Player.php'),
63
            Team_Extension::class                => realpath(__DIR__ . '/../mock/DataObjectAnnotatorTest_Team_Extension.php'),
64
            Annotatable::class                   => realpath(__DIR__ . '/../../src/Extensions/Annotatable.php'),
65
            TestAnnotatorPage_Extension::class   => realpath(__DIR__ . '/../mock/TestAnnotatorPage.php'),
66
            RootTeam::class                      => realpath(__DIR__ . '/../mock/RootTeam.php'),
67
            TestAnnotatorPage::class             => realpath(__DIR__ . '/../mock/TestAnnotatorPage.php'),
68
            TestAnnotatorPageController::class   => realpath(__DIR__ . '/../mock/TestAnnotatorPage.php'),
69
            TeamSupporter::class                 => realpath(__DIR__ . '/../mock/DataObjectAnnotatorTest_TeamSupporter.php'),
70
        ];
71
        $classes = $this->annotator->getClassesForModule('silverleague/ideannotator');
72
        // Sort the array, so we don't get accidental errors due to manual ordering
73
        ksort($expectedClasses);
74
        ksort($classes);
75
        $this->assertEquals($expectedClasses, $classes);
76
    }
77
78
    /**
79
     * As below, as we don't want to actively change the mocks, so enable mysite
80
     */
81
    public function testAnnotateObject()
82
    {
83
        $this->assertFalse($this->annotator->annotateObject(DataObject::class));
84
85
        Config::modify()->set(DataObjectAnnotator::class, 'enabled_modules', ['ideannotator', 'mysite', 'app']);
86
        $this->assertTrue($this->annotator->annotateObject(PageController::class));
87
    }
88
89
    /**
90
     * Not testing existing modules, as it wil actively alter the mock files, so enable mysite
91
     */
92
    public function testAnnotateModule()
93
    {
94
        $noModule = $this->annotator->annotateModule('');
95
        $this->assertFalse($noModule);
96
        $projectName = ModuleManifest::config()->get('project');
97
        if (!$projectName) {
98
            $projectName = 'app';
99
        }
100
        $noModule = $this->annotator->annotateModule($projectName);
101
        $this->assertFalse($noModule);
102
        // Enable 'mysite' (or 'app') for testing
103
        Config::modify()->set(DataObjectAnnotator::class, 'enabled_modules', [$projectName]);
104
105
        $module = $this->annotator->annotateModule($projectName);
106
        $this->assertTrue($module, "$projectName was not allowed");
107
    }
108
109
    /**
110
     * Test if the correct annotations are generated
111
     * for all database fields, relations and extensions
112
     * and that the start and end tags are present
113
     */
114
    public function testFileContentWithAnnotations()
115
    {
116
        $classInfo = new AnnotateClassInfo(Team::class);
117
        $filePath = $classInfo->getClassFilePath();
118
119
        $content = $this->annotator->getGeneratedFileContent(file_get_contents($filePath), Team::class);
120
121
        $type = OrmTagGenerator::defaultType();
122
123
        // ClassName title
124
        $this->assertStringContainsString(' * Class \SilverLeague\IDEAnnotator\Tests\Team', $content);
125
126
        // database fields
127
        $this->assertStringContainsString('@property ' . $type . ' $Title', $content);
128
        $this->assertStringContainsString('@property int $VisitCount', $content);
129
        $this->assertStringContainsString('@property float $Price', $content);
130
        $this->assertStringContainsString('@property ' . $type . ' $Dude', $content);
131
        $this->assertStringContainsString('@property ' . $type . ' $Dudette', $content);
132
133
        // has_one ID
134
        $this->assertStringContainsString('@property int $CaptainID', $content);
135
        // has_one relation
136
        $this->assertStringContainsString('@method \SilverLeague\IDEAnnotator\Tests\Player Captain()', $content);
137
        // has_many relation
138
        $this->assertStringContainsString(
139
            '@method \SilverStripe\ORM\DataList|\SilverLeague\IDEAnnotator\Tests\SubTeam[] SubTeams()',
140
            $content
141
        );
142
        // many_many relation
143
        $this->assertStringContainsString(
144
            '@method \SilverStripe\ORM\ManyManyList|\SilverLeague\IDEAnnotator\Tests\Player[] Players()',
145
            $content
146
        );
147
        $this->assertStringContainsString(
148
            '@method \SilverStripe\ORM\ManyManyList|\SilverLeague\IDEAnnotator\Tests\Player[] Reserves()',
149
            $content
150
        );
151
        $this->assertStringContainsString(
152
            '@method \SilverStripe\ORM\ManyManyList|\SilverLeague\IDEAnnotator\Tests\TeamSupporter[] Supporters()',
153
            $content
154
        );
155
156
        // DataExtension
157
        $this->assertStringContainsString('@mixin \SilverLeague\IDEAnnotator\Tests\Team_Extension', $content);
158
    }
159
160
    /**
161
     * Test if the correct annotations are generated
162
     * for all database fields, relations and extensions
163
     * and that the start and end tags are present
164
     */
165
    public function testShortFileContentWithAnnotations()
166
    {
167
        Config::modify()->set(DataObjectAnnotator::class, 'use_short_name', true);
168
169
        $classInfo = new AnnotateClassInfo(Team::class);
170
        $filePath = $classInfo->getClassFilePath();
171
172
        $content = $this->annotator->getGeneratedFileContent(file_get_contents($filePath), Team::class);
173
174
        $type = OrmTagGenerator::defaultType();
175
176
        // database fields
177
        $this->assertStringContainsString('@property ' . $type . ' $Title', $content);
178
        $this->assertStringContainsString('@property int $VisitCount', $content);
179
        $this->assertStringContainsString('@property float $Price', $content);
180
181
        // has_one ID
182
        $this->assertStringContainsString('@property int $CaptainID', $content);
183
        // has_one relation
184
        $this->assertStringContainsString('@method Player Captain()', $content);
185
        // has_many relation
186
        $this->assertStringContainsString(
187
            '@method DataList|SubTeam[] SubTeams()',
188
            $content
189
        );
190
        // many_many relation
191
        $this->assertStringContainsString(
192
            '@method ManyManyList|Player[] Players()',
193
            $content
194
        );
195
        $this->assertStringContainsString(
196
            '@method ManyManyList|Player[] Reserves()',
197
            $content
198
        );
199
        $this->assertStringContainsString(
200
            '@method ManyManyList|TeamSupporter[] Supporters()',
201
            $content
202
        );
203
204
        // DataExtension
205
        $this->assertStringContainsString('@mixin Team_Extension', $content);
206
    }
207
208
    public function testInversePlayerRelationOfTeam()
209
    {
210
        $classInfo = new AnnotateClassInfo(Player::class);
211
        $filePath = $classInfo->getClassFilePath();
212
213
        $content = $this->annotator->getGeneratedFileContent(file_get_contents($filePath), Player::class);
214
215
        $type = OrmTagGenerator::defaultType();
216
217
        $this->assertStringContainsString('@property bool $IsRetired', $content);
218
        $this->assertStringContainsString('@property ' . $type . ' $ShirtNumber', $content);
219
        $this->assertStringContainsString('@property ' . $type . ' $Shirt', $content);
220
        $this->assertStringContainsString('@property int $FavouriteTeamID', $content);
221
        $this->assertStringContainsString('@method \SilverLeague\IDEAnnotator\Tests\Team CaptainTeam()', $content);
222
        $this->assertStringContainsString('@method \SilverLeague\IDEAnnotator\Tests\Team FavouriteTeam()', $content);
223
        $this->assertStringContainsString('@property string $OtherObjectClass', $content);
224
        $this->assertStringContainsString('@property int $OtherObjectID', $content);
225
        $this->assertStringContainsString('@method \SilverStripe\ORM\DataObject OtherObject()', $content);
226
227
        $this->assertStringContainsString(
228
            '@method \SilverStripe\ORM\ManyManyList|\SilverLeague\IDEAnnotator\Tests\Team[] TeamPlayer()',
229
            $content
230
        );
231
        $this->assertStringContainsString(
232
            '@method \SilverStripe\ORM\ManyManyList|\SilverLeague\IDEAnnotator\Tests\Team[] TeamReserve()',
233
            $content
234
        );
235
    }
236
237
    public function testDefaults()
238
    {
239
        $count = 0;
240
        DataObjectAnnotator::pushExtensionClass(Page::class);
241
        foreach (DataObjectAnnotator::getExtensionClasses() as $class) {
242
            if ($class === 'Page') {
243
                $count++;
244
            }
245
        }
246
        $this->assertEquals(1, $count);
247
    }
248
249
    public function testSetExtensionClasses()
250
    {
251
        $expected = [
252
            'SilverLeague\IDEAnnotator\Tests\TestAnnotatorPageController',
253
            'SilverLeague\IDEAnnotator\Tests\Team',
254
            'SilverStripe\Admin\LeftAndMain',
255
            'SilverStripe\Admin\ModalController',
256
            // 'SilverStripe\Assets\File',
257
            // 'SilverStripe\AssetAdmin\Forms\FileFormFactory',
258
            // 'SilverStripe\Assets\Shortcodes\FileShortcodeProvider',
259
            'SilverStripe\CMS\Controllers\ContentController',
260
            'SilverStripe\CMS\Controllers\ModelAsController',
261
            'SilverStripe\CMS\Model\SiteTree',
262
            'SilverStripe\Control\Controller',
263
            'SilverStripe\Dev\DevBuildController',
264
            'SilverStripe\Forms\Form',
265
            'SilverStripe\ORM\DataObject',
266
            'SilverStripe\Security\Group',
267
            'SilverStripe\Security\Member',
268
            'SilverStripe\Forms\GridField\GridFieldDetailForm',
269
            'SilverStripe\Forms\GridField\GridFieldPrintButton',
270
            // 'SilverStripe\ORM\FieldType\DBField',
271
        ];
272
273
        // Instantiate - triggers extension class list generation
274
        new DataObjectAnnotator();
275
        $result = DataObjectAnnotator::getExtensionClasses();
276
        foreach ($expected as $expectedClass) {
277
            $this->assertContains($expectedClass, $result, "Classes are: " . json_encode($result));
278
        }
279
    }
280
281
    public function testShortInversePlayerRelationOfTeam()
282
    {
283
        Config::modify()->set(DataObjectAnnotator::class, 'use_short_name', true);
284
285
        $classInfo = new AnnotateClassInfo(Player::class);
286
        $filePath = $classInfo->getClassFilePath();
287
288
        $content = $this->annotator->getGeneratedFileContent(file_get_contents($filePath), Player::class);
289
290
        $type = OrmTagGenerator::defaultType();
291
292
        $this->assertStringContainsString('@property bool $IsRetired', $content);
293
        $this->assertStringContainsString('@property ' . $type . ' $ShirtNumber', $content);
294
        $this->assertStringContainsString('@property int $FavouriteTeamID', $content);
295
        $this->assertStringContainsString('@method Team FavouriteTeam()', $content);
296
        $this->assertStringContainsString('@property string $OtherObjectClass', $content);
297
        $this->assertStringContainsString('@property int $OtherObjectID', $content);
298
        $this->assertStringContainsString('@method DataObject OtherObject()', $content);
299
300
        $this->assertStringContainsString(
301
            '@method ManyManyList|Team[] TeamPlayer()',
302
            $content
303
        );
304
        $this->assertStringContainsString(
305
            '@method ManyManyList|Team[] TeamReserve()',
306
            $content
307
        );
308
    }
309
310
    public function testExistingMethodsWillNotBeTagged()
311
    {
312
        $classInfo = new AnnotateClassInfo(Team::class);
313
        $filePath = $classInfo->getClassFilePath();
314
315
        $content = $this->annotator->getGeneratedFileContent(file_get_contents($filePath), Team::class);
316
        $this->assertStringNotContainsString(
317
            '@method \SilverStripe\ORM\ManyManyList|\SilverLeague\IDEAnnotator\Tests\SubTeam[] SecondarySubTeams()',
318
            $content
319
        );
320
    }
321
322
    public function testShortExistingMethodsWillNotBeTagged()
323
    {
324
        Config::modify()->set(DataObjectAnnotator::class, 'use_short_name', true);
325
326
        $classInfo = new AnnotateClassInfo(Team::class);
327
        $filePath = $classInfo->getClassFilePath();
328
329
        $content = $this->annotator->getGeneratedFileContent(file_get_contents($filePath), Team::class);
330
        $this->assertStringNotContainsString(
331
            '@method ManyManyList|SubTeam[] SecondarySubTeams()',
332
            $content
333
        );
334
    }
335
336
    /**
337
     * Test that multiple annotation runs won't generate ducplicate docblocks
338
     */
339
    public function testNothingHasChangedAfterSecondAnnotation()
340
    {
341
        $classInfo = new AnnotateClassInfo(Team::class);
342
        $filePath = $classInfo->getClassFilePath();
343
        $original = file_get_contents($filePath);
344
        $firstRun = $this->annotator->getGeneratedFileContent($original, Team::class);
345
        $secondRun = $this->annotator->getGeneratedFileContent($firstRun, Team::class);
346
        $this->assertEquals($firstRun, $secondRun);
347
    }
348
349
    /**
350
     * Test that root (non-namespaced) classes get annotated
351
     */
352
    public function testRootAnnotations()
353
    {
354
        $classInfo = new AnnotateClassInfo(RootTeam::class);
355
        $filePath = $classInfo->getClassFilePath();
356
        $run = $this->annotator->getGeneratedFileContent(file_get_contents($filePath), RootTeam::class);
357
358
        $type = OrmTagGenerator::defaultType();
359
360
        $this->assertStringContainsString('@property ' . $type . ' $Title', $run);
361
    }
362
363
    /**
364
     * Test the generation of annotations for a DataExtension
365
     */
366
    public function testAnnotateDataExtension()
367
    {
368
        $classInfo = new AnnotateClassInfo(Team_Extension::class);
369
        $filePath = $classInfo->getClassFilePath();
370
        $original = file_get_contents($filePath);
371
        $annotated = $this->annotator->getGeneratedFileContent($original, Team_Extension::class);
372
373
        $type = OrmTagGenerator::defaultType();
374
375
        $this->assertStringContainsString(
376
            '@property \SilverLeague\IDEAnnotator\Tests\Team|\SilverLeague\IDEAnnotator\Tests\Team_Extension $owner',
377
            $annotated
378
        );
379
        $this->assertStringContainsString('@property ' . $type . ' $ExtendedVarcharField', $annotated);
380
        $this->assertStringContainsString('@property int $ExtendedIntField', $annotated);
381
        $this->assertStringContainsString('@property int $ExtendedHasOneRelationshipID', $annotated);
382
        $this->assertStringContainsString(
383
            '@method \SilverLeague\IDEAnnotator\Tests\Player ExtendedHasOneRelationship()',
384
            $annotated
385
        );
386
    }
387
388
    /**
389
     * Test the generation of annotations for a DataExtension
390
     */
391
    public function testShortAnnotateDataExtension()
392
    {
393
        Config::modify()->set(DataObjectAnnotator::class, 'use_short_name', true);
394
395
        $classInfo = new AnnotateClassInfo(Team_Extension::class);
396
        $filePath = $classInfo->getClassFilePath();
397
        $original = file_get_contents($filePath);
398
        $annotated = $this->annotator->getGeneratedFileContent($original, Team_Extension::class);
399
400
        $type = OrmTagGenerator::defaultType();
401
402
        $this->assertStringContainsString(
403
            '@property Team|Team_Extension $owner',
404
            $annotated
405
        );
406
        $this->assertStringContainsString('@property ' . $type . ' $ExtendedVarcharField', $annotated);
407
        $this->assertStringContainsString('@property int $ExtendedIntField', $annotated);
408
        $this->assertStringContainsString('@property int $ExtendedHasOneRelationshipID', $annotated);
409
        $this->assertStringContainsString(
410
            '@method Player ExtendedHasOneRelationship()',
411
            $annotated
412
        );
413
    }
414
415
    /**
416
     *
417
     */
418
    public function testTwoClassesInOneFile()
419
    {
420
        $classInfo = new AnnotateClassInfo(DoubleDataObjectInOneFile1::class);
421
        $filePath = $classInfo->getClassFilePath();
422
        $original = file_get_contents($filePath);
423
        $annotated = $this->annotator->getGeneratedFileContent($original, DoubleDataObjectInOneFile1::class);
424
425
        $type = OrmTagGenerator::defaultType();
426
427
        $this->assertStringContainsString('@property ' . $type . ' $Title', $annotated);
428
429
        $annotated = $this->annotator->getGeneratedFileContent($annotated, DoubleDataObjectInOneFile2::class);
430
431
        $this->assertStringContainsString('@property ' . $type . ' $Name', $annotated);
432
    }
433
434
    /**
435
     * Setup Defaults
436
     */
437
    protected function setUp(): void
438
    {
439
        parent::setUp();
440
        Config::modify()->set(DataObjectAnnotator::class, 'use_short_name', false);
441
442
        Config::modify()->set(DataObjectAnnotator::class, 'enabled', true);
443
        Config::modify()->set(DataObjectAnnotator::class, 'enabled_modules', ['silverleague/ideannotator']);
444
445
        $this->annotator = Injector::inst()->get(MockDataObjectAnnotator::class);
446
        $this->permissionChecker = Injector::inst()->get(AnnotatePermissionChecker::class);
447
    }
448
}
449