moyf_easy-copy/src/pasteResolution.test.ts

181 lines
5.3 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 {
decidePasteResolution,
PasteResolutionInput,
shouldOmitAliasForSameFile,
shouldRegisterPasteHandler,
} from './pasteResolution';
import { CopyMetadata } from './copyMetadata';
import { LinkFormat } from './type';
describe('shouldRegisterPasteHandler', () => {
it('returns true when toggle is on', () => {
expect(shouldRegisterPasteHandler({ resolveLinkPathOnPaste: true })).toBe(true);
});
it('returns false when toggle is off', () => {
expect(shouldRegisterPasteHandler({ resolveLinkPathOnPaste: false })).toBe(false);
});
});
const META: CopyMetadata = {
clipboardText: '[[note#Heading]]',
sourceFilePath: 'notes/note.md',
subpath: '#Heading',
alias: 'Heading',
isEmbed: false,
timestamp: 1_000_000,
};
const TTL = 5 * 60 * 1000;
const baseInput: PasteResolutionInput = {
defaultPrevented: false,
resolveLinkPathOnPaste: true,
lastCopyMeta: META,
clipboardText: META.clipboardText,
now: META.timestamp + 1000,
ttlMs: TTL,
};
describe('decidePasteResolution', () => {
it('returns rewrite when all guards pass', () => {
expect(decidePasteResolution(baseInput)).toBe('rewrite');
});
it('skips when another handler already preventDefault\'d', () => {
expect(decidePasteResolution({ ...baseInput, defaultPrevented: true })).toBe('skip');
});
it('skips when the toggle is off', () => {
expect(decidePasteResolution({ ...baseInput, resolveLinkPathOnPaste: false })).toBe('skip');
});
it('skips when there is no lastCopyMeta', () => {
expect(decidePasteResolution({ ...baseInput, lastCopyMeta: null })).toBe('skip');
});
it('resets and skips when meta is older than TTL', () => {
expect(decidePasteResolution({
...baseInput,
now: META.timestamp + TTL + 1,
})).toBe('reset-and-skip');
});
it('does not consider meta stale at exactly the TTL boundary', () => {
expect(decidePasteResolution({
...baseInput,
now: META.timestamp + TTL,
})).toBe('rewrite');
});
it('resets and skips when clipboard text differs from copied text', () => {
expect(decidePasteResolution({
...baseInput,
clipboardText: 'something else entirely',
})).toBe('reset-and-skip');
});
it('resets and skips when clipboard text is undefined', () => {
expect(decidePasteResolution({
...baseInput,
clipboardText: undefined,
})).toBe('reset-and-skip');
});
it('defaultPrevented takes precedence over toggle and other state', () => {
expect(decidePasteResolution({
...baseInput,
defaultPrevented: true,
resolveLinkPathOnPaste: false,
lastCopyMeta: null,
})).toBe('skip');
});
it('toggle takes precedence over meta state', () => {
expect(decidePasteResolution({
...baseInput,
resolveLinkPathOnPaste: false,
lastCopyMeta: null,
})).toBe('skip');
});
it('TTL check runs before clipboard match', () => {
// 过期的 meta 加上不匹配的剪贴板文本——TTL 检查会先触发,
// 两种情况下结果都是 reset-and-skip但根因是过期。
expect(decidePasteResolution({
...baseInput,
now: META.timestamp + TTL + 1,
clipboardText: 'mismatch',
})).toBe('reset-and-skip');
});
});
describe('shouldOmitAliasForSameFile', () => {
const sameFile = 'notes/SomeThing.md';
const otherFile = 'notes/MyNote.md';
const base = {
effectiveLinkFormat: LinkFormat.WIKILINK,
sourceFilePath: sameFile,
destFilePath: sameFile,
subpath: '#Other Heading',
alias: 'Other Heading',
useHeadingAsDisplayText: true,
};
it('omits when WIKI + same-file + heading subpath matches alias (sanitized)', () => {
expect(shouldOmitAliasForSameFile(base)).toBe(true);
});
it('keeps alias when WIKI + same-file but alias does not match heading', () => {
expect(shouldOmitAliasForSameFile({ ...base, alias: 'Different' })).toBe(false);
});
it('keeps alias when useHeadingAsDisplayText is false', () => {
expect(shouldOmitAliasForSameFile({
...base,
useHeadingAsDisplayText: false,
alias: 'SomeThing#Other Heading',
})).toBe(false);
});
it('keeps alias on cross-file paste (WIKI)', () => {
expect(shouldOmitAliasForSameFile({ ...base, destFilePath: otherFile })).toBe(false);
});
it('keeps alias for MD format even on same-file', () => {
expect(shouldOmitAliasForSameFile({ ...base, effectiveLinkFormat: LinkFormat.MDLINK })).toBe(false);
});
it('keeps alias when subpath is empty', () => {
expect(shouldOmitAliasForSameFile({ ...base, subpath: '' })).toBe(false);
});
it('keeps alias when alias is empty', () => {
expect(shouldOmitAliasForSameFile({ ...base, alias: '' })).toBe(false);
});
it('keeps alias on block-link subpath (#^id)', () => {
expect(shouldOmitAliasForSameFile({
...base,
subpath: '#^abc123',
alias: 'The quick brown',
})).toBe(false);
});
it('omits when sanitization round-trip collapses alias to subpath', () => {
// alias 中的 | 会被 sanitizeHeadingForLink 折叠为空格
expect(shouldOmitAliasForSameFile({
...base,
subpath: '#Some Heading',
alias: 'Some|Heading',
})).toBe(true);
});
it('keeps alias when OBSIDIAN is passed (helper expects already-resolved format)', () => {
// 防御性检查:调用方不应在此传入 OBSIDIAN
// 即使误传,本函数也会安全地返回 false。
expect(shouldOmitAliasForSameFile({ ...base, effectiveLinkFormat: LinkFormat.OBSIDIAN })).toBe(false);
});
});