diff --git a/app/Services/CcliPasteParser.php b/app/Services/CcliPasteParser.php new file mode 100644 index 0000000..4fc183c --- /dev/null +++ b/app/Services/CcliPasteParser.php @@ -0,0 +1,33 @@ +toBeInstanceOf(CcliPasteParser::class); +}); + +test('CcliPasteParser can be instantiated with closure injections', function (): void { + $parser = new CcliPasteParser( + sectionDetector: fn (string $line): bool => str_starts_with($line, 'Verse'), + metadataDetector: fn (string $line): bool => str_contains($line, '©'), + ); + + expect($parser)->toBeInstanceOf(CcliPasteParser::class); +}); + +test('CcliPasteParser resolves from Laravel container', function (): void { + $parser = app(CcliPasteParser::class); + + expect($parser)->toBeInstanceOf(CcliPasteParser::class); +}); + +test('CcliPasteParser::parse returns ParsedCcliSong DTO', function (): void { + $parser = new CcliPasteParser; + + $result = $parser->parse('some text'); + + expect($result)->toBeInstanceOf(ParsedCcliSong::class); +}); + +test('ParsedCcliSong DTO has all required properties', function (): void { + $song = new ParsedCcliSong( + title: 'Test Song', + author: 'Test Author', + ccliId: '9999001', + year: '2024', + copyrightText: '© 2024 Test', + sourceUrl: 'https://songselect.ccli.com/Songs/9999001', + sections: [], + ); + + expect($song->title)->toBe('Test Song'); + expect($song->author)->toBe('Test Author'); + expect($song->ccliId)->toBe('9999001'); + expect($song->year)->toBe('2024'); + expect($song->copyrightText)->toBe('© 2024 Test'); + expect($song->sourceUrl)->toContain('songselect.ccli.com'); + expect($song->sections)->toBeArray(); +}); + +test('ParsedCcliSection DTO has all required properties including linesTranslated', function (): void { + $section = new ParsedCcliSection( + label: 'Verse 1', + kind: 'Verse', + number: '1', + modifier: null, + lines: ['Line 1', 'Line 2'], + linesTranslated: ['Zeile 1', 'Zeile 2'], + ); + + expect($section->label)->toBe('Verse 1'); + expect($section->kind)->toBe('Verse'); + expect($section->number)->toBe('1'); + expect($section->modifier)->toBeNull(); + expect($section->lines)->toBe(['Line 1', 'Line 2']); + expect($section->linesTranslated)->toBe(['Zeile 1', 'Zeile 2']); +}); + +test('ParsedCcliSection linesTranslated defaults to null', function (): void { + $section = new ParsedCcliSection( + label: 'Chorus', + kind: 'Chorus', + number: null, + modifier: null, + lines: ['Line 1'], + ); + + expect($section->linesTranslated)->toBeNull(); +}); + +test('CcliPasteParser can be injected with closure override in container', function (): void { + $called = false; + $fakeParser = new CcliPasteParser( + sectionDetector: function () use (&$called): bool { + $called = true; + + return true; + }, + ); + + app()->instance(CcliPasteParser::class, $fakeParser); + $resolved = app(CcliPasteParser::class); + + expect($resolved)->toBe($fakeParser); + + app()->forgetInstance(CcliPasteParser::class); +});