diff --git a/src/globals/String.ts b/src/globals/String.ts new file mode 100644 index 0000000..84ff3be --- /dev/null +++ b/src/globals/String.ts @@ -0,0 +1,57 @@ +/** + * @athenna/common + * + * (c) Robson Trasel + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { String as StringHelper } from '#src/helpers/String' + +export class AthennaString { + public constructor(private value: string) {} + + /** + * Check if at least one of the provided search strings + * is included in the given string value. + * + * @example + * ```ts + * 'Hello model.id'.athenna.includesSome('models.id', 'models.provider') // false + * 'Hello models.id'.athenna.includesSome(['models.id', 'provider']) // true + * ``` + */ + public includesSome(...searches: (string | string[])[]): boolean { + return StringHelper.includesSome(this.value, ...searches) + } + + /** + * Check if every provided search string is included + * in the given string value. + * + * @example + * ```ts + * 'Hello model.id'.athenna.includesEvery('models.id', 'models.provider') // false + * 'Hello model.id'.athenna.includesEvery(['model.id', 'Hello']) // true + * ``` + */ + public includesEvery(...searches: (string | string[])[]): boolean { + return StringHelper.includesEvery(this.value, ...searches) + } +} + +declare global { + interface String { + athenna: AthennaString + } +} + +if (!String.prototype.athenna) { + // eslint-disable-next-line no-extend-native + Object.defineProperty(String.prototype, 'athenna', { + get: function () { + return new AthennaString(this) + } + }) +} diff --git a/src/helpers/String.ts b/src/helpers/String.ts index ccff41d..0da3b9b 100644 --- a/src/helpers/String.ts +++ b/src/helpers/String.ts @@ -252,4 +252,41 @@ export class String extends Macroable { return `${value}th` } } + + /** + * Check if at least one of the provided search strings + * is included in the given value. + * + * @example + * ```ts + * String.includesSome('Hello model.id', 'models.id', 'models.provider') // false + * String.includesSome('Hello models.id', ['models.id', 'provider']) // true (models.id is found) + * ``` + */ + public static includesSome( + value: string, + ...searches: (string | string[])[] + ): boolean { + const terms = Array.isArray(searches[0]) ? (searches[0] as string[]) : (searches as string[]) + return terms.some(term => value.includes(term)) + } + + /** + * Check if every provided search string is included + * in the given value. + * + * @example + * ```ts + * String.includesEvery('Hello model.id', 'models.id', 'models.provider') // false + * String.includesEvery('Hello model.id', ['model.id', 'Hello']) // true (both are found) + * ``` + */ + public static includesEvery( + value: string, + ...searches: (string | string[])[] + ): boolean { + const terms = Array.isArray(searches[0]) ? (searches[0] as string[]) : (searches as string[]) + return terms.every(term => value.includes(term)) + } + } diff --git a/src/index.ts b/src/index.ts index 31e72c9..5dcdd8e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,9 +11,15 @@ export * from '#src/types' export * from '#src/constants/alphabet' +import '#src/globals/Enum' +import '#src/globals/Error' +import '#src/globals/Array' +import '#src/globals/String' + export * from '#src/globals/Enum' export * from '#src/globals/Error' export * from '#src/globals/Array' +export * from '#src/globals/String' export * from '#src/helpers/Exception' export * from '#src/helpers/Clean' diff --git a/tests/unit/globals/StringTest.ts b/tests/unit/globals/StringTest.ts new file mode 100644 index 0000000..e1967c2 --- /dev/null +++ b/tests/unit/globals/StringTest.ts @@ -0,0 +1,71 @@ +/** + * @athenna/common + * + * (c) Robson Trasel + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { String } from '#src' +import { Test, type Context } from '@athenna/test' + +export default class GlobalStringTest { + @Test() + public async shouldBeAbleToUseStaticMethodsFromStringHelper({ assert }: Context) { + const value = 'Hello model.id and some text' + + assert.isTrue(String.includesSome(value, 'model.id', 'nope')) + assert.isTrue(String.includesEvery(value, 'Hello', 'model.id')) + } + + @Test() + public async shouldReturnTrueIfAtLeastOneTermMatchesUsingMultipleParams({ assert }: Context) { + const value = 'Hello model.id and some other text' + + assert.isTrue(value.athenna.includesSome('model.id', 'nope')) + assert.isTrue(value.athenna.includesSome('some', 'anything')) + assert.isFalse(value.athenna.includesSome('not-found', 'nope')) + } + + @Test() + public async shouldReturnTrueIfAtLeastOneTermMatchesUsingAnArray({ assert }: Context) { + const value = 'Hello model.id and some other text' + + assert.isTrue(value.athenna.includesSome(['model.id', 'random'])) + assert.isFalse(value.athenna.includesSome(['aaa', 'bbb'])) + } + + @Test() + public async shouldReturnFalseForIncludesSomeWithEmptyTerms({ assert }: Context) { + const value = 'anything' + + assert.isFalse(value.athenna.includesSome()) + assert.isFalse(value.athenna.includesSome([])) + } + + @Test() + public async shouldReturnTrueOnlyIfAllTermsMatchUsingMultipleParams({ assert }: Context) { + const value = 'Hello model.id and some text' + + assert.isFalse(value.athenna.includesEvery('Hello', 'somethingElse')) + assert.isTrue(value.athenna.includesEvery('Hello', 'model.id')) + assert.isFalse(value.athenna.includesEvery('model.id', 'not-found')) + } + + @Test() + public async shouldReturnTrueOnlyIfAllTermsMatchUsingAnArray({ assert }: Context) { + const value = 'Hello model.id and some text' + + assert.isTrue(value.athenna.includesEvery(['Hello', 'model.id'])) + assert.isFalse(value.athenna.includesEvery(['Hello', 'random'])) + } + + @Test() + public async shouldReturnTrueForIncludesEveryWithEmptyTerms({ assert }: Context) { + const value = 'anything' + + assert.isTrue(value.athenna.includesEvery()) + assert.isTrue(value.athenna.includesEvery([])) + } +} diff --git a/tests/unit/helpers/StringTest.ts b/tests/unit/helpers/StringTest.ts index 7545097..65d2fd6 100644 --- a/tests/unit/helpers/StringTest.ts +++ b/tests/unit/helpers/StringTest.ts @@ -128,4 +128,50 @@ export default class StringTest { assert.throws(useCase, OrdinalNanException) } + + @Test() + public async shouldReturnTrueIfAtLeastOneTermMatchesUsingMultipleParams({ assert }: Context) { + const base = 'Hello model.id and some other text' + + assert.isTrue(String.includesSome(base, 'model.id', 'nope')) + assert.isTrue(String.includesSome(base, 'some', 'anything')) + assert.isFalse(String.includesSome(base, 'not-found', 'nope')) + } + + @Test() + public async shouldReturnTrueIfAtLeastOneTermMatchesUsingAnArray({ assert }: Context) { + const base = 'Hello model.id and some other text' + + assert.isTrue(String.includesSome(base, ['model.id', 'random'])) + assert.isFalse(String.includesSome(base, ['aaa', 'bbb'])) + } + + @Test() + public async shouldReturnFalseForIncludesSomeWithEmptyTerms({ assert }: Context) { + assert.isFalse(String.includesSome('anything')) + assert.isFalse(String.includesSome('anything', [])) + } + + @Test() + public async shouldReturnTrueOnlyIfAllTermsMatchUsingMultipleParams({ assert }: Context) { + const base = 'Hello model.id and some text' + + assert.isFalse(String.includesEvery(base, 'Hello', 'somethingElse')) + assert.isTrue(String.includesEvery(base, 'Hello', 'model.id')) + assert.isFalse(String.includesEvery(base, 'model.id', 'not-found')) + } + + @Test() + public async shouldReturnTrueOnlyIfAllTermsMatchUsingAnArray({ assert }: Context) { + const base = 'Hello model.id and some text' + + assert.isTrue(String.includesEvery(base, ['Hello', 'model.id'])) + assert.isFalse(String.includesEvery(base, ['Hello', 'random'])) + } + + @Test() + public async shouldReturnTrueForIncludesEveryWithEmptyTerms({ assert }: Context) { + assert.isTrue(String.includesEvery('anything')) + assert.isTrue(String.includesEvery('anything', [])) + } }