import { assert, expect } from 'chai'; import { describe, it } from 'mocha'; import { generateArtistsStr, } from "../../../core/tests/utils/PlayTestUtils.ts"; import { parseArtistCredits, parseCredits, parseTrackCredits, uniqueNormalizedStrArr } from "../../utils/StringUtils.ts"; import testData from '../utils/playTestData.json' with { type: "json" }; import { intersect } from "../../utils.ts"; import type {ExpectedResults} from "../utils/interfaces.ts"; interface PlayTestFixture { caseHints: string[] data: { track: string artists: string[] album?: string } expected: ExpectedResults } describe('#PlayParse Parsing Artists from String', function() { it('Parses Artists from an Artist-like string', function () { for(const i of Array(40)) { const [str, primaries, secondaries] = generateArtistsStr({primary: {max: 3, ambiguousJoinedNames: true, trailingAmpersand: true, finalJoiner: false}}); const credits = parseArtistCredits(str); const allArtists = primaries.concat(secondaries); const parsed = [credits!.primary].concat(credits!.secondary ?? []); expect(primaries.concat(secondaries),` '${str}' Expected => ${allArtists.join(' || ')} Found => ${parsed.join(' || ')}`) .eql(parsed) } }); it('Parses & as "local" joiner when other delimiters present', function () { const data = [ { str: `Melendi \\ Ryan Lewis \\ The Righteous Brothers (featuring Joan Jett & The Blackhearts \\ Robin Schulz)`, expected: ['Melendi', 'Ryan Lewis', 'The Righteous Brothers', 'Joan Jett & The Blackhearts', 'Robin Schulz'] }, { str: `Gigi D'Agostino \\ YOASOBI (vs Sam Hunt, Lisa Loeb & Booba)`, expected: [`Gigi D'Agostino`, 'YOASOBI', 'Sam Hunt', 'Lisa Loeb', 'Booba'] }, { str: `Wham!, Hillsong Worship & Bruce Channel feat. I Prevail`, expected: ['Wham!', 'Hillsong Worship & Bruce Channel', 'I Prevail'] } ]; for(const d of data) { const credits = parseArtistCredits(d.str); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(d.expected).eql(parsed) } }); it('Only parses & as "global" joiner when no other delimiters present', function () { const data = [{ str: `Melendi & Ryan Lewis & The Righteous Brothers (featuring The Blackhearts \\ Robin Schulz)`, expected: ['Melendi', 'Ryan Lewis', 'The Righteous Brothers', 'The Blackhearts', 'Robin Schulz'] }]; for(const d of data) { const credits = parseArtistCredits(d.str); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(d.expected).eql(parsed) } }); it('Does not split artist name when only one joiner is present', function () { const data = [ { str: `Melendi & Ryan Lewis`, expected: ['Melendi & Ryan Lewis'] },{ str: `Melendi and Ryan Lewis`, expected: ['Melendi and Ryan Lewis'] }, ]; for(const d of data) { const credits = parseArtistCredits(d.str); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(d.expected).eql(parsed) } }); it('Parses secondary free regex', function () { const data = [{ str: `Diddy & Grand Funk Railroad feat. Daya & (G)I-DLE`, expected: ['Diddy', 'Grand Funk Railroad', 'Daya', '(G)I-DLE'] }]; for(const d of data) { const credits = parseArtistCredits(d.str); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(d.expected).eql(parsed) } }); it('Parses singlar Artist with wrapped vs multiple', function () { const [str, primaries, secondaries] = generateArtistsStr({primary: 1, secondary: {num: 2, ft: 'vs', joiner: '/', ftWrap: true}}); const credits = parseArtistCredits(str); const moreCredits = parseCredits(str); expect(true).eq(true); }); describe('When joiner is known', function () { it('Parses many primary artists', function () { for(const i of Array(40)) { const [str, primaries, secondaries] = generateArtistsStr({primary: {max: 3, joiner: '/'}, secondary: 0}); const credits = parseArtistCredits(str, ['/']); const allArtists = primaries.concat(secondaries); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(primaries.concat(secondaries),` '${str}' Expected => ${allArtists.join(' || ')} Found => ${parsed.join(' || ')}`) .eql(parsed) } }); it('Parses many secondary artists', function () { // fails on -- Peso Pluma / Lil Baby / R. Kelly (featuring TOMORROW X TOGETHER / AC/DC / DaVido) for(const i of Array(40)) { const [str, primaries, secondaries] = generateArtistsStr({primary: {max: 3, joiner: '/'}, secondary: {joiner: '/', finalJoiner: false}}); const credits = parseArtistCredits(str, ['/']); const allArtists = primaries.concat(secondaries); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(primaries.concat(secondaries),` '${str}' Expected => ${allArtists.join(' || ')} Found => ${parsed.join(' || ')} Primaries => ${primaries.join(' || ')} Secondaries => ${secondaries.join(' || ')} `) .eql(parsed) } }); it('Parses edge case artists formats', function () { const edgeCases: ReturnType[] = [ [ 'Paul Whiteman / BROCKHAMPTON / The Rolling Stones featuring Sonu Nigam / Supertramp / Cliff Edwards (Ukelele Ike)', ['Paul Whiteman','BROCKHAMPTON','The Rolling Stones'], ['Sonu Nigam','Supertramp','Cliff Edwards (Ukelele Ike)'] ], [ 'Stray Kids / Mario Lanza vs Cliff Edwards (Ukelele Ike)', ['Stray Kids','Mario Lanza'], ['Cliff Edwards (Ukelele Ike)'] ], [ 'Peso Pluma / Lil Baby / R. Kelly (featuring TOMORROW X TOGETHER / AC/DC / DaVido)', ['Peso Pluma','Lil Baby', 'R. Kelly'], ['TOMORROW X TOGETHER', 'AC/DC','DaVido'] ], [ 'Roy Orbison featuring Snow Patrol / The Detroit Spinners / Cliff Edwards (Ukelele Ike)', ['Roy Orbison'], ['Snow Patrol','The Detroit Spinners','Cliff Edwards (Ukelele Ike)'] ], [ 'Stray Kids / Mario Lanza (featuring Snow Patrol / Cliff Edwards (Ukelele Ike))', ['Stray Kids','Mario Lanza'], ['Snow Patrol','Cliff Edwards (Ukelele Ike)'] ], [ 'Cliff Edwards (Ukelele Ike) / Stray Kids feat. Snow Patrol', ['Cliff Edwards (Ukelele Ike)','Stray Kids'], ['Snow Patrol'] ], ]; for(const edge of edgeCases) { const [str, primaries, secondaries] = edge; const credits = parseArtistCredits(str, ['/']); const allArtists = primaries.concat(secondaries); const parsed = [credits!.primary].concat(credits!.secondary ?? []) expect(primaries.concat(secondaries),` '${str}' Expected => ${allArtists.join(' || ')} Found => ${parsed.join(' || ')}`) .eql(parsed) } }); }); }); describe('Play Track Strings',function () { it('should keep unwrapped trailing parenthetical as track suffix', function() { const res = parseTrackCredits('Criminal mind Ft Akon (Remix Braquer vos tĂȘtes)')!; expect(res.secondary).eql(['Akon']); expect(res.primaryComposite).eq('Criminal mind (Remix Braquer vos tĂȘtes)'); }); const testFixtures = testData as unknown as PlayTestFixture[]; const joinerData = testFixtures.filter(x => intersect(['joiner','track'], x.caseHints).length === 2); it('should parse joiners from track title', function() { for(const test of joinerData) { const res = parseTrackCredits(test.data.track)!; let artists: string[] = [...test.data.artists]; if(res.secondary !== undefined) { artists = uniqueNormalizedStrArr([...artists, ...res.secondary]); } assert.equal(res.primaryComposite, test.expected.track); assert.sameDeepMembers(artists, test.expected.artists); } }); });