Passed
Pull Request — master (#168)
by Ed
19:49
created

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