Completed
Pull Request — master (#114)
by Bart
04:05
created

CategoryGroupsTest   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 279
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 11
lcom 1
cbo 7
dl 0
loc 279
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
B _before() 0 39 1
A testSuccessfulExport() 0 8 1
A testSuccessfulImport() 0 8 1
A testImportWithForceOption() 0 9 1
B provideValidCategoryGroups() 0 30 1
B provideValidCategoryGroupDefinitions() 0 27 1
B getMockCategoryGroupDefinition() 0 24 1
B getMockCategoryGroup() 0 32 1
A expectList() 0 7 1
A expectSaves() 0 7 1
A expectDeletes() 0 6 1
1
<?php
2
3
namespace NerdsAndCompany\Schematic\Services;
4
5
use Craft;
6
use craft\console\Application;
7
use craft\models\CategoryGroup;
8
use craft\models\CategoryGroup_SiteSettings;
9
use craft\models\FieldLayout;
10
use craft\models\Site;
11
use craft\services\Categories;
12
use craft\services\Fields;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, NerdsAndCompany\Schematic\Services\Fields.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
13
use craft\services\Sites;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, NerdsAndCompany\Schematic\Services\Sites.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
14
use Codeception\Test\Unit;
15
use NerdsAndCompany\Schematic\Schematic;
16
17
/**
18
 * Class CategoryGroupsTest.
19
 *
20
 * @author    Nerds & Company
21
 * @copyright Copyright (c) 2015-2017, Nerds & Company
22
 * @license   MIT
23
 *
24
 * @see      http://www.nerds.company
25
 */
26
class CategoryGroupsTest extends Unit
27
{
28
    /**
29
     * @var CategoryGroups
30
     */
31
    private $service;
32
33
    /**
34
     * Set the service.
35
     *
36
     * @SuppressWarnings(PHPMD.CamelCaseMethodName)
37
     */
38
    protected function _before()
39
    {
40
        $mockCategoryGroups = $this->getMockBuilder(Categories::class)
41
                                    ->disableOriginalConstructor()
42
                                    ->getMock();
43
44
        $mockFields = $this->getMockBuilder(Fields::class)
45
                           ->disableOriginalConstructor()
46
                           ->getMock();
47
48
        $mockSite = $this->getMockBuilder(Site::class)
49
                         ->disableOriginalConstructor()
50
                         ->getMock();
51
52
        $mockSites = $this->getMockBuilder(Sites::class)
53
                          ->disableOriginalConstructor()
54
                          ->getMock();
55
56
        $mockSites->expects($this->any())
57
                  ->method('getSiteByHandle')
58
                  ->willReturn($mockSite);
59
60
        $mockApp = $this->getMockBuilder(Application::class)
61
                        ->disableOriginalConstructor()
62
                        ->getMock();
63
64
        $mockApp->expects($this->any())
65
            ->method('__get')
66
            ->willReturnMap([
67
                ['categories', $mockCategoryGroups],
68
                ['fields', $mockFields],
69
                ['sites', $mockSites],
70
            ]);
71
72
        Craft::$app = $mockApp;
0 ignored issues
show
Documentation Bug introduced by
It seems like $mockApp of type object<PHPUnit\Framework\MockObject\MockObject> is incompatible with the declared type object<craft\web\Applica...ft\console\Application> of property $app.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
73
        Schematic::$force = false;
74
75
        $this->service = new CategoryGroups();
76
    }
77
78
    //==============================================================================================================
79
    //=================================================  TESTS  ====================================================
80
    //==============================================================================================================
81
82
    /**
83
     * @dataProvider provideValidCategoryGroups
84
     *
85
     * @param CategoryGroupModel[] $groups
86
     * @param array                $expectedResult
87
     */
88
    public function testSuccessfulExport(array $groups, array $expectedResult = [])
89
    {
90
        $this->expectList($groups);
91
92
        $actualResult = $this->service->export();
93
94
        $this->assertSame($expectedResult, $actualResult);
95
    }
96
97
    /**
98
     * @dataProvider provideValidCategoryGroupDefinitions
99
     *
100
     * @param array $groupDefinitions
101
     */
102
    public function testSuccessfulImport(array $groupDefinitions, array $existingGroups, int $saveCount)
103
    {
104
        $this->expectList($existingGroups);
105
        $this->expectSaves($saveCount);
106
        $this->expectDeletes(0);
107
108
        $this->service->import($groupDefinitions);
109
    }
110
111
    /**
112
     * @dataProvider provideValidCategoryGroupDefinitions
113
     *
114
     * @param array $groupDefinitions
115
     */
116
    public function testImportWithForceOption(array $groupDefinitions, array $existingGroups, int $saveCount, int $deleteCount)
117
    {
118
        Schematic::$force = true;
119
        $this->expectList($existingGroups);
120
        $this->expectSaves($saveCount);
121
        $this->expectDeletes($deleteCount);
122
123
        $this->service->import($groupDefinitions);
124
    }
125
126
    //==============================================================================================================
127
    //==============================================  PROVIDERS  ===================================================
128
    //==============================================================================================================
129
130
    /**
131
     * @return array
132
     */
133
    public function provideValidCategoryGroups()
134
    {
135
        $mockCategoryGroup1 = $this->getMockCategoryGroup(1);
136
        $mockCategoryGroup2 = $this->getMockCategoryGroup(2);
137
138
        return [
139
            'emptyArray' => [
140
                'CategoryGroups' => [],
141
                'expectedResult' => [],
142
            ],
143
            'single group' => [
144
                'CategoryGroups' => [
145
                    'group1' => $mockCategoryGroup1,
146
                ],
147
                'expectedResult' => [
148
                    'groupHandle1' => $this->getMockCategoryGroupDefinition($mockCategoryGroup1),
149
                ],
150
            ],
151
            'multiple groups' => [
152
                'CategoryGroups' => [
153
                    'group1' => $mockCategoryGroup1,
154
                    'group2' => $mockCategoryGroup2,
155
                ],
156
                'expectedResult' => [
157
                    'groupHandle1' => $this->getMockCategoryGroupDefinition($mockCategoryGroup1),
158
                    'groupHandle2' => $this->getMockCategoryGroupDefinition($mockCategoryGroup2),
159
                ],
160
            ],
161
        ];
162
    }
163
164
    /**
165
     * @return array
166
     */
167
    public function provideValidCategoryGroupDefinitions()
168
    {
169
        $mockCategoryGroup1 = $this->getMockCategoryGroup(1);
170
        $mockCategoryGroup2 = $this->getMockCategoryGroup(2);
171
172
        return [
173
            'emptyArray' => [
174
                'groupDefinitions' => [],
175
                'existingGroups' => [
176
                    $mockCategoryGroup1,
177
                ],
178
                'saveCount' => 0,
179
                'deleteCount' => 1,
180
            ],
181
            'single group' => [
182
                'groupDefinitions' => [
183
                    'groupHandle1' => $this->getMockCategoryGroupDefinition($mockCategoryGroup1),
184
                    'groupHandle2' => $this->getMockCategoryGroupDefinition($mockCategoryGroup2),
185
                ],
186
                'existingGroups' => [
187
                    $mockCategoryGroup1,
188
                ],
189
                'saveCount' => 1,
190
                'deleteCount' => 0,
191
            ],
192
        ];
193
    }
194
195
    //==============================================================================================================
196
    //================================================  HELPERS  ===================================================
197
    //==============================================================================================================
198
199
    /**
200
     * @param CategoryGroup $$group
0 ignored issues
show
Bug introduced by
There is no parameter named $$group. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
201
     *
202
     * @return array
203
     */
204
    private function getMockCategoryGroupDefinition(CategoryGroup $mockCategoryGroup)
205
    {
206
        return [
207
            'class' => get_class($mockCategoryGroup),
208
            'attributes' => [
209
                'name' => $mockCategoryGroup->name,
210
                'handle' => $mockCategoryGroup->handle,
211
                'maxLevels' => 3,
212
            ],
213
            'fieldLayout' => [
214
                'fields' => [],
215
            ],
216
            'siteSettings' => [
217
                '' => [
218
                    'class' => get_class($mockCategoryGroup->getSiteSettings()[0]),
219
                    'attributes' => [
220
                        'hasUrls' => null,
221
                        'uriFormat' => null,
222
                        'template' => null,
223
                    ],
224
                ],
225
            ],
226
        ];
227
    }
228
229
    /**
230
     * @param string $groupId
231
     *
232
     * @return Mock|CategoryGroup
233
     */
234
    private function getMockCategoryGroup($groupId)
235
    {
236
        $mockGroup = $this->getMockBuilder(CategoryGroup::class)
237
                                    ->setMethods(['getFieldLayout', 'getSiteSettings'])
238
                                    ->getMock();
239
        $mockGroup->setAttributes([
240
            'id' => $groupId,
241
            'fieldLayoutId' => $groupId,
242
            'handle' => 'groupHandle'.$groupId,
243
            'name' => 'groupName'.$groupId,
244
            'maxLevels' => 3,
245
        ]);
246
247
        $mockFieldLayout = $this->getMockBuilder(FieldLayout::class)->getMock();
248
249
        $mockGroup->expects($this->any())
250
                  ->method('getFieldLayout')
251
                  ->willReturn($mockFieldLayout);
252
253
        $mocksiteSettings = $this->getMockBuilder(CategoryGroup_SiteSettings::class)->setMethods(['getSite'])->getMock();
254
        $mockSite = $this->getMockBuilder(Site::class)->getMock();
255
256
        $mocksiteSettings->expects($this->any())
257
            ->method('getSite')
258
            ->willReturn($mockSite);
259
260
        $mockGroup->expects($this->any())
261
                  ->method('getSiteSettings')
262
                  ->willReturn([$mocksiteSettings]);
263
264
        return $mockGroup;
265
    }
266
267
    /**
268
     * Expect a list of category groups.
269
     *
270
     * @param CategoryGroup[] $categoryGroups
271
     */
272
    private function expectList(array $categoryGroups)
273
    {
274
        Craft::$app->categories
275
                   ->expects($this->exactly(1))
276
                   ->method('getAllGroups')
277
                   ->willReturn($categoryGroups);
278
    }
279
280
    /**
281
     * Expect a number of group saves.
282
     *
283
     * @param int $saveCount
284
     */
285
    private function expectSaves(int $saveCount)
286
    {
287
        Craft::$app->categories
288
                   ->expects($this->exactly($saveCount))
289
                   ->method('saveGroup')
290
                   ->willReturn(true);
291
    }
292
293
    /**
294
     * Expect a number of group deletes.
295
     *
296
     * @param int $deleteCount
297
     */
298
    private function expectDeletes(int $deleteCount)
299
    {
300
        Craft::$app->categories
301
                    ->expects($this->exactly($deleteCount))
302
                    ->method('deleteGroupById');
303
    }
304
}
305