mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 13:29:44 +00:00
Merge pull request #213258 from microsoft/connor4312/fix-test-followup-followup
testing: polish followups a little, selfhost with copilot
This commit is contained in:
commit
eccc47493a
|
@ -41,16 +41,15 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||
const ctrl = vscode.tests.createTestController('selfhost-test-controller', 'VS Code Tests');
|
||||
const fileChangedEmitter = new vscode.EventEmitter<FileChangeEvent>();
|
||||
|
||||
// todo@connor4312: tidy this up and make it work
|
||||
// context.subscriptions.push(vscode.tests.registerTestFollowupProvider({
|
||||
// async provideFollowup(result, test, taskIndex, messageIndex, token) {
|
||||
// await new Promise(r => setTimeout(r, 2000));
|
||||
// return [{
|
||||
// title: '$(sparkle) Ask copilot for help',
|
||||
// command: 'asdf'
|
||||
// }];
|
||||
// },
|
||||
// }));
|
||||
context.subscriptions.push(vscode.tests.registerTestFollowupProvider({
|
||||
async provideFollowup(_result, test, taskIndex, messageIndex, _token) {
|
||||
return [{
|
||||
title: '$(sparkle) Ask copilot for help',
|
||||
command: 'github.copilot.tests.fixTestFailure',
|
||||
arguments: [{ source: 'peekFollowup', test, message: test.taskStates[taskIndex].messages[messageIndex] }]
|
||||
}];
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
ctrl.resolveHandler = async test => {
|
||||
|
|
|
@ -44,6 +44,12 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
|||
super();
|
||||
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostTesting);
|
||||
|
||||
this._register(this.testService.registerExtHost({
|
||||
provideTestFollowups: (req, token) => this.proxy.$provideTestFollowups(req, token),
|
||||
executeTestFollowup: id => this.proxy.$executeTestFollowup(id),
|
||||
disposeTestFollowups: ids => this.proxy.$disposeTestFollowups(ids),
|
||||
}));
|
||||
|
||||
this._register(this.testService.onDidCancelTestRun(({ runId }) => {
|
||||
this.proxy.$cancelExtensionTestRun(runId);
|
||||
}));
|
||||
|
@ -233,9 +239,6 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
|||
runTests: (reqs, token) => this.proxy.$runControllerTests(reqs, token),
|
||||
startContinuousRun: (reqs, token) => this.proxy.$startContinuousRun(reqs, token),
|
||||
expandTest: (testId, levels) => this.proxy.$expandTest(testId, isFinite(levels) ? levels : -1),
|
||||
provideTestFollowups: (req, token) => this.proxy.$provideTestFollowups(req, token),
|
||||
executeTestFollowup: id => this.proxy.$executeTestFollowup(id),
|
||||
disposeTestFollowups: ids => this.proxy.$disposeTestFollowups(ids),
|
||||
};
|
||||
|
||||
disposable.add(toDisposable(() => this.testProfiles.removeProfile(controllerId)));
|
||||
|
|
|
@ -277,6 +277,9 @@
|
|||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(transparent, var(--vscode-peekViewEditor-background) 50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
|
||||
&.animated {
|
||||
animation: fadeIn 150ms ease-out;
|
||||
|
@ -289,6 +292,7 @@
|
|||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
width: fit-content;
|
||||
flex-shrink: 0;
|
||||
|
||||
&, .codicon {
|
||||
color: var(--vscode-textLink-foreground);
|
||||
|
|
|
@ -69,6 +69,7 @@ import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listSe
|
|||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities';
|
||||
|
@ -780,6 +781,7 @@ class FollowupActionWidget extends Disposable {
|
|||
constructor(
|
||||
private readonly container: HTMLElement,
|
||||
@ITestService private readonly testService: ITestService,
|
||||
@IQuickInputService private readonly quickInput: IQuickInputService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
@ -794,6 +796,12 @@ class FollowupActionWidget extends Disposable {
|
|||
private async showMessage(subject: MessageSubject) {
|
||||
const cts = this.visibleStore.add(new CancellationTokenSource());
|
||||
const start = Date.now();
|
||||
|
||||
// Wait for completion otherwise results will not be available to the ext host:
|
||||
if (subject.result instanceof LiveTestResult && !subject.result.completedAt) {
|
||||
await new Promise(r => Event.once((subject.result as LiveTestResult).onComplete)(r));
|
||||
}
|
||||
|
||||
const followups = await this.testService.provideTestFollowups({
|
||||
extId: subject.test.extId,
|
||||
messageIndex: subject.messageIndex,
|
||||
|
@ -811,20 +819,10 @@ class FollowupActionWidget extends Disposable {
|
|||
|
||||
dom.clearNode(this.el.root);
|
||||
this.el.root.classList.toggle('animated', Date.now() - start > FOLLOWUP_ANIMATION_MIN_TIME);
|
||||
for (const fu of followups.followups) {
|
||||
const link = document.createElement('a');
|
||||
link.tabIndex = 0;
|
||||
dom.reset(link, ...renderLabelWithIcons(fu.message));
|
||||
|
||||
this.visibleStore.add(dom.addDisposableListener(link, 'click', () => this.actionFollowup(link, fu)));
|
||||
this.visibleStore.add(dom.addDisposableListener(link, 'keydown', e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) {
|
||||
this.actionFollowup(link, fu);
|
||||
}
|
||||
}));
|
||||
|
||||
this.el.root.appendChild(link);
|
||||
this.el.root.appendChild(this.makeFollowupLink(followups.followups[0]));
|
||||
if (followups.followups.length > 1) {
|
||||
this.el.root.appendChild(this.makeMoreLink(followups.followups));
|
||||
}
|
||||
|
||||
this.container.appendChild(this.el.root);
|
||||
|
@ -833,6 +831,42 @@ class FollowupActionWidget extends Disposable {
|
|||
}));
|
||||
}
|
||||
|
||||
private makeFollowupLink(first: ITestFollowup) {
|
||||
const link = this.makeLink(() => this.actionFollowup(link, first));
|
||||
dom.reset(link, ...renderLabelWithIcons(first.message));
|
||||
return link;
|
||||
}
|
||||
|
||||
private makeMoreLink(followups: ITestFollowup[]) {
|
||||
const link = this.makeLink(() =>
|
||||
this.quickInput.pick(followups.map((f, i) => ({
|
||||
label: f.message,
|
||||
index: i
|
||||
}))).then(picked => {
|
||||
if (picked?.length) {
|
||||
followups[picked[0].index].execute();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
link.innerText = localize('testFollowup.more', '+{0} More...', followups.length - 1);
|
||||
return link;
|
||||
}
|
||||
|
||||
private makeLink(onClick: () => void) {
|
||||
const link = document.createElement('a');
|
||||
link.tabIndex = 0;
|
||||
this.visibleStore.add(dom.addDisposableListener(link, 'click', onClick));
|
||||
this.visibleStore.add(dom.addDisposableListener(link, 'keydown', e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) {
|
||||
onClick();
|
||||
}
|
||||
}));
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
private actionFollowup(link: HTMLAnchorElement, fu: ITestFollowup) {
|
||||
if (link.ariaDisabled !== 'true') {
|
||||
link.ariaDisabled = 'true';
|
||||
|
|
|
@ -29,6 +29,9 @@ export interface IMainThreadTestController {
|
|||
expandTest(id: string, levels: number): Promise<void>;
|
||||
startContinuousRun(request: ICallProfileRunHandler[], token: CancellationToken): Promise<IStartControllerTestsResult[]>;
|
||||
runTests(request: IStartControllerTests[], token: CancellationToken): Promise<IStartControllerTestsResult[]>;
|
||||
}
|
||||
|
||||
export interface IMainThreadTestHostProxy {
|
||||
provideTestFollowups(req: TestMessageFollowupRequest, token: CancellationToken): Promise<TestMessageFollowupResponse[]>;
|
||||
executeTestFollowup(id: number): Promise<void>;
|
||||
disposeTestFollowups(ids: number[]): void;
|
||||
|
@ -273,6 +276,11 @@ export interface ITestService {
|
|||
*/
|
||||
readonly showInlineOutput: MutableObservableValue<boolean>;
|
||||
|
||||
/**
|
||||
* Registers an interface that represents an extension host..
|
||||
*/
|
||||
registerExtHost(controller: IMainThreadTestHostProxy): IDisposable;
|
||||
|
||||
/**
|
||||
* Registers an interface that runs tests for the given provider ID.
|
||||
*/
|
||||
|
|
|
@ -27,13 +27,14 @@ import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingC
|
|||
import { canUseProfileWithTest, ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService';
|
||||
import { ITestResult } from 'vs/workbench/contrib/testing/common/testResult';
|
||||
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { AmbiguousRunTestsRequest, IMainThreadTestController, ITestFollowups, ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { AmbiguousRunTestsRequest, IMainThreadTestController, IMainThreadTestHostProxy, ITestFollowups, ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { ResolvedTestRunRequest, TestDiffOpType, TestMessageFollowupRequest, TestsDiff } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
export class TestService extends Disposable implements ITestService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
private testControllers = new Map<string, IMainThreadTestController>();
|
||||
private testExtHosts = new Set<IMainThreadTestHostProxy>();
|
||||
|
||||
private readonly cancelExtensionTestRunEmitter = new Emitter<{ runId: string | undefined }>();
|
||||
private readonly willProcessDiffEmitter = new Emitter<TestsDiff>();
|
||||
|
@ -268,8 +269,8 @@ export class TestService extends Disposable implements ITestService {
|
|||
* @inheritdoc
|
||||
*/
|
||||
public async provideTestFollowups(req: TestMessageFollowupRequest, token: CancellationToken): Promise<ITestFollowups> {
|
||||
const reqs = await Promise.all([...this.testControllers.values()]
|
||||
.map(async ctrl => ({ ctrl, followups: await ctrl.provideTestFollowups(req, token) })));
|
||||
const reqs = await Promise.all([...this.testExtHosts].map(async ctrl =>
|
||||
({ ctrl, followups: await ctrl.provideTestFollowups(req, token) })));
|
||||
|
||||
const followups: ITestFollowups = {
|
||||
followups: reqs.flatMap(({ ctrl, followups }) => followups.map(f => ({
|
||||
|
@ -351,6 +352,14 @@ export class TestService extends Disposable implements ITestService {
|
|||
this.isRefreshingTests.set(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
registerExtHost(controller: IMainThreadTestHostProxy): IDisposable {
|
||||
this.testExtHosts.add(controller);
|
||||
return toDisposable(() => this.testExtHosts.delete(controller));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue