From cc3425dc7706734f7a2a214f6f72eab5539fbe9b Mon Sep 17 00:00:00 2001 From: Istvan Olah <1.olah.istvan.75@gmail.com> Date: Tue, 31 Oct 2023 13:34:24 +0200 Subject: [PATCH] feat(new tool): random MAC address generator (#657) * #521 Random MAC address generator * refactor(mac-address-generator): improved ux * refactor(mac-address-generator): improved ux --------- Co-authored-by: Corentin THOMASSET --- components.d.ts | 1 + src/tools/index.ts | 3 +- src/tools/mac-address-generator/index.ts | 12 ++ .../mac-address-generator.e2e.spec.ts | 11 ++ .../mac-address-generator.vue | 103 ++++++++++++++++++ .../mac-adress-generator.models.test.ts | 43 ++++++++ .../mac-adress-generator.models.ts | 18 +++ src/utils/macAddress.ts | 16 ++- 8 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 src/tools/mac-address-generator/index.ts create mode 100644 src/tools/mac-address-generator/mac-address-generator.e2e.spec.ts create mode 100644 src/tools/mac-address-generator/mac-address-generator.vue create mode 100644 src/tools/mac-address-generator/mac-adress-generator.models.test.ts create mode 100644 src/tools/mac-address-generator/mac-adress-generator.models.ts diff --git a/components.d.ts b/components.d.ts index 7e0ca9205..91f8fe08e 100644 --- a/components.d.ts +++ b/components.d.ts @@ -116,6 +116,7 @@ declare module '@vue/runtime-core' { KeycodeInfo: typeof import('./src/tools/keycode-info/keycode-info.vue')['default'] ListConverter: typeof import('./src/tools/list-converter/list-converter.vue')['default'] LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default'] + MacAddressGenerator: typeof import('./src/tools/mac-address-generator/mac-address-generator.vue')['default'] MacAddressLookup: typeof import('./src/tools/mac-address-lookup/mac-address-lookup.vue')['default'] MathEvaluator: typeof import('./src/tools/math-evaluator/math-evaluator.vue')['default'] MenuBar: typeof import('./src/tools/html-wysiwyg-editor/editor/menu-bar.vue')['default'] diff --git a/src/tools/index.ts b/src/tools/index.ts index e6df8cc36..22db0770c 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,6 +1,7 @@ import { tool as base64FileConverter } from './base64-file-converter'; import { tool as base64StringConverter } from './base64-string-converter'; import { tool as basicAuthGenerator } from './basic-auth-generator'; +import { tool as macAddressGenerator } from './mac-address-generator'; import { tool as textToBinary } from './text-to-binary'; import { tool as ulidGenerator } from './ulid-generator'; import { tool as ibanValidatorAndParser } from './iban-validator-and-parser'; @@ -140,7 +141,7 @@ export const toolsByCategory: ToolCategory[] = [ }, { name: 'Network', - components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, ipv6UlaGenerator], + components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator], }, { name: 'Math', diff --git a/src/tools/mac-address-generator/index.ts b/src/tools/mac-address-generator/index.ts new file mode 100644 index 000000000..9d20fb697 --- /dev/null +++ b/src/tools/mac-address-generator/index.ts @@ -0,0 +1,12 @@ +import { Devices } from '@vicons/tabler'; +import { defineTool } from '../tool'; + +export const tool = defineTool({ + name: 'MAC address generator', + path: '/mac-address-generator', + description: 'Enter the quantity and prefix. MAC addresses will be generated in your chosen case (uppercase or lowercase)', + keywords: ['mac', 'address', 'generator', 'random', 'prefix'], + component: () => import('./mac-address-generator.vue'), + icon: Devices, + createdAt: new Date('2023-11-31'), +}); diff --git a/src/tools/mac-address-generator/mac-address-generator.e2e.spec.ts b/src/tools/mac-address-generator/mac-address-generator.e2e.spec.ts new file mode 100644 index 000000000..1d99ccd75 --- /dev/null +++ b/src/tools/mac-address-generator/mac-address-generator.e2e.spec.ts @@ -0,0 +1,11 @@ +import { expect, test } from '@playwright/test'; + +test.describe('Tool - MAC address generator', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/mac-address-generator'); + }); + + test('Has correct title', async ({ page }) => { + await expect(page).toHaveTitle('MAC address generator - IT Tools'); + }); +}); diff --git a/src/tools/mac-address-generator/mac-address-generator.vue b/src/tools/mac-address-generator/mac-address-generator.vue new file mode 100644 index 000000000..725070d32 --- /dev/null +++ b/src/tools/mac-address-generator/mac-address-generator.vue @@ -0,0 +1,103 @@ + + + diff --git a/src/tools/mac-address-generator/mac-adress-generator.models.test.ts b/src/tools/mac-address-generator/mac-adress-generator.models.test.ts new file mode 100644 index 000000000..5b660250b --- /dev/null +++ b/src/tools/mac-address-generator/mac-adress-generator.models.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, it } from 'vitest'; +import { generateRandomMacAddress, splitPrefix } from './mac-adress-generator.models'; + +describe('mac-adress-generator models', () => { + describe('splitPrefix', () => { + it('a mac address prefix is splitted around non hex characters', () => { + expect(splitPrefix('')).toEqual([]); + expect(splitPrefix('01')).toEqual(['01']); + expect(splitPrefix('01:')).toEqual(['01']); + expect(splitPrefix('01:23')).toEqual(['01', '23']); + expect(splitPrefix('01-23')).toEqual(['01', '23']); + }); + + it('when a prefix contains only hex characters, they are grouped by 2', () => { + expect(splitPrefix('0123')).toEqual(['01', '23']); + expect(splitPrefix('012345')).toEqual(['01', '23', '45']); + expect(splitPrefix('0123456')).toEqual(['01', '23', '45', '06']); + }); + }); + + describe('generateRandomMacAddress', () => { + const createRandomByteGenerator = () => { + let i = 0; + return () => (i++).toString(16).padStart(2, '0'); + }; + + it('generates a random mac address', () => { + expect(generateRandomMacAddress({ getRandomByte: createRandomByteGenerator() })).toBe('00:01:02:03:04:05'); + }); + + it('generates a random mac address with a prefix', () => { + expect(generateRandomMacAddress({ prefix: 'ff:ee:aa', getRandomByte: createRandomByteGenerator() })).toBe('ff:ee:aa:00:01:02'); + expect(generateRandomMacAddress({ prefix: 'ff:ee:a', getRandomByte: createRandomByteGenerator() })).toBe('ff:ee:0a:00:01:02'); + }); + + it('generates a random mac address with a prefix and a different separator', () => { + expect(generateRandomMacAddress({ prefix: 'ff-ee-aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02'); + expect(generateRandomMacAddress({ prefix: 'ff:ee:aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02'); + expect(generateRandomMacAddress({ prefix: 'ff-ee:aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02'); + expect(generateRandomMacAddress({ prefix: 'ff ee:aa', separator: '-', getRandomByte: createRandomByteGenerator() })).toBe('ff-ee-aa-00-01-02'); + }); + }); +}); diff --git a/src/tools/mac-address-generator/mac-adress-generator.models.ts b/src/tools/mac-address-generator/mac-adress-generator.models.ts new file mode 100644 index 000000000..45e434ebc --- /dev/null +++ b/src/tools/mac-address-generator/mac-adress-generator.models.ts @@ -0,0 +1,18 @@ +import _ from 'lodash'; + +export { splitPrefix, generateRandomMacAddress }; + +function splitPrefix(prefix: string): string[] { + const base = prefix.match(/[^0-9a-f]/i) === null ? prefix.match(/.{1,2}/g) ?? [] : prefix.split(/[^0-9a-f]/i); + + return base.filter(Boolean).map(byte => byte.padStart(2, '0')); +} + +function generateRandomMacAddress({ prefix: rawPrefix = '', separator = ':', getRandomByte = () => _.random(0, 255).toString(16).padStart(2, '0') }: { prefix?: string; separator?: string; getRandomByte?: () => string } = {}) { + const prefix = splitPrefix(rawPrefix); + + const randomBytes = _.times(6 - prefix.length, getRandomByte); + const bytes = [...prefix, ...randomBytes]; + + return bytes.join(separator); +} diff --git a/src/utils/macAddress.ts b/src/utils/macAddress.ts index 4488b3293..f55c334dd 100644 --- a/src/utils/macAddress.ts +++ b/src/utils/macAddress.ts @@ -15,4 +15,18 @@ function macAddressValidation(value: Ref) { }); } -export { macAddressValidation, macAddressValidationRules }; +const partialMacAddressValidationRules = [ + { + message: 'Invalid partial MAC address', + validator: (value: string) => value.trim().match(/^([0-9a-f]{2}[:\-. ]){0,5}([0-9a-f]{0,2})$/i), + }, +]; + +function usePartialMacAddressValidation(value: Ref) { + return useValidation({ + source: value, + rules: partialMacAddressValidationRules, + }); +} + +export { macAddressValidation, macAddressValidationRules, usePartialMacAddressValidation, partialMacAddressValidationRules };