$reflection->newInstance()) ->toThrow(\ReflectionException::class); }); it('transliterates accented and special characters to ascii', function (): void { // GIVEN $input = 'Émincés & Rôti, crème brûlée, ça va ?'; // WHEN $result = StringHelper::transliterate($input); // THEN expect($result)->toBe('Eminces & Roti, creme brulee, ca va ?'); }); it('handles empty strings and plain ascii strings without modification', function (): void { // GIVEN $empty = ''; $ascii = 'hello world 123'; // WHEN & THEN expect(StringHelper::transliterate($empty))->toBe('') ->and(StringHelper::transliterate($ascii))->toBe('hello world 123'); }); it('slugifies strings with custom separator', function (): void { expect(StringHelper::slugify('Émincés & Rôti'))->toBe('eminces-roti') ->and(StringHelper::slugify('Émincés & Rôti', '_'))->toBe('eminces_roti') ->and(StringHelper::slugify('Émincés & Rôti', ' '))->toBe('eminces roti') ->and(StringHelper::slugify(''))->toBe(''); }); it('converts strings to snake_case', function (): void { expect(StringHelper::snakeCase('fooBarBaz'))->toBe('foo_bar_baz') ->and(StringHelper::snakeCase('HTTPResponseCode'))->toBe('http_response_code') ->and(StringHelper::snakeCase('Le Gaulois Volaille'))->toBe('le_gaulois_volaille'); }); it('converts strings to kebab-case', function (): void { expect(StringHelper::kebabCase('fooBarBaz'))->toBe('foo-bar-baz') ->and(StringHelper::kebabCase('HTTPResponseCode'))->toBe('http-response-code'); }); it('converts strings to titleCase, pascalCase, and camelCase', function (): void { expect(StringHelper::titleCase('LE GAULOIS'))->toBe('Le Gaulois') ->and(StringHelper::titleCase('maître coq'))->toBe('Maître Coq') ->and(StringHelper::titleCase(''))->toBe('') ->and(StringHelper::pascalCase('foo_bar_baz'))->toBe('FooBarBaz') ->and(StringHelper::camelCase('foo_bar_baz'))->toBe('fooBarBaz') ->and(StringHelper::camelCase('HTTP_RESPONSE'))->toBe('httpResponse'); }); it('truncates strings to word limits with custom ending', function (): void { $text = 'Un deux trois quatre cinq six'; expect(StringHelper::words($text, 3, '...'))->toBe('Un deux trois...') ->and(StringHelper::words($text, 10))->toBe($text) ->and(StringHelper::words(''))->toBe(''); }); it('tokenizes multibyte strings with delimiters using mbStrtok', function (): void { $str = 'Gluten;Lait,Œufs'; $delim = ';,'; $first = StringHelper::mbStrtok($delim, $str); $second = StringHelper::mbStrtok($delim); $third = StringHelper::mbStrtok($delim); $fourth = StringHelper::mbStrtok($delim); expect($first)->toBe('Gluten') ->and($second)->toBe('Lait') ->and($third)->toBe('Œufs') ->and($fourth)->toBeFalse(); }); describe('sanitize', function (): void { it('returns null when input is null', function (): void { expect(StringHelper::sanitize(null))->toBeNull(); }); it('returns empty string when input is empty', function (): void { expect(StringHelper::sanitize(''))->toBe(''); }); it('strips HTML tags and decodes HTML entities', function (): void { $input = '
Poulet & Dinde
'; expect(StringHelper::sanitize($input))->toBe('Poulet & Dinde'); }); it('preserves newlines and removes non-printable control characters', function (): void { $input = "Ligne 1\x00\x08\r\nLigne 2\x1F"; expect(StringHelper::sanitize($input))->toBe("Ligne 1\nLigne 2"); }); }); });