diff --git a/src/lib/heuristics/extract/education.test.ts b/src/lib/heuristics/extract/education.test.ts index e437234b..751cb3e0 100644 --- a/src/lib/heuristics/extract/education.test.ts +++ b/src/lib/heuristics/extract/education.test.ts @@ -947,3 +947,59 @@ describe("extractEducation — cleanField strips leftover edge middot and bullet expect(value[0].field).toBeUndefined(); }); }); + +describe("extractEducation — parseDegreeAndField strips middot/bullet before in/of connective (#839)", () => { + it("strips middot then in connective from degree field", () => { + const { value } = extractEducation( + mkEduSection([ + "Springfield State University", + "B.S. · in Computer Science", + ]), + ); + expect(value).toHaveLength(1); + expect(value[0].degree).toBe("B.S."); + expect(value[0].field).toBe("Computer Science"); + }); + + it("strips bullet then of connective from degree field", () => { + const { value } = extractEducation( + mkEduSection([ + "Springfield State University", + "B.S. • of Computer Science", + ]), + ); + expect(value).toHaveLength(1); + expect(value[0].degree).toBe("B.S."); + expect(value[0].field).toBe("Computer Science"); + }); + + it("still strips hyphen and em dash before in connective", () => { + const { value: hyphen } = extractEducation( + mkEduSection([ + "Springfield State University", + "B.S. - in Computer Science", + ]), + ); + expect(hyphen[0].field).toBe("Computer Science"); + + const { value: emDash } = extractEducation( + mkEduSection([ + "Springfield State University", + "B.S. — in Computer Science", + ]), + ); + expect(emDash[0].field).toBe("Computer Science"); + }); + + it("preserves interior middot in a two-part field name", () => { + const { value } = extractEducation( + mkEduSection([ + "Springfield State University", + "B.S. Mathematics · Statistics", + ]), + ); + expect(value).toHaveLength(1); + expect(value[0].degree).toBe("B.S."); + expect(value[0].field).toBe("Mathematics · Statistics"); + }); +}); diff --git a/src/lib/heuristics/extract/education.ts b/src/lib/heuristics/extract/education.ts index 7ce31b79..035d7ea9 100644 --- a/src/lib/heuristics/extract/education.ts +++ b/src/lib/heuristics/extract/education.ts @@ -533,9 +533,14 @@ function parseDegreeAndField(line: string): { } const fieldRaw = line .slice(fieldStart) - // Drop a leading "in "/"of " connective or a "-"/"—"/":"/"," separator. + // Drop a leading "in "/"of " connective or a "-"/"–"/"—"/","/":"/"·"/"•" + // separator. The middot/bullet live here, not only in `cleanField`, because + // this strip runs FIRST — `cleanField`'s edge strip accepted them already but + // runs after the connective strips, too late to rescue a "· in " + // header (#839). The two classes still differ (`cleanField` also takes ";"); + // unifying the separator vocabulary repo-wide is #653. .replace(/^\s*(?:in|of)\s+/i, "") - .replace(/^\s*[-–—,:]\s*/, "") + .replace(/^\s*[-–—,:·•]\s*/, "") .replace(/^\s*(?:in|of)\s+/i, ""); return { degree, field: cleanField(fieldRaw) }; }