import { TestBed } from '@angular/core/testing'; import { provideRouter } from '@angular/router'; import { provideHttpClient } from '@angular/common/http'; import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing'; import { TaskPluginsComponent } from './task-plugins.component'; describe('TaskPluginsComponent', () => { let http: HttpTestingController; beforeEach(() => { TestBed.configureTestingModule({ imports: [TaskPluginsComponent], providers: [provideRouter([]), provideHttpClient(), provideHttpClientTesting()] }); http = TestBed.inject(HttpTestingController); }); afterEach(() => http.verify()); it('starts collapsed and automatic without an execution target selector', async () => { const fixture = TestBed.createComponent(TaskPluginsComponent); fixture.detectChanges(); http.expectNone('/api/plugins'); expect(fixture.nativeElement.textContent).toContain('自动'); expect(fixture.nativeElement.textContent).not.toContain('执行目标'); expect(fixture.nativeElement.querySelector('select')).toBeNull(); const emitted = jasmine.createSpy('changed'); fixture.componentInstance.changed.subscribe(emitted); fixture.nativeElement.querySelector('button').click(); fixture.detectChanges(); http.expectOne('/api/plugins').flush({ plugins: [] }); await fixture.whenStable(); fixture.detectChanges(); fixture.nativeElement.querySelector('input').click(); fixture.detectChanges(); expect(emitted).toHaveBeenCalledWith({ plugins: [] }); fixture.nativeElement.querySelector('input').click(); fixture.detectChanges(); expect(emitted).toHaveBeenCalledWith({}); }); it('uses the single authorized setting without exposing a connection selector', async () => { const fixture = TestBed.createComponent(TaskPluginsComponent); const component = fixture.componentInstance; component.toggle(); const plugin = { enabled: true, manifest: { id: 'desktop', name: '电脑控制', targets: ['computer', 'mixed'] }, profiles: [ { name: 'default', label: '旧配置', authorized: false }, { name: 'lab', label: '实验室', authorized: true, is_default: true } ] }; http.expectOne('/api/plugins').flush({ plugins: [plugin, { ...plugin, enabled: false, manifest: { ...plugin.manifest, id: 'disabled' } }] }); component.automatic = false; fixture.detectChanges(); await fixture.whenStable(); const emitted = jasmine.createSpy('changed'); component.changed.subscribe(emitted); const checkbox = fixture.nativeElement.querySelector('.choice input'); checkbox.click(); fixture.detectChanges(); await fixture.whenStable(); expect(emitted).toHaveBeenCalledWith({ plugins: [{ id: 'desktop', profile: 'lab' }] }); expect(fixture.nativeElement.querySelectorAll('.choice').length).toBe(1); expect(fixture.nativeElement.querySelector('select')).toBeNull(); expect(fixture.nativeElement.textContent).not.toContain('连接配置'); }); it('allows choosing among multiple authorized settings and excludes unauthorized ones', async () => { const fixture = TestBed.createComponent(TaskPluginsComponent); const component = fixture.componentInstance; component.toggle(); http.expectOne('/api/plugins').flush({ plugins: [{ enabled: true, manifest: { id: 'desktop', name: '电脑控制', targets: ['mixed'] }, profiles: [ { name: 'old', label: '旧配置', authorized: false }, { name: 'lab', label: '实验室', authorized: true, is_default: true }, { name: 'office', label: '办公室', authorized: true } ] }] }); component.automatic = false; fixture.detectChanges(); await fixture.whenStable(); const emitted = jasmine.createSpy('changed'); component.changed.subscribe(emitted); fixture.nativeElement.querySelector('.choice input').click(); fixture.detectChanges(); await fixture.whenStable(); const select: HTMLSelectElement = fixture.nativeElement.querySelector('select'); expect(select.value).toBe('lab'); expect(select.textContent).not.toContain('旧配置'); select.value = 'office'; select.dispatchEvent(new Event('change')); fixture.detectChanges(); await fixture.whenStable(); expect(emitted).toHaveBeenCalledWith({ plugins: [{ id: 'desktop', profile: 'office' }] }); }); it('shows a retryable error and keeps automatic mode on lookup failure', () => { const fixture = TestBed.createComponent(TaskPluginsComponent); fixture.componentInstance.toggle(); http.expectOne('/api/plugins').flush({}, { status: 503, statusText: 'Unavailable' }); fixture.detectChanges(); expect(fixture.nativeElement.textContent).toContain('重试'); expect(fixture.componentInstance.automatic).toBeTrue(); }); it('filters PC-compatible plugins and removes incompatible manual choices on target change', () => { const fixture = TestBed.createComponent(TaskPluginsComponent), component=fixture.componentInstance; component.toggle(); const profile={name:'default',label:'默认',authorized:true,is_default:true}; const plugin=(id:string,targets:string[])=>({enabled:true,manifest:{id,name:id,targets},profiles:[profile]}); http.expectOne('/api/plugins').flush({plugins:[plugin('android-only',['android']),plugin('mixed-only',['mixed']),plugin('desktop',['computer'])]}); component.automatic=false;component.selected={'android-only':'default'}; const emitted=jasmine.createSpy('changed');component.changed.subscribe(emitted); fixture.componentRef.setInput('target','computer');fixture.detectChanges(); expect(component.available().map(p=>p.manifest.id)).toEqual(['desktop']); expect(emitted).toHaveBeenCalledWith({plugins:[]}); expect(fixture.nativeElement.textContent).toContain('画面观察'); const choice:HTMLInputElement=fixture.nativeElement.querySelector('.choice input');choice.click();fixture.detectChanges(); expect(emitted).toHaveBeenCalledWith({plugins:[{id:'desktop',profile:'default'}]}); fixture.componentRef.setInput('target','android');fixture.detectChanges(); expect(component.available().map(p=>p.manifest.id)).toEqual(['android-only','mixed-only']); expect(component.selected).toEqual({}); }); });