Skip to content

Commit

Permalink
Fixes parsing bug
Browse files Browse the repository at this point in the history
  • Loading branch information
arcanis committed Nov 30, 2024
1 parent ede363a commit 29084d4
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 8 deletions.
6 changes: 3 additions & 3 deletions src/components/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ interface AccordionProps {

export function Accordion({ title, isOpen, onToggle, children, action }: AccordionProps) {
return <>
<button
<div
onClick={onToggle}
className="w-full p-1 text-left mb-2 bg-postcard-nowhite rounded-lg"
className="w-full p-1 text-left mb-2 bg-postcard-nowhite rounded-lg cursor-pointer"
>
<div className="flex px-4 py-2 items-center justify-between text-white rounded">
<h2 className={`text-xl`}>{title}</h2>
Expand All @@ -23,7 +23,7 @@ export function Accordion({ title, isOpen, onToggle, children, action }: Accordi
</span>
</div>
</div>
</button>
</div>

<div className={`${isOpen ? 'flex-1' : 'flex-0'} transition-[flex] h-0 duration-300 ease-in-out overflow-y-scroll`}>
<div className={`pb-2`}>
Expand Down
2 changes: 0 additions & 2 deletions src/components/SecretSantaLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ export function SecretSantaLinks({ assignments, instructions, participants, onGe
participants[receiver.id]?.hint,
]);

console.log(assignments.pairings, participants)

adjustedPairings.sort((a, b) => {
return a[0].localeCompare(b[0]);
});
Expand Down
172 changes: 172 additions & 0 deletions src/utils/parseParticipants.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { describe, it, expect } from 'vitest';

import { parseParticipantsText, formatParticipantText } from './parseParticipants';
import { Participant } from '../types';

describe('parseParticipantsText', () => {
it('should parse basic participant list', () => {
const input = `
Alice
Bob
Charlie
`;

const result = parseParticipantsText(input);
expect(result.ok).toBe(true);
if (!result.ok) return;

const participants = Object.values(result.participants);
expect(participants).toHaveLength(3);
expect(participants.map(p => p.name)).toEqual(['Alice', 'Bob', 'Charlie']);
expect(participants.every(p => p.rules.length === 0)).toBe(true);
});

it('should parse participants with hints', () => {
const input = `
Alice (likes cats)
Bob (allergic to chocolate)
Charlie
`;

const result = parseParticipantsText(input);
expect(result.ok).toBe(true);
if (!result.ok) return;

const participants = Object.values(result.participants);
expect(participants).toHaveLength(3);
expect(participants.find(p => p.name === 'Alice')?.hint).toBe('likes cats');
expect(participants.find(p => p.name === 'Bob')?.hint).toBe('allergic to chocolate');
expect(participants.find(p => p.name === 'Charlie')?.hint).toBeUndefined();
});

it('should parse participants with rules', () => {
const input = `
Alice =Bob !Charlie
Bob =Alice
Charlie !Alice
`;

const result = parseParticipantsText(input);
expect(result.ok).toBe(true);
if (!result.ok) return;

const participants = Object.values(result.participants);
const alice = participants.find(p => p.name === 'Alice');
const bob = participants.find(p => p.name === 'Bob');
const charlie = participants.find(p => p.name === 'Charlie');

expect(alice?.rules).toHaveLength(2);
expect(bob?.rules).toHaveLength(1);
expect(charlie?.rules).toHaveLength(1);

expect(alice?.rules[0]).toEqual({
type: 'must',
targetParticipantId: bob?.id
});
expect(alice?.rules[1]).toEqual({
type: 'mustNot',
targetParticipantId: charlie?.id
});
});

it('should handle empty lines', () => {
const input = `
Alice
Bob
Charlie
`;

const result = parseParticipantsText(input);
expect(result.ok).toBe(true);
if (!result.ok) return;

const participants = Object.values(result.participants);
expect(participants).toHaveLength(3);
});

it('should detect duplicate names', () => {
const input = `
Alice
Bob
Alice
`;

const result = parseParticipantsText(input);
expect(result.ok).toBe(false);
if (result.ok) return;

expect(result.key).toBe('errors.duplicateName');
expect(result.values?.name).toBe('Alice');
});

it('should detect unknown participants in rules', () => {
const input = `
Alice =Bob
Bob !David
`;

const result = parseParticipantsText(input);
expect(result.ok).toBe(false);
if (result.ok) return;

expect(result.key).toBe('errors.unknownParticipant');
expect(result.values?.name).toBe('David');
});

it('should preserve existing IDs when provided', () => {
const existingParticipants: Record<string, Participant> = {
'Alice': {
id: 'existing-alice-id',
name: 'Alice',
rules: []
}
};

const input = `
Alice
Bob
`;

const result = parseParticipantsText(input, existingParticipants);
expect(result.ok).toBe(true);
if (!result.ok) return;

const alice = Object.values(result.participants).find(p => p.name === 'Alice');
expect(alice?.id).toBe('existing-alice-id');
});
});

describe('formatParticipantText', () => {
it('should format participants back to text', () => {
const participants: Record<string, Participant> = {
'id1': {
id: 'id1',
name: 'Alice',
hint: 'likes cats',
rules: [
{ type: 'must', targetParticipantId: 'id2' },
{ type: 'mustNot', targetParticipantId: 'id3' }
]
},
'id2': {
id: 'id2',
name: 'Bob',
rules: []
},
'id3': {
id: 'id3',
name: 'Charlie',
rules: []
}
};

const result = formatParticipantText(participants);
expect(result).toBe(
'Alice (likes cats) =Bob !Charlie\n' +
'Bob\n' +
'Charlie\n'
);
});
});
5 changes: 2 additions & 3 deletions src/utils/parseParticipants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ export function formatParticipantText(participants: Record<string, Participant>)
}).join('');
}

const PAREN_1 = /[!=(]/g;
const PAREN_2 = /[()]/g;
const PAREN = /[!=(]/;

export function parseParticipantsText(input: string, existingParticipants?: Record<string, Participant>): ParseResult {
const lines = input.split('\n').map(line => line.trim());
Expand All @@ -52,7 +51,7 @@ export function parseParticipantsText(input: string, existingParticipants?: Reco
const line = lines[i].trim();
if (line === '') continue;

let splitIndex = PAREN_1.exec(line)?.index;
let splitIndex = PAREN.exec(line)?.index;

const name = typeof splitIndex === 'number'
? line.slice(0, splitIndex).trim()
Expand Down

0 comments on commit 29084d4

Please sign in to comment.