From 8068cce4eb51e36df1a95c62216e93ef78a9d36b Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Thu, 14 Nov 2024 22:31:03 +0100 Subject: [PATCH] infra(unicorn): consistent-function-scoping (#3255) --- eslint.config.ts | 1 - src/modules/color/index.ts | 26 +++++++---- src/modules/food/index.ts | 18 +++++--- src/modules/internet/index.ts | 83 ++++++++++++++++++++++------------- src/modules/system/index.ts | 21 +++++---- test/modules/date.spec.ts | 26 +++++------ test/modules/git.spec.ts | 23 +++++----- test/modules/number.spec.ts | 16 +++---- test/utils/mersenne.spec.ts | 8 ++-- 9 files changed, 132 insertions(+), 90 deletions(-) diff --git a/eslint.config.ts b/eslint.config.ts index aeebdf0e24d..6077b0a64d7 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -162,7 +162,6 @@ const config: ReturnType = tseslint.config( // TODO @Shinigami92 2023-09-23: The following rules currently conflict with our code. // Each rule should be checked whether it should be enabled/configured and the problems fixed, or stay disabled permanently. - 'unicorn/consistent-function-scoping': 'off', 'unicorn/prefer-export-from': 'off', 'unicorn/prevent-abbreviations': 'off', }, diff --git a/src/modules/color/index.ts b/src/modules/color/index.ts index 2b355062168..10d7023a245 100644 --- a/src/modules/color/index.ts +++ b/src/modules/color/index.ts @@ -101,6 +101,15 @@ function toBinary(values: number[]): string { return binary.join(' '); } +/** + * Converts the given value to a percentage (`round(value * 100)`). + * + * @param value The value to convert to a percentage. + */ +function toPercentage(value: number): number { + return Math.round(value * 100); +} + /** * Converts an array of numbers into CSS accepted format. * @@ -113,7 +122,6 @@ function toCSS( cssFunction: CssFunctionType = 'rgb', space: CssSpaceType = 'sRGB' ): string { - const percentage = (value: number) => Math.round(value * 100); switch (cssFunction) { case 'rgba': { return `rgba(${values[0]}, ${values[1]}, ${values[2]}, ${values[3]})`; @@ -124,35 +132,35 @@ function toCSS( } case 'cmyk': { - return `cmyk(${percentage(values[0])}%, ${percentage( + return `cmyk(${toPercentage(values[0])}%, ${toPercentage( values[1] - )}%, ${percentage(values[2])}%, ${percentage(values[3])}%)`; + )}%, ${toPercentage(values[2])}%, ${toPercentage(values[3])}%)`; } case 'hsl': { - return `hsl(${values[0]}deg ${percentage(values[1])}% ${percentage( + return `hsl(${values[0]}deg ${toPercentage(values[1])}% ${toPercentage( values[2] )}%)`; } case 'hsla': { - return `hsl(${values[0]}deg ${percentage(values[1])}% ${percentage( + return `hsl(${values[0]}deg ${toPercentage(values[1])}% ${toPercentage( values[2] - )}% / ${percentage(values[3])})`; + )}% / ${toPercentage(values[3])})`; } case 'hwb': { - return `hwb(${values[0]} ${percentage(values[1])}% ${percentage( + return `hwb(${values[0]} ${toPercentage(values[1])}% ${toPercentage( values[2] )}%)`; } case 'lab': { - return `lab(${percentage(values[0])}% ${values[1]} ${values[2]})`; + return `lab(${toPercentage(values[0])}% ${values[1]} ${values[2]})`; } case 'lch': { - return `lch(${percentage(values[0])}% ${values[1]} ${values[2]})`; + return `lch(${toPercentage(values[0])}% ${values[1]} ${values[2]})`; } case 'rgb': { diff --git a/src/modules/food/index.ts b/src/modules/food/index.ts index 45b91f3047e..8dd77741282 100644 --- a/src/modules/food/index.ts +++ b/src/modules/food/index.ts @@ -1,4 +1,17 @@ import { ModuleBase } from '../../internal/module-base'; + +/** + * Converts the given string to title case. + * + * @param text The text to convert. + */ +function toTitleCase(text: string): string { + return text + .split(' ') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +} + /** * Module for generating food-related data. * @@ -47,11 +60,6 @@ export class FoodModule extends ModuleBase { */ dish(): string { // A 50/50 mix of specific dishes and dish_patterns - const toTitleCase = (s: string) => - s - .split(' ') - .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) - .join(' '); if (this.faker.datatype.boolean()) { return toTitleCase( this.faker.helpers.fake(this.faker.definitions.food.dish_pattern) diff --git a/src/modules/internet/index.ts b/src/modules/internet/index.ts index 115bd21581d..4c78f4cb825 100644 --- a/src/modules/internet/index.ts +++ b/src/modules/internet/index.ts @@ -1,4 +1,5 @@ import { FakerError } from '../../errors/faker-error'; +import type { Faker } from '../../faker'; import { toBase64Url } from '../../internal/base64'; import { deprecated } from '../../internal/deprecated'; import { ModuleBase } from '../../internal/module-base'; @@ -102,6 +103,50 @@ const ipv4Networks: Record = { [IPv4Network.Multicast]: '224.0.0.0/4', }; +/** + * Checks whether the given string is a valid slug for `domainWord`s. + * + * @param slug The slug to check. + */ +function isValidDomainWordSlug(slug: string): boolean { + return /^[a-z][a-z-]*[a-z]$/i.exec(slug) !== null; +} + +/** + * Tries various ways to produce a valid domain word slug, falling back to a random string if needed. + * + * @param faker The faker instance to use. + * @param word The initial word to slugify. + */ +function makeValidDomainWordSlug(faker: Faker, word: string): string { + const slug1 = faker.helpers.slugify(word); + if (isValidDomainWordSlug(slug1)) { + return slug1; + } + + const slug2 = faker.helpers.slugify(faker.lorem.word()); + if (isValidDomainWordSlug(slug2)) { + return slug2; + } + + return faker.string.alpha({ + casing: 'lower', + length: faker.number.int({ min: 4, max: 8 }), + }); +} + +/** + * Generates a random color in hex format with the given base color. + * + * @param faker The faker instance to use. + * @param base The base color to use. + */ +function colorFromBase(faker: Faker, base: number): string { + return Math.floor((faker.number.int(256) + base) / 2) + .toString(16) + .padStart(2, '0'); +} + /** * Module to generate internet related entries. * @@ -597,29 +642,12 @@ export class InternetModule extends ModuleBase { domainWord(): string { // Generate an ASCII "word" in the form `noun-adjective` // For locales with non-ASCII characters, we fall back to lorem words, or a random string - const isValidSlug = (slug: string): boolean => { - return /^[a-z][a-z-]*[a-z]$/i.exec(slug) !== null; - }; - - const makeValidSlug = (word: string): string => { - const slug1 = this.faker.helpers.slugify(word); - if (isValidSlug(slug1)) { - return slug1; - } - const slug2 = this.faker.helpers.slugify(this.faker.lorem.word()); - if (isValidSlug(slug2)) { - return slug2; - } - - return this.faker.string.alpha({ - casing: 'lower', - length: this.faker.number.int({ min: 4, max: 8 }), - }); - }; - - const word1 = makeValidSlug(this.faker.word.adjective()); - const word2 = makeValidSlug(this.faker.word.noun()); + const word1 = makeValidDomainWordSlug( + this.faker, + this.faker.word.adjective() + ); + const word2 = makeValidDomainWordSlug(this.faker, this.faker.word.noun()); return `${word1}-${word2}`.toLowerCase(); } @@ -819,14 +847,9 @@ export class InternetModule extends ModuleBase { ): string { const { redBase = 0, greenBase = 0, blueBase = 0 } = options; - const colorFromBase = (base: number): string => - Math.floor((this.faker.number.int(256) + base) / 2) - .toString(16) - .padStart(2, '0'); - - const red = colorFromBase(redBase); - const green = colorFromBase(greenBase); - const blue = colorFromBase(blueBase); + const red = colorFromBase(this.faker, redBase); + const green = colorFromBase(this.faker, greenBase); + const blue = colorFromBase(this.faker, blueBase); return `#${red}${green}${blue}`; } diff --git a/src/modules/system/index.ts b/src/modules/system/index.ts index aec3525e747..1c387e519d8 100644 --- a/src/modules/system/index.ts +++ b/src/modules/system/index.ts @@ -263,17 +263,17 @@ export class SystemModule extends ModuleBase { let suffix: string; let prefix = ''; - const digit = () => this.faker.string.numeric({ allowLeadingZeros: true }); switch (interfaceSchema) { case 'index': { - suffix = digit(); + suffix = this.faker.string.numeric(); break; } case 'slot': { - suffix = `${digit()}${ - this.faker.helpers.maybe(() => `f${digit()}`) ?? '' - }${this.faker.helpers.maybe(() => `d${digit()}`) ?? ''}`; + suffix = `${this.faker.string.numeric()}${ + this.faker.helpers.maybe(() => `f${this.faker.string.numeric()}`) ?? + '' + }${this.faker.helpers.maybe(() => `d${this.faker.string.numeric()}`) ?? ''}`; break; } @@ -283,10 +283,13 @@ export class SystemModule extends ModuleBase { } case 'pci': { - prefix = this.faker.helpers.maybe(() => `P${digit()}`) ?? ''; - suffix = `${digit()}s${digit()}${ - this.faker.helpers.maybe(() => `f${digit()}`) ?? '' - }${this.faker.helpers.maybe(() => `d${digit()}`) ?? ''}`; + prefix = + this.faker.helpers.maybe(() => `P${this.faker.string.numeric()}`) ?? + ''; + suffix = `${this.faker.string.numeric()}s${this.faker.string.numeric()}${ + this.faker.helpers.maybe(() => `f${this.faker.string.numeric()}`) ?? + '' + }${this.faker.helpers.maybe(() => `d${this.faker.string.numeric()}`) ?? ''}`; break; } } diff --git a/test/modules/date.spec.ts b/test/modules/date.spec.ts index e30da8d9580..19a2d0a7504 100644 --- a/test/modules/date.spec.ts +++ b/test/modules/date.spec.ts @@ -12,6 +12,19 @@ const converterMap = [ const NON_SEEDED_BASED_RUN = 5; const refDate = '2021-02-21T17:09:15.711Z'; +function calculateAge(birthdate: Date, refDate: Date): number { + let age = refDate.getFullYear() - birthdate.getFullYear(); + if ( + refDate.getMonth() < birthdate.getMonth() || + (refDate.getMonth() === birthdate.getMonth() && + refDate.getDate() < birthdate.getDate()) + ) { + age--; + } + + return age; +} + describe('date', () => { seededTests(faker, 'date', (t) => { t.describe('anytime', (t) => { @@ -530,19 +543,6 @@ describe('date', () => { }); describe('birthdate', () => { - function calculateAge(birthdate: Date, refDate: Date): number { - let age = refDate.getFullYear() - birthdate.getFullYear(); - if ( - refDate.getMonth() < birthdate.getMonth() || - (refDate.getMonth() === birthdate.getMonth() && - refDate.getDate() < birthdate.getDate()) - ) { - age--; - } - - return age; - } - it('returns a random birthdate', () => { const birthdate = faker.date.birthdate(); expect(birthdate).toBeInstanceOf(Date); diff --git a/test/modules/git.spec.ts b/test/modules/git.spec.ts index e4692b20477..164b19c6e86 100644 --- a/test/modules/git.spec.ts +++ b/test/modules/git.spec.ts @@ -8,6 +8,16 @@ const NON_SEEDED_BASED_RUN = 5; const refDate = '2020-01-01T00:00:00.000Z'; +function isValidCommitAuthor(email: string): boolean { + // `validator.isEmail()` does not support display names + // that contain unquoted characters like . output by Git so we need + // to quote the display name + const quotedEmail = email.replace(/^(.*) { seededTests(faker, 'git', (t) => { t.itEach('branch', 'commitMessage'); @@ -56,27 +66,18 @@ describe('git', () => { expect(parts.length).toBeLessThanOrEqual(7); expect(parts[0]).toMatch(/^commit [a-f0-9]+$/); - const isValidAuthor = (email: string) => { - // `validator.isEmail()` does not support display names - // that contain unquoted characters like . output by Git so we need - // to quote the display name - const quotedEmail = email.replace(/^(.*) char === '0' || char === '1'); +} + describe('number', () => { seededTests(faker, 'number', (t) => { t.describeEach( @@ -259,10 +267,6 @@ describe('number', () => { }); describe('float', () => { - function isFloat(value: number) { - return value % 1 !== 0; - } - it('should return a float between 0 and 1 (inclusive) by default', () => { const actual = faker.number.float(); @@ -405,10 +409,6 @@ describe('number', () => { }); describe('binary', () => { - function isBinary(str: string) { - return [...str].every((char) => char === '0' || char === '1'); - } - it('generates single binary character when no additional argument was provided', () => { const binary = faker.number.binary(); diff --git a/test/utils/mersenne.spec.ts b/test/utils/mersenne.spec.ts index 5e9e4c27bc6..4699b9b0706 100644 --- a/test/utils/mersenne.spec.ts +++ b/test/utils/mersenne.spec.ts @@ -23,6 +23,10 @@ function newTwister( return twister; } +function randomSeed(): number { + return Math.ceil(Math.random() * 1_000_000_000); +} + describe('MersenneTwister19937', () => { describe('genrandInt32()', () => { it('should be able to return 0', () => { @@ -112,10 +116,6 @@ describe.each([ }); }); - function randomSeed(): number { - return Math.ceil(Math.random() * 1_000_000_000); - } - // Create and log-back the seed for debug purposes describe.each( times(NON_SEEDED_BASED_RUN).flatMap(() => [