mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-02 08:15:56 +01:00
Merge pull request #300818 from microsoft/ben/fancy-muskox
E2E sessions test updates
This commit is contained in:
@@ -1965,6 +1965,7 @@ export default tseslint.config(
|
||||
'vs/editor/contrib/*/~',
|
||||
'vs/editor/editor.all.js',
|
||||
'vs/sessions/~',
|
||||
'vs/sessions/services/*/~',
|
||||
'vs/sessions/contrib/*/~',
|
||||
'vs/workbench/~',
|
||||
'vs/workbench/api/~',
|
||||
@@ -1984,6 +1985,7 @@ export default tseslint.config(
|
||||
'vs/editor/contrib/*/~',
|
||||
'vs/editor/editor.all.js',
|
||||
'vs/sessions/~',
|
||||
'vs/sessions/services/*/~',
|
||||
'vs/sessions/contrib/*/~',
|
||||
'vs/workbench/~',
|
||||
'vs/workbench/api/~',
|
||||
@@ -2083,6 +2085,7 @@ export default tseslint.config(
|
||||
'vs/editor/contrib/*/~',
|
||||
'vs/workbench/~',
|
||||
'vs/workbench/services/*/~',
|
||||
'vs/sessions/~',
|
||||
'vs/sessions/services/*/~',
|
||||
{
|
||||
'when': 'test',
|
||||
|
||||
@@ -99,9 +99,14 @@ export class SessionsTerminalContribution extends Disposable implements IWorkben
|
||||
let existing = await this._findTerminalsForKey(key);
|
||||
|
||||
if (existing.length === 0) {
|
||||
existing = [await this._terminalService.createTerminal({ config: { cwd } })];
|
||||
this._terminalService.setActiveInstance(existing[0]);
|
||||
this._logService.trace(`[SessionsTerminal] Created terminal ${existing[0].instanceId} for ${cwd.fsPath}`);
|
||||
try {
|
||||
existing = [await this._terminalService.createTerminal({ config: { cwd } })];
|
||||
this._terminalService.setActiveInstance(existing[0]);
|
||||
this._logService.trace(`[SessionsTerminal] Created terminal ${existing[0].instanceId} for ${cwd.fsPath}`);
|
||||
} catch (e) {
|
||||
this._logService.trace(`[SessionsTerminal] Cannot create terminal for ${cwd.fsPath}: ${e}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
if (focus) {
|
||||
|
||||
10
src/vs/sessions/services/title/browser/titleService.ts
Normal file
10
src/vs/sessions/services/title/browser/titleService.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { ITitleService } from '../../../../workbench/services/title/browser/titleService.js';
|
||||
import { TitleService } from '../../../browser/parts/titlebarPart.js';
|
||||
|
||||
registerSingleton(ITitleService, TitleService, InstantiationType.Eager);
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { InstantiationType, registerSingleton } from '../../platform/instantiation/common/extensions.js';
|
||||
import { ITitleService } from '../../workbench/services/title/browser/titleService.js';
|
||||
import { NativeTitleService } from './parts/titlebarPart.js';
|
||||
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
|
||||
import { ITitleService } from '../../../../workbench/services/title/browser/titleService.js';
|
||||
import { NativeTitleService } from '../../../electron-browser/parts/titlebarPart.js';
|
||||
|
||||
registerSingleton(ITitleService, NativeTitleService, InstantiationType.Eager);
|
||||
@@ -42,7 +42,7 @@ import '../workbench/services/update/electron-browser/updateService.js';
|
||||
import '../workbench/services/url/electron-browser/urlService.js';
|
||||
import '../workbench/services/lifecycle/electron-browser/lifecycleService.js';
|
||||
import '../workbench/services/host/electron-browser/nativeHostService.js';
|
||||
import './electron-browser/titleService.js';
|
||||
import './services/title/electron-browser/titleService.js';
|
||||
import '../platform/meteredConnection/electron-browser/meteredConnectionService.js';
|
||||
import '../workbench/services/request/electron-browser/requestService.js';
|
||||
import '../workbench/services/clipboard/electron-browser/clipboardService.js';
|
||||
|
||||
@@ -90,8 +90,7 @@ import { UserDataAutoSyncService } from '../platform/userDataSync/common/userDat
|
||||
import { AccessibilityService } from '../platform/accessibility/browser/accessibilityService.js';
|
||||
import { ICustomEndpointTelemetryService } from '../platform/telemetry/common/telemetry.js';
|
||||
import { NullEndpointTelemetryService } from '../platform/telemetry/common/telemetryUtils.js';
|
||||
import { ITitleService } from '../workbench/services/title/browser/titleService.js';
|
||||
import { BrowserTitleService } from '../workbench/browser/parts/titlebar/titlebarPart.js';
|
||||
import './services/title/browser/titleService.js';
|
||||
import { ITimerService, TimerService } from '../workbench/services/timer/browser/timerService.js';
|
||||
import { IDiagnosticsService, NullDiagnosticsService } from '../platform/diagnostics/common/diagnostics.js';
|
||||
import { ILanguagePackService } from '../platform/languagePacks/common/languagePacks.js';
|
||||
@@ -111,7 +110,6 @@ registerSingleton(IUserDataSyncAccountService, UserDataSyncAccountService, Insta
|
||||
registerSingleton(IUserDataSyncService, UserDataSyncService, InstantiationType.Delayed);
|
||||
registerSingleton(IUserDataSyncResourceProviderService, UserDataSyncResourceProviderService, InstantiationType.Delayed);
|
||||
registerSingleton(IUserDataAutoSyncService, UserDataAutoSyncService, InstantiationType.Eager);
|
||||
registerSingleton(ITitleService, BrowserTitleService, InstantiationType.Eager);
|
||||
registerSingleton(IExtensionTipsService, ExtensionTipsService, InstantiationType.Delayed);
|
||||
registerSingleton(ITimerService, TimerService, InstantiationType.Delayed);
|
||||
registerSingleton(ICustomEndpointTelemetryService, NullEndpointTelemetryService, InstantiationType.Delayed);
|
||||
@@ -154,7 +152,9 @@ import './browser/layoutActions.js';
|
||||
|
||||
import './contrib/accountMenu/browser/account.contribution.js';
|
||||
import './contrib/aiCustomizationTreeView/browser/aiCustomizationTreeView.contribution.js';
|
||||
import './contrib/applyCommitsToParentRepo/browser/applyChangesToParentRepo.js';
|
||||
import './contrib/chat/browser/chat.contribution.js';
|
||||
import './contrib/terminal/browser/sessionsTerminalContribution.js';
|
||||
import './contrib/sessions/browser/sessions.contribution.js';
|
||||
import './contrib/sessions/browser/customizationsToolbar.contribution.js';
|
||||
import './contrib/changes/browser/changesView.contribution.js';
|
||||
|
||||
@@ -21,6 +21,8 @@ runs through the real code paths.
|
||||
| Chat agents (`copilotcli`, etc.) | Canned keyword-matched responses with `textEdit` progress items | No real LLM backend |
|
||||
| `mock-fs://` FileSystemProvider | `InMemoryFileSystemProvider` registered directly in the workbench (not extension host) | Must be available before any service tries to resolve workspace files |
|
||||
| GitHub authentication | Always-signed-in mock provider (extension) | No real OAuth flow |
|
||||
| Code Review command | Returns canned review comments per file (extension) | No real Copilot AI review |
|
||||
| PR commands (Create/Open/Merge) | No-op handlers that log and show info messages (extension) | No real GitHub API |
|
||||
|
||||
### What's Real (Everything Else)
|
||||
|
||||
@@ -39,6 +41,10 @@ exercise the actual code paths:
|
||||
observations
|
||||
- **Menu actions** — "Create PR", "Accept", "Reject" buttons appear based on
|
||||
real context key state
|
||||
- **`CodeReviewService`** — Orchestrates review requests, processes results from
|
||||
the mock `github.copilot.chat.codeReview.run` command, and stores comments
|
||||
- **`CodeReviewToolbarContribution`** — Shows the Code Review button in the
|
||||
Changes view toolbar based on real context key state
|
||||
|
||||
### Data Flow
|
||||
|
||||
@@ -57,6 +63,26 @@ User types message → Chat Widget → ChatService
|
||||
The mock agent is the **only** point where canned data enters the system.
|
||||
Everything downstream uses real service implementations.
|
||||
|
||||
### Code Review & PR Button Flow
|
||||
|
||||
```
|
||||
Code Review button clicked → sessions.codeReview.run (core action)
|
||||
→ CodeReviewService.requestReview()
|
||||
→ commandService.executeCommand('chat.internal.codeReview.run')
|
||||
→ Bridge forwards to 'github.copilot.chat.codeReview.run'
|
||||
→ Mock extension returns canned comments
|
||||
→ CodeReviewService stores results, updates observable state
|
||||
→ CodeReviewToolbarContribution updates button icon/badge
|
||||
|
||||
Create PR button clicked → github.copilot.chat.createPullRequestCopilotCLIAgentSession.createPR
|
||||
→ Mock extension logs and shows info message
|
||||
```
|
||||
|
||||
The PR buttons (Create PR, Open PR, Merge) are contributed via the mock
|
||||
extension's `package.json` menus, gated by `chatSessionType == copilotcli`.
|
||||
The `chatSessionType` context key is derived from the session URI scheme
|
||||
(`getChatSessionType()`), which returns `copilotcli` for mock sessions.
|
||||
|
||||
### Why the FileSystem Provider Is Registered in the Workbench
|
||||
|
||||
The `mock-fs://` `InMemoryFileSystemProvider` is registered directly on
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
/**
|
||||
* Mock extension for Sessions E2E testing.
|
||||
*
|
||||
* Provides a fake GitHub authentication provider (skips sign-in).
|
||||
* Provides:
|
||||
* - A fake GitHub authentication provider (skips sign-in)
|
||||
* - Mock command handlers for Code Review, Create PR, Open PR, and Merge
|
||||
*
|
||||
* The mock-fs:// FileSystemProvider and chat agents are registered
|
||||
* directly in the workbench (web.test.ts), not here.
|
||||
@@ -31,6 +33,9 @@ function activate(context) {
|
||||
// 1. Mock GitHub Authentication Provider
|
||||
context.subscriptions.push(registerMockAuth(vscode));
|
||||
|
||||
// 2. Mock command handlers for Code Review and PR actions
|
||||
context.subscriptions.push(...registerMockCommands(vscode));
|
||||
|
||||
// Note: The mock-fs:// FileSystemProvider is registered directly in the
|
||||
// workbench (web.test.ts → registerMockFileSystemProvider) so it is
|
||||
// available before any service tries to resolve workspace files.
|
||||
@@ -82,6 +87,88 @@ function registerMockAuth(vscode) {
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mock Command Handlers (Code Review + PR Actions)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Registers mock command handlers that stand in for the real GitHub Copilot
|
||||
* extension commands. These allow the Code Review and Create PR buttons to
|
||||
* function in the e2e test environment.
|
||||
*
|
||||
* @param {typeof import('vscode')} vscode
|
||||
* @returns {import('vscode').Disposable[]}
|
||||
*/
|
||||
function registerMockCommands(vscode) {
|
||||
const disposables = [];
|
||||
|
||||
// Mock create PR — simulates successful PR creation
|
||||
disposables.push(vscode.commands.registerCommand(
|
||||
'github.copilot.chat.createPullRequestCopilotCLIAgentSession.createPR',
|
||||
() => {
|
||||
console.log('[sessions-e2e-mock] Mock Create PR invoked');
|
||||
vscode.window.showInformationMessage('Mock: Pull request created successfully');
|
||||
}
|
||||
));
|
||||
|
||||
// Mock open PR — simulates opening a PR URL
|
||||
disposables.push(vscode.commands.registerCommand(
|
||||
'github.copilot.chat.openPullRequestCopilotCLIAgentSession.openPR',
|
||||
() => {
|
||||
console.log('[sessions-e2e-mock] Mock Open PR invoked');
|
||||
vscode.window.showInformationMessage('Mock: Opening pull request');
|
||||
}
|
||||
));
|
||||
|
||||
// Mock merge — simulates merging changes
|
||||
disposables.push(vscode.commands.registerCommand(
|
||||
'github.copilot.chat.mergeCopilotCLIAgentSessionChanges.merge',
|
||||
() => {
|
||||
console.log('[sessions-e2e-mock] Mock Merge invoked');
|
||||
vscode.window.showInformationMessage('Mock: Changes merged successfully');
|
||||
}
|
||||
));
|
||||
|
||||
// Mock merge and sync — simulates merging and syncing
|
||||
disposables.push(vscode.commands.registerCommand(
|
||||
'github.copilot.chat.mergeCopilotCLIAgentSessionChanges.mergeAndSync',
|
||||
() => {
|
||||
console.log('[sessions-e2e-mock] Mock Merge and Sync invoked');
|
||||
vscode.window.showInformationMessage('Mock: Changes merged and synced successfully');
|
||||
}
|
||||
));
|
||||
|
||||
// Mock apply changes — simulates applying session changes
|
||||
disposables.push(vscode.commands.registerCommand(
|
||||
'github.copilot.chat.applyCopilotCLIAgentSessionChanges.apply',
|
||||
() => {
|
||||
console.log('[sessions-e2e-mock] Mock Apply Changes invoked');
|
||||
vscode.window.showInformationMessage('Mock: Changes applied successfully');
|
||||
}
|
||||
));
|
||||
|
||||
// Mock checkout PR reroute — simulates checkout PR flow
|
||||
disposables.push(vscode.commands.registerCommand(
|
||||
'github.copilot.chat.checkoutPullRequestReroute',
|
||||
() => {
|
||||
console.log('[sessions-e2e-mock] Mock Checkout PR Reroute invoked');
|
||||
vscode.window.showInformationMessage('Mock: Checking out pull request');
|
||||
}
|
||||
));
|
||||
|
||||
// Mock update changes — simulates updating session changes
|
||||
disposables.push(vscode.commands.registerCommand(
|
||||
'github.copilot.chat.updateCopilotCLIAgentSessionChanges.update',
|
||||
() => {
|
||||
console.log('[sessions-e2e-mock] Mock Update Changes invoked');
|
||||
vscode.window.showInformationMessage('Mock: Changes updated successfully',);
|
||||
}
|
||||
));
|
||||
|
||||
console.log('[sessions-e2e-mock] Registered mock Code Review and PR command handlers');
|
||||
return disposables;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Exports
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"extensionKind": ["ui", "workspace"],
|
||||
"browser": "./extension.js",
|
||||
"activationEvents": ["*"],
|
||||
"enabledApiProposals": ["chatSessionsProvider"],
|
||||
"capabilities": {
|
||||
"virtualWorkspaces": true,
|
||||
"untrustedWorkspaces": {
|
||||
@@ -22,6 +23,88 @@
|
||||
"id": "github",
|
||||
"label": "GitHub (Mock)"
|
||||
}
|
||||
]
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"command": "github.copilot.chat.applyCopilotCLIAgentSessionChanges.apply",
|
||||
"title": "Apply Changes",
|
||||
"icon": "$(check)"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.checkoutPullRequestReroute",
|
||||
"title": "Checkout Pull Request",
|
||||
"icon": "$(git-pull-request)"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.openPullRequestCopilotCLIAgentSession.openPR",
|
||||
"title": "Open Pull Request",
|
||||
"icon": "$(link-external)"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.mergeCopilotCLIAgentSessionChanges.merge",
|
||||
"title": "Merge",
|
||||
"icon": "$(git-merge)"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.mergeCopilotCLIAgentSessionChanges.mergeAndSync",
|
||||
"title": "Merge and Sync",
|
||||
"icon": "$(git-merge)"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.createPullRequestCopilotCLIAgentSession.createPR",
|
||||
"title": "Create Pull Request",
|
||||
"icon": "$(git-pull-request-create)"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.updateCopilotCLIAgentSessionChanges.update",
|
||||
"title": "Update",
|
||||
"icon": "$(cloud-upload)"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.codeReview.run",
|
||||
"title": "Run Code Review"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"chat/input/editing/sessionToolbar": [
|
||||
{
|
||||
"command": "github.copilot.chat.applyCopilotCLIAgentSessionChanges.apply",
|
||||
"when": "chatSessionType == copilotcli && workbenchState != empty && !isSessionsWindow",
|
||||
"group": "navigation@0"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.checkoutPullRequestReroute",
|
||||
"when": "chatSessionType == copilot-cloud-agent && !github.vscode-pull-request-github.activated && gitOpenRepositoryCount != 0",
|
||||
"group": "navigation@0"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.openPullRequestCopilotCLIAgentSession.openPR",
|
||||
"when": "chatSessionType == copilotcli && isSessionsWindow && sessions.hasOpenPullRequest",
|
||||
"group": "navigation@1"
|
||||
}
|
||||
],
|
||||
"chat/input/editing/sessionApplyActions": [
|
||||
{
|
||||
"command": "github.copilot.chat.mergeCopilotCLIAgentSessionChanges.merge",
|
||||
"when": "chatSessionType == copilotcli && isSessionsWindow && !sessions.isMergeBaseBranchProtected",
|
||||
"group": "merge@1"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.mergeCopilotCLIAgentSessionChanges.mergeAndSync",
|
||||
"when": "chatSessionType == copilotcli && isSessionsWindow && !sessions.isMergeBaseBranchProtected",
|
||||
"group": "merge@2"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.createPullRequestCopilotCLIAgentSession.createPR",
|
||||
"when": "chatSessionType == copilotcli && isSessionsWindow && sessions.isMergeBaseBranchProtected && !sessions.hasOpenPullRequest",
|
||||
"group": "pull_request@1"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.updateCopilotCLIAgentSessionChanges.update",
|
||||
"when": "chatSessionType == copilotcli && isSessionsWindow",
|
||||
"group": "update@1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Scenario: Full workflow
|
||||
|
||||
## Steps
|
||||
1. Type "build the project" in the chat input
|
||||
2. Press Enter to submit
|
||||
3. Verify there is a response in the chat
|
||||
4. Toggle the secondary side bar
|
||||
5. Verify the changes view shows "CHANGES" with a badge
|
||||
6. Verify "package.json" is visible in the changes list
|
||||
7. Verify "build.ts" is visible in the changes list
|
||||
8. Verify "index.ts" is visible in the changes list
|
||||
9. Click on "index.ts" in the changes list
|
||||
10. Verify a diff editor opens with modified content
|
||||
11. Press Escape to close the diff editor
|
||||
12. Verify "Merge" button is visible in changes view header
|
||||
13. Verify the "Open Terminal" button is visible
|
||||
14. Click the "Open Terminal" button
|
||||
15. Verify the terminal panel becomes visible
|
||||
16. Verify the terminal tab shows "session-1" in its label
|
||||
17. Click "New Session" to create a new session
|
||||
18. Type "fix the bug" in the chat input
|
||||
19. Press Enter to submit
|
||||
20. Verify the terminal tab label changes to show "session-2"
|
||||
21. Click back on the first session in the sessions list
|
||||
22. Verify the terminal tab label changes back to show "session-1"
|
||||
@@ -0,0 +1,140 @@
|
||||
{
|
||||
"scenario": "Scenario: Full workflow",
|
||||
"generatedAt": "2026-03-12T22:48:50.725Z",
|
||||
"steps": [
|
||||
{
|
||||
"description": "Type \"build the project\" in the chat input",
|
||||
"commands": [
|
||||
"click textbox \"Chat input\"",
|
||||
"type \"build the project\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Press Enter to submit",
|
||||
"commands": [
|
||||
"press Enter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify there is a response in the chat",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: I'll help you build the project. Here are the changes:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Toggle the secondary side bar",
|
||||
"commands": [
|
||||
"click button \"Toggle Secondary Side Bar Visibility\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify the changes view shows \"CHANGES\" with a badge",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: Changes - 3 files changed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify \"package.json\" is visible in the changes list",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: package.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify \"build.ts\" is visible in the changes list",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: build.ts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify \"index.ts\" is visible in the changes list",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: index.ts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Click on \"index.ts\" in the changes list",
|
||||
"commands": [
|
||||
"click treeitem \"index.ts\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify a diff editor opens with modified content",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: index.ts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Press Escape to close the diff editor",
|
||||
"commands": [
|
||||
"press Escape"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify \"Merge\" button is visible in changes view header",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: Merge"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify the \"Open Terminal\" button is visible",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: Open Terminal"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Click the \"Open Terminal\" button",
|
||||
"commands": [
|
||||
"click button \"Open Terminal\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify the terminal panel becomes visible",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: Terminal"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify the terminal tab shows \"session-1\" in its label",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: bash - session-1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Click \"New Session\" to create a new session",
|
||||
"commands": [
|
||||
"click button \"New Session\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Type \"fix the bug\" in the chat input",
|
||||
"commands": [
|
||||
"click textbox \"Chat input\"",
|
||||
"type \"fix the bug\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Press Enter to submit",
|
||||
"commands": [
|
||||
"press Enter"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify the terminal tab label changes to show \"session-2\"",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: bash - session-2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Click back on the first session in the sessions list",
|
||||
"commands": [
|
||||
"click listitem \"build the project\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Verify the terminal tab label changes back to show \"session-1\"",
|
||||
"commands": [
|
||||
"# ASSERT_VISIBLE: bash - session-1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -25,6 +25,10 @@ import { IChatProgress } from '../../workbench/contrib/chat/common/chatService/c
|
||||
import { IChatSessionsService, IChatSessionItem, IChatSessionFileChange, ChatSessionStatus, IChatSessionHistoryItem, IChatSessionItemsDelta } from '../../workbench/contrib/chat/common/chatSessionsService.js';
|
||||
import { IGitService, IGitExtensionDelegate, IGitRepository } from '../../workbench/contrib/git/common/gitService.js';
|
||||
import { IFileService } from '../../platform/files/common/files.js';
|
||||
import { ITerminalService } from '../../workbench/contrib/terminal/browser/terminal.js';
|
||||
import { ITerminalBackend, ITerminalBackendRegistry, IProcessReadyEvent, IProcessProperty, ProcessPropertyType, TerminalExtensions, ITerminalProcessOptions, IShellLaunchConfig } from '../../platform/terminal/common/terminal.js';
|
||||
import { IProcessEnvironment } from '../../base/common/platform.js';
|
||||
import { Registry } from '../../platform/registry/common/platform.js';
|
||||
import { InMemoryFileSystemProvider } from '../../platform/files/common/inMemoryFilesystemProvider.js';
|
||||
import { VSBuffer } from '../../base/common/buffer.js';
|
||||
|
||||
@@ -230,16 +234,19 @@ class MockChatAgentContribution extends Disposable implements IWorkbenchContribu
|
||||
private readonly _sessionItems: IChatSessionItem[] = [];
|
||||
private readonly _itemsChangedEmitter = new Emitter<IChatSessionItemsDelta>();
|
||||
private readonly _sessionHistory = new Map<string, IChatSessionHistoryItem[]>();
|
||||
private _worktreeCounter = 0;
|
||||
|
||||
constructor(
|
||||
@IChatAgentService private readonly chatAgentService: IChatAgentService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IChatSessionsService private readonly chatSessionsService: IChatSessionsService,
|
||||
@ITerminalService private readonly terminalService: ITerminalService,
|
||||
) {
|
||||
super();
|
||||
this._register(this._itemsChangedEmitter);
|
||||
this.registerMockAgents();
|
||||
this.registerMockSessionProvider();
|
||||
this.registerMockTerminalBackend();
|
||||
this.preseedFolder();
|
||||
}
|
||||
|
||||
@@ -286,6 +293,7 @@ class MockChatAgentContribution extends Disposable implements IWorkbenchContribu
|
||||
label: message.slice(0, 50) || 'Mock Session',
|
||||
status: ChatSessionStatus.Completed,
|
||||
timing: { created: now, lastRequestStarted: now, lastRequestEnded: now },
|
||||
metadata: { worktreePath: `/mock-worktrees/session-${++this._worktreeCounter}` },
|
||||
...(changes ? { changes } : {}),
|
||||
};
|
||||
this._sessionItems.push(addedOrUpdated);
|
||||
@@ -311,7 +319,7 @@ class MockChatAgentContribution extends Disposable implements IWorkbenchContribu
|
||||
extensionVersion: '0.0.1',
|
||||
extensionPublisherId: 'vscode',
|
||||
extensionDisplayName: 'Sessions E2E Mock',
|
||||
isDefault: true,
|
||||
isDefault: agentId === 'copilotcli',
|
||||
metadata: {},
|
||||
slashCommands: [],
|
||||
locations: [ChatAgentLocation.Chat],
|
||||
@@ -385,11 +393,15 @@ class MockChatAgentContribution extends Disposable implements IWorkbenchContribu
|
||||
},
|
||||
}));
|
||||
|
||||
// Register an item controller so sessions appear in the sidebar list
|
||||
const items = this._sessionItems;
|
||||
// Register an item controller so sessions appear in the sidebar list.
|
||||
// Only copilotcli (Background) sessions need real items — the
|
||||
// copilot-cloud-agent controller must return an empty array to
|
||||
// prevent it from overwriting sessions with the wrong providerType
|
||||
// during a full model resolve.
|
||||
const controllerItems = scheme === 'copilotcli' ? this._sessionItems : [];
|
||||
this._register(this.chatSessionsService.registerChatSessionItemController(scheme, {
|
||||
onDidChangeChatSessionItems: this._itemsChangedEmitter.event,
|
||||
get items() { return items; },
|
||||
get items() { return controllerItems; },
|
||||
async refresh() { /* in-memory, no-op */ },
|
||||
}));
|
||||
|
||||
@@ -400,6 +412,83 @@ class MockChatAgentContribution extends Disposable implements IWorkbenchContribu
|
||||
}
|
||||
}
|
||||
|
||||
private registerMockTerminalBackend(): void {
|
||||
const terminalService = this.terminalService;
|
||||
const backend = this.createMockTerminalBackend();
|
||||
Registry.as<ITerminalBackendRegistry>(TerminalExtensions.Backend).registerTerminalBackend(backend);
|
||||
terminalService.registerProcessSupport(true);
|
||||
console.log('[Sessions Web Test] Registered mock terminal backend');
|
||||
}
|
||||
|
||||
private createMockTerminalBackend(): ITerminalBackend {
|
||||
return {
|
||||
remoteAuthority: undefined,
|
||||
isVirtualProcess: false,
|
||||
onDidRequestDetach: Event.None,
|
||||
attachToProcess: async () => { throw new Error('Not supported'); },
|
||||
attachToRevivedProcess: async () => { throw new Error('Not supported'); },
|
||||
listProcesses: async () => [],
|
||||
getProfiles: async () => [],
|
||||
getDefaultProfile: async () => undefined,
|
||||
getDefaultSystemShell: async () => '/bin/mock-shell',
|
||||
getShellEnvironment: async () => ({}),
|
||||
setTerminalLayoutInfo: async () => { },
|
||||
getTerminalLayoutInfo: async () => undefined,
|
||||
reduceConnectionGraceTime: () => { },
|
||||
requestDetachInstance: () => { },
|
||||
acceptDetachInstanceReply: () => { },
|
||||
persistTerminalState: () => { },
|
||||
createProcess: async (_shellLaunchConfig: IShellLaunchConfig, _cwd: string | URI, _cols: number, _rows: number, _unicodeVersion: string, _env: IProcessEnvironment, _options: ITerminalProcessOptions, _shouldPersist: boolean) => {
|
||||
const onProcessData = new Emitter<string>();
|
||||
const onProcessReady = new Emitter<IProcessReadyEvent>();
|
||||
const onProcessExit = new Emitter<number | undefined>();
|
||||
const onDidChangeHasChildProcesses = new Emitter<boolean>();
|
||||
const onDidChangeProperty = new Emitter<IProcessProperty<ProcessPropertyType>>();
|
||||
|
||||
// Resolve cwd from createProcess arg or shellLaunchConfig
|
||||
const rawCwd = _cwd || _shellLaunchConfig.cwd;
|
||||
const cwd = !rawCwd ? '/' : typeof rawCwd === 'string' ? rawCwd : rawCwd.path;
|
||||
console.log(`[Sessions Web Test] Mock terminal createProcess cwd: '${cwd}' (raw _cwd: '${_cwd}', slc.cwd: '${_shellLaunchConfig.cwd}')`);
|
||||
|
||||
// Fire ready after a microtask so the terminal service can wire up listeners
|
||||
setTimeout(() => {
|
||||
onProcessReady.fire({ pid: 1, cwd, windowsPty: undefined });
|
||||
}, 0);
|
||||
|
||||
return {
|
||||
id: 0,
|
||||
shouldPersist: false,
|
||||
onProcessData: onProcessData.event,
|
||||
onProcessReady: onProcessReady.event,
|
||||
onDidChangeHasChildProcesses: onDidChangeHasChildProcesses.event,
|
||||
onDidChangeProperty: onDidChangeProperty.event,
|
||||
onProcessExit: onProcessExit.event,
|
||||
start: async () => undefined,
|
||||
shutdown: async () => { },
|
||||
input: async () => { },
|
||||
resize: () => { },
|
||||
clearBuffer: () => { },
|
||||
acknowledgeDataEvent: () => { },
|
||||
setUnicodeVersion: async () => { },
|
||||
getInitialCwd: async () => cwd,
|
||||
getCwd: async () => cwd,
|
||||
getLatency: async () => [],
|
||||
processBinary: async () => { },
|
||||
refreshProperty: async (property: ProcessPropertyType) => { throw new Error(`Not supported: ${property}`); },
|
||||
updateProperty: async () => { },
|
||||
clearUnrespondedRequest: () => { },
|
||||
};
|
||||
},
|
||||
getWslPath: async (original: string, _direction: 'unix-to-win' | 'win-to-unix') => original,
|
||||
getEnvironment: async () => ({}),
|
||||
getPerformanceMarks: () => [],
|
||||
onPtyHostUnresponsive: Event.None,
|
||||
onPtyHostResponsive: Event.None,
|
||||
onPtyHostRestart: Event.None,
|
||||
onPtyHostConnected: Event.None,
|
||||
} as unknown as ITerminalBackend;
|
||||
}
|
||||
|
||||
private preseedFolder(): void {
|
||||
const mockFolderUri = URI.from({ scheme: 'mock-fs', authority: 'mock-repo', path: '/mock-repo' }).toString();
|
||||
this.storageService.store('agentSessions.lastPickedFolder', mockFolderUri, StorageScope.PROFILE, StorageTarget.MACHINE);
|
||||
|
||||
Reference in New Issue
Block a user