Skip to content

Commit abf9239

Browse files
authored
chore(vscode): add test for non installation of deps' (#4735)
1 parent b9d5e8f commit abf9239

3 files changed

Lines changed: 140 additions & 29 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { expect, test } from '@playwright/test'
2+
import fs from 'fs-extra'
3+
import os from 'os'
4+
import path from 'path'
5+
import {
6+
createVirtualEnvironment,
7+
pipInstall,
8+
REPO_ROOT,
9+
startVSCode,
10+
SUSHI_SOURCE_PATH,
11+
} from './utils'
12+
13+
test('missing LSP dependencies shows install prompt', async ({}, testInfo) => {
14+
testInfo.setTimeout(120_000) // 2 minutes for venv creation and package installation
15+
const tempDir = await fs.mkdtemp(
16+
path.join(os.tmpdir(), 'vscode-test-tcloud-'),
17+
)
18+
const pythonEnvDir = path.join(tempDir, '.venv')
19+
const pythonDetails = await createVirtualEnvironment(pythonEnvDir)
20+
const custom_materializations = path.join(
21+
REPO_ROOT,
22+
'examples',
23+
'custom_materializations',
24+
)
25+
const sqlmeshWithExtras = `${REPO_ROOT}[bigquery]`
26+
await pipInstall(pythonDetails, [sqlmeshWithExtras, custom_materializations])
27+
28+
try {
29+
// Copy sushi project
30+
await fs.copy(SUSHI_SOURCE_PATH, tempDir)
31+
32+
// Configure VS Code settings to use our Python environment
33+
const settings = {
34+
'python.defaultInterpreterPath': pythonDetails.pythonPath,
35+
'sqlmesh.environmentPath': pythonEnvDir,
36+
}
37+
await fs.ensureDir(path.join(tempDir, '.vscode'))
38+
await fs.writeJson(
39+
path.join(tempDir, '.vscode', 'settings.json'),
40+
settings,
41+
{ spaces: 2 },
42+
)
43+
44+
// Start VS Code
45+
const { window, close } = await startVSCode(tempDir)
46+
47+
// Open a SQL file to trigger SQLMesh activation
48+
// Wait for the models folder to be visible
49+
await window.waitForSelector('text=models')
50+
51+
// Click on the models folder
52+
await window
53+
.getByRole('treeitem', { name: 'models', exact: true })
54+
.locator('a')
55+
.click()
56+
57+
// Open the top_waiters model
58+
await window
59+
.getByRole('treeitem', { name: 'customers.sql', exact: true })
60+
.locator('a')
61+
.click()
62+
63+
// Wait for the message to show that LSP extras need to be installed
64+
await window.waitForSelector('text=LSP dependencies missing')
65+
expect(await window.locator('text=Install').count()).toBeGreaterThanOrEqual(
66+
1,
67+
)
68+
69+
await close()
70+
} finally {
71+
// Clean up
72+
await fs.remove(tempDir)
73+
}
74+
})

vscode/extension/tests/tcloud.spec.ts

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,35 @@ import { expect, test } from '@playwright/test'
22
import path from 'path'
33
import fs from 'fs-extra'
44
import os from 'os'
5-
import { exec } from 'child_process'
6-
import { promisify } from 'util'
7-
import { REPO_ROOT, startVSCode, SUSHI_SOURCE_PATH } from './utils'
8-
9-
const execAsync = promisify(exec)
5+
import {
6+
createVirtualEnvironment,
7+
pipInstall,
8+
REPO_ROOT,
9+
startVSCode,
10+
SUSHI_SOURCE_PATH,
11+
} from './utils'
1012

