Path: blob/main/extensions/copilot/test/inline/inlineGenerateCode.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 assert from 'assert';6import { EditCodeIntent } from '../../src/extension/intents/node/editCodeIntent';7import { GenerateCodeIntent } from '../../src/extension/intents/node/generateCodeIntent';8import { TestingServiceCollection } from '../../src/platform/test/node/services';9import { URI } from '../../src/util/vs/base/common/uri';10import { Uri } from '../../src/vscodeTypes';11import { NonExtensionConfiguration, ssuite, stest } from '../base/stest';12import { KnownDiagnosticProviders } from '../simulation/diagnosticProviders';13import { simulateInlineChat, simulateInlineChatIntent } from '../simulation/inlineChatSimulator';14import { assertContainsAllSnippets, assertNoDiagnosticsAsync, assertNoSyntacticDiagnosticsAsync } from '../simulation/outcomeValidators';15import { assertConversationalOutcome, assertInlineEdit, assertInlineEditShape, assertOccursOnce, assertOneOf, assertSomeStrings, fromFixture, toFile } from '../simulation/stestUtil';16import { EditTestStrategy, IScenario } from '../simulation/types';1718function executeEditTestStrategy(19strategy: EditTestStrategy,20testingServiceCollection: TestingServiceCollection,21scenario: IScenario22): Promise<void> {23if (strategy === EditTestStrategy.Inline) {24return simulateInlineChat(testingServiceCollection, scenario);25} else if (EditTestStrategy.InlineChatIntent) {26return simulateInlineChatIntent(testingServiceCollection, scenario);27} else {28throw new Error('Invalid edit test strategy');29}30}3132function forInlineChatIntent(callback: (strategy: EditTestStrategy, variant: '-InlineChatIntent', nonExtensionConfigurations?: NonExtensionConfiguration[]) => void): void {33callback(EditTestStrategy.InlineChatIntent, '-InlineChatIntent', [['chat.agent.autoFix', false]]);34}3536forInlineChatIntent((strategy, variant, nonExtensionConfigurations) => {3738ssuite({ title: `generate${variant}`, location: 'inline' }, () => {39stest({ description: 'gen-ts-ltrim', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {40return executeEditTestStrategy(strategy, testingServiceCollection, {41files: [42{ kind: 'relativeFile', fileName: 'new.ts', fileContents: '' }43],44queries: [45{46file: 'new.ts',47selection: [0, 0],48query: 'generate a function that will remove whitespace from the start of a string',49expectedIntent: GenerateCodeIntent.ID,50validate: async (outcome, workspace, accessor) => {51assertInlineEdit(outcome);52await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');53assertContainsAllSnippets(outcome.fileContents, ['function', ': string'], 'gen-ts-ltrim-01');54},55},56{57query: 'change it to take as argument the characters to remove from the start',58expectedIntent: EditCodeIntent.ID,59validate: async (outcome, workspace, accessor) => {60assertInlineEdit(outcome);61await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');62const text = outcome.fileContents;63const f1 = text.indexOf('function ');64const f2 = text.indexOf('function ', f1 + 1);65assert(f2 === -1);66assertContainsAllSnippets(text, ['function', ': string'], 'gen-ts-ltrim-02');67},68}69],70});71});7273stest({ description: 'Generate a nodejs server', language: 'javascript', nonExtensionConfigurations }, (testingServiceCollection) => {74return executeEditTestStrategy(strategy, testingServiceCollection, {75files: [{ kind: 'relativeFile', fileName: 'server.js', fileContents: '' }],76queries: [77{78file: 'server.js',79selection: [0, 0],80query: 'generate a nodejs server that responds with "Hello World"',81expectedIntent: GenerateCodeIntent.ID,82validate: async (outcome, workspace, accessor) => {83assertInlineEdit(outcome);84await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');85assertContainsAllSnippets(outcome.fileContents, ['http', 'createServer', 'listen', 'Hello World']);86},87},88{89query: 'change it to respond with "Goodbye World"',90expectedIntent: EditCodeIntent.ID,91validate: async (outcome, workspace, accessor) => {92assertInlineEdit(outcome);93await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');94assertContainsAllSnippets(outcome.fileContents, ['http', 'createServer', 'listen', 'Goodbye World']);95},96},97],98});99});100101stest({ description: 'generate rtrim', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {102103if (1) {104throw new Error('SKIPPED');105}106107return executeEditTestStrategy(strategy, testingServiceCollection, {108files: [109fromFixture('gen-top-level-function/charCode.ts'),110fromFixture('gen-top-level-function/strings.ts'),111],112queries: [113{114file: 'strings.ts',115selection: [770, 0],116query: 'generate rtrim',117expectedIntent: GenerateCodeIntent.ID,118validate: async (outcome, workspace, accessor) => {119assertInlineEdit(outcome);120await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');121const edit = assertInlineEditShape(outcome, [{122line: 770,123originalLength: 0,124modifiedLength: undefined125}, {126line: 770,127originalLength: 1,128modifiedLength: undefined129}]);130assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['rtrim', 'needle.length']);131},132},133],134});135});136137stest({ description: 'issue #2342: Use inline chat to generate a new function/property replaces other code', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {138return executeEditTestStrategy(strategy, testingServiceCollection, {139files: [140fromFixture('gen-top-level-function/charCode.ts'),141fromFixture('gen-top-level-function/strings.ts'),142],143queries: [144{145file: 'strings.ts',146selection: [31, 0],147query: 'generate a fibonacci',148expectedIntent: GenerateCodeIntent.ID,149validate: async (outcome, workspace, accessor) => {150assertInlineEdit(outcome);151await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');152const edit = assertInlineEditShape(outcome, [{153line: 31,154originalLength: 0,155modifiedLength: undefined156}, {157line: 31,158originalLength: 1,159modifiedLength: undefined160}]);161const changedModifiedLines = edit.changedModifiedLines.join('\n');162assertContainsAllSnippets(changedModifiedLines, ['function']);163assertOneOf([164() => assertContainsAllSnippets(changedModifiedLines, ['fibonacci']),165() => assertContainsAllSnippets(changedModifiedLines, ['Fibonacci']), // e.g., `generateFibonacci()`166]);167},168},169],170});171});172173stest({ description: 'issue #3602: gen method', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {174return executeEditTestStrategy(strategy, testingServiceCollection, {175files: [176fromFixture('gen-method-issue-3602/editor.ts'),177],178queries: [179{180file: 'editor.ts',181selection: [39, 0],182query: 'add an async function that moves a block of lines up by one',183expectedIntent: GenerateCodeIntent.ID,184validate: async (outcome, workspace, accessor) => {185assertInlineEdit(outcome);186await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');187const edit = assertInlineEditShape(outcome, [{188line: 39,189originalLength: 0,190modifiedLength: undefined191}, {192line: 39,193originalLength: 1,194modifiedLength: undefined195}]);196const changedModifiedLines = edit.changedModifiedLines.join('\n');197assertContainsAllSnippets(changedModifiedLines, ['async'], 'gen-method-issue-3602');198assertOneOf([199() => assertContainsAllSnippets(changedModifiedLines, ['moveBlockUp']),200() => assertContainsAllSnippets(changedModifiedLines, ['moveLinesUp']),201]);202},203},204],205});206});207208stest({ description: 'issue #3604: gen nestjs route', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {209return executeEditTestStrategy(strategy, testingServiceCollection, {210files: [211fromFixture('gen-nestjs-route-issue-3604/app.controller.ts'),212],213queries: [214{215file: 'app.controller.ts',216selection: [12, 4],217query: 'add a new /about page',218expectedIntent: GenerateCodeIntent.ID,219validate: async (outcome, workspace, accessor) => {220assertInlineEdit(outcome);221await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');222const edit = assertInlineEditShape(outcome, [{223line: 12,224originalLength: 0,225modifiedLength: undefined226}, {227line: 12,228originalLength: 1,229modifiedLength: undefined230}]);231assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['@Get', '(): string']);232},233},234],235});236});237238stest({ description: 'gen a palindrom fn', language: 'python', nonExtensionConfigurations }, (testingServiceCollection) => {239return executeEditTestStrategy(strategy, testingServiceCollection, {240files: [241{ kind: 'relativeFile', fileName: 'new.py', fileContents: '' }242],243queries: [244{245file: 'new.py',246selection: [0, 0],247query: 'generate a function that checks if a string is a palindrome',248expectedIntent: GenerateCodeIntent.ID,249validate: async (outcome, workspace, accessor) => {250assertInlineEdit(outcome);251assertContainsAllSnippets(outcome.fileContents, ['def', '[::-1]'], 'gen-python-palindrome');252},253},254],255});256});257258stest({ description: 'issue #3597: gen twice', language: 'javascript', nonExtensionConfigurations }, (testingServiceCollection) => {259return executeEditTestStrategy(strategy, testingServiceCollection, {260files: [261fromFixture('gen-twice-issue-3597/new.js')262],263queries: [264{265file: 'new.js',266selection: [27, 0],267query: 'create a function that checks whether a given number is a prime number',268expectedIntent: GenerateCodeIntent.ID,269validate: async (outcome, workspace, accessor) => {270assertInlineEdit(outcome);271await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');272const edit = assertInlineEditShape(outcome, [{273line: 27,274originalLength: 0,275modifiedLength: undefined276}, {277line: 27,278originalLength: 1,279modifiedLength: undefined280}]);281assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['function']);282},283},284{285query: 'create a function that checks if a given number is a fibonacci number',286expectedIntent: GenerateCodeIntent.ID,287validate: async (outcome, workspace, accessor) => {288assertInlineEdit(outcome);289await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');290const edit = assertInlineEditShape(outcome, [{291line: ~1,292originalLength: 0,293modifiedLength: undefined,294}, {295line: ~1,296originalLength: 1,297modifiedLength: undefined,298}, {299line: ~0,300originalLength: 0,301modifiedLength: undefined,302}]);303assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['function']);304}305}306],307});308});309310stest({ description: 'issue #3782: gen twice', language: 'javascript', nonExtensionConfigurations }, (testingServiceCollection) => {311return executeEditTestStrategy(strategy, testingServiceCollection, {312files: [313{ kind: 'relativeFile', fileName: 'new.js', fileContents: '' }314],315queries: [316{317file: 'new.js',318selection: [0, 0],319query: 'create a function `fibonacci` that computes the fibonacci numbers',320expectedIntent: GenerateCodeIntent.ID,321validate: async (outcome, workspace, accessor) => {322assertInlineEdit(outcome);323await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');324const edit = assertInlineEditShape(outcome, {325line: 0,326originalLength: 1,327modifiedLength: undefined,328});329assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['function fibonacci']);330},331},332{333query: 'create a second function `getPrimes` that compute prime numbers',334expectedIntent: GenerateCodeIntent.ID,335validate: async (outcome, workspace, accessor) => {336assertInlineEdit(outcome);337await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');338const edit = assertInlineEditShape(outcome, [{339line: ~1,340originalLength: 0,341modifiedLength: undefined,342}, {343line: ~0,344originalLength: 0,345modifiedLength: undefined,346}]);347assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['function getPrimes']);348}349},350{351query: 'generate a function `getFibonacciPrimes` that compute the first 100 prime numbers that are also fibonacci numbers using the `fibonacci` and the`getPrimes` function',352expectedIntent: GenerateCodeIntent.ID,353validate: async (outcome, workspace, accessor) => {354assertInlineEdit(outcome);355const text = outcome.fileContents;356// we need to have 2 functions357const f1 = text.indexOf('function fibonacci');358const f2 = text.indexOf('\nfunction getPrimes', f1 + 1);359const f3 = text.indexOf('\nfunction getFibonacciPrimes', f2 + 1);360assert(!(f1 === -1 || f2 === -1 || f3 === -1));361}362},363],364});365});366367stest({ description: 'parse keybindings', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {368return executeEditTestStrategy(strategy, testingServiceCollection, {369files: [fromFixture('gen/keybindingParser.ts')],370queries: [{371file: 'keybindingParser.ts',372selection: [15, 8],373query: 'parse ctrl+ shift+ alt+ cmd+ and remove them from input',374expectedIntent: GenerateCodeIntent.ID,375validate: async (outcome, workspace, accessor) => {376assertInlineEdit(outcome);377await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');378const edit = assertInlineEditShape(outcome, [{379line: 15,380originalLength: 1,381modifiedLength: undefined382}, {383line: 15,384originalLength: 0,385modifiedLength: undefined386}]);387assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['ctrl = true', 'shift = true', 'alt = true', 'meta = true']);388}389}]390});391});392393stest({ description: 'issue #2303: FILEPATH not removed from generated code in empty file', language: 'python', nonExtensionConfigurations }, (testingServiceCollection) => {394const uri = Uri.parse('untitled:Untitled-1');395return executeEditTestStrategy(strategy, testingServiceCollection, {396files: [{397kind: 'qualifiedFile',398uri,399fileContents: '',400languageId: 'python'401}],402queries: [403{404file: uri,405selection: [0, 0],406query: 'reverse a linked list in python',407expectedIntent: GenerateCodeIntent.ID,408validate: async (outcome, workspace, accessor) => {409assertInlineEdit(outcome);410assert.strictEqual(outcome.fileContents.includes('# FILEPATH'), false);411},412},413],414});415});416417stest({ description: 'issue #2589: IllegalArgument: line must be non-negative', language: 'json', nonExtensionConfigurations }, (testingServiceCollection) => {418const uri = Uri.parse('file:///home/.prettierrc');419return executeEditTestStrategy(strategy, testingServiceCollection, {420files: [{421kind: 'qualifiedFile',422uri,423fileContents: '',424languageId: 'json'425}],426queries: [427{428file: uri,429selection: [0, 0],430query: 'use tabs',431expectedIntent: GenerateCodeIntent.ID,432validate: async (outcome, workspace, accessor) => {433assertInlineEdit(outcome);434},435},436],437});438});439440stest({ description: 'issue #2269: BEGIN and END were included in diff', language: 'python', nonExtensionConfigurations }, (testingServiceCollection) => {441const uri = Uri.parse('untitled:Untitled-1');442return executeEditTestStrategy(strategy, testingServiceCollection, {443files: [{444kind: 'qualifiedFile',445uri,446fileContents: '',447languageId: 'javascript'448}],449queries: [450{451file: uri,452selection: [0, 0],453query: 'create a simple express server',454expectedIntent: GenerateCodeIntent.ID,455validate: async (outcome, workspace, accessor) => {456assertInlineEdit(outcome);457assert.strictEqual(outcome.fileContents.includes('BEGIN'), false);458},459},460{461query: 'add a date route that returns the current date and time',462expectedIntent: EditCodeIntent.ID,463validate: async (outcome, workspace, accessor) => {464assertInlineEdit(outcome);465assert.strictEqual(outcome.fileContents.includes('BEGIN'), false);466},467},468{469query: 'create an eval route that evaluates a mathematical equation. Support addition, multiplication, division, subtraction and square root',470expectedIntent: EditCodeIntent.ID,471validate: async (outcome, workspace, accessor) => {472assertInlineEdit(outcome);473assert.strictEqual(outcome.fileContents.includes('BEGIN'), false);474},475}476],477});478});479480stest({ description: 'Streaming gets confused due to jsdoc', language: 'json', nonExtensionConfigurations }, (testingServiceCollection) => {481482if (1) {483throw new Error('SKIPPED');484}485486return executeEditTestStrategy(strategy, testingServiceCollection, {487files: [488fromFixture('gen-top-level-function/strings.ts'),489],490queries: [491{492file: 'strings.ts',493selection: [75, 0],494query: 'add fibonacci function and use jsdoc to document it',495expectedIntent: GenerateCodeIntent.ID,496validate: async (outcome, workspace, accessor) => {497assertInlineEdit(outcome);498await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');499const edit = assertInlineEditShape(outcome, [{500line: 75,501originalLength: 0,502modifiedLength: undefined503}, {504line: 75,505originalLength: 1,506modifiedLength: undefined507}]);508assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['function fibonacci']);509},510}511],512});513});514515stest({ description: 'code below cursor is not duplicated', language: 'html', nonExtensionConfigurations }, (testingServiceCollection) => {516return executeEditTestStrategy(strategy, testingServiceCollection, {517files: [518fromFixture('editing-html/index.html')519],520queries: [521{522file: 'index.html',523selection: [3, 3],524query: 'make cursor use hand.png',525expectedIntent: GenerateCodeIntent.ID,526validate: async (outcome, workspace, accessor) => {527assertInlineEdit(outcome);528assertOccursOnce(outcome.fileContents, '<html>');529assertOccursOnce(outcome.fileContents, '<head>');530assertOccursOnce(outcome.fileContents, '<style>');531assertOccursOnce(outcome.fileContents, '</style>');532assertOccursOnce(outcome.fileContents, '</head>');533assertOccursOnce(outcome.fileContents, '<body>');534assertOccursOnce(outcome.fileContents, '</body>');535assertOccursOnce(outcome.fileContents, '</html>');536}537}538],539});540});541542stest({ description: 'issue #3370: generate code duplicates too much', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {543return executeEditTestStrategy(strategy, testingServiceCollection, {544files: [545fromFixture('gen/inlayHintsController.ts')546],547queries: [548{549file: 'inlayHintsController.ts',550selection: [673, 0],551query: 'add a function that takes a string and a length. the function should crop the string at length and insert `...` instead. The length is fuzzy and if possible the ellipses shouldn\'t follow whitespace or other "funny" characters',552expectedIntent: GenerateCodeIntent.ID,553validate: async (outcome, workspace, accessor) => {554assertInlineEdit(outcome);555556await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');557const edit = assertInlineEditShape(outcome, [{558line: 673,559originalLength: 0,560modifiedLength: undefined561}, {562line: 673,563originalLength: 1,564modifiedLength: undefined565}]);566assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['function', '...']);567assertOccursOnce(edit.changedModifiedLines.join('\n'), 'function');568}569}570],571});572});573574stest({ description: 'issue #2496: Range of interest is imprecise after a streaming edit', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {575576if (1) {577throw new Error('SKIPPED');578}579580return executeEditTestStrategy(strategy, testingServiceCollection, {581files: [582fromFixture('gen/strings.ts')583],584queries: [585{586file: 'strings.ts',587selection: [49, 0],588query: 'add fibonacci(n) returning the nth fibonacci with recursion',589expectedIntent: GenerateCodeIntent.ID,590validate: async (outcome, workspace, accessor) => {591assertInlineEdit(outcome);592593await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');594const edit = assertInlineEditShape(outcome, [{595line: 49,596originalLength: 0,597modifiedLength: undefined598}, {599line: 49,600originalLength: 1,601modifiedLength: undefined602}]);603assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['fibonacci']);604}605},606{607file: 'strings.ts',608selection: [49, 3],609wholeRange: [49, 0, 49, 3], // we want to simulate 100% vscode's behavior and vscode is peculiar here610query: 'avoid using recursion',611expectedIntent: EditCodeIntent.ID,612validate: async (outcome, workspace, accessor) => {613assertInlineEdit(outcome);614615await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');616assertOccursOnce(outcome.fileContents, 'function fibonacci');617}618}619],620});621});622623stest({ description: 'issue release#142: Inline chat updates code outside of area I expect', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {624return executeEditTestStrategy(strategy, testingServiceCollection, {625files: [626fromFixture('edit/issue-release-142/testAuthProvider.ts')627],628queries: [629{630file: 'testAuthProvider.ts',631selection: [28, 8],632query: 'implement this',633expectedIntent: GenerateCodeIntent.ID,634validate: async (outcome, workspace, accessor) => {635assertInlineEdit(outcome);636637await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');638const edit = assertInlineEditShape(outcome, [{639line: 28,640originalLength: 0,641modifiedLength: undefined642}, {643line: 28,644originalLength: 1,645modifiedLength: undefined646}]);647assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['sessionId', 'this._onDidChangeSessions']);648assertSomeStrings(edit.changedModifiedLines.join('\n'), ['filter', 'splice'], 1);649}650}651],652});653});654655stest({ description: 'issue #3778: Incorrect streaming edits', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {656return executeEditTestStrategy(strategy, testingServiceCollection, {657files: [658fromFixture('gen/modelLines.ts')659],660queries: [661{662file: 'modelLines.ts',663selection: [3, 0],664query: 'implement this!',665expectedIntent: GenerateCodeIntent.ID,666validate: async (outcome, workspace, accessor) => {667assertInlineEdit(outcome);668await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');669assertInlineEditShape(outcome, [{670line: 3,671originalLength: 0,672modifiedLength: undefined673}, {674line: 3,675originalLength: 1,676modifiedLength: undefined677}]);678}679}680],681});682});683684stest({ description: 'issue #4179: Imports aren\'t inserted to the top of the file anymore', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {685return executeEditTestStrategy(strategy, testingServiceCollection, {686files: [687fromFixture('gen/4179.ts')688],689queries: [690{691file: '4179.ts',692selection: [10, 0],693query: 'use node-lib to read a file',694expectedIntent: GenerateCodeIntent.ID,695validate: async (outcome, workspace, accessor) => {696assertInlineEdit(outcome);697await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');698const firstLine = outcome.fileContents.split(/\r\n|\r|\n/g)[0];699assertSomeStrings(firstLine, ['import', 'require'], 1);700}701}702],703});704});705706stest({ description: 'Remember my name', language: 'javascript', nonExtensionConfigurations }, (testingServiceCollection) => {707708const uri = URI.from({ scheme: 'foo', path: '/bar/baz.js' });709710return executeEditTestStrategy(strategy, testingServiceCollection, {711files: [{712kind: 'qualifiedFile',713uri,714fileContents: '',715languageId: 'javascript'716}],717queries: [718{719file: uri,720selection: [0, 0],721query: 'My name is Siglinde. Remember my name',722expectedIntent: GenerateCodeIntent.ID,723validate: async (outcome, workspace, accessor) => {724assertConversationalOutcome(outcome);725},726},727{728file: uri,729selection: [0, 0],730query: 'Generate a class that represents a person',731expectedIntent: GenerateCodeIntent.ID,732validate: async (outcome, workspace, accessor) => {733assertInlineEdit(outcome);734assert.ok(outcome.fileContents.includes('Person'));735},736},737{738// file: uri,739query: 'Print my name as a sample usage of the class',740expectedIntent: GenerateCodeIntent.ID,741validate: async (outcome, workspace, accessor) => {742assertInlineEdit(outcome);743744assert.ok(outcome.fileContents.includes('Siglinde'));745},746}747],748});749});750751stest({ description: 'issue #4080: Implementing a getter/method duplicates the signature', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {752return executeEditTestStrategy(strategy, testingServiceCollection, {753files: [754fromFixture('gen/4080.ts')755],756queries: [757{758file: '4080.ts',759selection: [40, 2],760query: 'implement this!',761expectedIntent: GenerateCodeIntent.ID,762validate: async (outcome, workspace, accessor) => {763assertInlineEdit(outcome);764await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');765const edit = assertInlineEditShape(outcome, [{766line: 40,767originalLength: 0,768modifiedLength: undefined769}, {770line: 40,771originalLength: 1,772modifiedLength: undefined773}]);774assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['CharCode.Tab', 'CharCode.Space', 'CharCode.LineFeed', 'while']);775}776}777],778});779});780781stest({ description: 'issue #3439: Bad edits in this case', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {782return executeEditTestStrategy(strategy, testingServiceCollection, {783files: [784fromFixture('gen/commandCenterControl.ts')785],786queries: [787{788file: 'commandCenterControl.ts',789selection: [188, 0],790query: 'when label contains \n or \r replace them with the rendered unicode character',791expectedIntent: GenerateCodeIntent.ID,792validate: async (outcome, workspace, accessor) => {793assertInlineEdit(outcome);794await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');795const edit = assertInlineEditShape(outcome, [{796line: 188,797originalLength: 0,798modifiedLength: undefined799}, {800line: 188,801originalLength: 1,802modifiedLength: undefined803}]);804assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['replace', '\\r', '\\n']);805}806}807],808});809});810811stest({ description: 'cpp code generation', language: 'cpp', nonExtensionConfigurations }, (testingServiceCollection) => {812return executeEditTestStrategy(strategy, testingServiceCollection, {813files: [814fromFixture('cpp/basic/main.cpp')815],816queries: [817{818file: 'main.cpp',819selection: [15, 0],820query: 'add validation to ensure that the input is not empty',821expectedIntent: GenerateCodeIntent.ID,822validate: async (outcome, workspace, accessor) => {823assertInlineEdit(outcome);824assertContainsAllSnippets(outcome.fileContents, ['empty']);825assertSomeStrings(outcome.fileContents, ['if', 'while']);826await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'cpp');827}828}829],830});831});832833stest({ description: 'templated code generation', language: 'cpp', nonExtensionConfigurations }, (testingServiceCollection) => {834return executeEditTestStrategy(strategy, testingServiceCollection, {835files: [836fromFixture('cpp/headers/json_fwd.hpp'),837fromFixture('cpp/headers/abi_macros.hpp')838],839queries: [840{841file: 'json_fwd.hpp',842selection: [71, 0],843query: 'add a sorted_map specialization',844expectedIntent: GenerateCodeIntent.ID,845validate: async (outcome, workspace, accessor) => {846assertInlineEdit(outcome);847// Validate the generated code matches naming and formatting conventions.848assertSomeStrings(outcome.fileContents, ['sorted_map', 'sorted_json', 'basic_json<nlohmann::sorted_map>']);849await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'cpp');850}851}852],853});854});855856stest({ description: 'issue #224: Lots of lines deleted when using interactive chat in a markdown file', language: 'markdown', nonExtensionConfigurations }, (testingServiceCollection) => {857858if (1) {859throw new Error('SKIPPED');860}861862return executeEditTestStrategy(strategy, testingServiceCollection, {863files: [864fromFixture('gen/CHANGELOG.md')865],866queries: [867{868file: 'CHANGELOG.md',869selection: [1, 0],870query: 'Add release notes for version 0.62.0',871expectedIntent: GenerateCodeIntent.ID,872validate: async (outcome, workspace, accessor) => {873assertInlineEdit(outcome);874assertInlineEditShape(outcome, [{875line: 1,876originalLength: 1,877modifiedLength: undefined878}]);879},880},881],882});883});884885stest({ description: 'doesn\'t handle markdown code response', language: 'markdown', nonExtensionConfigurations }, (testingServiceCollection) => {886const uri = Uri.parse('untitled:Untitled-1');887return executeEditTestStrategy(strategy, testingServiceCollection, {888files: [{889kind: 'qualifiedFile',890uri,891fileContents: '',892languageId: 'markdown'893}],894queries: [895{896file: uri,897selection: [0, 0],898query: 'describe fibonacci in markdown',899expectedIntent: GenerateCodeIntent.ID,900validate: async (outcome, workspace, accessor) => {901assertInlineEdit(outcome);902},903},904],905});906});907908stest({ description: 'issue #5439: import List in python', language: 'python', nonExtensionConfigurations }, (testingServiceCollection) => {909return executeEditTestStrategy(strategy, testingServiceCollection, {910files: [911fromFixture('gen/5439.py')912],913queries: [914{915file: '5439.py',916selection: [2, 0],917query: 'import List',918expectedIntent: GenerateCodeIntent.ID,919validate: async (outcome, workspace, accessor) => {920assertInlineEdit(outcome);921assertInlineEditShape(outcome, [{922line: 2,923originalLength: 1,924modifiedLength: 1925}]);926},927},928],929});930});931932stest({ description: 'issue #6234: generate a TS interface for some JSON', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {933return executeEditTestStrategy(strategy, testingServiceCollection, {934files: [fromFixture('gen/6234/top-packages.ts')],935queries: [936{937file: 'top-packages.ts',938selection: [4, 0, 4, 0],939query: 'generate an interface for this JSON:\n\n```\n{"name":"chalk","version":"5.3.0","description":"Terminal string styling done right","keywords":["color","colour","colors","terminal","console","cli","string","ansi","style","styles","tty","formatting","rgb","256","shell","xterm","log","logging","command-line","text"],"publisher":{"username":"sindresorhus","email":"[email protected]"},"maintainers":[{"username":"sindresorhus","email":"[email protected]"},{"username":"qix","email":"[email protected]"}],"links":{"npm":"https://www.npmjs.com/package/chalk","homepage":"https://github.com/chalk/chalk#readme","repository":"https://github.com/chalk/chalk"}}\n```',940diagnostics: 'tsc',941expectedIntent: 'generate',942validate: async (outcome, workspace, accessor) => {943await assertNoDiagnosticsAsync(accessor, outcome, workspace, 'tsc');944assertInlineEdit(outcome);945assertInlineEditShape(outcome, [{946line: 4,947originalLength: 1,948modifiedLength: undefined949}]);950}951}952]953});954});955956stest({ description: 'Inline chat response did not use code block #6554', language: 'powershell', nonExtensionConfigurations }, (testingServiceCollection) => {957return executeEditTestStrategy(strategy, testingServiceCollection, {958files: [fromFixture('gen/6554/update-vs-base.ps1')],959queries: [960{961file: 'update-vs-base.ps1',962selection: [13, 0, 13, 0],963query: 'copy folder to new location',964expectedIntent: 'generate',965validate: async (outcome, workspace, accessor) => {966assert.equal(outcome.type, 'inlineEdit');967}968}969]970});971});972973stest({ description: 'variables are used when generating', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {974return executeEditTestStrategy(strategy, testingServiceCollection, {975files: [976fromFixture('gen/variables/example.ts'),977fromFixture('gen/variables/output.ts')978],979queries: [980{981file: 'output.ts',982selection: [0, 0],983query: 'generate the same function like in #file:example.ts but for ArrayBuffer',984expectedIntent: 'generate',985validate: async (outcome, workspace, accessor) => {986assertInlineEdit(outcome);987assertContainsAllSnippets(outcome.fileContents, ['ArrayBuffer']);988assertSomeStrings(outcome.fileContents, ['arrayBufferInsert', 'arrayInsert'], 1);989}990}991]992});993});994995stest({ description: 'too much code generated #6696', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {996return executeEditTestStrategy(strategy, testingServiceCollection, {997files: [toFile({998fileName: 'generate/issue-6696/heatmapServiceImpl.ts',999fileContents: `/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation and GitHub. All rights reserved.\n *--------------------------------------------------------------------------------------------*/\n\nimport * as vscode from 'vscode';\nimport { DisposableStore } from '../../../util/vs/base/common/lifecycle';\nimport { ResourceMap } from '../../../util/vs/base/common/map';\nimport { IDocumentHeatMap, IDocumentHeatMapEntry, IHeatMapService } from '../common/heatmapService';\n\nclass DocumentHeatMap implements IDocumentHeatMap {\n\n\tprivate readonly _entries: IDocumentHeatMapEntry[] = [];\n\n\t\n\n\tgetEntries(): IDocumentHeatMapEntry[] {\n\t\treturn this._entries;\n\t}\n\n\tmarkClosed(): void {\n\n\t}\n\n\thandleSelectionChange(e: vscode.TextEditorSelectionChangeEvent): void {\n\t\tthis._entries.push({\n\t\t\ttimeStamp: Date.now(),\n\t\t\tposition: e.selections[0].active\n\t\t});\n\t}\n\n\thandleTextDocumentChange(e: vscode.TextDocumentChangeEvent): void {\n\n\t}\n}\n\nexport class HeatMapServiceImpl implements IHeatMapService {\n\n\t_serviceBrand: undefined;\n\n\tprivate readonly _store = new DisposableStore();\n\n\tprivate readonly _map = new ResourceMap<DocumentHeatMap>();\n\n\tconstructor() {\n\t\tthis._store.add(vscode.window.onDidChangeTextEditorSelection(e => {\n\t\t\tthis._ensureHeatMap(e.textEditor.document.uri).handleSelectionChange(e);\n\t\t}));\n\t\tthis._store.add(vscode.workspace.onDidChangeTextDocument(e => {\n\t\t\tthis._ensureHeatMap(e.document.uri).handleTextDocumentChange(e);\n\t\t}));\n\t\tthis._store.add(vscode.workspace.onDidCloseTextDocument(e => {\n\t\t\t//\n\t\t\tthis._map.get(e.uri)?.markClosed();\n\t\t}));\n\t}\n\n\tdispose(): void {\n\t\tthis._store.dispose();\n\t}\n\n\tgetDocumentHeatMap(uri: vscode.Uri): IDocumentHeatMap | undefined {\n\t\treturn this._map.get(uri);\n\t}\n\n\tprivate _ensureHeatMap(uri: vscode.Uri): DocumentHeatMap {\n\t\tlet heatMap = this._map.get(uri);\n\t\tif (!heatMap) {\n\t\t\theatMap = new DocumentHeatMap();\n\t\t\tthis._map.set(uri, heatMap);\n\t\t}\n\t\treturn heatMap;\n\t}\n}\n`1000})],1001queries: [1002{1003file: 'generate/issue-6696/heatmapServiceImpl.ts',1004selection: [13, 1, 13, 1],1005query: 'add constructor that takes the vscode.TextDocument',1006diagnostics: 'tsc',1007expectedIntent: 'generate',1008validate: async (outcome, workspace, accessor) => {1009assertInlineEdit(outcome);10101011const allNewText = outcome.appliedEdits.map(edit => edit.newText).join('');10121013assert.strictEqual(allNewText.includes('TextDocument'), true);1014assert.strictEqual(allNewText.includes('vscode.TextDocument'), true);1015assert.strictEqual(allNewText.includes('store.add'), false);1016assert.strictEqual(allNewText.includes('onDidChange'), false);1017}1018}1019]1020});1021});10221023stest({ description: 'issue #6163', language: 'json', nonExtensionConfigurations }, (testingServiceCollection) => {1024return executeEditTestStrategy(strategy, testingServiceCollection, {1025files: [fromFixture('generate/issue-6163/package.json')],1026queries: [1027{1028file: 'package.json',1029selection: [14, 1, 14, 1],1030query: 'add scripts section which invokes .esbuild.ts',1031expectedIntent: 'generate',1032validate: async (outcome, workspace, accessor) => {1033assertInlineEdit(outcome);1034assertInlineEditShape(outcome, [{1035line: 14,1036originalLength: 1,1037modifiedLength: undefined1038}]);1039}1040}1041]1042});1043});10441045stest({ description: 'issue #6788', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {1046return executeEditTestStrategy(strategy, testingServiceCollection, {1047files: [1048fromFixture('generate/issue-6788/terminalSuggestAddon.ts')1049],1050queries: [1051{1052file: 'terminalSuggestAddon.ts',1053selection: [551, 2, 551, 2],1054query: 'get common prefix length of replacementText and completion.label',1055diagnostics: 'tsc',1056expectedIntent: 'generate',1057validate: async (outcome, workspace, accessor) => {1058assertInlineEdit(outcome);1059await assertNoSyntacticDiagnosticsAsync(accessor, outcome, workspace, 'tsc');1060}1061}1062]1063});1064});10651066stest({ description: 'issue #6505', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {1067return executeEditTestStrategy(strategy, testingServiceCollection, {1068files: [fromFixture('generate/issue-6505/chatParserTypes.ts')],1069queries: [1070{1071file: 'chatParserTypes.ts',1072selection: [84, 1, 84, 1],1073query: 'add a getter isSynthetic when range.length = 0',1074diagnostics: 'tsc',1075expectedIntent: 'generate',1076validate: async (outcome, workspace, accessor) => {1077assertInlineEdit(outcome);1078await assertNoDiagnosticsAsync(accessor, outcome, workspace, KnownDiagnosticProviders.tscIgnoreImportErrors);1079const edit = assertInlineEditShape(outcome, [{1080line: 84,1081originalLength: 1,1082modifiedLength: undefined1083}]);1084assertContainsAllSnippets(edit.changedModifiedLines.join('\n'), ['this.range.length']);1085}1086}1087]1088});1089});10901091stest({ description: 'issue #7772', language: 'typescript', nonExtensionConfigurations }, (testingServiceCollection) => {1092return executeEditTestStrategy(strategy, testingServiceCollection, {1093files: [fromFixture('generate/issue-7772/builds.ts')],1094queries: [1095{1096file: 'builds.ts',1097selection: [141, 8, 141, 8],1098query: 'compare the `path` sha256 with the `sha256`',1099diagnostics: 'tsc',1100expectedIntent: 'generate',1101validate: async (outcome, workspace, accessor) => {1102assertInlineEdit(outcome);1103await assertNoDiagnosticsAsync(accessor, outcome, workspace, KnownDiagnosticProviders.tscIgnoreImportErrors);1104}1105}1106]1107});1108});11091110stest({ description: 'Issue #7088', language: 'powershell', nonExtensionConfigurations }, (accessor) => {1111return executeEditTestStrategy(strategy, accessor, {1112files: [toFile({1113filePath: fromFixture('generate/issue-7088/Microsoft.PowerShell_profile.ps1')1114})],1115queries: [1116{1117file: 'Microsoft.PowerShell_profile.ps1',1118selection: [3, 0, 3, 0],1119query: 'set alias c to code-insiders',1120expectedIntent: 'generate',1121validate: async (outcome, workspace, accessor) => {1122assertInlineEdit(outcome);1123}1124}1125]1126});1127});1128});1129});113011311132