-
Notifications
You must be signed in to change notification settings - Fork 926
Expand file tree
/
Copy pathappHostTree.e2e.test.ts
More file actions
163 lines (136 loc) · 9.1 KB
/
Copy pathappHostTree.e2e.test.ts
File metadata and controls
163 lines (136 loc) · 9.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import * as assert from 'assert';
import { getResources, getTerminalCommandCount, getTreeAppHostLabel, waitForCommandOutcome, waitForDashboardUrl, waitForExtensionState, waitForNoRunningAppHost, waitForRepositoryIdle, waitForResource, waitForRunningAppHost, waitForTerminalCommand, waitForWorkspaceAppHost } from './helpers/assertions';
import { executeE2eControlCommand, restoreWorkspaceCliPath, runE2eTeardown, setCliUnavailableForE2E, setDebugLaunchFailureForE2E, setE2eCliPathForE2E, setTerminalCommandExecutionSuppressedForE2E, stopPrimaryAppHostIfRunning, writeLsSequenceCliWrapper } from './helpers/fixtures';
import { getPrimaryAppHostProjectPath } from './helpers/paths';
import { cancelActiveInput, clickTreeItem, openAspireView, waitForTreeItem } from './helpers/vscode';
suite('Aspire AppHost tree E2E', function () {
this.timeout(240000);
teardown(async () => {
await runE2eTeardown([
() => setCliUnavailableForE2E(false),
() => setTerminalCommandExecutionSuppressedForE2E(false),
() => setDebugLaunchFailureForE2E(false),
() => restoreWorkspaceCliPath(),
() => stopPrimaryAppHostIfRunning(),
() => waitForNoRunningAppHost().catch(() => undefined),
], 'AppHost tree E2E teardown failed.');
});
test('discovers the workspace AppHost and renders it in the Aspire view', async () => {
await openAspireView();
await waitForRepositoryIdle();
const stateFile = await waitForWorkspaceAppHost();
const label = getTreeAppHostLabel(stateFile.state);
const section = await openAspireView();
const item = await waitForTreeItem(section, label);
assert.strictEqual(await item.getLabel(), label);
assert.ok(stateFile.state.workspaceAppHostCandidates.length >= 1);
});
test('runs, shows resources and dashboard state, routes resource commands, and stops from the tree', async () => {
await openAspireView();
await waitForRepositoryIdle();
const discovered = await waitForWorkspaceAppHost();
const appHostLabel = getTreeAppHostLabel(discovered.state);
let section = await openAspireView();
const idleItem = await waitForTreeItem(section, appHostLabel);
await idleItem.expand();
await clickTreeItem(section, 'Run AppHost');
await waitForCommandOutcome('aspire-vscode.runAppHost', 'success');
const running = await waitForRunningAppHost();
assert.ok(running.state.appHosts.length >= 1 || running.state.workspaceAppHost);
const workerState = await waitForResource('e2e-worker');
const dashboard = await waitForDashboardUrl();
assert.ok(dashboard.dashboardUrl?.startsWith('http'));
section = await openAspireView();
const runningItem = await waitForTreeItem(section, appHostLabel);
await runningItem.expand();
const workerItem = await waitForTreeItem(section, 'e2e-worker');
assert.ok(workerItem);
assert.ok(getResources(workerState.state).some(resource => (resource.displayName ?? resource.name) === 'e2e-worker'));
await executeE2eControlCommand({ name: 'executeResourceCommand', resourceName: 'e2e-worker' }, { waitFor: 'started' });
await cancelActiveInput();
await waitForCommandOutcome('aspire-vscode.executeResourceCommand', 'canceled');
await setTerminalCommandExecutionSuppressedForE2E(true);
try {
const beforeTerminalCommand = getTerminalCommandCount();
await executeE2eControlCommand(
{ name: 'stopAppHost', appHostPath: discovered.state.workspaceAppHostPath ?? getPrimaryAppHostProjectPath() },
{ waitFor: 'started' });
await waitForTerminalCommand(
event => event.executionSuppressed && event.subcommand.startsWith('stop '),
'suppressed AppHost stop terminal routing',
60000,
beforeTerminalCommand);
await waitForCommandOutcome('aspire-vscode.stopAppHost', 'success');
} finally {
await setTerminalCommandExecutionSuppressedForE2E(false);
}
await stopPrimaryAppHostIfRunning();
await waitForNoRunningAppHost();
});
test('promotes a possibly-buildable idle candidate to buildable after stop', async () => {
const appHostPath = getPrimaryAppHostProjectPath();
const wrapperPath = writeLsSequenceCliWrapper([
[{ path: appHostPath, language: 'csharp', status: 'possibly-buildable', selected: true }],
[{ path: appHostPath, language: 'csharp', status: 'buildable', selected: true }],
], 'aspire-ls-sequence-auto-buildable');
await setE2eCliPathForE2E(wrapperPath);
await openAspireView();
await waitForRepositoryIdle();
const refreshInvocationBefore = await waitForCommandOutcome('aspire-vscode.refreshAppHosts', 'success', 60000).then(event => event.sequence).catch(() => 0);
await executeE2eControlCommand({ name: 'refreshAppHosts' });
await waitForCommandOutcome('aspire-vscode.refreshAppHosts', 'success', 60000, refreshInvocationBefore);
await waitForExtensionState(
file => file.state.workspaceAppHostCandidates.some(candidate => candidate.path === appHostPath && candidate.status === 'possibly-buildable'),
'possibly-buildable workspace candidate',
60000);
const runBefore = await waitForCommandOutcome('aspire-vscode.runAppHost', 'success', 1000).then(event => event.sequence).catch(() => 0);
await executeE2eControlCommand({ name: 'runAppHost', appHostPath }, { waitFor: 'started' });
await waitForCommandOutcome('aspire-vscode.runAppHost', 'success', 120000, runBefore);
await waitForRunningAppHost();
// Snapshot the latest manual-refresh command sequence before stopping. The automatic
// refresh on debug-session-end calls dataRepository.refresh() directly and never raises the
// aspire-vscode.refreshAppHosts command, so this sequence must not advance.
const refreshSequenceBeforeStop = await waitForCommandOutcome('aspire-vscode.refreshAppHosts', 'success', 1000).then(event => event.sequence).catch(() => 0);
const stopBefore = await waitForCommandOutcome('aspire-vscode.stopAppHost', 'success', 1000).then(event => event.sequence).catch(() => 0);
await executeE2eControlCommand({ name: 'stopAppHost', appHostPath }, { waitFor: 'started' });
await waitForCommandOutcome('aspire-vscode.stopAppHost', 'success', 120000, stopBefore);
await waitForNoRunningAppHost();
const stateWithBuildableCandidate = await waitForExtensionState(
file => file.state.workspaceAppHostCandidates.some(candidate => candidate.path === appHostPath && candidate.status === 'buildable'),
'buildable workspace candidate after automatic refresh',
60000);
const refreshSequenceAfterStop = await waitForCommandOutcome('aspire-vscode.refreshAppHosts', 'success', 1000).then(event => event.sequence).catch(() => 0);
assert.strictEqual(
refreshSequenceAfterStop,
refreshSequenceBeforeStop,
'No manual refreshAppHosts command should run after stop; promotion must come from the automatic debug-session-end refresh.');
const appHostLabel = getTreeAppHostLabel(stateWithBuildableCandidate.state);
const section = await openAspireView();
const appHostItem = await waitForTreeItem(section, appHostLabel);
await appHostItem.expand();
await waitForTreeItem(section, 'Status: Buildable');
});
test('removes idle workspace candidate after a failed build', async () => {
const appHostPath = getPrimaryAppHostProjectPath();
const wrapperPath = writeLsSequenceCliWrapper([
[{ path: appHostPath, language: 'csharp', status: 'possibly-buildable', selected: true }],
[{ path: appHostPath, language: 'csharp', status: 'possibly-unbuildable', selected: true }],
], 'aspire-ls-sequence-failed-build');
await setE2eCliPathForE2E(wrapperPath);
await openAspireView();
await waitForRepositoryIdle();
const refreshInvocationBefore = await waitForCommandOutcome('aspire-vscode.refreshAppHosts', 'success', 60000).then(event => event.sequence).catch(() => 0);
await executeE2eControlCommand({ name: 'refreshAppHosts' });
await waitForCommandOutcome('aspire-vscode.refreshAppHosts', 'success', 60000, refreshInvocationBefore);
await waitForExtensionState(
file => file.state.workspaceAppHostCandidates.some(candidate => candidate.path === appHostPath && candidate.status === 'possibly-buildable'),
'possibly-buildable workspace candidate',
60000);
await setDebugLaunchFailureForE2E(true);
await executeE2eControlCommand({ name: 'runAppHost', appHostPath }, { waitFor: 'started' });
await waitForExtensionState(
file => file.state.workspaceAppHostCandidates.every(candidate => candidate.path !== appHostPath),
'workspace candidate removed after failed build',
60000);
});
});