1113
/**
1214
* Helper function to create and set up a Python virtual environment
1315
*/
1416
async function setupPythonEnvironment(envDir: string): Promise<string> {
1517
// Create virtual environment
16-
const pythonCmd = process.platform === 'win32' ? 'python' : 'python3'
17-
const { stderr } = await execAsync(`${pythonCmd} -m venv "${envDir}"`)
18-
if (stderr && !stderr.includes('WARNING')) {
19-
throw new Error(`Failed to create venv: ${stderr}`)
20-
}
21-
22-
// Get paths
23-
const isWindows = process.platform === 'win32'
24-
const binDir = path.join(envDir, isWindows ? 'Scripts' : 'bin')
25-
const pythonPath = path.join(binDir, isWindows ? 'python.exe' : 'python')
26-
const pipPath = path.join(binDir, isWindows ? 'pip.exe' : 'pip')
18+
const pythonDetails = await createVirtualEnvironment(envDir)
2719

2820
// Install the mock tcloud package
2921
const mockTcloudPath = path.join(__dirname, 'tcloud')
30-
const { stderr: pipErr1 } = await execAsync(
31-
`"${pipPath}" install -e "${mockTcloudPath}"`,
32-
)
33-
if (pipErr1 && !pipErr1.includes('WARNING') && !pipErr1.includes('notice')) {
34-
throw new Error(`Failed to install mock tcloud: ${pipErr1}`)
35-
}
22+
await pipInstall(pythonDetails, [mockTcloudPath])
3623

3724
// Install sqlmesh from the local repository with LSP support
38-
const sqlmeshRepoPath = path.join(__dirname, '..', '..', '..') // Navigate to repo root from tests dir
39-
const { stderr: pipErr2 } = await execAsync(
40-
`"${pipPath}" install -e "${sqlmeshRepoPath}[lsp,bigquery]" "${REPO_ROOT}/examples/custom_materializations"`,
25+
const customMaterializations = path.join(
26+
REPO_ROOT,
27+
'examples',
28+
'custom_materializations',
4129
)
42-
if (pipErr2 && !pipErr2.includes('WARNING') && !pipErr2.includes('notice')) {
43-
throw new Error(`Failed to install sqlmesh: ${pipErr2}`)
44-
}
30+
const sqlmeshWithExtras = `${REPO_ROOT}[lsp,bigquery]`
31+
await pipInstall(pythonDetails, [sqlmeshWithExtras, customMaterializations])
4532

46-
return pythonPath
33+
return pythonDetails.pythonPath
4734
}
4835

4936
/**

vscode/extension/tests/utils.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import path from 'path'
22
import fs from 'fs-extra'
33
import os from 'os'
44
import { _electron as electron, Page } from '@playwright/test'
5+
import { exec } from 'child_process'
6+
import { promisify } from 'util'
57

68
// Absolute path to the VS Code executable you downloaded in step 1.
79
export const VS_CODE_EXE = fs.readJsonSync(
@@ -87,3 +89,51 @@ export const clickExplorerTab = async (page: Page): Promise<void> => {
8789
await page.locator("text='Explorer'").waitFor({ state: 'visible' })
8890
}
8991
}
92+
93+
const execAsync = promisify(exec)
94+
95+
export interface PythonEnvironment {
96+
pythonPath: string
97+
pipPath: string
98+
}
99+
100+
/**
101+
* Create a virtual environment in the given directory.
102+
* @param venvDir The directory to create the virtual environment in.
103+
*/
104+
export const createVirtualEnvironment = async (
105+
venvDir: string,
106+
): Promise<PythonEnvironment> => {
107+
const pythonCmd = process.platform === 'win32' ? 'python' : 'python3'
108+
const { stderr } = await execAsync(`${pythonCmd} -m venv "${venvDir}"`)
109+
if (stderr && !stderr.includes('WARNING')) {
110+
throw new Error(`Failed to create venv: ${stderr}`)
111+
}
112+
// Get paths
113+
const isWindows = process.platform === 'win32'
114+
const binDir = path.join(venvDir, isWindows ? 'Scripts' : 'bin')
115+
const pythonPath = path.join(binDir, isWindows ? 'python.exe' : 'python')
116+
const pipPath = path.join(binDir, isWindows ? 'pip.exe' : 'pip')
117+
118+
return {
119+
pythonPath,
120+
pipPath,
121+
}
122+
}
123+
124+
/**
125+
* Install packages in the given virtual environment.
126+
* @param pythonDetails The Python environment to use.
127+
* @param packagePaths The paths to the packages to install (string[]).
128+
*/
129+
export const pipInstall = async (
130+
pythonDetails: PythonEnvironment,
131+
packagePaths: string[],
132+
): Promise<void> => {
133+
const { pipPath } = pythonDetails
134+
const execString = `"${pipPath}" install -e "${packagePaths.join('" -e "')}"`
135+
const { stderr } = await execAsync(execString)
136+
if (stderr && !stderr.includes('WARNING') && !stderr.includes('notice')) {
137+
throw new Error(`Failed to install package: ${stderr}`)
138+
}
139+
}

0 commit comments

Comments
 (0)