Fixes #289 * docs: add batch qualifying results entry design spec Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: add batch qualifying results entry implementation plan Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: ignore .worktrees directory * feat: add qualifying results text parser with tests * fix: handle no-space after dot separator, tighten T-prefix to uppercase only * feat: add batch-add-results server intent with duplicate filtering Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add create-participant intent for inline participant creation * fix: trim sportsSeasonId validation in create-participant intent * feat: fill 0-QP rows for unplaced participants on qualifying event finalize Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * perf: use bulk insert for 0-QP rows on qualifying event finalize * feat: add BatchResultEntry component for paste-and-parse batch result import Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: disable add participant button while create request is in flight * feat: render BatchResultEntry on qualifying event pages Add BatchResultEntry component import and render it on event pages when the event is a qualifying event, not a final_standings/playoff_game type, and not yet complete. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: address final review issues (sportsSeasonId authority, participant validation, CRLF, unused prop) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
110 lines
3.4 KiB
TypeScript
110 lines
3.4 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { parseResultsText } from "../parse-results-text";
|
|
|
|
describe("parseResultsText", () => {
|
|
describe("dot separator", () => {
|
|
it("parses '1. Player Name'", () => {
|
|
expect(parseResultsText("1. Gerwyn Price")).toEqual([
|
|
{ placement: 1, rawName: "Gerwyn Price" },
|
|
]);
|
|
});
|
|
|
|
it("parses '1.Player Name' (no space after dot)", () => {
|
|
expect(parseResultsText("1.Gerwyn Price")).toEqual([
|
|
{ placement: 1, rawName: "Gerwyn Price" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("comma separator", () => {
|
|
it("parses '1, Player Name'", () => {
|
|
expect(parseResultsText("1, Gerwyn Price")).toEqual([
|
|
{ placement: 1, rawName: "Gerwyn Price" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("space-only separator", () => {
|
|
it("parses '1 Player Name'", () => {
|
|
expect(parseResultsText("1 Gerwyn Price")).toEqual([
|
|
{ placement: 1, rawName: "Gerwyn Price" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("T-prefix ties", () => {
|
|
it("parses 'T3. Player Name' as placement 3", () => {
|
|
expect(parseResultsText("T3. Luke Littler")).toEqual([
|
|
{ placement: 3, rawName: "Luke Littler" },
|
|
]);
|
|
});
|
|
|
|
it("parses 'T3, Player Name' as placement 3", () => {
|
|
expect(parseResultsText("T3, Luke Littler")).toEqual([
|
|
{ placement: 3, rawName: "Luke Littler" },
|
|
]);
|
|
});
|
|
|
|
it("parses two T-prefix lines with same rank as both placement 3", () => {
|
|
const input = "T3. Luke Littler\nT3. Michael van Gerwen";
|
|
expect(parseResultsText(input)).toEqual([
|
|
{ placement: 3, rawName: "Luke Littler" },
|
|
{ placement: 3, rawName: "Michael van Gerwen" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("repeated-rank ties", () => {
|
|
it("parses two lines with the same rank number as both tied", () => {
|
|
const input = "3. Luke Littler\n3. Michael van Gerwen";
|
|
expect(parseResultsText(input)).toEqual([
|
|
{ placement: 3, rawName: "Luke Littler" },
|
|
{ placement: 3, rawName: "Michael van Gerwen" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("blank lines and whitespace", () => {
|
|
it("ignores blank lines", () => {
|
|
const input = "1. Gerwyn Price\n\n2. Luke Littler";
|
|
expect(parseResultsText(input)).toEqual([
|
|
{ placement: 1, rawName: "Gerwyn Price" },
|
|
{ placement: 2, rawName: "Luke Littler" },
|
|
]);
|
|
});
|
|
|
|
it("trims leading and trailing whitespace from names", () => {
|
|
expect(parseResultsText("1. Gerwyn Price ")).toEqual([
|
|
{ placement: 1, rawName: "Gerwyn Price" },
|
|
]);
|
|
});
|
|
|
|
it("handles \\r\\n line endings", () => {
|
|
const input = "1. Gerwyn Price\r\n2. Luke Littler";
|
|
expect(parseResultsText(input)).toEqual([
|
|
{ placement: 1, rawName: "Gerwyn Price" },
|
|
{ placement: 2, rawName: "Luke Littler" },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("multi-line mixed formats", () => {
|
|
it("parses a realistic paste with multiple formats", () => {
|
|
const input = [
|
|
"1. Gerwyn Price",
|
|
"2, Luke Littler",
|
|
"T3. Michael van Gerwen",
|
|
"T3. Peter Wright",
|
|
"",
|
|
"5 Damon Heta",
|
|
].join("\n");
|
|
expect(parseResultsText(input)).toEqual([
|
|
{ placement: 1, rawName: "Gerwyn Price" },
|
|
{ placement: 2, rawName: "Luke Littler" },
|
|
{ placement: 3, rawName: "Michael van Gerwen" },
|
|
{ placement: 3, rawName: "Peter Wright" },
|
|
{ placement: 5, rawName: "Damon Heta" },
|
|
]);
|
|
});
|
|
});
|
|
});
|