import { assert, expect } from 'chai'; import { describe, it } from 'mocha'; import { compareNormalizedStrings, normalizeStr, } from "../../utils/StringUtils.ts"; import { replaceInterpolatedValues } from "../../utils/DataUtils.ts"; import { splitByFirstFound } from '../../../core/StringUtils.ts'; import { noCasePropObj } from '../../utils/DataUtils.ts'; import { isrcNoHyphens, isrcWithHyphens, REGEX_ISRC_HYPHENS, REGEX_ISRC_NO_HYPHENS } from '../../../core/PlayUtils.ts'; describe('String Comparisons', function () { it('should ignore symbols', async function () { const result = compareNormalizedStrings('this string! is the. same', 'this string is the same'); assert.isAtLeast(result.highScore, 100); }); it('should ignore whitespace', async function () { const result = compareNormalizedStrings('this string is the same ', 'this string is the same'); assert.isAtLeast(result.highScore, 100); }); it('should ignore case', async function () { const result = compareNormalizedStrings('ThIs STRING iS THe SAMe', 'this string is the same'); assert.isAtLeast( result.highScore, 100); }); it('should normalize unicode', async function () { const tests = [ ['Enamorate Bailando','Enamórate Bailando'], ['Dina Ogon', 'Dina Ögon'], ['Nana', 'Nanã'], ['Nana', 'Nanä'] ] for(const test of tests) { const result = compareNormalizedStrings(test[0], test[1]); assert.equal(100, result.highScore); } }); it('should not erase non-english characters', async function () { const tests = [ ['VAPERROR / t e l e p a t h テレパシー能力者 - 切っても切れない', 'vaperror t e l e p a t h テレパシー能力者 切っても切れない'], ['Мой мармеладный (Speed Up)', 'мои мармеладныи speed up'], ['Мой мармеладный (Я не права) [Из сериала "Ольга", 2 Сезон]', 'мои мармеладныи я не права из сериала ольга 2 сезон'] ] for(const test of tests) { const result = normalizeStr(test[0], {keepSingleWhitespace: true}); assert.equal(result, test[1]); } }); it('should score small changes correctly', async function () { const tests = [ ['there is change', 'therr is change'], ['laser cannon', 'lazer cannon'], ['Monster', 'Manster'], ['What are you doing', 'hwat are you doing'] ] for(const test of tests) { const result = compareNormalizedStrings(test[0], test[1]); assert.isAtLeast( result.highScore, 75, `Comparing: '${test[0]}' | '${test[1]}'`); } }); it('should score word bags correctly', async function () { const tests = [ ['there change is', 'there is change'], ['laser cannon', 'cannon laser'], ['One two three four', 'One four two threee'], ['you, doin hwat are', 'hwat are you doing'], ['My Track (feat. Tourist1, Artist2)', 'My Track (feat. Artist2, Tourist1)'] ] for(const test of tests) { const result = compareNormalizedStrings(test[0], test[1]); assert.isAtLeast( result.highScore, 80, `Comparing: '${test[0]}' | '${test[1]}'`); } }); it('should not score very similar as identical', async function () { const tests = [ ['Another Brick in the Wall, Pt. 1', 'Another Brick in the Wall, Pt. 2'], ] for(const test of tests) { const result = compareNormalizedStrings(test[0], test[1]); assert.isAtMost( result.highScore, 99, `Comparing: '${test[0]}' | '${test[1]}'`); } }); it('should handle strings with different lengths', async function () { const tests = [ ['The Amazing Bongo Hop', 'The Bongo Hop'], ] for(const test of tests) { const result = compareNormalizedStrings(test[0], test[1]); assert.isAtMost( result.highScore, 58, `Comparing: '${test[0]}' | '${test[1]}'`); } }); it('should be string parameter order invariant', async function () { const longerString = 'Nidia Gongora TEST'; const shorterString = 'Nidia Gongora' const result1 = compareNormalizedStrings(longerString, shorterString); const result2 = compareNormalizedStrings(shorterString, longerString); assert.equal( result1.highScore, result2.highScore, `Comparing: '${longerString}' | '${shorterString}'`); }); }); describe('String Splitting', function() { it('should not split string with no delimiter', function() { const artistName = undefined; const artist = "Phil Collins"; const artistStrings = splitByFirstFound(artist, [','], [artistName]); expect(artistStrings).to.eql(['Phil Collins']) }); }); it('Proxy object has case-insensitive keys', function() { const myObj: Record = {FOO: 'bar'}; const iObj = noCasePropObj(myObj); expect(iObj.foO).is.not.undefined; expect(iObj.foo).eq('bar'); }); it('Proxy object handles spread', function() { const myObj = {FOO: 'bar', WIDget: 'cool'}; const iObj = noCasePropObj(myObj); const { foo, ...rest } = iObj; expect(foo).is.not.undefined; expect(foo).eq('bar'); expect(rest.widget).is.not.undefined; expect(rest.widget).eq('cool'); }); describe('Interpolation', function() { it('interpolates values', function() { const replaced = replaceInterpolatedValues('My cool string has [[asecret]] in it', {asecret: 'foo'}); expect(replaced).not.includes('['); expect(replaced).not.includes(']'); expect(replaced).includes('foo'); }); it('interpolates values case-insensitive', function() { const replaced = replaceInterpolatedValues('My cool string has [[ASECREt]] in it', {aSeCrEt: 'foo'}); expect(replaced).includes('foo'); }); it('interpolates values after trimming', function() { const replaced = replaceInterpolatedValues('My cool string has [[ ASECREt ]] in it', {aSeCrEt: 'foo'}); expect(replaced).includes('foo'); }); it('leaves match in place if not interpolated', function() { const replaced = replaceInterpolatedValues('My cool string has [[asecret]] and [[coolsecret]] in it', {bar: 'foo', coolsecret: 'xxx'}); expect(replaced).includes('[[asecret]]'); expect(replaced).includes('xxx'); expect(replaced).not.includes('[[coolsecret]]'); }); }); describe('ISRC Parsing', function() { describe('Regex tests', function() { it('isrc no-hypen regex matches isrc with no hyphens', function() { const isrcs = ['USRC17607839','GBWUL1805639','USUG11902962','QT65X2609829']; for(const i of isrcs) { expect(REGEX_ISRC_NO_HYPHENS.test(i)).is.true; } }); it('isrc no-hypen regex does not match isrc with hyphens or invalid', function() { const isrcs = ['US-RC1-76-07839','GB-WUL1-80-5639','US-UG1-19-02962','QTLASDSDASD','']; for(const i of isrcs) { expect(REGEX_ISRC_NO_HYPHENS.test(i)).is.false; } }); it('isrc hypen regex matches isrc with hyphens', function() { const isrcs = ['US-RC1-76-07839','GB-WUL-18-05639','US-UG1-19-02962']; for(const i of isrcs) { expect(REGEX_ISRC_HYPHENS.test(i), `${i} did not match`).is.true; } }); it('isrc hypen regex does not match isrc with no hyphens or invalid', function() { const isrcs = ['USRC17607839','GBWUL1805639','USUG11902962','QT65X2609829','QTLASDSDASD','']; for(const i of isrcs) { expect(REGEX_ISRC_HYPHENS.test(i)).is.false; } }); }); describe('ISRC Transform', function() { describe('To non-hyphenated', function() { it('Transforms from hyphenated', function() { const isrcs = ['US-RC1-76-07839','GB-WUL-18-05639','US-UG1-19-02962']; for(const i of isrcs) { const res = isrcNoHyphens(i); expect(res).eq(i.replaceAll(/-/g, '')); } }); it('Returns already non-hyphenated', function() { const isrcs = ['USRC17607839','GBWUL1805639','USUG11902962','QT65X2609829']; for(const i of isrcs) { const res = isrcNoHyphens(i); expect(res).eq(i); } }); it('Fails on invalid', function() { const isrcs = ['GB-WUL1-80-5639','QTLASDSDASD','']; for(const i of isrcs) { expect(() => isrcNoHyphens(i)).to.throw(); } }); }); describe('To hyphenated', function() { it('Transforms from non-hyphenated', function() { const res = isrcWithHyphens('USRC17607839'); expect(res).eq('US-RC1-76-07839'); }); it('Returns already non-hyphenated', function() { const isrcs = ['US-RC1-76-07839','GB-WUL-18-05639','US-UG1-19-02962']; for(const i of isrcs) { const res = isrcWithHyphens(i); expect(res).eq(i); } }); it('Fails on invalid', function() { const isrcs = ['GB-WUL1-80-5639','QTLASDSDASD','']; for(const i of isrcs) { expect(() => isrcNoHyphens(i)).to.throw(); } }); }); }); });