-
Notifications
You must be signed in to change notification settings - Fork 380
Expand file tree
/
Copy pathutils.ts
More file actions
236 lines (210 loc) · 6.77 KB
/
utils.ts
File metadata and controls
236 lines (210 loc) · 6.77 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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
import path from 'path'
import { Page } from '@playwright/test'
import { execAsync } from '../src/utilities/exec'
import { CodeServerContext } from './utils_code_server'
// Where your extension lives on disk
export const EXT_PATH = path.resolve(__dirname, '..')
// Where the sushi project lives which we copy from
export const SUSHI_SOURCE_PATH = path.join(
__dirname,
'..',
'..',
'..',
'examples',
'sushi',
)
export const MULTI_SOURCE_PATH = path.join(
__dirname,
'..',
'..',
'..',
'examples',
'multi',
)
export const REPO_ROOT = path.join(__dirname, '..', '..', '..')
/**
* Click on the Explorer tab in the VS Code activity bar if the Explorer tab is not already active.
* This is necessary because the Explorer tab may not be visible if the user has not opened it yet.
*/
export const clickExplorerTab = async (page: Page): Promise<void> => {
const isExplorerActive = await page.locator("text='Explorer'").isVisible()
if (!isExplorerActive) {
// Wait for the activity bar to be loaded
await page.waitForSelector('.actions-container[role="tablist"]')
// Click on the Explorer tab using the codicon class
await page.click('.codicon-explorer-view-icon')
// Wait a bit for the explorer view to activate
await page.locator("text='Explorer'").waitFor({ state: 'visible' })
}
}
export interface PythonEnvironment {
pythonPath: string
pipPath: string
}
/**
* Create a virtual environment in the given directory using uv.
* @param venvDir The directory to create the virtual environment in.
*/
export const createVirtualEnvironment = async (
venvDir: string,
): Promise<PythonEnvironment> => {
// Try to use uv first, fallback to python -m venv
const { exitCode, stderr } = await execAsync(`uv venv "${venvDir}"`)
if (exitCode !== 0) {
throw new Error(`Failed to create venv with uv: ${stderr}`)
}
// Get paths
const isWindows = process.platform === 'win32'
const binDir = path.join(venvDir, isWindows ? 'Scripts' : 'bin')
const pythonPath = path.join(binDir, isWindows ? 'python.exe' : 'python')
const pipPath = path.join(binDir, isWindows ? 'pip.exe' : 'pip')
return {
pythonPath,
pipPath,
}
}
/**
* Install packages in the given virtual environment using uv.
* @param pythonDetails The Python environment to use.
* @param packagePaths The paths to the packages to install (string[]).
*/
export const pipInstall = async (
pythonDetails: PythonEnvironment,
packagePaths: string[],
): Promise<void> => {
const packages = packagePaths.map(pkg => `-e "${pkg}"`).join(' ')
const execString = `uv pip install --python "${pythonDetails.pythonPath}" ${packages}`
const { stderr, exitCode } = await execAsync(execString)
if (exitCode !== 0) {
throw new Error(`Failed to install package with uv: ${stderr}`)
}
}
/**
* Open the lineage view in the given window.
*/
export const openLineageView = async (page: Page) =>
await runCommand(page, 'Lineage: Focus On View')
/**
* Open the problems/diagnostics view in the given window.
*/
export const openProblemsView = async (page: Page) =>
await runCommand(page, 'View: Focus Problems')
/**
* Restart the SQLMesh servers
*/
export const restartSqlmeshServers = async (page: Page) =>
runCommand(page, 'SQLMesh: Restart Servers')
/**
* Open the vscode command palette and run the given command.
* @param page The window to run the command in.
* @param command The command to run.
*/
export const runCommand = async (
page: Page,
command: string,
): Promise<void> => {
const maxRetries = 3
const retryDelay = 3000
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
await page.keyboard.press(
process.platform === 'darwin' ? 'Meta+Shift+P' : 'Control+Shift+P',
)
await page.waitForSelector(
'input[aria-label="Type the name of a command to run."]',
{ timeout: 5000 },
)
await page.keyboard.type(command)
const commandElement = await page.waitForSelector(
`a:has-text("${command}")`,
{ timeout: 5000 },
)
await commandElement.click()
return // Success, exit the retry loop
} catch (error) {
if (attempt === maxRetries - 1) {
throw error // Last attempt failed, throw the error
}
// Close any open command palette before retrying
await page.keyboard.press('Escape')
await page.waitForTimeout(retryDelay)
}
}
}
/**
* Go to definition. Assumes the location is clicked.
*/
export const goToDefinition = async (page: Page) =>
runCommand(page, 'Go to Definition')
/**
* Save file
*/
export const saveFile = async (page: Page) => runCommand(page, 'File: Save')
/**
* Rename Symbol opens the rename symbol dialog in VS Code.
*/
export const renameSymbol = async (page: Page) =>
runCommand(page, 'Rename Symbol')
/**
* Find all references to the symbol under the cursor.
*/
export const findAllReferences = async (page: Page): Promise<void> =>
runCommand(page, 'References: Find All References')
/**
* Go to references. Assumes the location is clicked.
*/
export const goToReferences = async (page: Page): Promise<void> =>
runCommand(page, 'Go to References')
/**
* Open the vscode code file picker and select the given file.
*/
export const openFile = async (page: Page, file: string): Promise<void> => {
const maxRetries = 3
const retryDelay = 3000
const fileName = path.basename(file)
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
await page.keyboard.press(
process.platform === 'darwin' ? 'Meta+P' : 'Control+P',
)
await page
.getByRole('textbox', { name: 'Search files by name' })
.waitFor({ state: 'visible', timeout: 5000 })
await page.keyboard.type(file)
const commandElement = await page.waitForSelector(
`a:has-text("${fileName}")`,
{ timeout: 5000 },
)
await commandElement.click()
return // Success, exit the retry loop
} catch (error) {
if (attempt === maxRetries - 1) {
throw error // Last attempt failed, throw the error
}
// Close any open command palette before retrying
await page.keyboard.press('Escape')
await page.waitForTimeout(retryDelay)
}
}
}
/**
* Wait for SQLMesh context to be loaded.
*/
export const waitForLoadedSQLMesh = (page: Page) =>
page.waitForSelector('text=Loaded SQLMesh Context')
/**
* Go to VSCode page
*/
export const openServerPage = async (
page: Page,
targetPath: string,
context: CodeServerContext,
) => {
const isWorkspace = targetPath.endsWith('.code-workspace')
const param = isWorkspace ? 'workspace' : 'folder'
await page.goto(
`http://127.0.0.1:${context.codeServerPort}/?${param}=${targetPath}`,
)
await page.waitForLoadState('networkidle')
await page.waitForSelector('[role="application"]', { timeout: 10000 })
}