import { Clipboard } from '@angular/cdk/clipboard'; import { TestBed } from '@angular/core/testing'; import { TaskIdCopyComponent } from './task-id-copy.component'; describe('任务 ID 复制', () => { let clipboard: jasmine.SpyObj; beforeEach(() => { spyOn(navigator.clipboard, 'writeText').and.rejectWith(new Error('blocked')); clipboard = jasmine.createSpyObj('Clipboard', ['copy']); TestBed.configureTestingModule({ providers: [{ provide: Clipboard, useValue: clipboard }] }); }); afterEach(() => window.getSelection()?.removeAllRanges()); function setup() { const fixture = TestBed.createComponent(TaskIdCopyComponent); fixture.componentRef.setInput('taskId', 'task-123'); fixture.detectChanges(); const target: HTMLElement = fixture.nativeElement.querySelector('[role="button"]'); return { fixture, target }; } it('点击复制完整 ID,并提示成功,不触发父卡片', async () => { clipboard.copy.and.returnValue(true); const { fixture, target } = setup(); const parentClick = jasmine.createSpy('parentClick'); fixture.nativeElement.addEventListener('click', parentClick); target.click(); await Promise.resolve(); fixture.detectChanges(); expect(clipboard.copy).toHaveBeenCalledOnceWith('task-123'); expect(fixture.nativeElement.querySelector('[role="status"]').textContent).toContain('已复制'); expect(parentClick).not.toHaveBeenCalled(); fixture.destroy(); }); it('优先使用浏览器剪贴板,并在写入完成后提示成功', async () => { const write = navigator.clipboard.writeText as jasmine.Spy; let complete!: () => void; write.and.returnValue(new Promise(resolve => complete = resolve)); const { fixture, target } = setup(); target.click(); expect(fixture.componentInstance.feedback()).toBe(''); complete(); await Promise.resolve(); expect(write).toHaveBeenCalledOnceWith('task-123'); expect(clipboard.copy).not.toHaveBeenCalled(); expect(fixture.componentInstance.feedback()).toBe('任务 ID 已复制'); fixture.destroy(); }); it('复制失败不会提示成功,ID 仍可手动选择', async () => { clipboard.copy.and.returnValue(false); const { fixture, target } = setup(); target.click(); await Promise.resolve(); fixture.detectChanges(); expect(fixture.nativeElement.textContent).toContain('复制失败,请选中任务 ID 手动复制'); expect(getComputedStyle(target.querySelector('.value')!).userSelect).toBe('text'); fixture.destroy(); }); it('拖选 ID 后的点击不会覆盖剪贴板或清空选区', () => { const { fixture, target } = setup(); const range = document.createRange(); range.selectNodeContents(target.querySelector('.value')!); const selection = window.getSelection()!; selection.removeAllRanges(); selection.addRange(range); target.click(); expect(clipboard.copy).not.toHaveBeenCalled(); expect(selection.toString()).toContain('task-123'); fixture.destroy(); }); it('支持键盘 Enter 复制', async () => { clipboard.copy.and.returnValue(true); const { fixture, target } = setup(); target.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); await Promise.resolve(); expect(clipboard.copy).toHaveBeenCalledOnceWith('task-123'); fixture.destroy(); }); });