Path: blob/main/extensions/copilot/test/e2e/newWorkspace.stest.ts
13388 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as fs from 'fs';6import * as path from 'path';7import { TextDecoder } from 'util';8import type { ChatResponseFileTree } from 'vscode';9import { INewWorkspacePreviewContentManager } from '../../src/extension/intents/node/newIntent';10import { ITestingServicesAccessor } from '../../src/platform/test/node/services';11import { IQualifiedFile, getLanguageForFile } from '../../src/platform/test/node/simulationWorkspace';12import { looksLikeDirectory } from '../../src/util/common/fileSystem';13import { ChatResponseFileTreePart, Uri } from '../../src/vscodeTypes';14import { ssuite, stest } from '../base/stest';15import { validate } from '../base/validate';16import { compileTSWorkspace } from '../simulation/diagnosticProviders/tsc';17import { cleanTempDir, createTempDir } from '../simulation/stestUtil';18import { Scenario, fetchConversationScenarios } from './scenarioLoader';19import { generateScenarioTestRunner } from './scenarioTest';2021(function () {2223ssuite.skip({ title: 'newWorkspace', subtitle: 'e2e', location: 'panel' }, (inputPath) => {2425const scenarioFolder = inputPath ?? path.join(__dirname, '..', 'test/scenarios/test-new-workspace');26const scenarios: Scenario[] = fetchConversationScenarios(scenarioFolder);2728for (const scenario of scenarios) {29stest({ description: scenario[0].question.replace('/new ', '') },30generateScenarioTestRunner(31scenario,32async (accessor, question, answer, _rawResponse, _index, _turn, commands) => {33const files: IQualifiedFile[] = [];34for (const command of commands) {35if (command.command === 'github.copilot.createProject') {36// validate project structure and contents in files37const projectItems: ChatResponseFileTreePart = command.arguments?.[0];38if (!projectItems || projectItems.value?.length === 0) {39return Promise.resolve({ success: false, errorMessage: 'Invalid projectItems' });40}41if (scenario[0].json.keywords) {42const err = validate(_rawResponse, scenario[0].json.keywords);43if (err) {44return { success: false, errorMessage: err };45}46}4748const contentManager = accessor.get(INewWorkspacePreviewContentManager);4950async function traverseFileTree(parentPath: string, fileTree: ChatResponseFileTree, baseUri: Uri): Promise<void | { success: boolean; errorMessage: string }> {51const itemPath = path.posix.join(parentPath, fileTree.name);52if (fileTree.children?.length || !looksLikeDirectory(fileTree.name)) {53files.push({ kind: 'qualifiedFile', uri: Uri.joinPath(baseUri, itemPath), fileContents: 'DIR' });54for (const item of fileTree.children ?? []) {55await traverseFileTree(itemPath, item, baseUri);56}57} else {58const result = await contentManager.get(Uri.joinPath(baseUri, itemPath))?.content;59const decoder = new TextDecoder();60const decodedString = decoder.decode(result);61if (!decodedString) {62return { success: false, errorMessage: `Content not found for ${itemPath}` };63}64files.push({ kind: 'qualifiedFile', uri: Uri.joinPath(baseUri, itemPath), fileContents: decodedString });65}66return;67}6869for (const projectItem of projectItems.value) {70await traverseFileTree('', projectItem, projectItems.baseUri);71}7273const tempDirPath = await createTempDir();74const projectRoot = path.join(tempDirPath, projectItems.baseUri.path);75await createTempWorkspace(tempDirPath, files);76const result = await compileWorkspace(accessor, projectRoot, files);77await cleanTempDir(tempDirPath);78return result;7980}81}82return Promise.resolve({ success: false, errorMessage: 'Failed to parse new response' });83}84));85}86});87})();8889// TODO @aiday-mar add possibility to execute python files and find the diagnostics or errors upon execution90async function compileWorkspace(accessor: ITestingServicesAccessor, projectRoot: string, files: IQualifiedFile[]): Promise<{ success: boolean; errorMessage?: string }> {91for (const file of files) {92const language = getLanguageForFile(file);93switch (language.languageId) {94case 'typescript':95{96// compute diagnostics97const tsDiagnostics = await compileTSWorkspace(accessor, projectRoot);98if (tsDiagnostics.length === 0) {99return { success: true };100}101const errors = tsDiagnostics.map(diagnostic => diagnostic.file + ' ' + diagnostic.code + ' : ' + diagnostic.message).join('\n');102return { success: false, errorMessage: 'Typescript diagnostics errors: \n' + errors };103}104}105}106return { success: true };107}108109async function createTempWorkspace(tempDirPath: string, files: IQualifiedFile[]): Promise<void> {110await fs.promises.rm(tempDirPath, { recursive: true, force: true });111await fs.promises.mkdir(tempDirPath, { recursive: true });112for (const file of files) {113const tmpPath = path.join(tempDirPath, file.uri.path);114if (file.fileContents !== 'DIR') {115await fs.promises.writeFile(tmpPath, file.fileContents);116}117else {118await fs.promises.mkdir(tmpPath, { recursive: true });119}120}121}122123124