Passed
Push — master ( a80842...bbbee0 )
by Caen
03:37 queued 13s
created

MakePublicationCommandTest   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 563
Duplicated Lines 0 %

Importance

Changes 16
Bugs 0 Features 0
Metric Value
eloc 306
dl 0
loc 563
rs 9.92
c 16
b 0
f 0
wmc 31
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Publications\Testing\Feature;
6
7
use function array_merge;
8
use function file_get_contents;
9
use function file_put_contents;
10
11
use Hyde\Facades\Filesystem;
12
use Hyde\Hyde;
13
use Hyde\Publications\Commands\Helpers\InputStreamHandler;
14
use Hyde\Testing\TestCase;
15
use Illuminate\Support\Carbon;
16
17
use function json_encode;
18
19
/**
20
 * @covers \Hyde\Publications\Commands\MakePublicationCommand
21
 * @covers \Hyde\Publications\Actions\CreatesNewPublicationPage
22
 */
23
class MakePublicationCommandTest extends TestCase
24
{
25
    protected function setUp(): void
26
    {
27
        parent::setUp();
28
        $this->throwOnConsoleException();
29
30
        Filesystem::makeDirectory('test-publication');
31
        Carbon::setTestNow(Carbon::create(2022));
32
    }
33
34
    protected function tearDown(): void
35
    {
36
        Filesystem::deleteDirectory('test-publication');
37
        parent::tearDown();
38
    }
39
40
    public function test_command_creates_publication()
41
    {
42
        $this->makeSchemaFile();
43
44
        $this->artisan('make:publication')
45
            ->expectsOutputToContain('Creating a new publication!')
46
            ->expectsChoice('Which publication type would you like to create a publication item for?', 'test-publication', ['test-publication'])
47
            ->expectsOutput('Creating a new publication of type [test-publication]')
48
            ->expectsQuestion('Enter data for field </>[<comment>title</comment>]', 'Hello World')
49
            ->expectsOutput('All done! Created file [test-publication/hello-world.md]')
50
            ->assertExitCode(0);
51
52
        $this->assertFileExists(Hyde::path('test-publication/hello-world.md'));
53
        $this->assertPublicationFileWasCreatedCorrectly();
54
    }
55
56
    public function test_command_with_no_publication_types()
57
    {
58
        $this->throwOnConsoleException(false);
59
        $this->artisan('make:publication')
60
            ->expectsOutputToContain('Creating a new publication!')
61
            ->expectsOutput('Error: Unable to locate any publication types. Did you create any?')
62
            ->assertExitCode(1);
63
    }
64
65
    public function test_command_selects_the_right_publication_using_the_names()
66
    {
67
        $this->makeSchemaFile([
68
            'canonicalField' => '__createdAt',
69
            'fields'         =>  [],
70
        ]);
71
        $this->directory('second-publication');
72
        file_put_contents(
73
            Hyde::path('second-publication/schema.json'),
74
            json_encode([
75
                'name'           => 'Second Publication',
76
                'canonicalField' => '__createdAt',
77
                'detailTemplate' => 'detail',
78
                'listTemplate'   => 'list',
79
                'pageSize'       => 10,
80
                'sortField'      => '__createdAt',
81
                'sortAscending'  => true,
82
                'fields'         =>  [],
83
            ])
84
        );
85
86
        $this->artisan('make:publication')
87
            ->expectsOutputToContain('Creating a new publication!')
88
            ->expectsChoice('Which publication type would you like to create a publication item for?', 'test-publication', [
89
                'second-publication',
90
                'test-publication',
91
            ], true)
92
            ->expectsOutput('Creating a new publication of type [test-publication]')
93
            ->expectsOutput('All done! Created file [test-publication/2022-01-01-000000.md]')
94
            ->assertExitCode(0);
95
96
        $this->artisan('make:publication')
97
            ->expectsOutputToContain('Creating a new publication!')
98
            ->expectsChoice('Which publication type would you like to create a publication item for?', 'second-publication', [
99
                'second-publication',
100
                'test-publication',
101
            ], true)
102
            ->expectsOutput('Creating a new publication of type [second-publication]')
103
            ->expectsOutput('All done! Created file [second-publication/2022-01-01-000000.md]')
104
            ->assertExitCode(0);
105
    }
106
107
    public function test_command_with_existing_publication()
108
    {
109
        $this->makeSchemaFile();
110
        file_put_contents(Hyde::path('test-publication/hello-world.md'), 'foo');
111
112
        $this->artisan('make:publication')
113
            ->expectsOutputToContain('Creating a new publication!')
114
            ->expectsChoice('Which publication type would you like to create a publication item for?', 'test-publication', ['test-publication'])
115
            ->expectsQuestion('Enter data for field </>[<comment>title</comment>]', 'Hello World')
116
            ->expectsOutput('Error: A publication already exists with the same canonical field value')
117
            ->expectsConfirmation('Do you wish to overwrite the existing file?')
118
            ->expectsOutput('Exiting without overwriting existing publication file!')
119
            ->doesntExpectOutput('Publication created successfully!')
120
            ->assertExitCode(130);
121
122
        $this->assertSame('foo', file_get_contents(Hyde::path('test-publication/hello-world.md')));
123
    }
124
125
    public function test_command_with_existing_publication_and_overwrite()
126
    {
127
        $this->makeSchemaFile();
128
        file_put_contents(Hyde::path('test-publication/hello-world.md'), 'foo');
129
130
        $this->artisan('make:publication')
131
            ->expectsOutputToContain('Creating a new publication!')
132
            ->expectsChoice('Which publication type would you like to create a publication item for?', 'test-publication', ['test-publication'])
133
            ->expectsQuestion('Enter data for field </>[<comment>title</comment>]', 'Hello World')
134
            ->expectsOutput('Error: A publication already exists with the same canonical field value')
135
            ->expectsConfirmation('Do you wish to overwrite the existing file?', 'yes')
136
            ->assertExitCode(0);
137
138
        $this->assertNotEquals('foo', file_get_contents(Hyde::path('test-publication/hello-world.md')));
139
    }
140
141
    public function test_can_overwrite_existing_publication_by_passing_force_flag()
142
    {
143
        $this->makeSchemaFile();
144
        file_put_contents(Hyde::path('test-publication/hello-world.md'), 'foo');
145
146
        $this->artisan('make:publication', ['--force' => true])
147
            ->expectsOutputToContain('Creating a new publication!')
148
            ->expectsChoice('Which publication type would you like to create a publication item for?', 'test-publication', ['test-publication'])
149
            ->expectsQuestion('Enter data for field </>[<comment>title</comment>]', 'Hello World')
150
            ->assertExitCode(0);
151
152
        $this->assertNotEquals('foo', file_get_contents(Hyde::path('test-publication/hello-world.md')));
153
    }
154
155
    public function test_command_with_publication_type_passed_as_argument()
156
    {
157
        $this->makeSchemaFile();
158
159
        $this->artisan('make:publication test-publication')
160
            ->expectsOutput('Creating a new publication of type [test-publication]')
161
            ->expectsQuestion('Enter data for field </>[<comment>title</comment>]', 'Hello World')
162
            ->expectsOutput('All done! Created file [test-publication/hello-world.md]')
163
            ->assertExitCode(0);
164
165
        $this->assertFileExists(Hyde::path('test-publication/hello-world.md'));
166
        $this->assertPublicationFileWasCreatedCorrectly();
167
    }
168
169
    public function test_command_with_invalid_publication_type_passed_as_argument()
170
    {
171
        $this->throwOnConsoleException(false);
172
        $this->makeSchemaFile();
173
174
        $this->artisan('make:publication foo')
175
            ->expectsOutput('Error: Unable to locate publication type [foo]')
176
            ->assertExitCode(1);
177
    }
178
179
    public function test_command_with_schema_using_canonical_meta_field()
180
    {
181
        $this->makeSchemaFile([
182
            'canonicalField' => '__createdAt',
183
            'fields' => [],
184
        ]);
185
186
        $this->artisan('make:publication test-publication')
187
            ->assertExitCode(0);
188
189
        $this->assertFileExists(Hyde::path('test-publication/2022-01-01-000000.md'));
190
        $this->assertEquals(
191
            <<<'MARKDOWN'
192
            ---
193
            __createdAt: 2022-01-01T00:00:00+00:00
194
            ---
195
            
196
            ## Write something awesome.
197
            
198
            
199
            MARKDOWN, file_get_contents(Hyde::path('test-publication/2022-01-01-000000.md')));
200
    }
201
202
    public function test_command_does_not_ask_user_to_fill_in_meta_fields()
203
    {
204
        $this->makeSchemaFile([
205
            'canonicalField' => '__createdAt',
206
            'fields' => [[
207
                'type' => 'string',
208
                'name' => '__createdAt',
209
            ]],
210
        ]);
211
212
        $this->artisan('make:publication test-publication')
213
            ->doesntExpectOutput('Enter data for field </>[<comment>__createdAt</comment>]')
214
            ->doesntExpectOutputToContain('__createdAt')
215
            ->assertExitCode(0);
216
217
        $this->assertDatedPublicationExists();
218
    }
219
220
    public function test_command_with_text_input()
221
    {
222
        InputStreamHandler::mockInput("Hello\nWorld\n<<<");
223
        $this->makeSchemaFile([
224
            'canonicalField' => '__createdAt',
225
            'fields'         =>  [[
226
                'type' => 'text',
227
                'name' => 'description',
228
            ],
229
            ],
230
        ]);
231
232
        $this->artisan('make:publication test-publication')
233
            ->assertExitCode(0);
234
235
        $this->assertDatedPublicationExists();
236
        $this->assertCreatedPublicationMatterEquals('description: |
237
    Hello
238
    World'
239
        );
240
    }
241
242
    public function test_command_with_boolean_input()
243
    {
244
        $this->makeSchemaFile([
245
            'canonicalField' => '__createdAt',
246
            'fields'         =>  [[
247
                'type' => 'boolean',
248
                'name' => 'published',
249
            ],
250
            ],
251
        ]);
252
        $this->artisan('make:publication test-publication')
253
            ->expectsQuestion('Enter data for field </>[<comment>published</comment>]', 'true')
254
            ->assertExitCode(0);
255
256
        $this->assertDatedPublicationExists();
257
        $this->assertCreatedPublicationMatterEquals('published: true');
258
    }
259
260
    public function test_command_with_array_input()
261
    {
262
        InputStreamHandler::mockInput("First Tag\nSecond Tag\nThird Tag\n<<<");
263
        $this->makeSchemaFile([
264
            'canonicalField' => '__createdAt',
265
            'fields'         =>  [[
266
                'type' => 'array',
267
                'name' => 'tags',
268
            ],
269
            ],
270
        ]);
271
272
        $this->artisan('make:publication test-publication')
273
            ->assertExitCode(0);
274
275
        $this->assertDatedPublicationExists();
276
        $this->assertCreatedPublicationMatterEquals(
277
            "tags:
278
    - 'First Tag'
279
    - 'Second Tag'
280
    - 'Third Tag'",
281
        );
282
    }
283
284
    public function test_command_with_media_input()
285
    {
286
        $this->directory('_media/test-publication');
287
        $this->file('_media/test-publication/image.jpg');
288
        $this->makeSchemaFile([
289
            'canonicalField' => '__createdAt',
290
            'fields'         =>  [[
291
                'type' => 'media',
292
                'name' => 'media',
293
            ],
294
            ],
295
        ]);
296
297
        $this->artisan('make:publication test-publication')
298
            ->expectsQuestion('Which file would you like to use?', '_media/test-publication/image.jpg')
299
            ->assertExitCode(0);
300
301
        $this->assertDatedPublicationExists();
302
        $this->assertCreatedPublicationMatterEquals('media: _media/test-publication/image.jpg');
303
    }
304
305
    public function test_media_input_selects_the_right_file()
306
    {
307
        $this->directory('_media/test-publication');
308
        $this->file('_media/test-publication/foo.jpg');
309
        $this->file('_media/test-publication/bar.png');
310
        $this->file('_media/test-publication/baz.svg');
311
312
        $this->makeSchemaFile([
313
            'canonicalField' => '__createdAt',
314
            'fields'         =>  [[
315
                'type' => 'media',
316
                'name' => 'media',
317
            ],
318
            ],
319
        ]);
320
321
        $this->artisan('make:publication test-publication')
322
            ->expectsQuestion('Which file would you like to use?', '_media/test-publication/bar.png')
323
            ->assertExitCode(0);
324
325
        $this->assertDatedPublicationExists();
326
        $this->assertCreatedPublicationMatterEquals('media: _media/test-publication/bar.png');
327
    }
328
329
    public function test_command_with_single_tag_input()
330
    {
331
        $this->markdown('test-publication/existing.md', matter: [
332
            'tag' => ['foo', 'bar', 'baz'],
333
        ]);
334
        $this->makeSchemaFile([
335
            'canonicalField' => '__createdAt',
336
            'fields'         =>  [[
337
                'type' => 'tag',
338
                'name' => 'tag',
339
            ],
340
            ],
341
        ]);
342
343
        $this->artisan('make:publication test-publication')
344
            ->expectsQuestion(/** @lang Text */'Select from existing or', '<comment>Add new tag</comment>')
345
            ->expectsQuestion('Enter tag(s) <fg=gray>(multiple tags separated by commas)</>', 'foo')
346
            ->assertExitCode(0);
347
348
        $this->assertDatedPublicationExists();
349
350
        $this->assertCreatedPublicationMatterEquals("tag:\n    - foo");
351
    }
352
353
    public function test_command_with_multiple_tag_inputs()
354
    {
355
        $this->markdown('test-publication/existing.md', matter: [
356
            'tags' => ['foo', 'bar', 'baz'],
357
        ]);
358
        $this->makeSchemaFile([
359
            'canonicalField' => '__createdAt',
360
            'fields'         =>  [[
361
                'type' => 'tag',
362
                'name' => 'tags',
363
            ],
364
            ],
365
        ]);
366
367
        $this->artisan('make:publication test-publication')
368
            ->expectsQuestion(/** @lang Text */'Select from existing or', '<comment>Add new tag</comment>')
369
            ->expectsQuestion('Enter tag(s) <fg=gray>(multiple tags separated by commas)</>', 'foo, bar')
370
            ->assertExitCode(0);
371
372
        $this->assertDatedPublicationExists();
373
        $this->assertCreatedPublicationMatterEquals('tags:
374
    - foo
375
    - bar');
376
    }
377
378
    public function test_media_input_with_no_images()
379
    {
380
        $this->throwOnConsoleException(false);
381
        $this->makeSchemaFile([
382
            'canonicalField' => '__createdAt',
383
            'fields'         =>  [[
384
                'type' => 'media',
385
                'name' => 'media',
386
            ],
387
            ],
388
        ]);
389
390
        $this->artisan('make:publication test-publication')
391
            ->expectsOutput('Warning: No media files found in directory _media/test-publication/')
392
            ->expectsConfirmation('Would you like to skip this field?')
393
            ->expectsOutput('Error: Unable to locate any media files for this publication type')
394
            ->assertExitCode(1);
395
396
        $this->assertFileDoesNotExist(Hyde::path('test-publication/2022-01-01-000000.md'));
397
    }
398
399
    public function test_media_input_with_no_files_but_skips()
400
    {
401
        $this->makeSchemaFile([
402
            'canonicalField' => '__createdAt',
403
            'fields'         =>  [[
404
                'type' => 'media',
405
                'name' => 'media',
406
            ],
407
            ],
408
        ]);
409
410
        $this->artisan('make:publication test-publication')
411
            ->expectsOutput('Warning: No media files found in directory _media/test-publication/')
412
            ->expectsConfirmation('Would you like to skip this field?', 'yes')
413
            ->doesntExpectOutput('Error: Unable to locate any media files for this publication type')
414
            ->assertExitCode(0);
415
416
        $this->assertDatedPublicationExists();
417
        $this->assertEquals(
418
            <<<'MARKDOWN'
419
            ---
420
            __createdAt: 2022-01-01T00:00:00+00:00
421
            ---
422
            
423
            ## Write something awesome.
424
            
425
            
426
            MARKDOWN, $this->getDatedPublicationContents());
427
    }
428
429
    public function test_tag_input_with_no_tags()
430
    {
431
        $this->throwOnConsoleException(false);
432
        $this->makeSchemaFile([
433
            'canonicalField' => '__createdAt',
434
            'fields'         =>  [[
435
                'type' => 'tag',
436
                'name' => 'tag',
437
            ],
438
            ],
439
        ]);
440
441
        $this->artisan('make:publication test-publication')
442
            ->expectsQuestion('Enter tag(s) <fg=gray>(multiple tags separated by commas)</>', 'foo, bar')
443
            ->assertExitCode(0);
444
445
        $this->assertFileExists(Hyde::path('test-publication/2022-01-01-000000.md'));
446
    }
447
448
    public function test_handleEmptyOptionsCollection_for_required_field()
449
    {
450
        $this->throwOnConsoleException(false);
451
        $this->makeSchemaFile([
452
            'canonicalField' => '__createdAt',
453
            'fields'         =>  [[
454
                'type' => 'media',
455
                'name' => 'media',
456
                'rules' => ['required'],
457
            ],
458
            ],
459
        ]);
460
461
        $this->artisan('make:publication test-publication')
462
            ->doesntExpectOutput('Warning: No media files found in directory _media/test-publication/')
463
            ->expectsOutput('Error: Unable to create publication as no media files were found in directory _media/test-publication/')
464
            ->assertExitCode(1);
465
    }
466
467
    public function test_with_custom_validation_rules()
468
    {
469
        $this->makeSchemaFile([
470
            'canonicalField' => '__createdAt',
471
            'fields'         =>  [[
472
                'type' => 'integer',
473
                'name' => 'integer',
474
                'rules' => ['max:10'],
475
            ],
476
            ],
477
        ]);
478
479
        $this->artisan('make:publication test-publication')
480
            ->expectsQuestion('Enter data for field </>[<comment>integer</comment>]', 'string')
481
            ->expectsOutput('The integer must be an integer.')
482
            ->expectsQuestion('Enter data for field </>[<comment>integer</comment>]', 15)
483
            ->expectsOutput('The integer must not be greater than 10.')
484
            ->expectsQuestion('Enter data for field </>[<comment>integer</comment>]', 5)
485
            ->assertExitCode(0);
486
487
        $this->assertDatedPublicationExists();
488
        $this->assertCreatedPublicationMatterEquals('integer: 5');
489
    }
490
491
    public function test_with_skipping_inputs()
492
    {
493
        $this->makeSchemaFile([
494
            'canonicalField' => '__createdAt',
495
            'fields'         =>  [[
496
                'type' => 'string',
497
                'name' => 'string',
498
            ],
499
            ],
500
        ]);
501
502
        $this->artisan('make:publication test-publication')
503
            ->expectsQuestion('Enter data for field </>[<comment>string</comment>]', '')
504
            ->expectsOutput(' > Skipping field string')
505
            ->assertExitCode(0);
506
507
        $this->assertDatedPublicationExists();
508
        $this->assertEquals(
509
            <<<'MARKDOWN'
510
            ---
511
            __createdAt: 2022-01-01T00:00:00+00:00
512
            ---
513
            
514
            ## Write something awesome.
515
            
516
            
517
            MARKDOWN, $this->getDatedPublicationContents());
518
    }
519
520
    protected function makeSchemaFile(array $merge = []): void
521
    {
522
        file_put_contents(
523
            Hyde::path('test-publication/schema.json'),
524
            json_encode(array_merge([
525
                'name'           => 'Test Publication',
526
                'canonicalField' => 'title',
527
                'detailTemplate' => 'detail',
528
                'listTemplate'   => 'list',
529
                'pageSize'       => 10,
530
                'sortField'      => '__createdAt',
531
                'sortAscending'  => true,
532
                'fields'         =>  [
533
                    [
534
                        'name' => 'title',
535
                        'type' => 'string',
536
                    ],
537
                ],
538
            ], $merge))
539
        );
540
    }
541
542
    protected function assertPublicationFileWasCreatedCorrectly(): void
543
    {
544
        $this->assertEquals(
545
            <<<'MARKDOWN'
546
            ---
547
            __createdAt: 2022-01-01T00:00:00+00:00
548
            title: 'Hello World'
549
            ---
550
            
551
            ## Write something awesome.
552
            
553
            
554
            MARKDOWN, file_get_contents(Hyde::path('test-publication/hello-world.md'))
555
        );
556
    }
557
558
    protected function assertDatedPublicationExists(): void
559
    {
560
        $this->assertFileExists(Hyde::path('test-publication/2022-01-01-000000.md'));
561
    }
562
563
    protected function assertCreatedPublicationEquals(string $expected): void
564
    {
565
        $this->assertEquals($expected, $this->getDatedPublicationContents());
566
    }
567
568
    protected function assertCreatedPublicationMatterEquals(string $expected): void
569
    {
570
        $this->assertEquals(
571
            <<<MARKDOWN
572
            ---
573
            __createdAt: 2022-01-01T00:00:00+00:00
574
            $expected
575
            ---
576
            
577
            ## Write something awesome.
578
            
579
            
580
            MARKDOWN, $this->getDatedPublicationContents());
581
    }
582
583
    protected function getDatedPublicationContents(): string
584
    {
585
        return file_get_contents(Hyde::path('test-publication/2022-01-01-000000.md'));
586
    }
587
}
588