mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 16:13:15 +00:00
The Pro-mode bridge that turns an inline compose intent into a compose tab derived its working title from the draft subject or, failing that, the selected email's subject - regardless of mode. For reply/forward the selected email is the message being acted on; for a fresh compose it is whatever happens to be highlighted in the list, so "New Email" opened with an unrelated subject in the tab strip (#856). Extract the rule into buildComposeTabTitle: a fresh compose only ever uses its own draft subject (mailto, resumed draft) and otherwise starts on the "New message" fallback. Typing a subject already renames the tab live via handleSaveState, so nothing else changes.
88 lines
3.5 KiB
TypeScript
88 lines
3.5 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
||
import {
|
||
stripSubjectPrefixes,
|
||
buildReplySubject,
|
||
buildForwardSubject,
|
||
buildComposeTabTitle,
|
||
} 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('Re:foo')).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:');
|
||
});
|
||
});
|
||
|
||
describe('buildComposeTabTitle', () => {
|
||
const prefixes = { replyPrefix: 'Re:', forwardPrefix: 'Fwd:', fallback: 'New message' };
|
||
|
||
it('never inherits the selected email subject for a fresh compose (#856)', () => {
|
||
expect(buildComposeTabTitle({ mode: 'compose', sourceSubject: 'Invoice August 2026', ...prefixes }))
|
||
.toBe('New message');
|
||
});
|
||
|
||
it('uses the draft subject for a fresh compose when one exists (mailto, draft edit)', () => {
|
||
expect(buildComposeTabTitle({ mode: 'compose', draftSubject: 'Lunch?', sourceSubject: 'Invoice August 2026', ...prefixes }))
|
||
.toBe('Lunch?');
|
||
});
|
||
|
||
it('derives reply and forward titles from the source subject', () => {
|
||
expect(buildComposeTabTitle({ mode: 'reply', sourceSubject: 'Invoice August 2026', ...prefixes }))
|
||
.toBe('Re: Invoice August 2026');
|
||
expect(buildComposeTabTitle({ mode: 'replyAll', sourceSubject: 'AW: Invoice', ...prefixes }))
|
||
.toBe('Re: Invoice');
|
||
expect(buildComposeTabTitle({ mode: 'forward', sourceSubject: 'Invoice', ...prefixes }))
|
||
.toBe('Fwd: Invoice');
|
||
});
|
||
|
||
it('prefers a resumed draft subject over the source and falls back when both are blank', () => {
|
||
expect(buildComposeTabTitle({ mode: 'reply', draftSubject: 'Re: edited', sourceSubject: 'Original', ...prefixes }))
|
||
.toBe('Re: edited');
|
||
expect(buildComposeTabTitle({ mode: 'reply', sourceSubject: ' ', ...prefixes }))
|
||
.toBe('New message');
|
||
});
|
||
});
|