import { HtmlStringType, Mode, str, Str, Stringable } from '../src/main'; beforeEach((): void => { Str.createRandomStringsNormally(); Str.createUuidsNormally(); Str.createUlidsNormally(); }); describe('Str', (): void => { describe('Str.of', (): void => { test('returns instance of Stringable', (): void => { expect(Str.of('foo')).toBeInstanceOf(Stringable); }); }); describe('Str.after', (): void => { test('returns the subject if the "search" value is an empty string', (): void => { expect(Str.after('This is my name', '')).toEqual('This is my name'); }); test('returns everything after the given value in a string', (): void => { expect(Str.after('This is my name', 'This is')).toEqual(' my name'); }); }); describe('Str.afterLast', (): void => { test('returns the subject if the "search" value is an empty string', (): void => { expect(Str.afterLast('App\\Http\\Controllers\\Controller', '')).toEqual('App\\Http\\Controllers\\Controller'); }); test('returns the entire string if the "search" value is not found', (): void => { expect(Str.afterLast('App\\Http\\Controllers\\Controller', '!')).toEqual('App\\Http\\Controllers\\Controller'); }); test('returns everything after the last occurrence of the given value in a string', (): void => { expect(Str.afterLast('App\\Http\\Controllers\\Controller', '\\')).toEqual('Controller'); }); }); describe('Str.ascii', (): void => { test('transliterates accented characters to ASCII equivalents', (): void => { expect(Str.ascii('ü')).toEqual('u'); expect(Str.ascii('é')).toEqual('e'); expect(Str.ascii('ñ')).toEqual('n'); expect(Str.ascii('ç')).toEqual('c'); expect(Str.ascii('å')).toEqual('a'); }); test('removes diacritical marks', (): void => { expect(Str.ascii('c\u0327')).toEqual('c'); expect(Str.ascii('e\u0301')).toEqual('e'); expect(Str.ascii('a\u0308')).toEqual('a'); }); test('handles strings with only non-alphanumeric characters', (): void => { expect(Str.ascii('!@#$%^&*()_+-=')).toEqual('!@#$%^&*()_+-='); expect(Str.ascii(' ')).toEqual(' '); }); test('preserves case for ASCII letters', (): void => { expect(Str.ascii('HelloWorld')).toEqual('HelloWorld'); expect(Str.ascii('HELLOworld')).toEqual('HELLOworld'); }); test('handles mixed input', (): void => { expect(Str.ascii('Héllö Wörld! 123')).toEqual('Hello World! 123'); expect(Str.ascii('Café au lait')).toEqual('Cafe au lait'); expect(Str.ascii('Mëtàl Hëàd')).toEqual('Metal Head'); }); }); describe('Str.before', (): void => { test('returns the subject if the "search" value is an empty string', (): void => { expect(Str.before('This is my name', '')).toEqual('This is my name'); }); test('returns the entire string if the "search" value is not found', (): void => { expect(Str.before('This is my name', 'your')).toEqual('This is my name'); }); test('returns everything before the given value in a string', (): void => { expect(Str.before('This is my name', 'my name')).toEqual('This is '); }); }); describe('Str.beforeLast', (): void => { test('returns the subject if the "search" value is an empty string', (): void => { expect(Str.beforeLast('This is my name', '')).toEqual('This is my name'); }); test('returns the entire string if the "search" value is not found', (): void => { expect(Str.beforeLast('This is my name', 'your')).toEqual('This is my name'); }); test('returns everything before the last occurrence of the given value in a string', (): void => { expect(Str.beforeLast('This is my name', 'is')).toEqual('This '); }); }); describe('Str.between', (): void => { test('returns the subject if the "from" and "to" values are an empty string', (): void => { expect(Str.between('This is my name', '', '')).toEqual('This is my name'); }); test('returns the portion of a string between two values', (): void => { expect(Str.between('This is my name', 'This', 'name')).toEqual(' is my '); }); }); describe('Str.betweenFirst', (): void => { test('returns the subject if the "from" and "to" values are an empty string', (): void => { expect(Str.betweenFirst('This is my name', '', '')).toEqual('This is my name'); }); test('returns the smallest possible portion of a string between two values', (): void => { expect(Str.betweenFirst('[a] bc [d]', '[', ']')).toEqual('a'); }); }); describe('Str.camel', (): void => { test('converts the given string to Camel case', (): void => { expect(Str.camel('foo_bar')).toEqual('fooBar'); }); }); describe('Str.charAt', (): void => { test('returns the character at the specified index', (): void => { expect(Str.charAt('This is my name.', 6)).toEqual('s'); }); }); describe('Str.chopStart', (): void => { test('removes the given string if it exists at the start of the subject', (): void => { expect(Str.chopStart('Hello, world!', 'Hello, ')).toEqual('world!'); }); test('removes the first matching string from an array of needles', (): void => { expect(Str.chopStart('Hello, world!', ['Hello, ', 'Hi, '])).toEqual('world!'); }); test('removes only the first matching string when both are found at the start', (): void => { expect(Str.chopStart('Hello, Hello, world!', ['Hello, ', 'Hello, '])).toEqual('Hello, world!'); }); test('does not remove the string if it does not exist at the start of the subject', (): void => { expect(Str.chopStart('Hello, world!', 'world')).toEqual('Hello, world!'); }); }); describe('Str.chopEnd', (): void => { test('removes the given string if it exists at the end of the subject', (): void => { expect(Str.chopEnd('Hello, world!', 'world!')).toEqual('Hello, '); }); test('removes the first matching string from an array of needles', (): void => { expect(Str.chopEnd('Hello, world!', ['world!', 'planet!'])).toEqual('Hello, '); }); test('removes only the first matching string when both are found at the end', (): void => { expect(Str.chopEnd('Hello, world!world!', ['world!', 'world!'])).toEqual('Hello, world!'); }); test('does not remove the string if it does not exist at the end of the subject', (): void => { expect(Str.chopEnd('Hello, world!', 'Hello')).toEqual('Hello, world!'); }); }); describe('Str.contains', (): void => { test('determines if the given string contains the given value', (): void => { expect(Str.contains('This is my name', 'my')).toBeTruthy(); }); test('determines if the given string contains any of the values in the array', (): void => { expect(Str.contains('This is my name', ['my', 'foo'])).toBeTruthy(); }); test('determines if the given string contains the given value case-insensitively', (): void => { expect(Str.contains('This is my name', 'MY', true)).toBeTruthy(); }); test('determines if the given string contains any of the values in the array case-insensitively', (): void => { expect(Str.contains('This is my name', ['MY', 'foo'], true)).toBeTruthy(); }); }); describe('Str.containsAll', (): void => { test('determines if the given string contains all the values in a given array', (): void => { expect(Str.containsAll('This is my name', ['my', 'name'])).toBeTruthy(); }); test('determines if the given string contains all the values in a given array case-insensitively', (): void => { expect(Str.contains('This is my name', ['MY', 'NAME'], true)).toBeTruthy(); }); }); describe('Str.doesntContain', (): void => { test('determines if the given string doesnt contain the given value', (): void => { expect(Str.doesntContain('This is name', 'my')).toBeTruthy(); }); test('determines if the given string doesnt contain any of the values in the array', (): void => { expect(Str.doesntContain('This is name', ['my', 'foo'])).toBeTruthy(); }); test('determines if the given string doesnt contain the given value case-insensitively', (): void => { expect(Str.doesntContain('This is name', 'MY', true)).toBeTruthy(); }); test('determines if the given string doesnt contain any of the values in the array case-insensitively', (): void => { expect(Str.doesntContain('This is name', ['MY', 'FOO'], true)).toBeTruthy(); }); }); describe('Str.convertCase', (): void => { test('converts the case of a string', (): void => { expect(Str.convertCase('HeLLo')).toEqual('hello'); expect(Str.convertCase('hello', Mode.MB_CASE_UPPER)).toEqual('HELLO'); expect(Str.convertCase('WORLD', Mode.MB_CASE_LOWER)).toEqual('world'); expect(Str.convertCase('hello world', Mode.MB_CASE_TITLE)).toEqual('Hello World'); expect(Str.convertCase('HeLLo', Mode.MB_CASE_FOLD)).toEqual('hello'); expect(Str.convertCase('hello', Mode.MB_CASE_UPPER_SIMPLE)).toEqual('HELLO'); expect(Str.convertCase('HELLO', Mode.MB_CASE_LOWER_SIMPLE)).toEqual('hello'); expect(Str.convertCase('hello world', Mode.MB_CASE_TITLE_SIMPLE)).toEqual('Hello World'); expect(Str.convertCase('HeLLo', Mode.MB_CASE_FOLD_SIMPLE)).toEqual('hello'); expect(Str.convertCase('üöä', Mode.MB_CASE_UPPER)).toEqual('ÜÖÄ'); expect(Str.convertCase('ÜÖÄ', Mode.MB_CASE_LOWER)).toEqual('üöä'); }); test('throws error for invalid mode', (): void => { expect((): string => Str.convertCase('test', -1)).toThrow('Argument #2 (mode) must be one of the Mode.MB_CASE_* constants'); expect((): string => Str.convertCase('test', -1 as Mode)).toThrow('Argument #2 (mode) must be one of the Mode.MB_CASE_* constants'); }); }); describe('Str.deduplicate', (): void => { test('replace consecutive instances of a given character with a single character in the given string', (): void => { expect(Str.deduplicate(' laravel php framework ')).toEqual(' laravel php framework '); expect(Str.deduplicate('whaaat', 'a')).toEqual('what'); expect(Str.deduplicate('/some//odd//path/', '/')).toEqual('/some/odd/path/'); }); test('replace consecutive instances of multiple characters when given an array', (): void => { expect(Str.deduplicate('a--b++c**d', ['-', '+', '*'])).toEqual('a-b+c*d'); expect(Str.deduplicate(' hello !!world?? ', [' ', '!', '?'])).toEqual(' hello !world? '); expect(Str.deduplicate('mixed...spaces and---dashes', ['.', ' ', '-'])).toEqual('mixed.spaces and-dashes'); }); }); describe('Str.endsWith', (): void => { test('determines if the given string ends with the given value', (): void => { expect(Str.endsWith('This is my name', 'name')).toBeTruthy(); }); test('determines if the given string ends with any of the values in the array', (): void => { expect(Str.endsWith('This is my name', ['name', 'foo'])).toBeTruthy(); }); test('returns false if the given string does not end with the given value', (): void => { expect(Str.endsWith('This is my name', 'names')).toBeFalsy(); }); test('returns false if the given string does not end with any of the values in the array', (): void => { expect(Str.endsWith('This is my name', ['this', 'foo'])).toBeFalsy(); }); }); describe('Str.doesntEndWith', (): void => { test('determines if the given string does not end with the given value', (): void => { expect(Str.doesntEndWith('This is my name', 'names')).toBeTruthy(); }); test('determines if the given string does not end with any of the values in the array', (): void => { expect(Str.doesntEndWith('This is my name', ['names', 'foo'])).toBeTruthy(); }); test('returns false if the given string ends with the given value', (): void => { expect(Str.doesntEndWith('This is my name', 'name')).toBeFalsy(); }); test('returns false if the string ends with the given value', (): void => { expect(Str.doesntEndWith('This is my name', ['name', 'foo'])).toBeFalsy(); }); }); describe('Str.excerpt', (): void => { test('extracts an excerpt from a given string that matches the first instance of a phrase', (): void => { expect(Str.excerpt('This is my name', 'my', { 'radius': 3 })).toEqual('...is my na...'); }); test('allows definition of custom omission strings', (): void => { expect(Str.excerpt('This is my name', 'name', { 'radius': 3, 'omission': '(...) ' })).toEqual('(...) my name'); }); test('returns null when phrase is not found', (): void => { expect(Str.excerpt('This is my name', 'notfound')).toBeNull(); }); test('handles empty string as phrase', (): void => { expect(Str.excerpt('This is my name', '')).toEqual('This is my name'); }); test('handles phrase at the start of string', (): void => { expect(Str.excerpt('Hello world, this is a test', 'Hello', { radius: 5 })).toEqual('Hello worl...'); }); test('handles phrase at the end of string', (): void => { expect(Str.excerpt('This is the end of the test', 'test', { radius: 5 })).toEqual('...the test'); }); test('handles radius larger than text length', (): void => { expect(Str.excerpt('Short text', 'text', { radius: 100 })).toEqual('Short text'); }); }); describe('Str.finish', (): void => { test('adds a single instance of the given value to a string if it does not already end with that value', (): void => { expect(Str.finish('this/string', '/')).toEqual('this/string/'); }); test('does not add a value to a string that already ends with that value', (): void => { expect(Str.finish('this/string/', '/')).toEqual('this/string/'); }); }); describe('Str.wrap', (): void => { test('wraps the given string with an additional string or a pair of strings', (): void => { expect(Str.wrap('Laravel', '"')).toEqual('"Laravel"'); expect(Str.wrap('is', 'This ', ' Laravel!')).toEqual('This is Laravel!'); }); }); describe('Str.unwrap', (): void => { test('removes the specified strings from the beginning and end of a given string', (): void => { expect(Str.unwrap('-Laravel-', '-')).toEqual('Laravel'); expect(Str.unwrap('{framework: "Laravel"}', '{', '}')).toEqual('framework: "Laravel"'); }); }); describe('Str.is', (): void => { test('determines if a given string matches a given pattern', (): void => { expect(Str.is('a', 'a')).toBeTruthy(); expect(Str.is('foo*', 'foobar')).toBeTruthy(); expect(Str.is('baz*', 'foobar')).toBeFalsy(); expect(Str.is(['a*', 'b*'], 'b/')).toBeTruthy(); expect(Str.is(['a*', 'b*'], 'f/')).toBeFalsy(); }); test('determines if a given string matches a given pattern case-insensitively', (): void => { expect(Str.is('A', 'a', true)).toBeTruthy(); expect(Str.is('FOO*', 'foobar', true)).toBeTruthy(); expect(Str.is('baz*', 'foobar', true)).toBeFalsy(); expect(Str.is(['A*', 'B*'], 'b/', true)).toBeTruthy(); expect(Str.is(['A*', 'B*'], 'f/', true)).toBeFalsy(); }); }); describe('Str.isAscii', (): void => { test('determines if a given string is 7-bit ASCII', (): void => { expect(Str.isAscii('Taylor')).toBeTruthy(); expect(Str.isAscii('ü')).toBeFalsy(); }); }); describe('Str.isJson', (): void => { test('determines if the given string is valid JSON', (): void => { expect(Str.isJson('[1,2,3]')).toBeTruthy(); expect(Str.isJson('{"first": "John", "last": "Doe"}')).toBeTruthy(); expect(Str.isJson('{first: "John", last: "Doe"}')).toBeFalsy(); }); }); describe('Str.isUrl', (): void => { test('determines if the given string is a valid URL', (): void => { expect(Str.isUrl('https://example.com')).toBeTruthy(); expect(Str.isUrl('laravel')).toBeFalsy(); }); }); describe('Str.isUuid', (): void => { test('determines if the given string is a valid UUID', (): void => { expect(Str.isUuid('a0a2a2d2-0b87-4a18-83f2-2529882be2de')).toBeTruthy(); expect(Str.isUuid('laravel')).toBeFalsy(); }); }); describe('Str.isUlid', (): void => { test('validates correct ULID format', (): void => { expect(Str.isUlid('01gd6r360bp37zj17nxb55yv40')).toBeTruthy(); expect(Str.isUlid('7ZZZZZZZZZZZZZZZZZZZZZZZZZ')).toBeTruthy(); }); test('rejects invalid ULIDs', (): void => { expect(Str.isUlid('01gd6r360bp37zj17nxb55yv4!')).toBeFalsy(); expect(Str.isUlid('01gd6r360bp37zj17nxb55yv4i')).toBeFalsy(); expect(Str.isUlid('01gd6r360bp37zj17nxb55yv4l')).toBeFalsy(); expect(Str.isUlid('01gd6r360bp37zj17nxb55yv4o')).toBeFalsy(); expect(Str.isUlid('01gd6r360bp37zj17nxb55yv4I')).toBeFalsy(); expect(Str.isUlid('01gd6r360bp37zj17nxb55yv4O')).toBeFalsy(); }); test('validates ULID length', (): void => { expect(Str.isUlid('')).toBeFalsy(); expect(Str.isUlid('01gd6r360bp37zj17nxb55yv4')).toBeFalsy(); expect(Str.isUlid('01gd6r360bp37zj17nxb55yv400')).toBeFalsy(); }); test('validates timestamp overflow', (): void => { expect(Str.isUlid('81gd6r360bp37zj17nxb55yv40')).toBeFalsy(); expect(Str.isUlid('90000000000000000000000000')).toBeFalsy(); }); test('handles edge cases', (): void => { expect(Str.isUlid('01GD6R360BP37ZJ17NXB55YV40')).toBeTruthy(); expect(Str.isUlid('00000000000000000000000000')).toBeTruthy(); expect(Str.isUlid('7ZZZZZZZZZZZZZZZZZZZZZZZZZ')).toBeTruthy(); }); test('rejects non-string inputs', (): void => { // @ts-expect-error expect(Str.isUlid(123)).toBeFalsy(); // @ts-expect-error expect(Str.isUlid(null)).toBeFalsy(); // @ts-expect-error expect(Str.isUlid(undefined)).toBeFalsy(); // @ts-expect-error expect(Str.isUlid({})).toBeFalsy(); }); }); describe('Str.kebab', (): void => { test('converts the given string to kebab-case', (): void => { expect(Str.kebab('fooBar')).toEqual('foo-bar'); }); }); describe('Str.length', (): void => { test('returns the length of the given string', (): void => { expect(Str.length('Laravel')).toEqual(7); }); }); describe('Str.limit', (): void => { test('returns the original string when length is within limit', (): void => { expect(Str.limit('The quick brown fox jumps over the lazy dog', 100)).toBe('The quick brown fox jumps over the lazy dog'); }); test('truncates the given string to the specified length', (): void => { expect(Str.limit('The quick brown fox jumps over the lazy dog', 20)).toEqual('The quick brown fox...'); }); test('truncates the string and appends a custom string', (): void => { expect(Str.limit('The quick brown fox jumps over the lazy dog', 20, ' (...)')).toEqual('The quick brown fox (...)'); }); test('handles space after limit when "preserveWord" is set to true', (): void => { expect(Str.limit('The quick brown fox jumps over the lazy dog', 3, '...', true)).toEqual('The...'); }); test('respects word boundaries if "preserveWord" is set to true', (): void => { expect(Str.limit('The quick brown fox jumps over the lazy dog', 20, '...', true)).toEqual('The quick brown...'); }); }); describe('Str.lower', (): void => { test('converts the given string to lowercase', (): void => { expect(Str.lower('LARAVEL')).toEqual('laravel'); }); }); describe('Str.words', (): void => { test('returns the original string when words limit is not reached', (): void => { expect(Str.words('Perfectly balanced, as all things should be.', 10)).toEqual('Perfectly balanced, as all things should be.'); }); test('returns the original string when it contains only one word', (): void => { expect(Str.words('Word', 3)).toEqual('Word'); }); test('returns the original string when words limit equals the number of words', (): void => { expect(Str.words('Perfectly balanced, as all things should be.', 7)).toEqual('Perfectly balanced, as all things should be.'); }); test('limits the number of words in the string', (): void => { expect(Str.words('Perfectly balanced, as all things should be.', 3)).toEqual('Perfectly balanced, as...'); }); test('prepends the end of the string', (): void => { expect(Str.words('Perfectly balanced, as all things should be.', 3, ' >>>')).toEqual('Perfectly balanced, as >>>'); }); }); describe('Str.mask', (): void => { test('masks a portion of a string with a repeated character', (): void => { expect(Str.mask('taylor@example.com', '*', 3)).toEqual('tay***************'); expect(Str.mask('taylor@example.com', '*', -15, 3)).toEqual('tay***@example.com'); }); test('returns original string when character is empty', (): void => { expect(Str.mask('taylor@example.com', '', 2, 4)).toEqual('taylor@example.com'); }); test('returns original string when segment is empty', (): void => { expect(Str.mask('taylor@example.com', '*', 20)).toEqual('taylor@example.com'); expect(Str.mask('taylor@example.com', '*', 2, 0)).toEqual('taylor@example.com'); }); }); describe('Str.match', (): void => { test('returns the portion of a string that matches a given regular expression pattern', (): void => { expect(Str.match(/bar/, 'foo bar')).toEqual('bar'); }); test('returns the portion of a string that matches a regular expression with a capturing group', (): void => { expect(Str.match(/foo (.*)/, 'foo bar')).toEqual('bar'); }); }); describe('Str.isMatch', (): void => { test('determines if the string matches a given regular expression', (): void => { expect(Str.isMatch(/foo (.*)/, 'foo bar')).toBeTruthy(); expect(Str.isMatch([/foo baz/, /foo (.*)/], 'foo bar')).toBeTruthy(); }); test('determines if the string does not match a given regular expression', (): void => { expect(Str.isMatch(/foo (.*)/, 'laravel')).toBeFalsy(); }); }); describe('Str.matchAll', (): void => { test('returns an array containing portions of a string that match a given regular expression pattern', (): void => { expect(Str.matchAll(/bar/, 'bar foo bar')).toEqual(['bar', 'bar']); }); test('returns an empty array when there are no matches', (): void => { expect(Str.matchAll(/baz/, 'bar foo bar')).toEqual([]); }); test('returns an array containing matches of a regular expression with a capturing group', (): void => { expect(Str.matchAll(/f(\w*)/, 'bar fun bar fly')).toEqual(['un', 'ly']); }); }); describe('Str.numbers', (): void => { test('removes all non-numeric characters from a string', (): void => { expect(Str.numbers('(555) 123-4567')).toEqual('5551234567'); expect(Str.numbers('L4r4v3l!')).toEqual('443'); expect(Str.numbers('Laravel!')).toEqual(''); }); }); describe('Str.padBoth', (): void => { test('pads both sides of a string until the final string reaches a desired length', (): void => { expect(Str.padBoth('James', 10, '_')).toEqual('__James___'); expect(Str.padBoth('James', 10)).toEqual(' James '); }); }); describe('Str.padLeft', (): void => { test('pads the left side of a string until the final string reaches a desired length', (): void => { expect(Str.padLeft('James', 10, '-=')).toEqual('-=-=-James'); expect(Str.padLeft('James', 10)).toEqual(' James'); }); }); describe('Str.padRight', (): void => { test('pads the right side of a string until the final string reaches a desired length', (): void => { expect(Str.padRight('James', 10, '-')).toEqual('James-----'); expect(Str.padRight('James', 10)).toEqual('James '); }); }); describe('Str.plural', (): void => { test('converts singular to plural for regular nouns', (): void => { expect(Str.plural('car')).toBe('cars'); expect(Str.plural('book')).toBe('books'); expect(Str.plural('apple')).toBe('apples'); }); test('handles "count" value correctly', (): void => { expect(Str.plural('child', 1)).toBe('child'); expect(Str.plural('child', 2)).toBe('children'); expect(Str.plural('person', 1)).toBe('person'); expect(Str.plural('person', 3)).toBe('people'); }); test('converts irregular nouns correctly', (): void => { // A expect(Str.plural('alumna')).toBe('alumnae'); expect(Str.plural('analysis')).toBe('analyses'); expect(Str.plural('axis')).toBe('axes'); // B-C expect(Str.plural('bacterium')).toBe('bacteria'); expect(Str.plural('child')).toBe('children'); expect(Str.plural('crisis')).toBe('crises'); // D-F expect(Str.plural('datum')).toBe('data'); expect(Str.plural('foot')).toBe('feet'); expect(Str.plural('fungus')).toBe('fungi'); // G-M expect(Str.plural('goose')).toBe('geese'); expect(Str.plural('man')).toBe('men'); expect(Str.plural('mouse')).toBe('mice'); // N-S expect(Str.plural('nucleus')).toBe('nuclei'); expect(Str.plural('person')).toBe('people'); expect(Str.plural('thesis')).toBe('theses'); // T-Z expect(Str.plural('tooth')).toBe('teeth'); expect(Str.plural('wife')).toBe('wives'); expect(Str.plural('zombie')).toBe('zombies'); }); test('handles uncountable nouns correctly', (): void => { expect(Str.plural('sheep')).toBe('sheep'); expect(Str.plural('fish')).toBe('fish'); expect(Str.plural('series')).toBe('series'); expect(Str.plural('money')).toBe('money'); expect(Str.plural('information')).toBe('information'); expect(Str.plural('equipment')).toBe('equipment'); }); test('handles special pluralization rules', (): void => { // -f/-fe → -ves expect(Str.plural('leaf')).toBe('leaves'); expect(Str.plural('knife')).toBe('knives'); // -y → -ies expect(Str.plural('city')).toBe('cities'); expect(Str.plural('baby')).toBe('babies'); // -o → -oes expect(Str.plural('potato')).toBe('potatoes'); expect(Str.plural('volcano')).toBe('volcanoes'); // -us → -i expect(Str.plural('cactus')).toBe('cacti'); expect(Str.plural('focus')).toBe('foci'); // -is → -es expect(Str.plural('analysis')).toBe('analyses'); expect(Str.plural('basis')).toBe('bases'); // -ix → -ices expect(Str.plural('matrix')).toBe('matrices'); expect(Str.plural('index')).toBe('indices'); }); test('handles compound words and special cases', (): void => { expect(Str.plural('passerby')).toBe('passersby'); expect(Str.plural('runner-up')).toBe('runners-up'); }); test('handles words with multiple plural forms', (): void => { expect(Str.plural('octopus')).toBe('octopuses'); expect(Str.plural('hoof')).toBe('hoofs'); }); test('preserves case sensitivity', (): void => { expect(Str.plural('Hero')).toBe('Heroes'); expect(Str.plural('CHILD')).toBe('CHILDREN'); expect(Str.plural('Analysis')).toBe('Analyses'); }); test('handles edge cases', (): void => { expect(Str.plural('')).toBe(''); expect(Str.plural(' ')).toBe(' '); expect(Str.plural('sheep', 0)).toBe('sheep'); expect(Str.plural('person', 1.5)).toBe('people'); }); test('preserves case sensitivity in pluralization', (): void => { expect(Str.plural('HERO')).toBe('HEROES'); expect(Str.plural('PERSON')).toBe('PEOPLE'); expect(Str.plural('CHILD')).toBe('CHILDREN'); expect(Str.plural('SHEEP')).toBe('SHEEP'); expect(Str.plural('Hero')).toBe('Heroes'); expect(Str.plural('Person')).toBe('People'); expect(Str.plural('Child')).toBe('Children'); expect(Str.plural('Sheep')).toBe('Sheep'); expect(Str.plural('HeRo')).toBe('HeRoes'); expect(Str.plural('PeRsOn')).toBe('People'); expect(Str.plural('ChIlD')).toBe('Children'); expect(Str.plural('ShEep')).toBe('ShEep'); expect(Str.plural('uSeR')).toBe('uSeRs'); }); }); describe('Str.pluralStudly', (): void => { test('converts a singular word string formatted in studly caps case to its plural form', (): void => { expect(Str.pluralStudly('VerifiedHuman')).toEqual('VerifiedHumans'); expect(Str.pluralStudly('UserFeedback')).toEqual('UserFeedback'); expect(Str.pluralStudly('VerifiedHuman', 2)).toEqual('VerifiedHumans'); expect(Str.pluralStudly('VerifiedHuman', 1)).toEqual('VerifiedHuman'); }); }); describe('Str.pluralPascal', (): void => { test('converts a singular word string formatted in Pascal case to its plural form', (): void => { expect(Str.pluralPascal('VerifiedHuman')).toEqual('VerifiedHumans'); expect(Str.pluralPascal('UserFeedback')).toEqual('UserFeedback'); expect(Str.pluralPascal('VerifiedHuman', 2)).toEqual('VerifiedHumans'); expect(Str.pluralPascal('VerifiedHuman', 1)).toEqual('VerifiedHuman'); }); }); describe('Str.password', (): void => { test('generates a secure, random password of a given length', (): void => { expect(Str.password()).toHaveLength(32); expect(Str.password(12)).toHaveLength(12); }); test('includes letters when "letters" when is set to true', (): void => { expect(Str.password(12, true, false, false, false)).toMatch(/^[a-zA-Z]+$/); }); test('excludes letters when "letters" when is set to false', (): void => { expect(Str.password(12, false, true, true, true)).not.toMatch(/[a-zA-Z]/); }); test('includes numbers when "numbers" when is set to true', (): void => { expect(Str.password(12, false, true, false, false)).toMatch(/^\d+$/); }); test('excludes numbers when "numbers" when is set to false', (): void => { expect(Str.password(12, true, false, true, true)).not.toMatch(/\d/); }); test('includes symbols when "symbols" when is set to true', (): void => { expect(Str.password(12, false, false, true, false)).toMatch(/^[~!#$%^&*()\-_.,<>?\/\\{}[\]|:;]+$/); }); test('excludes symbols when "symbols" when is set to false', (): void => { expect(Str.password(12, true, true, false, true)).not.toMatch(/[~!#$%^&*()\-_.,<>?\/\\{}[\]|:;]/); }); test('includes spaces when "spaces" when is set to true', (): void => { expect(Str.password(12, true, true, true, true)).toContain(' '); }); test('excludes spaces when "spaces" when is set to false', (): void => { expect(Str.password(12, true, true, true, false)).not.toContain(' '); }); }); describe('Str.position', (): void => { test('returns the position of the first occurrence of a substring in a string', (): void => { expect(Str.position('Hello, World!', 'Hello')).toEqual(0); }); test('returns the position of the first occurrence of a substring in a string', (): void => { expect(Str.position('Hello, World!', 'W')).toEqual(7); }); }); describe('Str.random', (): void => { test('generates a random string of the specified length', (): void => { expect(Str.random(40)).toHaveLength(40); }); }); describe('Str.createRandomStringsUsing', (): void => { test('creates random strings using the specified callback', (): void => { Str.createRandomStringsUsing((length: number): string => `length:${length}`); expect(Str.random(7)).toEqual('length:7'); expect(Str.random(7)).toEqual('length:7'); Str.createRandomStringsNormally(); expect(Str.random(7)).not.toEqual('length:7'); }); }); describe('Str.createRandomStringsUsingSequence', (): void => { test('returns strings from the sequence in order', (): void => { Str.createRandomStringsUsingSequence(['first', undefined, 'second', 'third']); expect(Str.random()).toBe('first'); expect(Str.random()).toHaveLength(16); expect(Str.random()).toBe('second'); expect(Str.random()).toBe('third'); expect(Str.random()).toHaveLength(16); }); test('uses default fallback when none provided', (): void => { Str.createRandomStringsUsingSequence([Str.random(), Str.random()], (): never => { throw new Error('Out of random strings.'); }); Str.random(); Str.random(); expect((): string => Str.random()).toThrow('Out of random strings.'); }); }); describe('Str.createRandomStringsNormally', (): void => { test('resets random string generation to default behavior', (): void => { Str.createRandomStringsUsingSequence(['first', 'second']); expect(Str.random()).toBe('first'); expect(Str.random()).toBe('second'); Str.createRandomStringsNormally(); const first: string = Str.random(); const second: string = Str.random(); expect(first).toHaveLength(16); expect(second).toHaveLength(16); expect(first).not.toEqual(second); }); }); describe('Str.repeat', (): void => { test('repeats the given string', (): void => { expect(Str.repeat('a', 5)).toEqual('aaaaa'); }); }); describe('Str.replaceArray', (): void => { test('replaces a given value in the string sequentially using an array', (): void => { expect(Str.replaceArray('?', ['8:30', '9:00'], 'The event will take place between ? and ?')).toEqual('The event will take place between 8:30 and 9:00'); }); }); describe('Str.replace', (): void => { test('replaces a given string within the string', (): void => { expect(Str.replace('9.x', '10.x', 'Laravel 9.x')).toEqual('Laravel 10.x'); }); test('replaces a given string within the string case-insensitively', (): void => { expect(Str.replace('framework', 'Laravel', 'Framework 10.x', false)).toEqual('Laravel 10.x'); expect(Str.replace(['?1', '?2', '?3'], ['foo', 'bar', 'baz'], '?1 ?2 ?3')).toEqual('foo bar baz'); expect(Str.replace(['?1', '?2', '?3'], ['foo', 'bar', 'baz'], ['?1', '?2', '?3'])).toEqual(['foo', 'bar', 'baz']); }); test('replaces a given array of strings within the given array of strings', (): void => { expect(Str.replace(['?1', '?2', '?3'], ['foo', 'bar', 'baz'], '?1 ?2 ?3')).toEqual('foo bar baz'); expect(Str.replace(['?1', '?2', '?3'], ['foo', 'bar', 'baz'], ['?1', '?2', '?3'])).toEqual(['foo', 'bar', 'baz']); }); }); describe('Str.replaceFirst', (): void => { test('replaces the first occurrence of a given value in a string', (): void => { expect(Str.replaceFirst('the', 'a', 'the quick brown fox jumps over the lazy dog')).toEqual('a quick brown fox jumps over the lazy dog'); }); test('returns the subject when "search" string is not found', (): void => { expect(Str.replaceFirst('baz', 'bar', 'the quick brown fox jumps over the lazy dog')).toEqual('the quick brown fox jumps over the lazy dog'); }); test('returns the subject when "search" string is empty', (): void => { expect(Str.replaceFirst('', 'bar', 'the quick brown fox jumps over the lazy dog')).toEqual('the quick brown fox jumps over the lazy dog'); }); test('returns the subject when "search" string is not found', (): void => { expect(Str.replaceFirst('non existent', 'replacement', 'the quick brown fox jumps over the lazy dog')).toEqual('the quick brown fox jumps over the lazy dog'); }); }); describe('Str.replaceStart', (): void => { test('replaces the first occurrence of the given value only if it appears at the start of the string', (): void => { expect(Str.replaceStart('Hello', 'Laravel', 'Hello World')).toEqual('Laravel World'); expect(Str.replaceStart('World', 'Laravel', 'Hello World')).toEqual('Hello World'); }); test('returns the subject when "search" string is empty', (): void => { expect(Str.replaceStart('', 'Laravel', 'Hello World')).toEqual('Hello World'); }); test('replaces only at the start of the string', (): void => { expect(Str.replaceStart('Hello', 'Hi', 'Hello World, Hello Universe')).toEqual('Hi World, Hello Universe'); }); test('returns the subject when "search" string is not found', (): void => { expect(Str.replaceStart('Greetings', 'Hi', 'Hello World')).toEqual('Hello World'); }); test('handles empty subject string', (): void => { expect(Str.replaceStart('Hello', 'Hi', '')).toEqual(''); }); test('replaces when search and subject are the same', (): void => { expect(Str.replaceStart('Hello', 'Hi', 'Hello')).toEqual('Hi'); }); }); describe('Str.replaceLast', (): void => { test('replaces the last occurrence of the given value', (): void => { expect(Str.replaceLast('the', 'a', 'the quick brown fox jumps over the lazy dog')).toEqual('the quick brown fox jumps over a lazy dog'); }); test('returns the subject when "search" string is not found', (): void => { expect(Str.replaceLast('baz', 'bar', 'the quick brown fox jumps over the lazy dog')).toEqual('the quick brown fox jumps over the lazy dog'); }); test('returns the subject when "search" string is empty', (): void => { expect(Str.replaceLast('', 'bar', 'the quick brown fox jumps over the lazy dog')).toEqual('the quick brown fox jumps over the lazy dog'); }); test('replaces the only occurrence when it appears once', (): void => { expect(Str.replaceLast('quick', 'slow', 'the quick brown fox')).toEqual('the slow brown fox'); }); test('replaces the last occurrence when multiple exist', (): void => { expect(Str.replaceLast('the', 'a', 'the quick the brown fox')).toEqual('the quick a brown fox'); }); }); describe('Str.replaceEnd', (): void => { test('replaces the last occurrence of the given value only if it appears at the end of the string', (): void => { expect(Str.replaceEnd('World', 'Laravel', 'Hello World')).toEqual('Hello Laravel'); expect(Str.replaceEnd('Hello', 'Hi', 'Hello World')).toEqual('Hello World'); }); test('returns the subject when "search" string is empty', (): void => { expect(Str.replaceEnd('', 'Laravel', 'Hello World')).toEqual('Hello World'); }); test('replaces only at the end of the string', (): void => { expect(Str.replaceEnd('Universe', 'World', 'Hello Universe, Hello Universe')).toEqual('Hello Universe, Hello World'); }); test('returns the subject when "search" string is not found', (): void => { expect(Str.replaceEnd('Greetings', 'World', 'Hello Universe')).toEqual('Hello Universe'); }); test('handles empty subject string', (): void => { expect(Str.replaceEnd('Hello', 'Hi', '')).toEqual(''); }); test('replaces when search and subject are the same', (): void => { expect(Str.replaceEnd('Hello', 'Hi', 'Hello')).toEqual('Hi'); }); test('replaces only the ending match', (): void => { expect(Str.replaceEnd('end', 'finish', 'This is the end of the end')).toEqual('This is the end of the finish'); }); }); describe('Str.replaceMatches', (): void => { test('replaces all portions of a string matching a pattern with the given replacement string', (): void => { expect(Str.replaceMatches(/baz/, 'bar', 'foo bar baz')).toEqual('foo bar bar'); expect(Str.replaceMatches(/404/, 'found', 'foo bar baz')).toEqual('foo bar baz'); }); test('replaces all portions of a string matching a pattern with the given array of replacement strings', (): void => { expect(Str.replaceMatches([/bar/, /baz/], ['XXX', 'YYY'], 'foo bar baz')).toEqual('foo XXX YYY'); expect(Str.replaceMatches([/bar/, /baz/], ['XXX'], 'foo bar baz')).toEqual('foo XXX '); }); test('replaces all portions of a string matching a pattern with the given callback as a replacement', (): void => { expect(Str.replaceMatches(/\d/, (matches: string[]): string => `[${matches[0]}]`, '123')).toEqual('[1][2][3]'); expect(Str.replaceMatches(/ba(.)/, (matches: [string, string]): string => `ba${(matches[1]).toUpperCase()}`, 'foo baz bar')).toEqual('foo baZ baR'); }); test('limits the number of replacements when "limit" value is provided', (): void => { expect(Str.replaceMatches(/ba(.)/, 'bar', 'foo baz baz', 1)).toEqual('foo bar baz'); expect(Str.replaceMatches(/\d/, (matches: string[]): string => `[${matches[0]}]`, '123', 1)).toEqual('[1]23'); }); }); describe('Str.remove', (): void => { test('removes the given value from the string', (): void => { expect(Str.remove('e', 'Peter Piper picked a peck of pickled peppers.')).toEqual('Ptr Pipr pickd a pck of pickld ppprs.'); }); test('removes the given value from the string case-insensitively', (): void => { expect(Str.remove('E', 'Peter Piper picked a peck of pickled peppers.', false)).toEqual('Ptr Pipr pickd a pck of pickld ppprs.'); }); test('removes the given array of values from the string', (): void => { expect(Str.remove(['e', 'p'], 'Peter Piper picked a peck of pickled peppers.')).toEqual('Ptr Pir ickd a ck of ickld rs.'); }); test('removes the given array of values from the string case-insensitively', (): void => { expect(Str.remove(['E', 'P'], 'Peter Piper picked a peck of pickled peppers.', false)).toEqual('tr ir ickd a ck of ickld rs.'); }); test('removes a given array of strings within the given string or strings', (): void => { expect(Str.remove(['e', 'p'], 'Peter Piper picked a peck of pickled peppers.')).toEqual('Ptr Pir ickd a ck of ickld rs.'); expect(Str.remove(['e', 'p'], ['Peter', 'Piper'])).toEqual(['Ptr', 'Pier']); }); }); describe('Str.reverse', (): void => { test('reverses the given string', (): void => { expect(Str.reverse('Hello World')).toEqual('dlroW olleH'); }); }); describe('Str.start', (): void => { test('adds a single instance of the given value to a string if it does not already start with that value', (): void => { expect(Str.start('this/string', '/')).toEqual('/this/string'); expect(Str.start('/this/string', '/')).toEqual('/this/string'); }); }); describe('Str.upper', (): void => { test('converts the given string to uppercase', (): void => { expect(Str.upper('laravel')).toEqual('LARAVEL'); }); }); describe('Str.title', (): void => { test('converts the given string to Title Case', (): void => { expect(Str.title('a nice title uses the correct case')).toEqual('A Nice Title Uses The Correct Case'); }); }); describe('Str.headline', (): void => { test('converts strings delimited by casing, hyphens, or underscores into a space delimited string with each word’s first letter capitalized', (): void => { expect(Str.headline('steve_jobs')).toEqual('Steve Jobs'); expect(Str.headline('EmailNotificationSent')).toEqual('Email Notification Sent'); expect(Str.headline('too many spaces')).toEqual('Too Many Spaces'); }); }); describe('Str.apa', (): void => { test('converts the given string to title case following the APA guidelines', (): void => { expect(Str.apa('the great gatsby')).toBe('The Great Gatsby'); expect(Str.apa('the cat in the hat')).toBe('The Cat in the Hat'); expect(Str.apa('to be or not to be')).toBe('To Be or Not to Be'); expect(Str.apa('the mother-in-law')).toBe('The Mother-in-Law'); expect(Str.apa('')).toBe(''); expect(Str.apa('a')).toBe('A'); expect(Str.apa('the')).toBe('The'); expect(Str.apa('the end. the beginning')).toBe('The End. The Beginning'); expect(Str.apa('tHe QuiCk bRoWn fOx')).toBe('The Quick Brown Fox'); expect(Str.apa('the art of war')).toBe('The Art of War'); expect(Str.apa('a tale of two cities')).toBe('A Tale of Two Cities'); expect(Str.apa('the lord of the rings: the fellowship of the ring')).toBe('The Lord of the Rings: The Fellowship of the Ring'); }); }); describe('Str.singular', (): void => { test('converts plural to singular for regular nouns', (): void => { expect(Str.singular('cars')).toBe('car'); expect(Str.singular('books')).toBe('book'); expect(Str.singular('apples')).toBe('apple'); }); test('handles irregular nouns correctly', (): void => { // A expect(Str.singular('alumnae')).toBe('alumna'); expect(Str.singular('analyses')).toBe('analysis'); expect(Str.singular('axes')).toBe('axis'); // B-C expect(Str.singular('bacteria')).toBe('bacterium'); expect(Str.singular('children')).toBe('child'); expect(Str.singular('crises')).toBe('crisis'); // D-F expect(Str.singular('demos')).toBe('demo'); expect(Str.singular('feet')).toBe('foot'); expect(Str.singular('fungi')).toBe('fungus'); // G-M expect(Str.singular('geese')).toBe('goose'); expect(Str.singular('men')).toBe('man'); expect(Str.singular('mice')).toBe('mouse'); // N-S expect(Str.singular('nuclei')).toBe('nucleus'); expect(Str.singular('people')).toBe('person'); expect(Str.singular('theses')).toBe('thesis'); // T-Z expect(Str.singular('teeth')).toBe('tooth'); expect(Str.singular('wives')).toBe('wife'); expect(Str.singular('zombies')).toBe('zombie'); }); test('handles uncountable nouns correctly', (): void => { expect(Str.singular('sheep')).toBe('sheep'); expect(Str.singular('fish')).toBe('fish'); expect(Str.singular('series')).toBe('series'); expect(Str.singular('money')).toBe('money'); expect(Str.singular('information')).toBe('information'); }); test('handles special singularization rules', (): void => { // -ves → -f/-fe expect(Str.singular('leaves')).toBe('leaf'); expect(Str.singular('knives')).toBe('knife'); // -ies → -y expect(Str.singular('cities')).toBe('city'); expect(Str.singular('babies')).toBe('baby'); // -oes → -o expect(Str.singular('potatoes')).toBe('potato'); expect(Str.singular('volcanoes')).toBe('volcano'); // -i → -us expect(Str.singular('cacti')).toBe('cactus'); expect(Str.singular('foci')).toBe('focus'); // -es → -is expect(Str.singular('analyses')).toBe('analysis'); expect(Str.singular('bases')).toBe('basis'); // -ices → -ix/ex expect(Str.singular('matrices')).toBe('matrix'); expect(Str.singular('indices')).toBe('index'); }); test('handles compound words and special cases', (): void => { expect(Str.singular('passersby')).toBe('passerby'); expect(Str.singular('runners-up')).toBe('runner-up'); }); test('handles words with multiple singular forms', (): void => { expect(Str.singular('octopuses')).toBe('octopus'); expect(Str.singular('hoofs')).toBe('hoof'); }); test('preserves case sensitivity', (): void => { expect(Str.singular('Heroes')).toBe('Hero'); expect(Str.singular('CHILDREN')).toBe('CHILD'); expect(Str.singular('Analyses')).toBe('Analysis'); }); test('handles edge cases', (): void => { expect(Str.singular('')).toBe(''); expect(Str.singular(' ')).toBe(' '); expect(Str.singular('123')).toBe('123'); }); }); describe('Str.slug', (): void => { test('generates a URL friendly "slug" from the given string', (): void => { expect(Str.slug('Laravel 5 Framework', '-')).toEqual('laravel-5-framework'); }); }); describe('Str.snake', (): void => { test('converts the given string to snake_case', (): void => { expect(Str.snake('fooBar')).toEqual('foo_bar'); expect(Str.snake('fooBar', '-')).toEqual('foo-bar'); }); }); describe('Str.trim', (): void => { test('removes all whitespace from both ends of a string', (): void => { expect(Str.trim(' Laravel ')).toEqual('Laravel'); expect(Str.trim('Laravel ')).toEqual('Laravel'); expect(Str.trim(' Laravel')).toEqual('Laravel'); expect(Str.trim('Laravel')).toEqual('Laravel'); }); test('removes all whitespace from both ends of a string with specified characters', (): void => { expect(Str.trim(' Laravel ', '')).toEqual(' Laravel '); expect(Str.trim(' Laravel ', ' ')).toEqual('Laravel'); expect(Str.trim('-Laravel Framework_', '-_')).toEqual('Laravel Framework'); }); }); describe('Str.ltrim', (): void => { test('removes all whitespace from the beginning of a string', (): void => { expect(Str.ltrim(' Laravel ')).toEqual('Laravel '); expect(Str.ltrim('Laravel ')).toEqual('Laravel '); expect(Str.ltrim(' Laravel')).toEqual('Laravel'); expect(Str.ltrim('Laravel')).toEqual('Laravel'); }); test('removes all whitespace from the beginning of a string with specified characters', (): void => { expect(Str.ltrim(' Laravel ', '')).toEqual(' Laravel '); expect(Str.ltrim(' Laravel ', ' ')).toEqual('Laravel '); expect(Str.ltrim('-Laravel Framework_', '-_')).toEqual('Laravel Framework_'); }); }); describe('Str.rtrim', (): void => { test('removes all whitespace from the end of a string', (): void => { expect(Str.rtrim(' Laravel ')).toEqual(' Laravel'); expect(Str.rtrim('Laravel ')).toEqual('Laravel'); expect(Str.rtrim(' Laravel')).toEqual(' Laravel'); expect(Str.rtrim('Laravel')).toEqual('Laravel'); }); test('removes all whitespace from the end of a string with specified characters', (): void => { expect(Str.rtrim(' Laravel ', '')).toEqual(' Laravel '); expect(Str.rtrim(' Laravel ', ' ')).toEqual(' Laravel'); expect(Str.rtrim('-Laravel Framework_', '-_')).toEqual('-Laravel Framework'); }); }); describe('Str.squish', (): void => { test('removes all extraneous white space from a string', (): void => { expect(Str.squish(' laravel framework ')).toEqual('laravel framework'); }); }); describe('Str.startsWith', (): void => { test('determines if the given string begins with the given value', (): void => { expect(Str.startsWith('This is my name', 'This')).toBeTruthy(); }); test('determines if the given string begins with any of the values in the array', (): void => { expect(Str.startsWith('This is my name', ['This', 'That', 'There'])).toBeTruthy(); }); test('returns false if the given string does not start with the given value', (): void => { expect(Str.startsWith('This is my name', 'There')).toBeFalsy(); }); test('returns false if the given string does not start with any of the values in the array', (): void => { expect(Str.startsWith('This is my name', ['That', 'There'])).toBeFalsy(); }); }); describe('Str.doesntStartWith', (): void => { test('determines if the given string does not start with the given value', (): void => { expect(Str.doesntStartWith('This is my name', 'There')).toBeTruthy(); }); test('determines if the given string does not start with any of the values in the array', (): void => { expect(Str.doesntStartWith('This is my name', ['There', 'foo'])).toBeTruthy(); }); test('returns false if the given string starts with the given value', (): void => { expect(Str.doesntStartWith('This is my name', 'This')).toBeFalsy(); }); test('returns false if the string ends starts the given value', (): void => { expect(Str.doesntStartWith('This is my name', ['This', 'foo'])).toBeFalsy(); }); }); describe('Str.studly', (): void => { test('converts the given string to studly caps case', (): void => { expect(Str.studly('foo_bar')).toEqual('FooBar'); }); }); describe('Str.pascal', (): void => { test('converts the given string to Pascal case', (): void => { expect(Str.pascal('foo_bar')).toEqual('FooBar'); }); }); describe('Str.substr', (): void => { test('returns the portion of string specified by the start and length values', (): void => { expect(Str.substr('The Laravel Framework', 4, 7)).toEqual('Laravel'); }); }); describe('Str.substrCount', (): void => { test('returns the number of occurrences of a given value in the given string', (): void => { expect(Str.substrCount('If you like ice cream, you will like snow cones.', 'like')).toEqual(2); }); test('respects the "offset" and "length" values when counting occurrences', (): void => { expect(Str.substrCount('hello hello hello', 'hello', 6)).toEqual(2); expect(Str.substrCount('hello hello hello', 'hello', 0, 5)).toEqual(1); expect(Str.substrCount('hello hello hello', 'hello', 6, 6)).toEqual(1); }); }); describe('Str.substrReplace', (): void => { test('replaces text within a portion of a string', (): void => { expect(Str.substrReplace('1300', ':', 2)).toEqual('13:'); expect(Str.substrReplace('1300', ':', 2, 0)).toEqual('13:00'); }); }); describe('Str.swap', (): void => { test('replaces multiple values in the given string', (): void => { expect(Str.swap({ 'Tacos': 'Burritos', 'great': 'fantastic' }, 'Tacos are great!')).toEqual('Burritos are fantastic!'); }); }); describe('Str.take', (): void => { test('takes the first n characters when limit is positive', (): void => { expect(Str.take('Hello, world!', 5)).toEqual('Hello'); }); test('takes the last n characters when limit is negative', (): void => { expect(Str.take('Hello, world!', -5)).toEqual('orld!'); }); test('returns an empty string when limit is zero', (): void => { expect(Str.take('Hello, world!', 0)).toEqual(''); }); test('returns the entire string when limit is greater than string length', (): void => { expect(Str.take('Hello', 10)).toEqual('Hello'); }); test('returns the entire string when limit is less than negative string length', (): void => { expect(Str.take('Hello', -10)).toEqual('Hello'); }); }); describe('Str.toBase64', (): void => { test('converts the given string to Base64', (): void => { expect(Str.toBase64('Laravel')).toEqual('TGFyYXZlbA=='); }); }); describe('Str.fromBase64', (): void => { test('converts the given string from Base64', (): void => { expect(Str.fromBase64('TGFyYXZlbA==')).toEqual('Laravel'); }); }); describe('Str.lcfirst', (): void => { test('returns the given string with the first character lowercased', (): void => { expect(Str.lcfirst('Foo Bar')).toEqual('foo Bar'); }); }); describe('Str.ucwords', (): void => { test('returns the given string with the first character of each word capitalized', (): void => { expect(Str.ucwords('laravel')).toEqual('Laravel'); expect(Str.ucwords('laravel framework')).toEqual('Laravel Framework'); expect(Str.ucwords('мама')).toEqual('Мама'); expect(Str.ucwords('мама мыла раму')).toEqual('Мама Мыла Раму'); }); test('handles custom separators when capitalizing words', (): void => { expect(Str.ucwords('laravel-framework', '-')).toEqual('Laravel-Framework'); }); }); describe('Str.ucfirst', (): void => { test('returns the given string with the first character capitalized', (): void => { expect(Str.ucfirst('foo bar')).toEqual('Foo bar'); }); }); describe('Str.ucsplit', (): void => { test('splits the given string into an array by uppercase characters', (): void => { expect(Str.ucsplit('FooBar')).toEqual(['Foo', 'Bar']); }); }); describe('Str.wordCount', (): void => { test('returns the number of words that a string contains', (): void => { expect(Str.wordCount('Hello, world!')).toEqual(2); }); }); describe('Str.wordWrap', (): void => { test('wraps a string to a given number of characters', (): void => { expect(Str.wordWrap('The quick brown fox jumped over the lazy dog.', 20, '
\n')).toEqual('The quick brown fox
\njumped over the lazy
\ndog.'); }); }); describe('Str.uuid', (): void => { test('generates a UUID (version 4)', (): void => { expect(Str.uuid()).toMatch(/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/); }); }); describe('Str.uuid7', (): void => { test('generates a UUID (version 7)', (): void => { expect(Str.uuid7()).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); }); test('uses current time when no timestamp provided', (): void => { const before: number = Date.now(); const uuid: string = Str.uuid7(); const after: Number = Date.now(); const time: number = parseInt(uuid.slice(0, 8) + uuid.slice(9, 13), 16); expect(time).toBeGreaterThanOrEqual(before); expect(time).toBeLessThanOrEqual(after as number); }); test('accepts custom timestamp', (): void => { expect(Str.uuid7(new Date('2023-01-01T00:00:00Z')).startsWith('01856aa0-c800')).toBeTruthy(); }); test('handles minimum timestamp correctly', (): void => { expect(Str.uuid7(new Date(0)).startsWith('00000000-0000-7')).toBeTruthy(); }); test('handles maximum 48-bit timestamp correctly', (): void => { expect(Str.uuid7(new Date(281474976710655)).startsWith('ffffffff-ffff-7')).toBeTruthy(); }); test('throws error for invalid timestamps', (): void => { expect((): string => Str.uuid7(new Date(-1))).toThrow(RangeError); expect((): string => Str.uuid7(new Date(281474976710655 + 1))).toThrow(RangeError); }); test('contains correct version and variant bits', (): void => { const parts: string[] = Str.uuid7().split('-'); expect((parts[2] as string)[0]).toBe('7'); expect(['8', '9', 'a', 'b']).toContain((parts[3] as string)[0]); }); test('has correct byte structure', (): void => { const uuid: string = Str.uuid7(); const bytes: string = uuid.replace(/-/g, ''); const byte6: number = parseInt(bytes.substring(12, 14), 16); const byte8: number = parseInt(bytes.substring(16, 18), 16); expect((byte6 & 0xf0) >> 4).toBe(7); expect((byte8 & 0xc0) >> 6).toBe(2); }); test('generates unique values', (): void => { const uuids = new Set(); const count = 1000; for (let i: number = 0; i < count; i++) { uuids.add(Str.uuid7()); } expect(uuids.size).toBe(count); }); test('uses the custom UUID factory when set', (): void => { const uuid: string = '123e4567-e89b-12d3-a456-426614174000'; Str.createUuidsUsing((): string => uuid); expect(Str.uuid7()).toBe(uuid); expect(Str.uuid7(new Date())).toBe(uuid); }); }); describe('Str.orderedUuid', (): void => { test('generates a "timestamp first" UUID', (): void => { expect(Str.orderedUuid()).toMatch(/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/); }); test('uses the custom UUID factory when set', (): void => { const uuid: string = '123e4567-e89b-12d3-a456-426614174000'; Str.createUuidsUsing((): string => uuid); expect(Str.orderedUuid()).toBe(uuid); }); }); describe('Str.createUuidsUsing', (): void => { test('creates UUIDs using the specified callback', (): void => { const uuid: string = 'e7d145db-1f4b-40a9-9684-5e7ad7673494'; Str.createUuidsUsing((): string => uuid); expect(Str.uuid()).toBe(uuid); expect(Str.uuid()).toBe(uuid); Str.createUuidsNormally(); const first: string = Str.uuid(); const second: string = Str.uuid(); expect(first).not.toBe(uuid); expect(second).not.toBe(uuid); expect(first).not.toBe(second); }); }); describe('Str.createUuidsUsingSequence', (): void => { test('returns UUIDs from the sequence in order', (): void => { const first: string = Str.uuid(); const second: string = Str.uuid(); const third: string = 'third'; Str.createUuidsUsingSequence([first, undefined, second, third]); expect(Str.uuid()).toBe(first); expect(Str.uuid()).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i); expect(Str.uuid()).toBe(second); expect(Str.uuid()).toBe(third); expect(Str.uuid()).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i); Str.createUuidsUsingSequence([]); }); test('uses default fallback when none provided', (): void => { const uuid: string = Str.uuid(); Str.createUuidsUsingSequence([uuid], (): never => { throw new Error('Out of UUIDs.'); }); expect(Str.uuid()).toBe(uuid); expect((): string => Str.uuid()).toThrow('Out of UUIDs.'); }); }); describe('Str.freezeUuids', (): void => { test('returns the same UUID for all calls', (): void => { const frozen: string = Str.freezeUuids(); try { expect(Str.uuid()).toBe(frozen); expect(Str.uuid()).toBe(frozen); } finally { Str.createUuidsNormally(); } }); test('returns the same UUID for all calls within the callback', (): void => { const frozen: string = Str.freezeUuids((uuid: string): void => { expect(Str.uuid()).toBe(uuid); expect(Str.uuid()).toBe(uuid); }); expect(Str.uuid()).not.toBe(frozen); }); }); describe('Str.createUuidsNormally', (): void => { test('resets UUID generation to default behavior', (): void => { const uuid: string = 'e7d145db-1f4b-40a9-9684-5e7ad7673494'; Str.createUuidsUsing((): string => uuid); expect(Str.uuid()).toBe(uuid); Str.createUuidsNormally(); const first: string = Str.uuid(); const second: string = Str.uuid(); expect(first).not.toBe(uuid); expect(second).not.toBe(uuid); expect(first).not.toBe(second); expect(first).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i); }); }); describe('Str.ulid', (): void => { test('generates a ULID', (): void => { expect(Str.ulid()).toMatch(/[0-9A-Z]{26}/); }); }); describe('Str.createUlidsUsing', (): void => { test('creates ULIDs using the specified callback', (): void => { const ulid: string = '01K5FBP7ZJ1W659T73KNH9XK46'; Str.createUlidsUsing((): string => ulid); expect(Str.ulid()).toBe(ulid); expect(Str.ulid()).toBe(ulid); Str.createUlidsNormally(); const first: string = Str.ulid(); const second: string = Str.ulid(); expect(first).not.toBe(ulid); expect(second).not.toBe(ulid); expect(first).not.toBe(second); }); }); describe('Str.createUlidsUsingSequence', (): void => { test('returns ULIDs from the sequence in order', (): void => { const first: string = '01K5FBP7ZJ1W659T73KNH9XK46'; const second: string = '01H5KK0ZQZ9Z9Z9Z9Z9Z9Z9Z9'; const third: string = 'third'; Str.createUlidsUsingSequence([first, undefined, second, third]); expect(Str.ulid()).toBe(first); expect(Str.ulid()).toMatch(/^[0-9A-Z]{26}$/); expect(Str.ulid()).toBe(second); expect(Str.ulid()).toBe(third); expect(Str.ulid()).toMatch(/^[0-9A-Z]{26}$/); Str.createUlidsUsingSequence([]); }); test('uses default fallback when none provided', (): void => { const ulid: string = '01K5FBP7ZJ1W659T73KNH9XK46'; Str.createUlidsUsingSequence([ulid], (): never => { throw new Error('Out of ULIDs.'); }); expect(Str.ulid()).toBe(ulid); expect((): string => Str.ulid()).toThrow('Out of ULIDs.'); }); }); describe('Str.freezeUlids', (): void => { test('returns the same ULID for all calls', (): void => { const frozen: string = Str.freezeUlids(); try { expect(Str.ulid()).toBe(frozen); expect(Str.ulid()).toBe(frozen); } finally { Str.createUlidsNormally(); } }); test('returns the same ULID for all calls within the callback', (): void => { const frozen: string = Str.freezeUlids((ulid: string): void => { expect(Str.ulid()).toBe(ulid); expect(Str.ulid()).toBe(ulid); }); expect(Str.ulid()).not.toBe(frozen); }); }); describe('Str.createUlidsNormally', (): void => { test('resets ULID generation to default behavior', (): void => { const ulid: string = '01K5FBP7ZJ1W659T73KNH9XK46'; Str.createUlidsUsing((): string => ulid); expect(Str.ulid()).toBe(ulid); Str.createUlidsNormally(); const first: string = Str.ulid(); const second: string = Str.ulid(); expect(first).not.toBe(ulid); expect(second).not.toBe(ulid); expect(first).not.toBe(second); expect(first).toMatch(/^[0-9A-Z]{26}$/); }); }); describe('Str.toStringOr', (): void => { test('converts values to strings', (): void => { // @ts-ignore expect(Str.toStringOr(123, 'fallback')).toBe('123'); // @ts-ignore expect(Str.toStringOr(true, 'fallback')).toBe('true'); // @ts-ignore expect(Str.toStringOr('test', 'fallback')).toBe('test'); }); test('returns fallback for null, undefined, objects and functions', (): void => { // @ts-ignore expect(Str.toStringOr(null, 'fallback')).toBe('fallback'); // @ts-ignore expect(Str.toStringOr(undefined, 'fallback')).toBe('fallback'); // @ts-ignore expect(Str.toStringOr((): string => 'test', 'fallback')).toBe('fallback'); // @ts-ignore expect(Str.toStringOr({}, 'fallback')).toBe('fallback'); // @ts-ignore expect(Str.toStringOr([1, 2, 3], 'fallback')).toBe('fallback'); }); }); }); describe('Stringable', (): void => { describe('after', (): void => { test('returns the subject if the "search" value is an empty string', (): void => { expect(Str.of('This is my name').after('').toString()).toEqual('This is my name'); }); test('returns everything after the given value in a string', (): void => { expect(Str.of('This is my name').after('This is').toString()).toEqual(' my name'); }); }); describe('afterLast', (): void => { test('returns the subject if the "search" value is an empty string', (): void => { expect(Str.of('App\\Http\\Controllers\\Controller').afterLast('').toString()).toEqual('App\\Http\\Controllers\\Controller'); }); test('returns the entire string if the "search" value is not found', (): void => { expect(Str.of('App\\Http\\Controllers\\Controller').afterLast('!').toString()).toEqual('App\\Http\\Controllers\\Controller'); }); test('returns everything after the last occurrence of the given value in a string', (): void => { expect(Str.of('App\\Http\\Controllers\\Controller').afterLast('\\').toString()).toEqual('Controller'); }); }); describe('append', (): void => { test('appends the given values to the string', (): void => { expect(Str.of('Taylor').append(' Otwell').toString()).toEqual('Taylor Otwell'); }); }); describe('newLine', (): void => { test('appends an "end of line" character to a string', (): void => { expect(Str.of('Laravel').newLine().append('Framework').toString()).toEqual('Laravel\nFramework'); }); }); describe('ascii', (): void => { test('transliterates accented characters to ASCII equivalents', (): void => { expect(Str.of('ü').ascii().toString()).toEqual('u'); expect(Str.of('é').ascii().toString()).toEqual('e'); expect(Str.of('ñ').ascii().toString()).toEqual('n'); expect(Str.of('ç').ascii().toString()).toEqual('c'); expect(Str.of('å').ascii().toString()).toEqual('a'); }); test('removes diacritical marks', (): void => { expect(Str.of('c\u0327').ascii().toString()).toEqual('c'); expect(Str.of('e\u0301').ascii().toString()).toEqual('e'); expect(Str.of('a\u0308').ascii().toString()).toEqual('a'); }); test('handles strings with only non-alphanumeric characters', (): void => { expect(Str.of('!@#$%^&*()_+-=').ascii().toString()).toEqual('!@#$%^&*()_+-='); expect(Str.of(' ').ascii().toString()).toEqual(' '); }); test('preserves case for ASCII letters', (): void => { expect(Str.of('HelloWorld').ascii().toString()).toEqual('HelloWorld'); expect(Str.of('HELLOworld').ascii().toString()).toEqual('HELLOworld'); }); test('handles mixed input', (): void => { expect(Str.of('Héllö Wörld! 123').ascii().toString()).toEqual('Hello World! 123'); expect(Str.of('Café au lait').ascii().toString()).toEqual('Cafe au lait'); expect(Str.of('Mëtàl Hëàd').ascii().toString()).toEqual('Metal Head'); }); }); describe('basename', (): void => { test('returns the trailing name component of the given string', (): void => { expect(Str.of('/foo/bar/baz').basename().toString()).toEqual('baz'); expect(Str.of('/foo/bar/baz.jpg').basename('.jpg').toString()).toEqual('baz'); }); }); describe('charAt', (): void => { test('returns the character at the specified index', (): void => { expect(Str.of('This is my name.').charAt(6).toString()).toEqual('s'); }); }); describe('chopStart', (): void => { test('removes the given string if it exists at the start of the subject', (): void => { expect(Str.of('Hello, world!').chopStart('Hello, ').toString()).toEqual('world!'); }); test('removes the first matching string from an array of needles', (): void => { expect(Str.of('Hello, world!').chopStart(['Hello, ', 'Hi, ']).toString()).toEqual('world!'); }); test('removes only the first matching string when both are found at the start', (): void => { expect(Str.of('Hello, Hello, world!').chopStart(['Hello, ', 'Hello, ']).toString()).toEqual('Hello, world!'); }); test('does not remove the string if it does not exist at the start of the subject', (): void => { expect(Str.of('Hello, world!').chopStart('world').toString()).toEqual('Hello, world!'); }); }); describe('chopEnd', (): void => { test('removes the given string if it exists at the end of the subject', (): void => { expect(Str.of('Hello, world!').chopEnd('world!').toString()).toEqual('Hello, '); }); test('removes the first matching string from an array of needles', (): void => { expect(Str.of('Hello, world!').chopEnd(['world!', 'planet!']).toString()).toEqual('Hello, '); }); test('removes only the first matching string when both are found at the end', (): void => { expect(Str.of('Hello, world!world').chopEnd(['world', 'world!']).toString()).toEqual('Hello, world!'); }); test('does not remove the string if it does not exist at the end of the subject', (): void => { expect(Str.of('Hello, world!').chopEnd('Hello').toString()).toEqual('Hello, world!'); }); }); describe('classBasename', (): void => { test('returns the class name of the given class with the class\'s namespace removed', (): void => { expect(Str.of('Foo\\Bar\\Baz').classBasename().toString()).toEqual('Baz'); }); }); describe('before', (): void => { test('returns the subject if the "search" value is an empty string', (): void => { expect(Str.of('This is my name').before('').toString()).toEqual('This is my name'); }); test('returns the entire string if the "search" value is not found', (): void => { expect(Str.of('This is my name').before('your').toString()).toEqual('This is my name'); }); test('returns everything before the given value in a string', (): void => { expect(Str.of('This is my name').before('my name').toString()).toEqual('This is '); }); }); describe('beforeLast', (): void => { test('returns the subject if the "search" value is an empty string', (): void => { expect(Str.of('This is my name').beforeLast('').toString()).toEqual('This is my name'); }); test('returns the entire string if the "search" value is not found', (): void => { expect(Str.of('This is my name').beforeLast('your').toString()).toEqual('This is my name'); }); test('returns everything before the last occurrence of the given value in a string', (): void => { expect(Str.of('This is my name').beforeLast('is').toString()).toEqual('This '); }); }); describe('between', (): void => { test('returns the subject if the "from" and "to" values are an empty string', (): void => { expect(Str.of('This is my name').between('', '').toString()).toEqual('This is my name'); }); test('returns the portion of a string between two values', (): void => { expect(Str.of('This is my name').between('This', 'name').toString()).toEqual(' is my '); }); }); describe('betweenFirst', (): void => { test('returns the subject if the "from" and "to" values are an empty string', (): void => { expect(Str.of('This is my name').betweenFirst('', '').toString()).toEqual('This is my name'); }); test('returns the smallest possible portion of a string between two values', (): void => { expect(Str.of('[a] bc [d]').betweenFirst('[', ']').toString()).toEqual('a'); }); }); describe('camel', (): void => { test('converts the given string to camelCase', (): void => { expect(Str.of('foo_bar').camel().toString()).toEqual('fooBar'); }); }); describe('contains', (): void => { test('determines if the given string contains the given value', (): void => { expect(Str.of('This is my name').contains('my')).toBeTruthy(); }); test('determines if the given string contains any of the values in the array', (): void => { expect(Str.of('This is my name').contains(['my', 'foo'])).toBeTruthy(); }); test('determines if the given string contains the given value case-insensitively', (): void => { expect(Str.of('This is my name').contains('MY', true)).toBeTruthy(); }); }); describe('containsAll', (): void => { test('determines if the given string contains all the values in a given array', (): void => { expect(Str.of('This is my name').containsAll(['my', 'name'])).toBeTruthy(); }); test('determines if the given string contains all the values in a given array case-insensitively', (): void => { expect(Str.of('This is my name').containsAll(['my', 'name'])).toBeTruthy(); }); }); describe('doesntContain', (): void => { test('determines if the given string doesnt contain the given value', (): void => { expect(Str.of('This is name').doesntContain('my')).toBeTruthy(); }); test('determines if the given string doesnt contain any of the values in the array', (): void => { expect(Str.of('This is name').doesntContain(['my', 'foo'])).toBeTruthy(); }); test('determines if the given string doesnt contain the given value case-insensitively', (): void => { expect(Str.of('This is name').doesntContain('MY', true)).toBeTruthy(); }); }); describe('convertCase', (): void => { test('converts the case of a string', (): void => { expect(Str.of('HeLLo').convertCase().toString()).toEqual('hello'); expect(Str.of('hello').convertCase(Mode.MB_CASE_UPPER).toString()).toEqual('HELLO'); expect(Str.of('WORLD').convertCase(Mode.MB_CASE_LOWER).toString()).toEqual('world'); expect(Str.of('hello world').convertCase(Mode.MB_CASE_TITLE).toString()).toEqual('Hello World'); expect(Str.of('HeLLo').convertCase(Mode.MB_CASE_FOLD).toString()).toEqual('hello'); expect(Str.of('hello').convertCase(Mode.MB_CASE_UPPER_SIMPLE).toString()).toEqual('HELLO'); expect(Str.of('HELLO').convertCase(Mode.MB_CASE_LOWER_SIMPLE).toString()).toEqual('hello'); expect(Str.of('hello world').convertCase(Mode.MB_CASE_TITLE_SIMPLE).toString()).toEqual('Hello World'); expect(Str.of('HeLLo').convertCase(Mode.MB_CASE_FOLD_SIMPLE).toString()).toEqual('hello'); expect(Str.of('üöä').convertCase(Mode.MB_CASE_UPPER).toString()).toEqual('ÜÖÄ'); expect(Str.of('ÜÖÄ').convertCase(Mode.MB_CASE_LOWER).toString()).toEqual('üöä'); }); test('handles empty string and whitespace', (): void => { expect(Str.of('').convertCase(Mode.MB_CASE_UPPER).toString()).toEqual(''); expect(Str.of(' ').convertCase(Mode.MB_CASE_LOWER).toString()).toEqual(' '); expect(Str.of(' test ').convertCase(Mode.MB_CASE_TITLE).toString()).toEqual(' Test '); }); test('throws error for invalid mode', (): void => { // @ts-expect-error expect((): string => Str.of('test').convertCase(-1)).toThrow('Argument #2 (mode) must be one of the Mode.MB_CASE_* constants'); // @ts-expect-error expect((): string => Str.of('test').convertCase(-1 as Mode)).toThrow('Argument #2 (mode) must be one of the Mode.MB_CASE_* constants'); }); }); describe('deduplicate', (): void => { test('replace consecutive instances of a given character with a single character in the given string', (): void => { expect(Str.of(' laravel php framework ').deduplicate().toString()).toEqual(' laravel php framework '); expect(Str.of('whaaat').deduplicate('a').toString()).toEqual('what'); expect(Str.of('/some//odd//path/').deduplicate('/').toString()).toEqual('/some/odd/path/'); }); test('replace consecutive instances of multiple characters when given an array', (): void => { expect(Str.of('a--b++c**d').deduplicate(['-', '+', '*']).toString()).toEqual('a-b+c*d'); expect(Str.of(' hello !!world?? ').deduplicate([' ', '!', '?']).toString()).toEqual(' hello !world? '); expect(Str.of('mixed...spaces and---dashes').deduplicate(['.', ' ', '-']).toString()).toEqual('mixed.spaces and-dashes'); }); }); describe('dirname', (): void => { test('returns the parent directory portion of the given string', (): void => { expect(Str.of('/foo/bar/baz').dirname().toString()).toEqual('/foo/bar'); expect(Str.of('/foo/bar/baz').dirname(2).toString()).toEqual('/foo'); }); test('handles Windows-style paths', (): void => { expect(Str.of('C:\\foo\\bar\\baz').dirname().toString()).toEqual('C:\\foo\\bar'); expect(Str.of('C:\\foo\\bar\\baz').dirname(2).toString()).toEqual('C:\\foo'); }); test('returns current directory for single-level paths', (): void => { expect(Str.of('file.txt').dirname().toString()).toEqual('.'); expect(Str.of('C:file.txt').dirname().toString()).toEqual('.'); }); test('handles root directory', (): void => { expect(Str.of('/file.txt').dirname().toString()).toEqual('/'); expect(Str.of('C:\\file.txt').dirname().toString()).toEqual('C:\\'); }); test('handles multiple levels up', (): void => { expect(Str.of('/a/b/c/d').dirname(2).toString()).toEqual('/a/b'); expect(Str.of('C:\\a\\b\\c\\d').dirname(3).toString()).toEqual('C:\\a'); }); test('returns root when levels exceed path depth', (): void => { expect(Str.of('/a/b').dirname(5).toString()).toEqual('/'); expect(Str.of('C:\\a\\b').dirname(5).toString()).toEqual('C:\\'); }); test('handles empty string', (): void => { expect(Str.of('').dirname().toString()).toEqual('.'); }); test('handles edge cases', (): void => { expect(Str.of('a').dirname().toString()).toEqual('.'); expect(Str.of('//').dirname().toString()).toEqual('/'); expect(Str.of('C:/foo\\bar/baz').dirname().toString()).toEqual('C:/foo\\bar'); }); }); describe('endsWith', (): void => { test('determines if the given string ends with the given value', (): void => { expect(Str.of('This is my name').endsWith('name')).toBeTruthy(); }); test('determines if the given string ends with any of the values in the array', (): void => { expect(Str.of('This is my name').endsWith(['name', 'foo'])).toBeTruthy(); }); test('returns false if the given string does not end with the given value', (): void => { expect(Str.of('This is my name').endsWith('names')).toBeFalsy(); }); test('returns false if the given string does not end with any of the values in the array', (): void => { expect(Str.of('This is my name').endsWith(['this', 'foo'])).toBeFalsy(); }); }); describe('doesntEndWith', (): void => { test('determines if the given string does not end with the given value', (): void => { expect(Str.of('This is my name').doesntEndWith('names')).toBeTruthy(); }); test('determines if the given string does not end with any of the values in the array', (): void => { expect(Str.of('This is my name').doesntEndWith(['names', 'foo'])).toBeTruthy(); }); test('returns false if the given string ends with the given value', (): void => { expect(Str.of('This is my name').doesntEndWith('name')).toBeFalsy(); }); test('returns false if the string ends with the given value', (): void => { expect(Str.of('This is my name').doesntEndWith(['name', 'foo'])).toBeFalsy(); }); }); describe('exactly', (): void => { test('determines if the given string is an exact match with another string', (): void => { expect(Str.of('Laravel').exactly('Laravel')).toBeTruthy(); expect(Str.of('Laravel').exactly('Laravel ')).toBeFalsy(); }); test('determines if the given string is an exact match with another string as Stringable', (): void => { expect(Str.of('Laravel').exactly(Str.of('Laravel'))).toBeTruthy(); expect(Str.of('Laravel').exactly(Str.of('Laravel '))).toBeFalsy(); }); }); describe('excerpt', (): void => { test('extracts an excerpt from a given string that matches the first instance of a phrase', (): void => { expect(Str.of('This is my name').excerpt('my', { 'radius': 3 })).toEqual('...is my na...'); }); test('allows definition of custom omission strings', (): void => { expect(Str.of('This is my name').excerpt('name', { 'radius': 3, 'omission': '(...) ' })).toEqual('(...) my name'); }); test('returns null when phrase is not found', (): void => { expect(Str.of('This is my name').excerpt('notfound')).toBeNull(); }); test('handles empty string as phrase', (): void => { expect(Str.of('This is my name').excerpt('')).toEqual('This is my name'); }); test('handles phrase at the start of string', (): void => { expect(Str.of('Hello world, this is a test').excerpt('Hello', { radius: 5 })).toEqual('Hello worl...'); }); test('handles phrase at the end of string', (): void => { expect(Str.of('This is the end of the test').excerpt('test', { radius: 5 })).toEqual('...the test'); }); test('handles radius larger than text length', (): void => { expect(Str.of('Short text').excerpt('text', { radius: 100 })).toEqual('Short text'); }); test('handles zero radius', (): void => { expect(Str.of('This is a test').excerpt('is', { radius: 0 })).toEqual('...is...'); }); test('handles custom omission with empty string', (): void => { expect(Str.of('This is a test').excerpt('is', { radius: 1, omission: '' })).toEqual('his'); }); test('handles multiple occurrences of phrase', (): void => { expect(Str.of('This is a test and this is another test').excerpt('test', { radius: 5 })).toEqual('...is a test and...'); }); test('handles whitespace in phrase', (): void => { expect(Str.of('This is a test with spaces').excerpt('a test', { radius: 3 })).toEqual('...is a test wi...'); }); }); describe('explode', (): void => { test('splits the string by the given delimiter and returns an array containing each section of the split string', (): void => { expect(Str.of('foo bar baz').explode(' ')).toEqual(['foo', 'bar', 'baz']); }); test('limits the number of splits when "limit" value is provided', (): void => { expect(Str.of('foo bar baz').explode(' ', 2)).toEqual(['foo', 'bar baz']); }); test('handles limit greater than array length', (): void => { expect(Str.of('foo bar baz').explode(' ', 10)).toEqual(['foo', 'bar', 'baz']); }); test('handles limit equal to array length', (): void => { expect(Str.of('foo bar baz').explode(' ', 3)).toEqual(['foo', 'bar', 'baz']); }); }); describe('split', (): void => { test('splits a string into an array using a regular expression', (): void => { expect(Str.of('one, two, three').split(/[\s,]+/)).toEqual(['one', 'two', 'three']); }); test('splits a string into an array using a number', (): void => { expect(Str.of('foobarbaz').split(3)).toEqual(['foo', 'bar', 'baz']); }); test('handles the "limit" value correctly', (): void => { expect(Str.of('one two three four').split(/ /, 2)).toEqual(['one', 'two three four']); expect(Str.of('one two three').split(/ /, 3)).toEqual(['one', 'two', 'three']); expect(Str.of('one two').split(/ /, 5)).toEqual(['one', 'two']); expect(Str.of('one two three').split(/ /, 1)).toEqual(['one two three']); }); test('handles regex patterns', (): void => { expect(Str.of('ONE, TWO, THREE').split(/[\s,]+/i)).toEqual(['ONE', 'TWO', 'THREE']); expect(Str.of('one1two2three').split(/\d+/)).toEqual(['one', 'two', 'three']); }); test('trims whitespace from results', (): void => { expect(Str.of(' one , two , three ').split(/\s*,\s*/)).toEqual(['one', 'two', 'three']); }); }); describe('finish', (): void => { test('adds a single instance of the given value to a string if it does not already end with that value', (): void => { expect(Str.of('this/string').finish('/').toString()).toEqual('this/string/'); }); test('does not add a value to a string that already ends with that value', (): void => { expect(Str.of('this/string/').finish('/').toString()).toEqual('this/string/'); }); }); describe('is', (): void => { test('determines if a given string matches a given pattern', (): void => { expect(Str.of('a').is('a')).toBeTruthy(); expect(Str.of('foobar').is('foo*')).toBeTruthy(); expect(Str.of('foobar').is('baz*')).toBeFalsy(); expect(Str.of('b/').is(['a*', 'b*'])).toBeTruthy(); expect(Str.of('f/').is(['a*', 'b*'])).toBeFalsy(); }); test('determines if a given string matches a given pattern case-insensitively', (): void => { expect(Str.of('a').is('A', true)).toBeTruthy(); expect(Str.of('foobar').is('FOO*', true)).toBeTruthy(); expect(Str.of('foobar').is('baz*', true)).toBeFalsy(); expect(Str.of('b/').is(['A*', 'B*'], true)).toBeTruthy(); expect(Str.of('f/').is(['A*', 'B*'], true)).toBeFalsy(); }); }); describe('isAscii', (): void => { test('determines if a given string is an ASCII string', (): void => { expect(Str.of('Taylor').isAscii()).toBeTruthy(); expect(Str.of('ü').isAscii()).toBeFalsy(); }); }); describe('isJson', (): void => { test('determines if a given string is valid JSON', (): void => { expect(Str.of('[1,2,3]').isJson()).toBeTruthy(); expect(Str.of('{"first": "John", "last": "Doe"}').isJson()).toBeTruthy(); expect(Str.of('{first: "John", last: "Doe"}').isJson()).toBeFalsy(); }); }); describe('isUrl', (): void => { test('determines if a given string is a URL', (): void => { expect(Str.of('https://example.com').isUrl()).toBeTruthy(); expect(Str.of('Taylor').isUrl()).toBeFalsy(); }); }); describe('isUuid', (): void => { test('determines if a given string is a UUID', (): void => { expect(Str.of('5ace9ab9-e9cf-4ec6-a19d-5881212a452c').isUuid()).toBeTruthy(); expect(Str.of('Taylor').isUuid()).toBeFalsy(); }); }); describe('isUlid', (): void => { test('validates correct ULID format', (): void => { expect(Str.of('01gd6r360bp37zj17nxb55yv40').isUlid()).toBeTruthy(); expect(Str.of('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').isUlid()).toBeTruthy(); }); test('rejects invalid ULIDs', (): void => { expect(Str.of('01gd6r360bp37zj17nxb55yv4!').isUlid()).toBeFalsy(); expect(Str.of('01gd6r360bp37zj17nxb55yv4i').isUlid()).toBeFalsy(); expect(Str.of('01gd6r360bp37zj17nxb55yv4l').isUlid()).toBeFalsy(); expect(Str.of('01gd6r360bp37zj17nxb55yv4o').isUlid()).toBeFalsy(); expect(Str.of('01gd6r360bp37zj17nxb55yv4I').isUlid()).toBeFalsy(); expect(Str.of('01gd6r360bp37zj17nxb55yv4O').isUlid()).toBeFalsy(); }); test('validates ULID length', (): void => { expect(Str.of('').isUlid()).toBeFalsy(); expect(Str.of('01gd6r360bp37zj17nxb55yv4').isUlid()).toBeFalsy(); expect(Str.of('01gd6r360bp37zj17nxb55yv400').isUlid()).toBeFalsy(); }); test('validates timestamp overflow', (): void => { expect(Str.of('81gd6r360bp37zj17nxb55yv40').isUlid()).toBeFalsy(); expect(Str.of('90000000000000000000000000').isUlid()).toBeFalsy(); }); test('handles edge cases', (): void => { expect(Str.of('01GD6R360BP37ZJ17NXB55YV40').isUlid()).toBeTruthy(); expect(Str.of('00000000000000000000000000').isUlid()).toBeTruthy(); expect(Str.of('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').isUlid()).toBeTruthy(); }); test('rejects non-string inputs', (): void => { // @ts-expect-error expect(Str.of(123).isUlid()).toBeFalsy(); // @ts-expect-error expect(Str.of(null).isUlid()).toBeFalsy(); // @ts-expect-error expect(Str.of(undefined).isUlid()).toBeFalsy(); // @ts-expect-error expect(Str.of({}).isUlid()).toBeFalsy(); }); }); describe('isEmpty', (): void => { test('determines if the given string is empty', (): void => { expect(Str.of(' ').trim().isEmpty()).toBeTruthy(); expect(Str.of('Laravel').trim().isEmpty()).toBeFalsy(); }); }); describe('isNotEmpty', (): void => { test('determines if the given string is not empty', (): void => { expect(Str.of(' ').trim().isNotEmpty()).toBeFalsy(); expect(Str.of('Laravel').trim().isNotEmpty()).toBeTruthy(); }); }); describe('kebab', (): void => { test('converts the given string to kebab-case', (): void => { expect(Str.of('fooBar').kebab().toString()).toEqual('foo-bar'); }); }); describe('length', (): void => { test('returns the length of the given string', (): void => { expect(Str.of('Laravel').length()).toEqual(7); }); }); describe('limit', (): void => { test('returns the original string when length is within limit', (): void => { expect(Str.of('The quick brown fox jumps over the lazy dog').limit(100).toString()).toBe('The quick brown fox jumps over the lazy dog'); }); test('truncates the given string to the specified length', (): void => { expect(Str.of('The quick brown fox jumps over the lazy dog').limit(20).toString()).toEqual('The quick brown fox...'); }); test('truncates the string and appends a custom string', (): void => { expect(Str.of('The quick brown fox jumps over the lazy dog').limit(20, ' (...)').toString()).toEqual('The quick brown fox (...)'); }); test('handles space after limit when "preserveWord" is set to true', (): void => { expect(Str.of('The quick brown fox jumps over the lazy dog').limit(3, '...', true).toString()).toEqual('The...'); }); test('respects word boundaries if "preserveWord" is set to true', (): void => { expect(Str.of('The quick brown fox jumps over the lazy dog').limit(20, '...', true).toString()).toEqual('The quick brown...'); }); }); describe('lower', (): void => { test('converts the given string to lowercase', (): void => { expect(Str.of('LARAVEL').lower().toString()).toEqual('laravel'); }); }); describe('mask', (): void => { test('masks a portion of a string with a repeated character', (): void => { expect(Str.of('taylor@example.com').mask('*', 3).toString()).toEqual('tay***************'); expect(Str.of('taylor@example.com').mask('*', -15, 3).toString()).toEqual('tay***@example.com'); }); test('returns original string when character is empty', (): void => { expect(Str.of('taylor@example.com').mask('', 2, 4).toString()).toEqual('taylor@example.com'); }); test('returns original string when segment is empty', (): void => { expect(Str.of('taylor@example.com').mask('*', 20).toString()).toEqual('taylor@example.com'); expect(Str.of('taylor@example.com').mask('*', 2, 0).toString()).toEqual('taylor@example.com'); }); }); describe('match', (): void => { test('returns the portion of a string that matches a given regular expression pattern', (): void => { expect(Str.of('foo bar').match(/bar/).toString()).toEqual('bar'); }); test('returns the portion of a string that matches a regular expression with a capturing group', (): void => { expect(Str.of('foo bar').match(/foo (.*)/).toString()).toEqual('bar'); }); }); describe('isMatch', (): void => { test('determines if the string matches a given regular expression', (): void => { expect(Str.of('foo bar').isMatch(/foo (.*)/)).toBeTruthy(); }); test('determines if the string does not match a given regular expression', (): void => { expect(Str.of('laravel').isMatch(/foo (.*)/)).toBeFalsy(); }); }); describe('matchAll', (): void => { test('returns an array containing portions of a string that match a given regular expression pattern', (): void => { expect(Str.of('bar foo bar').matchAll(/bar/)).toEqual(['bar', 'bar']); }); test('returns an empty array when there are no matches', (): void => { expect(Str.of('bar foo bar').matchAll(/baz/)).toEqual([]); }); test('returns an array containing matches of a regular expression with a capturing group', (): void => { expect(Str.of('bar fun bar fly').matchAll(/f(\w*)/)).toEqual(['un', 'ly']); }); }); describe('test', (): void => { test('determines if a string matches the given regular expression pattern', (): void => { expect(Str.of('Laravel Framework').test(/Laravel/).toString()).toEqual('true'); }); }); describe('numbers', (): void => { test('removes all non-numeric characters from a string', (): void => { expect(Str.of('(555) 123-4567').numbers().toString()).toEqual('5551234567'); expect(Str.of('L4r4v3l!').numbers().toString()).toEqual('443'); expect(Str.of('Laravel!').numbers().toString()).toEqual(''); }); }); describe('padBoth', (): void => { test('pads both sides of a string with another string until the final string reaches the desired length', (): void => { expect(Str.of('James').padBoth(10, '_').toString()).toEqual('__James___'); }); test('pads both sides of a string until the final string reaches the desired length', (): void => { expect(Str.of('James').padBoth(10).toString()).toEqual(' James '); }); }); describe('padLeft', (): void => { test('pads the left side of a string with another string until the final string reaches the desired length', (): void => { expect(Str.of('James').padLeft(10, '-=').toString()).toEqual('-=-=-James'); }); test('pads the left side of a string until the final string reaches the desired length', (): void => { expect(Str.of('James').padLeft(10).toString()).toEqual(' James'); }); }); describe('padRight', (): void => { test('pads the right side of a string with another string until the final string reaches the desired length', (): void => { expect(Str.of('James').padRight(10, '-').toString()).toEqual('James-----'); }); test('pads the right side of a string until the final string reaches the desired length', (): void => { expect(Str.of('James').padRight(10).toString()).toEqual('James '); }); }); describe('pipe', (): void => { test('returns a Stringable instance', (): void => { const string: Stringable = Str.of('hello world').pipe((string: Stringable): string => string + '!'); expect(string).toBeInstanceOf(Stringable); expect(string.toString()).toEqual('hello world!'); }); test('calls the method on the string value when callback is a string', (): void => { expect(Str.of('hello world').pipe('toUpperCase').toString()).toEqual('HELLO WORLD'); }); test('calls the given function with the string value', (): void => { const string: Stringable = Str.of('hello world'); const callback: ((instance: Stringable) => any) = (string: Stringable): string => string.explode(' ').join('-'); const result: Stringable = string.pipe(callback); expect(result.toString()).toEqual('hello-world'); }); }); describe('plural', (): void => { test('converts singular to plural for regular nouns', (): void => { expect(Str.of('car').plural().toString()).toBe('cars'); expect(Str.of('book').plural().toString()).toBe('books'); expect(Str.of('apple').plural().toString()).toBe('apples'); }); test('handles "count" value correctly', (): void => { expect(Str.of('child').plural(1).toString()).toBe('child'); expect(Str.of('child').plural(2).toString()).toBe('children'); expect(Str.of('person').plural(1).toString()).toBe('person'); expect(Str.of('person').plural(3).toString()).toBe('people'); }); test('converts irregular nouns correctly', (): void => { // A expect(Str.of('alumna').plural().toString()).toBe('alumnae'); expect(Str.of('analysis').plural().toString()).toBe('analyses'); expect(Str.of('axis').plural().toString()).toBe('axes'); // B-C expect(Str.of('bacterium').plural().toString()).toBe('bacteria'); expect(Str.of('child').plural().toString()).toBe('children'); expect(Str.of('crisis').plural().toString()).toBe('crises'); // D-F expect(Str.of('datum').plural().toString()).toBe('data'); expect(Str.of('foot').plural().toString()).toBe('feet'); expect(Str.of('fungus').plural().toString()).toBe('fungi'); // G-M expect(Str.of('goose').plural().toString()).toBe('geese'); expect(Str.of('man').plural().toString()).toBe('men'); expect(Str.of('mouse').plural().toString()).toBe('mice'); // N-S expect(Str.of('nucleus').plural().toString()).toBe('nuclei'); expect(Str.of('person').plural().toString()).toBe('people'); expect(Str.of('thesis').plural().toString()).toBe('theses'); // T-Z expect(Str.of('tooth').plural().toString()).toBe('teeth'); expect(Str.of('wife').plural().toString()).toBe('wives'); expect(Str.of('zombie').plural().toString()).toBe('zombies'); }); test('handles uncountable nouns correctly', (): void => { expect(Str.of('sheep').plural().toString()).toBe('sheep'); expect(Str.of('fish').plural().toString()).toBe('fish'); expect(Str.of('series').plural().toString()).toBe('series'); expect(Str.of('money').plural().toString()).toBe('money'); expect(Str.of('information').plural().toString()).toBe('information'); expect(Str.of('equipment').plural().toString()).toBe('equipment'); }); test('handles special pluralization rules', (): void => { // -f/-fe → -ves expect(Str.of('leaf').plural().toString()).toBe('leaves'); expect(Str.of('knife').plural().toString()).toBe('knives'); // -y → -ies expect(Str.of('city').plural().toString()).toBe('cities'); expect(Str.of('baby').plural().toString()).toBe('babies'); // -o → -oes expect(Str.of('potato').plural().toString()).toBe('potatoes'); expect(Str.of('volcano').plural().toString()).toBe('volcanoes'); // -us → -i expect(Str.of('cactus').plural().toString()).toBe('cacti'); expect(Str.of('focus').plural().toString()).toBe('foci'); // -is → -es expect(Str.of('analysis').plural().toString()).toBe('analyses'); expect(Str.of('basis').plural().toString()).toBe('bases'); // -ix → -ices expect(Str.of('matrix').plural().toString()).toBe('matrices'); expect(Str.of('index').plural().toString()).toBe('indices'); }); test('handles compound words and special cases', (): void => { expect(Str.of('passerby').plural().toString()).toBe('passersby'); expect(Str.of('runner-up').plural().toString()).toBe('runners-up'); }); test('handles words with multiple plural forms', (): void => { expect(Str.of('octopus').plural().toString()).toBe('octopuses'); expect(Str.of('hoof').plural().toString()).toBe('hoofs'); }); test('preserves case sensitivity', (): void => { expect(Str.of('Hero').plural().toString()).toBe('Heroes'); expect(Str.of('CHILD').plural().toString()).toBe('CHILDREN'); expect(Str.of('Analysis').plural().toString()).toBe('Analyses'); }); test('handles edge cases', (): void => { expect(Str.of('').plural().toString()).toBe(''); expect(Str.of(' ').plural().toString()).toBe(' '); expect(Str.of('sheep').plural(0).toString()).toBe('sheep'); expect(Str.of('person').plural(1.5).toString()).toBe('people'); }); test('preserves case sensitivity in pluralization', (): void => { expect(Str.of('HERO').plural().toString()).toBe('HEROES'); expect(Str.of('PERSON').plural().toString()).toBe('PEOPLE'); expect(Str.of('CHILD').plural().toString()).toBe('CHILDREN'); expect(Str.of('SHEEP').plural().toString()).toBe('SHEEP'); expect(Str.of('Hero').plural().toString()).toBe('Heroes'); expect(Str.of('Person').plural().toString()).toBe('People'); expect(Str.of('Child').plural().toString()).toBe('Children'); expect(Str.of('Sheep').plural().toString()).toBe('Sheep'); expect(Str.of('HeRo').plural().toString()).toBe('HeRoes'); expect(Str.of('PeRsOn').plural().toString()).toBe('People'); expect(Str.of('ChIlD').plural().toString()).toBe('Children'); expect(Str.of('ShEep').plural().toString()).toBe('ShEep'); expect(Str.of('uSeR').plural().toString()).toBe('uSeRs'); }); }); describe('pluralStudly', (): void => { test('converts a singular word string formatted in studly caps case to its plural form', (): void => { expect(Str.of('VerifiedHuman').pluralStudly().toString()).toEqual('VerifiedHumans'); expect(Str.of('UserFeedback').pluralStudly().toString()).toEqual('UserFeedback'); expect(Str.of('VerifiedHuman').pluralStudly(2).toString()).toEqual('VerifiedHumans'); expect(Str.of('VerifiedHuman').pluralStudly(1).toString()).toEqual('VerifiedHuman'); }); }); describe('pluralPascal', (): void => { test('converts a singular word string formatted in Pascal case to its plural form', (): void => { expect(Str.of('VerifiedHuman').pluralPascal().toString()).toEqual('VerifiedHumans'); expect(Str.of('UserFeedback').pluralPascal().toString()).toEqual('UserFeedback'); expect(Str.of('VerifiedHuman').pluralPascal(2).toString()).toEqual('VerifiedHumans'); expect(Str.of('VerifiedHuman').pluralPascal(1).toString()).toEqual('VerifiedHuman'); }); }); describe('position', (): void => { test('returns the position of the first occurrence of a substring in a string', (): void => { expect(Str.of('Hello, World!').position('Hello')).toEqual(0); }); test('returns the position of the first occurrence of a substring in a string', (): void => { expect(Str.of('Hello, World!').position('W')).toEqual(7); }); }); describe('prepend', (): void => { test('prepends the given values onto the string', (): void => { expect(Str.of('Framework').prepend('Laravel ').toString()).toEqual('Laravel Framework'); }); }); describe('remove', (): void => { test('removes the given value from the string', (): void => { expect(Str.of('Peter Piper picked a peck of pickled peppers.').remove('e').toString()).toEqual('Ptr Pipr pickd a pck of pickld ppprs.'); }); test('removes the given value from the string case-insensitively', (): void => { expect(Str.of('Peter Piper picked a peck of pickled peppers.').remove('E', false).toString()).toEqual('Ptr Pipr pickd a pck of pickld ppprs.'); }); test('removes the given array of values from the string', (): void => { expect(Str.of('Peter Piper picked a peck of pickled peppers.').remove(['e', 'p']).toString()).toEqual('Ptr Pir ickd a ck of ickld rs.'); }); test('removes the given array of values from the string case-insensitively', (): void => { expect(Str.of('Peter Piper picked a peck of pickled peppers.').remove(['E', 'P'], false).toString()).toEqual('tr ir ickd a ck of ickld rs.'); }); test('removes a given array of strings within the given string or strings', (): void => { expect(Str.of('Peter Piper picked a peck of pickled peppers.').remove(['e', 'p']).toString()).toEqual('Ptr Pir ickd a ck of ickld rs.'); }); }); describe('reverse', (): void => { test('reverses the given string', (): void => { expect(Str.of('Hello World').reverse().toString()).toEqual('dlroW olleH'); }); }); describe('repeat', (): void => { test('repeats the given string', (): void => { expect(Str.of('a').repeat(5).toString()).toEqual('aaaaa'); }); }); describe('replace', (): void => { test('replaces a given string within the string', (): void => { expect(Str.of('Laravel 9.x').replace('9.x', '10.x').toString()).toEqual('Laravel 10.x'); }); test('replaces a given string within the string case-insensitively', (): void => { expect(Str.of('Framework 10.x').replace('framework', 'Laravel', false).toString()).toEqual('Laravel 10.x'); expect(Str.of('?1 ?2 ?3').replace(['?1', '?2', '?3'], ['foo', 'bar', 'baz']).toString()).toEqual('foo bar baz'); }); test('replaces a given array of strings within the given array of strings', (): void => { expect(Str.of('?1 ?2 ?3').replace(['?1', '?2', '?3'], ['foo', 'bar', 'baz']).toString()).toEqual('foo bar baz'); }); }); describe('replaceArray', (): void => { test('replaces a given value in the string sequentially using an array', (): void => { expect(Str.of('The event will take place between ? and ?').replaceArray('?', ['8:30', '9:00']).toString()).toEqual('The event will take place between 8:30 and 9:00'); }); }); describe('replaceFirst', (): void => { test('replaces the first occurrence of a given value in a string', (): void => { expect(Str.of('the quick brown fox jumps over the lazy dog').replaceFirst('the', 'a').toString()).toEqual('a quick brown fox jumps over the lazy dog'); }); test('returns the subject when "search" string is not found', (): void => { expect(Str.of('the quick brown fox jumps over the lazy dog').replaceFirst('baz', 'bar').toString()).toEqual('the quick brown fox jumps over the lazy dog'); }); test('returns the subject when "search" string is empty', (): void => { expect(Str.of('the quick brown fox jumps over the lazy dog').replaceFirst('', 'bar').toString()).toEqual('the quick brown fox jumps over the lazy dog'); }); test('returns the subject when "search" string is not found', (): void => { expect(Str.of('the quick brown fox jumps over the lazy dog').replaceFirst('non existent', 'replacement').toString()).toEqual('the quick brown fox jumps over the lazy dog'); }); }); describe('replaceStart', (): void => { test('replaces the first occurrence of the given value only if it appears at the start of the string', (): void => { expect(Str.of('Hello World').replaceStart('Hello', 'Laravel').toString()).toEqual('Laravel World'); expect(Str.of('Hello World').replaceStart('World', 'Laravel').toString()).toEqual('Hello World'); }); test('returns the subject when "search" string is empty', (): void => { expect(Str.of('Hello World').replaceStart('', 'Laravel').toString()).toEqual('Hello World'); }); test('replaces only at the start of the string', (): void => { expect(Str.of('Hello World, Hello Universe').replaceStart('Hello', 'Hi').toString()).toEqual('Hi World, Hello Universe'); }); test('returns the subject when "search" string is not found', (): void => { expect(Str.of('Hello World').replaceStart('Greetings', 'Hi').toString()).toEqual('Hello World'); }); test('handles empty subject string', (): void => { expect(Str.replaceStart('Hello', 'Hi', '').toString()).toEqual(''); }); test('replaces when search and subject are the same', (): void => { expect(Str.of('Hello').replaceStart('Hello', 'Hi').toString()).toEqual('Hi'); }); }); describe('replaceLast', (): void => { test('replaces the last occurrence of the given value', (): void => { expect(Str.of('the quick brown fox jumps over the lazy dog').replaceLast('the', 'a').toString()).toEqual('the quick brown fox jumps over a lazy dog'); }); test('returns the subject when "search" string is not found', (): void => { expect(Str.of('the quick brown fox jumps over the lazy dog').replaceLast('baz', 'bar').toString()).toEqual('the quick brown fox jumps over the lazy dog'); }); test('returns the subject when "search" string is empty', (): void => { expect(Str.of('the quick brown fox jumps over the lazy dog').replaceLast('', 'bar').toString()).toEqual('the quick brown fox jumps over the lazy dog'); }); test('replaces the only occurrence when it appears once', (): void => { expect(Str.of('the quick brown fox').replaceLast('quick', 'slow').toString()).toEqual('the slow brown fox'); }); test('replaces the last occurrence when multiple exist', (): void => { expect(Str.of('the quick the brown fox').replaceLast('the', 'a').toString()).toEqual('the quick a brown fox'); }); }); describe('replaceEnd', (): void => { test('replaces the last occurrence of the given value only if it appears at the end of the string', (): void => { expect(Str.of('Hello World').replaceEnd('World', 'Laravel').toString()).toEqual('Hello Laravel'); expect(Str.of('Hello World').replaceEnd('Hello', 'Hi').toString()).toEqual('Hello World'); }); test('returns the subject when "search" string is empty', (): void => { expect(Str.of('Hello World').replaceEnd('', 'Laravel').toString()).toEqual('Hello World'); }); test('replaces only at the end of the string', (): void => { expect(Str.of('Hello Universe, Hello Universe').replaceEnd('Universe', 'World',).toString()).toEqual('Hello Universe, Hello World'); }); test('returns the subject when "search" string is not found', (): void => { expect(Str.of('Hello Universe').replaceEnd('Greetings', 'World').toString()).toEqual('Hello Universe'); }); test('handles empty subject string', (): void => { expect(Str.of('').replaceEnd('Hello', 'Hi').toString()).toEqual(''); }); test('replaces when search and subject are the same', (): void => { expect(Str.of('Hello').replaceEnd('Hello', 'Hi').toString()).toEqual('Hi'); }); test('replaces only the ending match', (): void => { expect(Str.of('This is the end of the end').replaceEnd('end', 'finish').toString()).toEqual('This is the end of the finish'); }); }); describe('replaceMatches', (): void => { test('replaces all portions of a string matching a pattern with the given replacement string', (): void => { expect(Str.of('foo bar baz').replaceMatches(/baz/, 'bar').toString()).toEqual('foo bar bar'); expect(Str.of('foo bar baz').replaceMatches(/404/, 'found').toString()).toEqual('foo bar baz'); }); test('replaces all portions of a string matching a pattern with the given array of replacement strings', (): void => { expect(Str.of('foo bar baz').replaceMatches([/bar/, /baz/], ['XXX', 'YYY']).toString()).toEqual('foo XXX YYY'); expect(Str.of('foo bar baz').replaceMatches([/bar/, /baz/], ['XXX']).toString()).toEqual('foo XXX '); }); test('replaces all portions of a string matching a pattern with the given callback as a replacement', (): void => { expect(Str.of('123').replaceMatches(/\d/, (matches: string[]): string => `[${matches[0]}]`).toString()).toEqual('[1][2][3]'); expect(Str.of('foo baz bar').replaceMatches(/ba(.)/, (matches: [string, string]): string => `ba${(matches[1]).toUpperCase()}`).toString()).toEqual('foo baZ baR'); }); test('limits the number of replacements when "limit" value is provided', (): void => { expect(Str.of('foo baz baz').replaceMatches(/ba(.)/, 'bar', 1).toString()).toEqual('foo bar baz'); expect(Str.of('123').replaceMatches(/\d/, (matches: string[]): string => `[${matches[0]}]`, 1).toString()).toEqual('[1]23'); }); }); describe('squish', (): void => { test('removes all extraneous white space from a string', (): void => { expect(Str.of(' laravel framework ').squish().toString()).toEqual('laravel framework'); }); }); describe('startsWith', (): void => { test('determines if the given string begins with the given value', (): void => { expect(Str.of('This is my name').startsWith('This')).toBeTruthy(); }); test('determines if the given string begins with any of the values in the array', (): void => { expect(Str.of('This is my name').startsWith(['This', 'That', 'There'])).toBeTruthy(); }); test('returns false if the given string does not start with the given value', (): void => { expect(Str.of('This is my name').startsWith('There')).toBeFalsy(); }); test('returns false if the given string does not start with any of the values in the array', (): void => { expect(Str.of('This is my name').startsWith(['That', 'There'])).toBeFalsy(); }); }); describe('doesntStartWith', (): void => { test('determines if the given string does not start with the given value', (): void => { expect(Str.of('This is my name').doesntStartWith('There')).toBeTruthy(); }); test('determines if the given string does not start with any of the values in the array', (): void => { expect(Str.of('This is my name').doesntStartWith(['There', 'foo'])).toBeTruthy(); }); test('returns false if the given string starts with the given value', (): void => { expect(Str.of('This is my name').doesntStartWith('This')).toBeFalsy(); }); test('returns false if the string ends starts the given value', (): void => { expect(Str.of('This is my name').doesntStartWith(['This', 'foo'])).toBeFalsy(); }); }); describe('start', (): void => { test('adds a single instance of the given value to a string if it does not already start with that value', (): void => { expect(Str.of('this/string').start('/').toString()).toEqual('/this/string'); }); test('does not add a value to a string that already starts with that value', (): void => { expect(Str.of('/this/string').start('/').toString()).toEqual('/this/string'); }); }); describe('upper', (): void => { test('converts the given string to uppercase', (): void => { expect(Str.of('laravel').upper().toString()).toEqual('LARAVEL'); }); }); describe('title', (): void => { test('converts the given string to Title Case', (): void => { expect(Str.of('a nice title uses the correct case').title().toString()).toEqual('A Nice Title Uses The Correct Case'); }); }); describe('headline', (): void => { test('converts strings delimited by casing, hyphens, or underscores into a space delimited string with each word\'s first letter capitalized', (): void => { expect(Str.of('taylor_otwell').headline().toString()).toEqual('Taylor Otwell'); expect(Str.of('EmailNotificationSent').headline().toString()).toEqual('Email Notification Sent'); expect(Str.of('too many spaces').headline().toString()).toEqual('Too Many Spaces'); }); }); describe('apa', (): void => { test('converts the given string to title case following the APA guidelines', (): void => { expect(Str.of('a nice title uses the correct case').apa().toString()).toEqual('A Nice Title Uses the Correct Case'); expect(Str.of('the great gatsby').apa().toString()).toBe('The Great Gatsby'); expect(Str.of('the cat in the hat').apa().toString()).toBe('The Cat in the Hat'); expect(Str.of('to be or not to be').apa().toString()).toBe('To Be or Not to Be'); expect(Str.of('the mother-in-law').apa().toString()).toBe('The Mother-in-Law'); expect(Str.of('').apa().toString()).toBe(''); expect(Str.of('a').apa().toString()).toBe('A'); expect(Str.of('the').apa().toString()).toBe('The'); expect(Str.of('the end. the beginning').apa().toString()).toBe('The End. The Beginning'); expect(Str.of('tHe QuiCk bRoWn fOx').apa().toString()).toBe('The Quick Brown Fox'); expect(Str.of('the art of war').apa().toString()).toBe('The Art of War'); expect(Str.of('a tale of two cities').apa().toString()).toBe('A Tale of Two Cities'); expect(Str.of('the lord of the rings: the fellowship of the ring').apa().toString()).toBe('The Lord of the Rings: The Fellowship of the Ring'); }); }); describe('singular', (): void => { test('converts plural to singular for regular nouns', (): void => { expect(Str.of('cars').singular().toString()).toBe('car'); expect(Str.of('books').singular().toString()).toBe('book'); expect(Str.of('apples').singular().toString()).toBe('apple'); }); test('handles irregular nouns correctly', (): void => { // A expect(Str.of('alumnae').singular().toString()).toBe('alumna'); expect(Str.of('analyses').singular().toString()).toBe('analysis'); expect(Str.of('axes').singular().toString()).toBe('axis'); // B-C expect(Str.of('bacteria').singular().toString()).toBe('bacterium'); expect(Str.of('children').singular().toString()).toBe('child'); expect(Str.of('crises').singular().toString()).toBe('crisis'); // D-F expect(Str.of('demos').singular().toString()).toBe('demo'); expect(Str.of('feet').singular().toString()).toBe('foot'); expect(Str.of('fungi').singular().toString()).toBe('fungus'); // G-M expect(Str.of('geese').singular().toString()).toBe('goose'); expect(Str.of('men').singular().toString()).toBe('man'); expect(Str.of('mice').singular().toString()).toBe('mouse'); // N-S expect(Str.of('nuclei').singular().toString()).toBe('nucleus'); expect(Str.of('people').singular().toString()).toBe('person'); expect(Str.of('theses').singular().toString()).toBe('thesis'); // T-Z expect(Str.of('teeth').singular().toString()).toBe('tooth'); expect(Str.of('wives').singular().toString()).toBe('wife'); expect(Str.of('zombies').singular().toString()).toBe('zombie'); }); test('handles uncountable nouns correctly', (): void => { expect(Str.of('sheep').singular().toString()).toBe('sheep'); expect(Str.of('fish').singular().toString()).toBe('fish'); expect(Str.of('series').singular().toString()).toBe('series'); expect(Str.of('money').singular().toString()).toBe('money'); expect(Str.of('information').singular().toString()).toBe('information'); }); test('handles special singularization rules', (): void => { // -ves → -f/-fe expect(Str.of('leaves').singular().toString()).toBe('leaf'); expect(Str.of('knives').singular().toString()).toBe('knife'); // -ies → -y expect(Str.of('cities').singular().toString()).toBe('city'); expect(Str.of('babies').singular().toString()).toBe('baby'); // -oes → -o expect(Str.of('potatoes').singular().toString()).toBe('potato'); expect(Str.of('volcanoes').singular().toString()).toBe('volcano'); // -i → -us expect(Str.of('cacti').singular().toString()).toBe('cactus'); expect(Str.of('foci').singular().toString()).toBe('focus'); // -es → -is expect(Str.of('analyses').singular().toString()).toBe('analysis'); expect(Str.of('bases').singular().toString()).toBe('basis'); // -ices → -ix/ex expect(Str.of('matrices').singular().toString()).toBe('matrix'); expect(Str.of('indices').singular().toString()).toBe('index'); }); test('handles compound words and special cases', (): void => { expect(Str.of('passersby').singular().toString()).toBe('passerby'); expect(Str.of('runners-up').singular().toString()).toBe('runner-up'); }); test('handles words with multiple singular forms', (): void => { expect(Str.of('octopuses').singular().toString()).toBe('octopus'); expect(Str.of('hoofs').singular().toString()).toBe('hoof'); }); test('preserves case sensitivity', (): void => { expect(Str.of('Heroes').singular().toString()).toBe('Hero'); expect(Str.of('CHILDREN').singular().toString()).toBe('CHILD'); expect(Str.of('Analyses').singular().toString()).toBe('Analysis'); }); test('handles edge cases', (): void => { expect(Str.of('').singular().toString()).toBe(''); expect(Str.of(' ').singular().toString()).toBe(' '); expect(Str.of('123').singular().toString()).toBe('123'); }); }); describe('slug', (): void => { test('generates a URL friendly "slug" from the given string', (): void => { expect(Str.of('Laravel Framework').slug('-').toString()).toEqual('laravel-framework'); }); }); describe('snake', (): void => { test('converts the given string to snake_case', (): void => { expect(Str.of('fooBar').snake().toString()).toEqual('foo_bar'); }); }); describe('startsWith', (): void => { test('determines if the given string begins with the given value', (): void => { expect(Str.of('This is my name').startsWith('This')).toBeTruthy(); }); }); describe('studly', (): void => { test('converts the given string to studly caps case', (): void => { expect(Str.of('foo_bar').studly().toString()).toEqual('FooBar'); }); }); describe('pascal', (): void => { test('converts the given string to Pascal case', (): void => { expect(Str.of('foo_bar').pascal().toString()).toEqual('FooBar'); }); }); describe('substr', (): void => { test('returns the portion of the string specified by the given "start" value', (): void => { expect(Str.of('Laravel Framework').substr(8).toString()).toEqual('Framework'); }); test('returns the portion of the string specified by the given "start" and "length" values', (): void => { expect(Str.of('Laravel Framework').substr(8, 5).toString()).toEqual('Frame'); }); }); describe('substrCount', (): void => { test('returns the number of occurrences of a given value in the given string', (): void => { expect(Str.of('If you like ice cream, you will like snow cones.').substrCount('like')).toEqual(2); }); test('respects the "offset" and "length" values when counting occurrences', (): void => { expect(Str.of('hello hello hello').substrCount('hello', 6)).toEqual(2); expect(Str.of('hello hello hello').substrCount('hello', 0, 5)).toEqual(1); expect(Str.of('hello hello hello').substrCount('hello', 6, 6)).toEqual(1); }); }); describe('substrReplace', (): void => { test('replaces text within a portion of a string starting at the specified position', (): void => { expect(Str.of('1300').substrReplace(':', 2).toString()).toEqual('13:'); }); test('inserts the string at the specified position without replacing any existing characters', (): void => { expect(Str.of('The Framework').substrReplace(' Laravel', 3, 0).toString()).toEqual('The Laravel Framework'); }); }); describe('swap', (): void => { test('replaces multiple values in the string', (): void => { expect(Str.of('Tacos are great!').swap({ 'Tacos': 'Burritos', 'great': 'fantastic' }).toString()).toEqual('Burritos are fantastic!'); }); }); describe('take', (): void => { test('returns specified number of characters from the beginning of the string', (): void => { expect(Str.of('Build something amazing!').take(5).toString()).toEqual('Build'); expect(Str.of('Hello World').take(-5).toString()).toEqual('World'); }); }); describe('tap', (): void => { test('passes the string to the given closure', (): void => { expect(Str.of('Laravel').tap((string: Stringable): Stringable => string.lower()).upper().toString()).toEqual('LARAVEL'); }); }); describe('trim', (): void => { test('trims the given string', (): void => { expect(Str.of(' Laravel ').trim().toString()).toEqual('Laravel'); }); test('trims the given string with custom characters', (): void => { expect(Str.of('/Laravel/').trim('/').toString()).toEqual('Laravel'); }); }); describe('ltrim', (): void => { test('trims the left side of the string', (): void => { expect(Str.of(' Laravel ').ltrim().toString()).toEqual('Laravel '); }); test('trims the left side of the string with specified characters', (): void => { expect(Str.of('/Laravel/').ltrim('/').toString()).toEqual('Laravel/'); }); }); describe('rtrim', (): void => { test('trims the right side of the given string', (): void => { expect(Str.of(' Laravel ').rtrim().toString()).toEqual(' Laravel'); }); test('trims the right side of the string with specified characters', (): void => { expect(Str.of('/Laravel/').rtrim('/').toString()).toEqual('/Laravel'); }); }); describe('lcfirst', (): void => { test('returns the given string with the first character lowercased', (): void => { expect(Str.of('Foo Bar').lcfirst().toString()).toEqual('foo Bar'); }); }); describe('ucfirst', (): void => { test('returns the given string with the first character capitalized', (): void => { expect(Str.of('foo bar').ucfirst().toString()).toEqual('Foo bar'); }); }); describe('ucwords', (): void => { test('returns the given string with the first character of each word capitalized', (): void => { expect(Str.of('laravel').ucwords().toString()).toEqual('Laravel'); expect(Str.of('laravel framework').ucwords().toString()).toEqual('Laravel Framework'); expect(Str.of('мама').ucwords().toString()).toEqual('Мама'); expect(Str.of('мама мыла раму').ucwords().toString()).toEqual('Мама Мыла Раму'); }); test('handles custom separators when capitalizing words', (): void => { expect(Str.of('laravel-framework').ucwords('-').toString()).toEqual('Laravel-Framework'); }); }); describe('ucsplit', (): void => { test('splits the given string into an array by uppercase characters', (): void => { expect(Str.of('FooBar').ucsplit()).toEqual(['Foo', 'Bar']); }); }); describe('when', (): void => { test('invokes the given closure if a given condition is true', (): void => { expect(Str.of('Taylor').when(true, (string: Stringable): Stringable => string.append(' Otwell')).toString()).toEqual('Taylor Otwell'); }); test('invokes the fallback closure if the condition is false', (): void => { expect(Str.of('Taylor').when(false, (string: Stringable): Stringable => string.append(' Otwell'), (string: Stringable): Stringable => string.append(' Swift')).toString()).toEqual('Taylor Swift'); }); }); describe('unless', (): void => { test('invokes the given closure if a given condition is false', (): void => { expect(Str.of('Taylor').unless(false, (string: Stringable): Stringable => string.append(' Otwell')).toString()).toEqual('Taylor Otwell'); }); test('invokes the fallback closure if the condition is true', (): void => { expect(Str.of('Taylor').unless(true, (string: Stringable): Stringable => string.append(' Otwell'), (string: Stringable): Stringable => string.append(' Swift')).toString()).toEqual('Taylor Swift'); }); }); describe('whenContains', (): void => { test('invokes the given closure if the string contains the given value', (): void => { expect(Str.of('tony stark').whenContains('tony', (string: Stringable): Stringable => string.title()).toString()).toEqual('Tony Stark'); }); test('invokes the given closure if the string contains any value in the array', (): void => { expect(Str.of('tony stark').whenContains(['tony', 'hulk'], (string: Stringable): Stringable => string.title()).toString()).toEqual('Tony Stark'); }); test('invokes the fallback closure if the string does not contain the given value', (): void => { expect(Str.of('tony stark').whenContains('hulk', (string: Stringable): Stringable => string.title(), (string: Stringable): Stringable => string.upper()).toString()).toEqual('TONY STARK'); }); }); describe('whenContainsAll', (): void => { test('invokes the given closure if the string contains all the given sub-strings', (): void => { expect(Str.of('tony stark').whenContainsAll(['tony', 'stark'], (string: Stringable): Stringable => string.title()).toString()).toEqual('Tony Stark'); }); test('invokes the fallback closure if the string does not contain all the given sub-strings', (): void => { expect(Str.of('tony stark').whenContainsAll(['tony', 'stark', 'ironman'], (string: Stringable): Stringable => string.title(), (string: Stringable): Stringable => string.upper()).toString()).toEqual('TONY STARK'); }); }); describe('whenEmpty', (): void => { test('invokes the given closure if the string is empty', (): void => { expect(Str.of(' ').whenEmpty((string: Stringable): Stringable => string.trim().prepend('Laravel')).toString()).toEqual('Laravel'); }); test('invokes the fallback closure if the string is not empty', (): void => { expect(Str.of('not empty').whenEmpty((string: Stringable): Stringable => string.trim().prepend('Laravel'), (string: Stringable): Stringable => string.upper()).toString()).toEqual('NOT EMPTY'); }); }); describe('whenNotEmpty', (): void => { test('invokes the given closure if the string is not empty', (): void => { expect(Str.of('Framework').whenNotEmpty((string: Stringable): Stringable => string.prepend('Laravel ')).toString()).toEqual('Laravel Framework'); }); test('invokes the fallback closure if the string is empty', (): void => { expect(Str.of(' ').whenNotEmpty((string: Stringable): Stringable => string.prepend('Laravel '), (string: Stringable): Stringable => string.trim().prepend('Fallback: ')).toString()).toEqual('Fallback: '); }); }); describe('whenEndsWith', (): void => { test('invokes the given closure if the string ends with the given sub-string', (): void => { expect(Str.of('disney world').whenEndsWith('world', (string: Stringable): Stringable => string.title()).toString()).toEqual('Disney World'); }); test('invokes the given closure if the string ends with any of the given sub-strings', (): void => { expect(Str.of('disney world').whenEndsWith(['hello', 'world'], (string: Stringable): Stringable => string.title()).toString()).toEqual('Disney World'); }); test('invokes the fallback closure if the string does not end with the given sub-string', (): void => { expect(Str.of('disney world').whenEndsWith('hello', (string: Stringable): Stringable => string.title(), (string: Stringable): Stringable => string.upper()).toString()).toEqual('DISNEY WORLD'); }); }); describe('whenDoesntEndWith', (): void => { test('invokes the given closure if the string does not end with the given sub-string', (): void => { expect(Str.of('disney world').whenDoesntEndWith('land', (string: Stringable): Stringable => string.title()).toString()).toEqual('Disney World'); }); test('invokes the given closure if the string does not end with any of the given sub-strings', (): void => { expect(Str.of('disney world').whenDoesntEndWith(['land', 'moon'], (string: Stringable): Stringable => string.title()).toString()).toEqual('Disney World'); }); test('invokes the fallback closure if the string ends with the given sub-string', (): void => { expect(Str.of('disney world').whenDoesntEndWith('world', (string: Stringable): Stringable => string.title(), (string: Stringable): Stringable => string.upper()).toString()).toEqual('DISNEY WORLD'); }); }); describe('whenExactly', (): void => { test('invokes the given closure if the string exactly matches the given string', (): void => { expect(Str.of('laravel').whenExactly('laravel', (string: Stringable): Stringable => string.title()).toString()).toEqual('Laravel'); }); test('invokes the fallback closure if the string does not exactly match the given string', (): void => { expect(Str.of('laravel').whenExactly('Laravel', (string: Stringable): Stringable => string.title(), (string: Stringable): Stringable => string.upper()).toString()).toEqual('LARAVEL'); }); }); describe('whenNotExactly', (): void => { test('invokes the given closure if the string does not exactly match the given string', (): void => { expect(Str.of('framework').whenNotExactly('laravel', (string: Stringable): Stringable => string.title()).toString()).toEqual('Framework'); }); test('invokes the fallback closure if the string exactly matches the given string', (): void => { expect(Str.of('laravel').whenNotExactly('laravel', (string: Stringable): Stringable => string.title(), (string: Stringable): Stringable => string.upper()).toString()).toEqual('LARAVEL'); }); }); describe('whenIs', (): void => { test('invokes the given closure if the string matches a given pattern', (): void => { expect(Str.of('foo/bar').whenIs('foo/*', (string: Stringable): Stringable => string.append('/baz')).toString()).toEqual('foo/bar/baz'); }); test('invokes the fallback closure if the string does not match the given pattern', (): void => { expect(Str.of('foo/bar').whenIs('bar/*', (string: Stringable): Stringable => string.append('/baz'), (string: Stringable): Stringable => string.upper()).toString()).toEqual('FOO/BAR'); }); }); describe('whenIsAscii', (): void => { test('invokes the given closure if the string is 7-bit ASCII', (): void => { expect(Str.of('laravel').whenIsAscii((string: Stringable): Stringable => string.title()).toString()).toEqual('Laravel'); }); test('invokes the fallback closure if the string is not 7-bit ASCII', (): void => { expect(Str.of('läråvèl').whenIsAscii((string: Stringable): Stringable => string.title(), (string: Stringable): Stringable => string.upper()).toString()).toEqual('LÄRÅVÈL'); }); }); describe('whenIsUuid', (): void => { test('invokes the given closure if the string is a valid UUID', (): void => { expect(Str.of('a0a2a2d2-0b87-4a18-83f2-2529882be2de').whenIsUuid((string: Stringable): Stringable => string.substr(0, 8)).toString()).toEqual('a0a2a2d2'); }); }); describe('whenIsUlid', (): void => { test('invokes the given closure if the string is a valid ULID', (): void => { expect(Str.of('01gd6r360bp37zj17nxb55yv40').whenIsUlid((string: Stringable): Stringable => string.substr(0, 8)).toString()).toEqual('01gd6r36'); }); test('invokes the fallback closure if the string is not a valid UUID', (): void => { expect(Str.of('not-a-uuid').whenIsUuid((string: Stringable): Stringable => string.substr(0, 8), (string: Stringable): Stringable => string.upper()).toString()).toEqual('NOT-A-UUID'); }); }); describe('whenStartsWith', (): void => { test('invokes the given closure if the string starts with the given sub-string', (): void => { expect(Str.of('disney world').whenStartsWith('disney', (string: Stringable): Stringable => string.title()).toString()).toEqual('Disney World'); }); test('invokes the given closure if the string starts with any of the given sub-strings', (): void => { expect(Str.of('disney world').whenStartsWith(['hello', 'disney'], (string: Stringable): Stringable => string.title()).toString()).toEqual('Disney World'); }); test('invokes the fallback closure if the string does not start with the given sub-string', (): void => { expect(Str.of('disney world').whenStartsWith('hello', (string: Stringable): Stringable => string.title(), (string: Stringable): Stringable => string.upper()).toString()).toEqual('DISNEY WORLD'); }); }); describe('whenDoesntStartWith', (): void => { test('invokes the given closure if the string does not start with the given sub-string', (): void => { expect(Str.of('disney world').whenDoesntStartWith('hello', (string: Stringable): Stringable => string.title()).toString()).toEqual('Disney World'); }); test('invokes the given closure if the string does not start with any of the given sub-strings', (): void => { expect(Str.of('disney world').whenDoesntStartWith(['foo', 'bar'], (string: Stringable): Stringable => string.title()).toString()).toEqual('Disney World'); }); test('invokes the fallback closure if the string starts with the given sub-string', (): void => { expect(Str.of('disney world').whenDoesntStartWith('dis', (string: Stringable): Stringable => string.title(), (string: Stringable): Stringable => string.upper()).toString()).toEqual('DISNEY WORLD'); }); }); describe('whenTest', (): void => { test('invokes the given closure if the string matches the given regular expression', (): void => { expect(Str.of('laravel framework').whenTest(/laravel/, (string: Stringable): Stringable => string.title()).toString()).toEqual('Laravel Framework'); }); test('invokes the fallback closure if the string does not match the given regular expression', (): void => { expect(Str.of('laravel framework').whenTest(/^[0-9]+$/, (string: Stringable): Stringable => string.title(), (string: Stringable): Stringable => string.upper()).toString()).toEqual('LARAVEL FRAMEWORK'); }); }); describe('words', (): void => { test('returns the original string when words limit is not reached', (): void => { expect(Str.of('Perfectly balanced, as all things should be.').words(10).toString()).toEqual('Perfectly balanced, as all things should be.'); }); test('returns the original string when it contains only one word', (): void => { expect(Str.of('Word').words(3).toString()).toEqual('Word'); }); test('returns the original string when words limit equals the number of words', (): void => { expect(Str.of('Perfectly balanced, as all things should be.').words(7).toString()).toEqual('Perfectly balanced, as all things should be.'); }); test('limits the number of words in the string', (): void => { expect(Str.of('Perfectly balanced, as all things should be.').words(3).toString()).toEqual('Perfectly balanced, as...'); }); test('prepends the end of the string', (): void => { expect(Str.of('Perfectly balanced, as all things should be.').words(3, ' >>>').toString()).toEqual('Perfectly balanced, as >>>'); }); }); describe('wordCount', (): void => { test('returns the number of words in the string', (): void => { expect(Str.of('Hello, world!').wordCount()).toEqual(2); }); }); describe('wordWrap', (): void => { test('wraps a string to a given number of characters', (): void => { expect(Str.of('The quick brown fox jumped over the lazy dog.').wordWrap(20, '
\n').toString()).toEqual('The quick brown fox
\njumped over the lazy
\ndog.'); }); }); describe('wrap', (): void => { test('wraps the given string with an additional string or a pair of strings', (): void => { expect(Str.of('Laravel').wrap('"').toString()).toEqual('"Laravel"'); expect(Str.of('is').wrap('This ', ' Laravel!').toString()).toEqual('This is Laravel!'); }); }); describe('unwrap', (): void => { test('removes the specified strings from the beginning and end of a given string', (): void => { expect(Str.of('-Laravel-').unwrap('-').toString()).toEqual('Laravel'); }); test('removes the specified strings from the beginning and end of a given string with different delimiters', (): void => { expect(Str.of('{framework: "Laravel"}').unwrap('{', '}').toString()).toEqual('framework: "Laravel"'); }); }); describe('toHtmlString', (): void => { test('converts the string instance to an instance of HTMLElement', (): void => { const input: HTMLInputElement = Str.of('').toHtmlString() as HtmlStringType as HTMLInputElement; expect(input).toBeInstanceOf(HTMLInputElement); expect(input.type).toEqual('text'); expect(input.placeholder).toEqual('Hello'); }); test('returns a string If no valid HTML is provided', (): void => { expect(Str.of('Hello').toHtmlString()).toEqual('Hello'); }); }); describe('toBase64', (): void => { test('converts the given string to Base64', (): void => { expect(Str.of('Laravel').toBase64().toString()).toEqual('TGFyYXZlbA=='); }); }); describe('fromBase64', (): void => { test('converts the given string from Base64', (): void => { expect(Str.of('TGFyYXZlbA==').fromBase64().toString()).toEqual('Laravel'); }); }); describe('dd', (): void => { test('dumps the given string and ends execution of the script', (): void => { const $console: jest.SpyInstance = jest.spyOn(console, 'log').mockImplementation(); expect((): never => Str.of('Laravel').dd()).toThrow('dd()'); expect($console).toHaveBeenCalledWith('Laravel'); $console.mockRestore(); }); }); describe('dump', (): void => { test('dumps the given string to the console', (): void => { const $console: jest.SpyInstance = jest.spyOn(console, 'log').mockImplementation(); Str.of('Laravel').dump(); expect($console).toHaveBeenCalledWith('Laravel'); $console.mockRestore(); }); }); describe('value', (): void => { test('gets the underlying string value', (): void => { expect(Str.of('Hello World').value()).toEqual('Hello World'); }); }); describe('toString', (): void => { test('gets the raw string value', (): void => { expect(Str.of('Laravel').toString()).toEqual('Laravel'); }); }); describe('toInteger', (): void => { test('returns the underlying string value as an integer', (): void => { expect(Str.of('42').toInteger()).toEqual(42); expect(Str.of('-13').toInteger()).toEqual(-13); }); test('returns 0 if the underlying string value is not a number', (): void => { expect(Str.of('Laravel').toInteger()).toEqual(0); expect(Str.of('').toInteger()).toEqual(0); expect(Str.of(' ').toInteger()).toEqual(0); }); test('handles different base values', (): void => { expect(Str.of('077').toInteger(0)).toEqual(77); expect(Str.of('1010').toInteger(2)).toEqual(10); expect(Str.of('42').toInteger(8)).toEqual(34); expect(Str.of('0xFF').toInteger(16)).toEqual(255); expect(Str.of('Z').toInteger(36)).toEqual(35); }); test('handles very large numbers', (): void => { expect(Str.of('9007199254740991').toInteger()).toEqual(Number.MAX_SAFE_INTEGER); }); test('handles partial number strings', (): void => { expect(Str.of('123abc').toInteger()).toEqual(123); expect(Str.of('abc123').toInteger()).toEqual(0); }); test('handles numeric separators', (): void => { expect(Str.of('1_000').toInteger()).toEqual(1); }); test('handles edge values', (): void => { expect(Str.of('Infinity').toInteger()).toEqual(0); expect(Str.of('NaN').toInteger()).toEqual(0); }); }); describe('toFloat', (): void => { test('returns the underlying string value as a float', (): void => { expect(Str.of('1.5').toFloat()).toEqual(1.5); }); test('returns 0 if the underlying string value is not a number', (): void => { expect(Str.of('Laravel').toFloat()).toEqual(0); }); }); describe('toBoolean', (): void => { test('returns the underlying string value as a boolean', (): void => { expect(Str.of('true').toBoolean()).toEqual(true); }); test('returns false if the underlying string value is not recognized as true', (): void => { expect(Str.of('Laravel').toBoolean()).toEqual(false); }); }); describe('toDate', (): void => { test('gets the underlying string value as a formatted Date string', (): void => { expect(Str.of('13 September 2023, 12:00 PM').toDate()).toEqual('9/13/2023, 12:00:00'); }); test('formats the date and sets the Timezone', (): void => { expect(Str.of('13 September 2023, 12:00 PM').toDate('Y-m-d H:i:s', 'Europe/London')).toEqual('2023-09-13 11:00:00'); expect(Str.of('13 September 2023, 12:00 PM').toDate('Y-m-d H:i:s', 'America/Toronto')).toEqual('2023-09-13 06:00:00'); }); test('returns "Invalid Date" for incorrect Date/Time string', (): void => { expect(Str.of('Laravel').toDate()).toEqual('Invalid Date'); }); test('format "d" returns the day of the month, 2 digits with leading zeros', (): void => { expect(Str.of('2024-02-29').toDate('d', 'CET')).toEqual('29'); }); test('format "D" returns a textual representation of a day, three letters', (): void => { expect(Str.of('2024-02-29').toDate('D', 'CET')).toEqual('Thu'); }); test('format "j" returns the day of the month without leading zeros', (): void => { expect(Str.of('2024-02-29').toDate('j', 'CET')).toEqual('29'); }); test('format "l" returns a full textual representation of the day of the week', (): void => { expect(Str.of('2024-02-29').toDate('l', 'CET')).toEqual('Thursday'); }); test('format "N" returns ISO 8601 numeric representation of the day of the week', (): void => { expect(Str.of('2024-02-29').toDate('N', 'CET')).toEqual('4'); }); test('format "S" returns English ordinal suffix for the day of the month, 2 characters', (): void => { expect(Str.of('2024-02-29').toDate('S', 'CET')).toEqual('th'); }); test('format "w" returns numeric representation of the day of the week', (): void => { expect(Str.of('2024-02-29').toDate('w', 'CET')).toEqual('4'); }); test('format "z" returns numeric representation of the day of the week', (): void => { expect(Str.of('2024-02-29').toDate('z', 'CET')).toEqual('60'); }); test('format "W" returns ISO 8601 week number of year, weeks starting on Monday', (): void => { expect(Str.of('2024-02-29').toDate('W', 'CET')).toEqual('09'); }); test('format "F" returns a full textual representation of a month', (): void => { expect(Str.of('2024-02-29').toDate('F', 'CET')).toEqual('February'); }); test('format "m" returns numeric representation of a month, with leading zeros', (): void => { expect(Str.of('2024-02-29').toDate('m', 'CET')).toEqual('02'); }); test('format "M" returns a short textual representation of a month, three letters', (): void => { expect(Str.of('2024-02-29').toDate('M', 'CET')).toEqual('Feb'); }); test('format "n" returns numeric representation of a month, without leading zeros', (): void => { expect(Str.of('2024-02-29').toDate('n', 'CET')).toEqual('2'); }); test('format "t" returns number of days in the given month', (): void => { expect(Str.of('2024-02-29').toDate('t', 'CET')).toEqual('29'); }); test('format "L"\' returns whether it"s a leap year', (): void => { expect(Str.of('2024-02-29').toDate('L', 'CET')).toEqual('1'); }); test('format "o" returns ISO 8601 week-numbering year', (): void => { expect(Str.of('2024-02-29').toDate('o', 'CET')).toEqual('2024'); }); test('format "X" returns an expanded full numeric representation of a year', (): void => { expect(Str.of('2024-02-29').toDate('X', 'CET')).toEqual('+2024'); }); test('format "x" returns an expanded full numeric representation if required', (): void => { expect(Str.of('2024-02-29').toDate('x', 'CET')).toEqual('2024'); }); test('format "Y" returns a full numeric representation of a year', (): void => { expect(Str.of('2024-02-29').toDate('Y', 'CET')).toEqual('2024'); }); test('format "y" returns a two-digit representation of a year', (): void => { expect(Str.of('2024-02-29').toDate('y', 'CET')).toEqual('24'); }); test('format "a" returns lowercase Ante meridiem and Post meridiem', (): void => { expect(Str.of('2024-02-29 08:00:00').toDate('a', 'CET')).toEqual('am'); }); test('format "A" returns uppercase Ante meridiem and Post meridiem', (): void => { expect(Str.of('2024-02-29 20:00:00').toDate('A', 'CET')).toEqual('PM'); }); test('format "B" returns Swatch Internet time', (): void => { expect(Str.of('2024-02-29 12:00:00').toDate('B', 'CET')).toEqual('500'); }); test('format "g" returns 12-hour format of an hour without leading zeros', (): void => { expect(Str.of('2024-02-29 08:00:00').toDate('g', 'CET')).toEqual('8'); }); test('format "G" returns 24-hour format of an hour without leading zeros', (): void => { expect(Str.of('2024-02-29 20:00:00').toDate('G', 'CET')).toEqual('20'); }); test('format "h" returns 12-hour format of an hour with leading zeros', (): void => { expect(Str.of('2024-02-29 08:00:00').toDate('h', 'CET')).toEqual('08'); }); test('format "H" returns 24-hour format of an hour with leading zeros', (): void => { expect(Str.of('2024-02-29 20:00:00').toDate('H', 'CET')).toEqual('20'); }); test('format "i" returns minutes with leading zeros', (): void => { expect(Str.of('2024-02-29 08:09:00').toDate('i', 'CET')).toEqual('09'); }); test('format "s" returns seconds with leading zeros', (): void => { expect(Str.of('2024-02-29 08:09:07').toDate('s', 'CET')).toEqual('07'); }); test('format "u" returns microseconds', (): void => { expect((): string => Str.of('2024-02-29 08:09:07.654321').toDate('u', 'CET')).toThrow('Microseconds are not supported.'); }); test('format "v" returns milliseconds', (): void => { expect(Str.of('2024-02-29 08:09:07.654').toDate('v', 'CET')).toEqual('654'); }); test('format "e" returns timezone identifier', (): void => { expect(Str.of('2024-02-29 08:09:07').toDate('e', 'CET')).toMatch(/^(?:GMT|UTC|[A-Za-z\/_]+(?:[+\-][0-9]+)?)$/); }); test('format "I" returns whether or not the date is in daylight saving time', (): void => { expect(Str.of('2024-02-29').toDate('I', 'CET')).toMatch(/^[01]$/); }); test('format "O" returns difference to Greenwich time (GMT) without colon between hours and minutes', (): void => { expect(Str.of('2024-02-29').toDate('O', 'CET')).toMatch(/^[+\-]\d{4}$/); }); test('format "P" returns difference to Greenwich time (GMT) with colon between hours and minutes', (): void => { expect(Str.of('2024-02-29').toDate('P', 'CET')).toMatch(/^[+\-]\d{2}:\d{2}$/); }); test('format "p" returns the same as P, but returns Z instead of +00:00', (): void => { expect(Str.of('2024-02-29').toDate('p', 'CET')).toMatch(/^[+\-]\d{2}:\d{2}$/); }); test('format "T" returns timezone abbreviation, if known; otherwise the GMT offset', (): void => { expect(Str.of('2024-02-29').toDate('T', 'CET')).toMatch(/^[A-Za-z]+|[+\-]\d{2}:\d{2}$/); }); test('format "Z" returns timezone offset in seconds', (): void => { expect(Str.of('2024-02-29').toDate('Z', 'CET')).toMatch(/([+\-]?\d+)/); }); test('format "c" returns ISO 8601 date', (): void => { expect(Str.of('2024-02-29 08:09:07').toDate('c', 'CET')).toEqual('2024-02-29T08:09:07+01:00'); }); test('format "r" returns seconds since the Unix Epoch', (): void => { expect(Str.of('2024-02-29').toDate('r', 'CET')).toEqual('Thu, 29 Feb 2024 01:00:00 +0100'); }); test('format "U" returns RFC 2822/RFC 5322 formatted date', (): void => { expect(Str.of('2024-02-29').toDate('U', 'CET')).toMatch(/^-?\d+$/); }); }); }); describe('str', (): void => { test('returns instance of Stringable', (): void => { expect(str()).toBeInstanceOf(Stringable); }); });