Files
Artemis-Eapil/apps/showcase_ui/src/app/components/task-plugins/task-plugins.component.spec.ts
T
chenmingxuan be6e8c2abd feat: release 2.2.0 with PC-only tests and execution reliability fixes
Unify task scheduling and queue state, fix Android input and observation recovery, preserve screenshot paths, and match final review criteria accurately. Add collapsible copyable results and publish the complete 2.1.0 upgrade notes. Validation: 3189 backend tests, 326 frontend tests, 24 client tests, production build and quality gates.
2026-10-27 01:13:41 +08:00

98 lines
6.1 KiB
TypeScript

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({});
});
});