Files
SRCmail/lib/__tests__/subject-prefix.test.ts
T
Stefan HildebrandtandLinus Rath 8af6694152 fix: strip reply/forward prefixes followed by a full-width colon
The prefix-stripping regex only matched an ASCII ":", so a localized
prefix from a CJK mail client (e.g. "回复:foo", using the full-width
colon U+FF1A) was left in place. On reply this caused the user's own
prefix to be stacked on top, growing the subject chain.

Accept both ":" and ":" after the prefix token. Adds tests.
2026-06-24 15:56:38 +02:00

57 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from 'vitest';
import {
stripSubjectPrefixes,
buildReplySubject,
buildForwardSubject,
} from '@/lib/subject-prefix';
describe('stripSubjectPrefixes', () => {
it('strips a chain of mixed-language prefixes', () => {
expect(stripSubjectPrefixes('Re: AW: WG: foo')).toBe('foo');
});
it('strips the Outlook [N] and Eudora *N counters', () => {
expect(stripSubjectPrefixes('Re[2]: foo')).toBe('foo');
expect(stripSubjectPrefixes('Re*3: foo')).toBe('foo');
});
it('is case-insensitive and idempotent', () => {
expect(stripSubjectPrefixes('RE: Re: foo')).toBe('foo');
expect(stripSubjectPrefixes(stripSubjectPrefixes('RE: Re: foo'))).toBe('foo');
});
it('strips a Cyrillic token and an ASCII-colon Chinese token', () => {
expect(stripSubjectPrefixes('Ответ: foo')).toBe('foo');
expect(stripSubjectPrefixes('回复: foo')).toBe('foo');
});
it('strips a token followed by a full-width colon (CJK clients)', () => {
expect(stripSubjectPrefixes('回复:foo')).toBe('foo');
expect(stripSubjectPrefixes('回覆:foo')).toBe('foo');
expect(stripSubjectPrefixes('Refoo')).toBe('foo');
});
it('still does not strip a bare single-letter "R:"', () => {
expect(stripSubjectPrefixes('R: budget 2024')).toBe('R: budget 2024');
});
it('returns "" for empty / null / undefined and leaves clean subjects alone', () => {
expect(stripSubjectPrefixes('')).toBe('');
expect(stripSubjectPrefixes(null)).toBe('');
expect(stripSubjectPrefixes(undefined)).toBe('');
expect(stripSubjectPrefixes('foo')).toBe('foo');
});
});
describe('buildReplySubject / buildForwardSubject', () => {
it('replaces a prefix chain (incl. a full-width colon) with the given prefix', () => {
expect(buildReplySubject('回复:foo', 'Re:')).toBe('Re: foo');
expect(buildForwardSubject('Re: foo', 'Fwd:')).toBe('Fwd: foo');
});
it('prepends to a clean subject and returns the bare prefix for empty input', () => {
expect(buildReplySubject('foo', 'AW:')).toBe('AW: foo');
expect(buildReplySubject('', 'AW:')).toBe('AW:');
});
});