import { getNormalizedNumber, getNormalizedRomanNumber, prependWithZeros, romanToIntStr, NumberRegexStr, CompoundNumberDotRegexStr, CompoundNumberDashRegexStr, RomanNumberRegexStr, CompoundRomanNumberDotRegexStr, CompoundRomanNumberDashRegexStr, WordInASCIIRegexStr, WordInAnyLanguageRegexStr } from "./matchers"; import {SortingSpecProcessor} from "./sorting-spec-processor"; describe('Plain numbers regexp', () => { let regexp: RegExp; beforeEach(() => { regexp = new RegExp('^' + NumberRegexStr, 'i'); }); it.each([ ['', null], [' ', null], [' 1', '1'], // leading spaces are swallowed ['-1', null], ['1', '1'], ['1a', '1'], ['3580', '3580'], ['9', '9'], ['7328964783268794325496783', '7328964783268794325496783'] ])('%s => %s', (s: string, out: string | null) => { const match: RegExpMatchArray | null = s.match(regexp) if (out) { expect(match).not.toBeNull() expect(match?.[1]).toBe(out) } else { expect(match).toBeNull() } }) }) describe('Plain compound numbers regexp (dot)', () => { let regexp: RegExp; beforeEach(() => { regexp = new RegExp('^' + CompoundNumberDotRegexStr, 'i'); }); it.each([ ['', null], [' ', null], [' 1', '1'], // leading spaces are swallowed ['-1', null], ['1', '1'], ['1a', '1'], ['3580', '3580'], ['9', '9'], ['7328964783268794325496783', '7328964783268794325496783'], ['9.', '9'], ['5.2', '5.2'], ['5.-2', '5'], ['12. 34', '12'], ['.12 .34', null], ['56.78.000.1', '56.78.000.1'], ['56.78.000.1 ', '56.78.000.1'], ['56.78.000.1abc', '56.78.000.1'], ['56.78.-.1abc', '56.78'], ['56.78-.1abc', '56.78'], ])('%s => %s', (s: string, out: string | null) => { const match: RegExpMatchArray | null = s.match(regexp) if (out) { expect(match).not.toBeNull() expect(match?.[1]).toBe(out) } else { expect(match).toBeNull() } }) }) describe('Plain compound numbers regexp (dash)', () => { let regexp: RegExp; beforeEach(() => { regexp = new RegExp('^' + CompoundNumberDashRegexStr, 'i'); }); it.each([ ['', null], [' ', null], [' 1', '1'], // leading spaces are swallowed ['.1', null], ['1', '1'], ['1a', '1'], ['3580', '3580'], ['9', '9'], ['7328964783268794325496783', '7328964783268794325496783'], ['9-', '9'], ['5-2', '5-2'], ['5-.2', '5'], ['12- 34', '12'], ['-12 -34', null], ['56-78-000-1', '56-78-000-1'], ['56-78-000-1 ', '56-78-000-1'], ['56-78-000-1abc', '56-78-000-1'], ['56-78-.-1abc', '56-78'], ['56-78.-1abc', '56-78'], ])('%s => %s', (s: string, out: string | null) => { const match: RegExpMatchArray | null = s.match(regexp) if (out) { expect(match).not.toBeNull() expect(match?.[1]).toBe(out) } else { expect(match).toBeNull() } }) }) describe('Plain Roman numbers regexp', () => { let regexp: RegExp; beforeEach(() => { regexp = new RegExp('^' + RomanNumberRegexStr, 'i'); }); it.each([ ['', null], [' ', null], [' i', 'i'], // leading spaces are swallowed ['-i', null], ['i', 'i'], ['ia', 'i'], ['mdclxv', 'mdclxv'], ['iiiii', 'iiiii'], ['viviviv794325496783', 'viviviv'] ])('%s => %s', (s: string, out: string | null) => { const match: RegExpMatchArray | null = s.match(regexp) if (out) { expect(match).not.toBeNull() expect(match?.[1]).toBe(out) } else { expect(match).toBeNull() } }) }) describe('Roman compound numbers regexp (dot)', () => { let regexp: RegExp; beforeEach(() => { regexp = new RegExp('^' + CompoundRomanNumberDotRegexStr, 'i'); }); it.each([ ['', null], [' ', null], [' I', 'I'], // leading spaces are swallowed ['.I', null], ['v', 'v'], ['va', 'v'], ['vava ', 'v'], ['ix', 'ix'], ['mclv96783', 'mclv'], ['C.', 'C'], ['v.ii', 'v.ii'], ['xx.-x', 'xx'], ['xx.x', 'xx.x'], ['iv- v', 'iv'], ['.12 .34', null], ['vv.mc.lx.i', 'vv.mc.lx.i'], ['mcm.m.mmm.l ', 'mcm.m.mmm.l'], ['iv.I.DDD.Iabc', 'iv.I.DDD.I'], ['xiii.viii.-.1abc', 'xiii.viii'], ['xvx.d-.iabc', 'xvx.d'], ['xvx.d..iabc', 'xvx.d'], ])('%s => %s', (s: string, out: string | null) => { const match: RegExpMatchArray | null = s.match(regexp) if (out) { expect(match).not.toBeNull() expect(match?.[1]).toBe(out) } else { expect(match).toBeNull() } }) }) describe('Roman compound numbers regexp (dash)', () => { let regexp: RegExp; beforeEach(() => { regexp = new RegExp('^' + CompoundRomanNumberDashRegexStr, 'i'); }); it.each([ ['', null], [' ', null], [' I', 'I'], // leading spaces are swallowed ['.I', null], ['v', 'v'], ['va', 'v'], ['vava ', 'v'], ['ix', 'ix'], ['mclv96783', 'mclv'], ['C-', 'C'], ['v-ii', 'v-ii'], ['xx.-x', 'xx'], ['xx.x', 'xx'], ['iv- v', 'iv'], ['-12 -34', null], ['vv-mc-lx-i', 'vv-mc-lx-i'], ['mcm-m-mmm-l ', 'mcm-m-mmm-l'], ['iv-I-DDD-Iabc', 'iv-I-DDD-I'], ['xiii-viii-.-1abc', 'xiii-viii'], ['xvx-d.-iabc', 'xvx-d'], ['xvx-d--iabc', 'xvx-d'] ])('%s => %s', (s: string, out: string | null) => { const match: RegExpMatchArray | null = s.match(regexp) if (out) { expect(match).not.toBeNull() expect(match?.[1]).toBe(out) } else { expect(match).toBeNull() } }) }) describe('ASCII word regexp', () => { let regexp: RegExp; beforeEach(() => { regexp = new RegExp('^' + WordInASCIIRegexStr, 'i'); }); it.each([ ['', null], [' ', null], [' I', null], // leading spaces are not swallowed ['I ', 'I'], // trailing spaces are swallowed ['Abc', 'Abc'], ['Sun', 'Sun'], ['Hello123', 'Hello'], ['John_', 'John'], ['Title.', 'Title'], ['Deutschstäder', 'Deutschst'], ['ItalianoàèéìòùÈ', 'Italiano'], ['PolskićśńĄł', 'Polski'] ])('%s => %s', (s: string, out: string | null) => { const match: RegExpMatchArray | null = s.match(regexp) if (out) { expect(match).not.toBeNull() expect(match?.[1]).toBe(out) } else { expect(match).toBeNull() } }) }) describe('Unicode word regexp', () => { let regexp: RegExp; beforeEach(() => { regexp = new RegExp('^' + WordInAnyLanguageRegexStr, 'ui'); }); it.each([ ['', null], [' ', null], [' I', null], // leading spaces are not swallowed ['I ', 'I'], // trailing characters are ignored in unit test ['Abc', 'Abc'], ['Sun', 'Sun'], ['Hello123', 'Hello'], ['John_', 'John'], ['Title.', 'Title'], ['Deutschstäder_', 'Deutschstäder'], ['ItalianoàèéìòùÈ', 'ItalianoàèéìòùÈ'], ['PolskićśńĄł', 'PolskićśńĄł'] ])('%s => %s', (s: string, out: string | null) => { const match: RegExpMatchArray | null = s.match(regexp) if (out) { expect(match).not.toBeNull() expect(match?.[1]).toBe(out) } else { expect(match).toBeNull() } }) }) describe('prependWithZeros', () => { const Length = 5; it('should add leading zeros to empty string', () => { const s = prependWithZeros('', Length); expect(s).toBe('00000') }) it('should add leading zeros to shorter string', () => { const s = prependWithZeros('12', Length); expect(s).toBe('00012') }) it('should not add leading zeros to sufficient string', () => { const s = prependWithZeros('12345', Length); expect(s).toBe('12345') }) it('should not add leading zeros to longer string', () => { const s = prependWithZeros('12345678', Length); expect(s).toBe('12345678') }) }) describe('getNormalizedNumber', () => { const LEN = 5; const params = [ ['', '00000000//', null], ['1', '00000001//', undefined], ['000', '00000000//', ' '], ['000', '00000000//', ''], ['1234567890123', '1234567890123//', ''], ['1234567890123456789012345', '1234567890123456789012345//', ''], // No conversion to number should happen // Compound numbers - dot ['1', '00000001//', '.'], ['1 .1', '0000001 |00000001//', '.'], // Invalid case, Regexp on matcher in the caller should guard against this ['1. 1', '00000001|000000 1//', '.'], // Invalid case, Regexp on matcher in the caller should guard against this ['1..1', '00000001|00000001//', '.'], // Edge case, consecutive separators treated as one ['1.2', '00000001|00000002//', '.'], ['1.2.', '00000001|00000002//', '.'], // Edge case, trailing dangling separator is swallowed ['1.2.3', '00000001|00000002|00000003//', '.'], ['44.2314325423432.4', '00000044|2314325423432|00000004//', '.'], ['0.0.0.0.', '00000000|00000000|00000000|00000000//', '.'], // Edge case, trailing dangling separator is swallowed ['0.0.0.0', '00000000|00000000|00000000|00000000//', '.'], ['0.0-0.0', '00000000|000000-0|00000000//', '.'], // Invalid case, Regexp on matcher in the caller should guard against this // Compound numbers - dash ['1', '00000001//', '-'], ['1 -1', '0000001 |00000001//', '-'], // Invalid case, Regexp on matcher in the caller should guard against this ['1- 1', '00000001|000000 1//', '-'], // Invalid case, Regexp on matcher in the caller should guard against this ['1--1', '00000001|00000001//', '-'], // Edge case, consecutive separators treated as one ['1-2', '00000001|00000002//', '-'], ['1-2-', '00000001|00000002//', '-'], // Edge case, trailing dangling separator is swallowed ['1-2-3', '00000001|00000002|00000003//', '-'], ['44-2314325423432-4', '00044|2314325423432|00004//', '-5'], ['0-7-0-0-', '00000000|00000007|00000000|00000000//', '-'], // // Edge case, trailing dangling separator is swallowed ['0-0.3-0', '00000|000.3|00000//', '-5'], ]; it.each(params)('>%s< should become %s (sep >%s<)', (s: string, out: string, separator?: string) => { if (separator === '-5') { expect(getNormalizedNumber(s, '-', LEN)).toBe(out) } else { expect(getNormalizedNumber(s, separator)).toBe(out) } }) }) describe('romanToIntStr', () => { const params = [ ['', '0'], ['I', '1'], ['II', '2'], ['III', '3'], ['IIII', '4'], ['IIIII', '5'], ['iv', '4'], ['v', '5'], ['vi', '6'], ['vii', '7'], ['viii', '8'], ['iX', '9'], ['x', '10'], ['XI', '11'], ['L', '50'], ['C', '100'], ['d', '500'], ['M', '1000'], // formally correct, unused ['iv', '4'], ['iiv', '5'], ['iiiv', '6'], ['iiiiv', '7'], ['iiiiiv', '8'], ['12345', '0'], ]; it.each(params)('>%s< should be %s', (s: string, out: string) => { expect(romanToIntStr(s)).toBe(out) }) }) describe('getNormalizedRomanNumber', () => { const LEN = 5 const params = [ ['', '00000//', null], ['1', '00000//', undefined], ['000', '00000//', ' '], ['000', '00000//', ''], ['1234567890123//', '00000//', ''], ['00123', '00000//', ''], ['1234567890123456789012345//', '00000//', ''], // No conversion to number should happen // Compound numbers - dot ['i', '00001//', '.'], ['ii .ii', '00002|00002//', '.'], // Invalid case, Regexp on matcher in the caller should guard against this ['iv. vi', '00004|00006//', '.'], // Invalid case, Regexp on matcher in the caller should guard against this ['X..C', '00010|00100//', '.'], // Edge case, consecutive separators treated as one ['C.M', '00100|01000//', '.'], ['I.II.', '00001|00002//', '.'], // Edge case, trailing dangling separator is swallowed ['i.i.iv', '00001|00001|00004//', '.'], ['MCMLXX.2314325423432.CM', '01970|00000|00900//', '.'], ['0.0.0.0.', '00000|00000|00000|00000//', '.'], // Edge case, trailing dangling separator is swallowed ['L.l.M.iiii', '00050|00050|01000|00004//', '.'], ['v.v-v.v', '00005|00010|00005//', '.'], // Invalid case, Regexp on matcher in the caller should guard against this // Compound numbers - dash ['i', '00001//', '-'], ['ii -ii', '00002|00002//', '-'], // Invalid case, Regexp on matcher in the caller should guard against this ['iv- vi', '00004|00006//', '-'], // Invalid case, Regexp on matcher in the caller should guard against this ['X--C', '00010|00100//', '-'], // Edge case, consecutive separators treated as one ['C-M', '00100|01000//', '-'], ['I-II-', '00001|00002//', '-'], // Edge case, trailing dangling separator is swallowed ['i-i-iv', '00001|00001|00004//', '-'], ['MCMLXX-2314325423432-CM', '01970|00000|00900//', '-'], ['0-0-0-0-', '00000|00000|00000|00000//', '-'], // Edge case, trailing dangling separator is swallowed ['L-l-M-iiii', '00050|00050|01000|00004//', '-'], ['v-v.v-v', '00005|00010|00005//', '-'], // Invalid case, Regexp on matcher in the caller should guard against this ]; it.each(params)('>%s< should become %s (sep >%s<)', (s: string, out: string, separator?: string) => { expect(getNormalizedRomanNumber(s, separator, LEN)).toBe(out) }) })