diff --git a/src/modules/random/index.ts b/src/modules/random/index.ts index a95b44ab887..57783c9bbf8 100644 --- a/src/modules/random/index.ts +++ b/src/modules/random/index.ts @@ -1,6 +1,7 @@ import type { Faker } from '../..'; import { FakerError } from '../../errors/faker-error'; import { deprecated } from '../../internal/deprecated'; +import type { LiteralUnion } from '../../utils/types'; export type Casing = 'upper' | 'lower' | 'mixed'; @@ -8,6 +9,77 @@ const UPPER_CHARS: readonly string[] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); const LOWER_CHARS: readonly string[] = 'abcdefghijklmnopqrstuvwxyz'.split(''); const DIGIT_CHARS: readonly string[] = '0123456789'.split(''); +export type LowerAlphaChar = + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | 'g' + | 'h' + | 'i' + | 'j' + | 'k' + | 'l' + | 'm' + | 'n' + | 'o' + | 'p' + | 'q' + | 'r' + | 's' + | 't' + | 'u' + | 'v' + | 'w' + | 'x' + | 'y' + | 'z'; + +export type UpperAlphaChar = + | 'A' + | 'B' + | 'C' + | 'D' + | 'E' + | 'F' + | 'G' + | 'H' + | 'I' + | 'J' + | 'K' + | 'L' + | 'M' + | 'N' + | 'O' + | 'P' + | 'Q' + | 'R' + | 'S' + | 'T' + | 'U' + | 'V' + | 'W' + | 'X' + | 'Y' + | 'Z'; + +export type NumericChar = + | '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9'; + +export type AlphaChar = LowerAlphaChar | UpperAlphaChar; +export type AlphaNumericChar = AlphaChar | NumericChar; + /** * Method to reduce array of characters. * @@ -172,7 +244,7 @@ export class Random { */ upcase?: boolean; casing?: Casing; - bannedChars?: readonly string[]; + bannedChars?: readonly LiteralUnion[] | string; } = {} ): string { if (typeof options === 'number') { @@ -180,7 +252,13 @@ export class Random { count: options, }; } - const { count = 1, upcase, bannedChars = [] } = options; + + const { count = 1, upcase } = options; + let { bannedChars = [] } = options; + + if (typeof bannedChars === 'string') { + bannedChars = bannedChars.split(''); + } if (count <= 0) { return ''; @@ -244,7 +322,7 @@ export class Random { count: number = 1, options: { casing?: Casing; - bannedChars?: readonly string[]; + bannedChars?: readonly LiteralUnion[] | string; } = {} ): string { if (count <= 0) { @@ -254,8 +332,12 @@ export class Random { const { // Switch to 'mixed' with v8.0 casing = 'lower', - bannedChars = [], } = options; + let { bannedChars = [] } = options; + + if (typeof bannedChars === 'string') { + bannedChars = bannedChars.split(''); + } let charsArray = [...DIGIT_CHARS]; @@ -304,14 +386,19 @@ export class Random { length: number = 1, options: { allowLeadingZeros?: boolean; - bannedDigits?: readonly string[]; + bannedDigits?: readonly LiteralUnion[] | string; } = {} ): string { if (length <= 0) { return ''; } - const { allowLeadingZeros = false, bannedDigits = [] } = options; + const { allowLeadingZeros = false } = options; + let { bannedDigits = [] } = options; + + if (typeof bannedDigits === 'string') { + bannedDigits = bannedDigits.split(''); + } const allowedDigits = DIGIT_CHARS.filter( (digit) => !bannedDigits.includes(digit) diff --git a/test/random.spec.ts b/test/random.spec.ts index 3ff24ff7558..fc57ca435b0 100644 --- a/test/random.spec.ts +++ b/test/random.spec.ts @@ -213,6 +213,16 @@ describe('random', () => { expect(actual).toMatch(/^[b-oq-z]{5}$/); }); + it('should be able to ban some characters via string', () => { + const actual = faker.random.alpha({ + count: 5, + bannedChars: 'ap', + }); + + expect(actual).toHaveLength(5); + expect(actual).toMatch(/^[b-oq-z]{5}$/); + }); + it('should be able handle mistake in banned characters array', () => { const alphaText = faker.random.alpha({ count: 5, @@ -296,6 +306,18 @@ describe('random', () => { } }); + it('should be able to ban all alphabetic characters via string', () => { + const bannedChars = 'abcdefghijklmnopqrstuvwxyz'; + const alphaText = faker.random.alphaNumeric(5, { + bannedChars, + }); + + expect(alphaText).toHaveLength(5); + for (const bannedChar of bannedChars) { + expect(alphaText).not.includes(bannedChar); + } + }); + it('should be able to ban all numeric characters', () => { const bannedChars = '0123456789'.split(''); const alphaText = faker.random.alphaNumeric(5, { @@ -308,6 +330,18 @@ describe('random', () => { } }); + it('should be able to ban all numeric characters via string', () => { + const bannedChars = '0123456789'; + const alphaText = faker.random.alphaNumeric(5, { + bannedChars, + }); + + expect(alphaText).toHaveLength(5); + for (const bannedChar of bannedChars) { + expect(alphaText).not.includes(bannedChar); + } + }); + it('should be able to handle mistake in banned characters array', () => { const alphaText = faker.random.alphaNumeric(5, { bannedChars: ['a', 'p', 'a'], @@ -330,6 +364,15 @@ describe('random', () => { ); }); + it('should throw if all possible characters being banned via string', () => { + const bannedChars = 'abcdefghijklmnopqrstuvwxyz0123456789'; + expect(() => + faker.random.alphaNumeric(5, { + bannedChars, + }) + ).toThrowError(); + }); + it('should not mutate the input object', () => { const input: { bannedChars: string[]; @@ -395,6 +438,15 @@ describe('random', () => { expect(actual).toBe('0000'); }); + it('should allow leading zeros via option and all other digits banned via string', () => { + const actual = faker.random.numeric(4, { + allowLeadingZeros: true, + bannedDigits: '123456789', + }); + + expect(actual).toBe('0000'); + }); + it('should fail on leading zeros via option and all other digits banned', () => { expect(() => faker.random.numeric(4, { @@ -408,6 +460,19 @@ describe('random', () => { ); }); + it('should fail on leading zeros via option and all other digits banned via string', () => { + expect(() => + faker.random.numeric(4, { + allowLeadingZeros: false, + bannedDigits: '123456789', + }) + ).toThrowError( + new FakerError( + 'Unable to generate numeric string, because all possible digits are banned.' + ) + ); + }); + it('should ban all digits passed via bannedDigits', () => { const actual = faker.random.numeric(1000, { bannedDigits: 'c84U1'.split(''), @@ -416,6 +481,15 @@ describe('random', () => { expect(actual).toHaveLength(1000); expect(actual).toMatch(/^[0235679]{1000}$/); }); + + it('should ban all digits passed via bannedDigits via string', () => { + const actual = faker.random.numeric(1000, { + bannedDigits: 'c84U1', + }); + + expect(actual).toHaveLength(1000); + expect(actual).toMatch(/^[0235679]{1000}$/); + }); }); }); });