22// Use of this source code is governed by a BSD-style license that can be
33// found in the LICENSE file.
44
5+ import * as Common from '../../core/common/common.js' ;
56import * as Host from '../../core/host/host.js' ;
67import * as SDK from '../../core/sdk/sdk.js' ;
78import * as Protocol from '../../generated/protocol.js' ;
89import * as ComputedStyle from '../../models/computed_style/computed_style.js' ;
9- import { renderElementIntoDOM } from '../../testing/DOMHelpers.js' ;
10+ import { raf , renderElementIntoDOM } from '../../testing/DOMHelpers.js' ;
1011import {
1112 createTarget ,
1213 describeWithEnvironment ,
@@ -1203,6 +1204,110 @@ describe('StylesSidebarPane', () => {
12031204 } ) ;
12041205 } ) ;
12051206 } ) ;
1207+
1208+ describe ( 'ai code completion provider callbacks' , ( ) => {
1209+ let stylesWrapper : UI . Widget . VBox ;
1210+ let stylesSidebarPane : Elements . StylesSidebarPane . StylesSidebarPane ;
1211+
1212+ beforeEach ( async ( ) => {
1213+ updateHostConfig ( {
1214+ devToolsAiCodeCompletionStyles : {
1215+ enabled : true ,
1216+ } ,
1217+ aidaAvailability : {
1218+ enabled : true ,
1219+ blockedByAge : false ,
1220+ blockedByGeo : false ,
1221+ } ,
1222+ } ) ;
1223+ const aiCodeCompletionProviderStub =
1224+ sinon . createStubInstance ( TextEditor . AiCodeCompletionProvider . AiCodeCompletionProvider ) ;
1225+ aiCodeCompletionProviderStub . extension . returns ( [ ] ) ;
1226+ sinon . stub ( TextEditor . AiCodeCompletionProvider . AiCodeCompletionProvider , 'createInstance' )
1227+ . returns ( aiCodeCompletionProviderStub ) ;
1228+ Common . Settings . Settings . instance ( ) . createSetting ( 'ai-code-completion-enabled' , true ) ;
1229+ stylesWrapper = new UI . Widget . VBox ( ) ;
1230+ stylesWrapper . element . classList . add ( 'style-panes-wrapper' ) ;
1231+ stylesSidebarPane =
1232+ new Elements . StylesSidebarPane . StylesSidebarPane ( new ComputedStyle . ComputedStyleModel . ComputedStyleModel ( ) ) ;
1233+ stylesSidebarPane . show ( stylesWrapper . element ) ;
1234+ } ) ;
1235+
1236+ it ( 'initializes toolbar when the feature is enabled' , async ( ) => {
1237+ const providerConfig = stylesSidebarPane . aiCodeCompletionConfig ;
1238+ assert . exists ( providerConfig ) ;
1239+
1240+ providerConfig . onFeatureEnabled ( ) ;
1241+
1242+ assert . exists ( stylesWrapper . contentElement . querySelector ( 'div.ai-code-completion-summary-toolbar-container' ) ) ;
1243+ } ) ;
1244+
1245+ it ( 'cleans up toolbar when the feature is disabled' , async ( ) => {
1246+ const providerConfig = stylesSidebarPane . aiCodeCompletionConfig ;
1247+ assert . exists ( providerConfig ) ;
1248+ providerConfig . onFeatureEnabled ( ) ;
1249+ assert . exists ( stylesWrapper . contentElement . querySelector ( 'div.ai-code-completion-summary-toolbar-container' ) ) ;
1250+
1251+ providerConfig . onFeatureDisabled ( ) ;
1252+
1253+ assert . notExists (
1254+ stylesWrapper . contentElement . querySelector ( 'div.ai-code-completion-summary-toolbar-container' ) ) ;
1255+ } ) ;
1256+
1257+ it ( 'shows a loading state when a request is triggered' , async ( ) => {
1258+ const setLoadingSpy = sinon . stub ( PanelsCommon . AiCodeCompletionSummaryToolbar . prototype , 'setLoading' ) ;
1259+ const providerConfig = stylesSidebarPane . aiCodeCompletionConfig ;
1260+ assert . exists ( providerConfig ) ;
1261+ providerConfig . onFeatureEnabled ( ) ;
1262+
1263+ providerConfig . onRequestTriggered ( ) ;
1264+
1265+ sinon . assert . calledOnce ( setLoadingSpy ) ;
1266+ assert . isTrue ( setLoadingSpy . firstCall . args [ 0 ] ) ;
1267+ } ) ;
1268+
1269+ it ( 'hides the loading indicator when a response is received' , async ( ) => {
1270+ const setLoadingSpy = sinon . stub ( PanelsCommon . AiCodeCompletionSummaryToolbar . prototype , 'setLoading' ) ;
1271+ const providerConfig = stylesSidebarPane . aiCodeCompletionConfig ;
1272+ assert . exists ( providerConfig ) ;
1273+ providerConfig . onFeatureEnabled ( ) ;
1274+ providerConfig . onRequestTriggered ( ) ;
1275+ sinon . assert . calledOnce ( setLoadingSpy ) ;
1276+ assert . isTrue ( setLoadingSpy . firstCall . args [ 0 ] ) ;
1277+
1278+ providerConfig . onResponseReceived ( ) ;
1279+
1280+ sinon . assert . calledTwice ( setLoadingSpy ) ;
1281+ assert . isFalse ( setLoadingSpy . secondCall . args [ 0 ] ) ;
1282+ } ) ;
1283+
1284+ it ( 'attaches the citations toolbar when a suggestion with citations is accepted' , async ( ) => {
1285+ const updateCitationsSpy = sinon . spy ( PanelsCommon . AiCodeCompletionSummaryToolbar . prototype , 'updateCitations' ) ;
1286+ const providerConfig = stylesSidebarPane . aiCodeCompletionConfig ;
1287+ assert . exists ( providerConfig ) ;
1288+
1289+ providerConfig . onFeatureEnabled ( ) ;
1290+ providerConfig . onResponseReceived ( ) ;
1291+
1292+ providerConfig . onSuggestionAccepted ( [ { uri : 'https://example.com/source' } ] ) ;
1293+
1294+ sinon . assert . calledOnce ( updateCitationsSpy ) ;
1295+ assert . deepEqual ( updateCitationsSpy . firstCall . args , [ [ 'https://example.com/source' ] ] ) ;
1296+ } ) ;
1297+
1298+ it ( 'does not attach the citations toolbar if there are no citations' , async ( ) => {
1299+ const updateCitationsSpy = sinon . spy ( PanelsCommon . AiCodeCompletionSummaryToolbar . prototype , 'updateCitations' ) ;
1300+ const providerConfig = stylesSidebarPane . aiCodeCompletionConfig ;
1301+ assert . exists ( providerConfig ) ;
1302+
1303+ providerConfig . onFeatureEnabled ( ) ;
1304+ providerConfig . onResponseReceived ( ) ;
1305+
1306+ providerConfig . onSuggestionAccepted ( [ ] ) ;
1307+
1308+ sinon . assert . notCalled ( updateCitationsSpy ) ;
1309+ } ) ;
1310+ } ) ;
12061311 } ) ;
12071312
12081313 describe ( 'IdleCallbackManager' , ( ) => {
@@ -1265,6 +1370,7 @@ describe('StylesSidebarPane', () => {
12651370 configurable : true ,
12661371 } ) ;
12671372 sinon . stub ( section , 'activeAiSuggestion' ) . get ( ( ) => activeAiSuggestion ) ;
1373+ section . commitActiveAiSuggestion . resolves ( ) ;
12681374 mockTreeItem = {
12691375 property : {
12701376 name : 'color' ,
@@ -1471,6 +1577,7 @@ describe('StylesSidebarPane', () => {
14711577 startTime : 0 ,
14721578 clearCachedRequest : ( ) => { } ,
14731579 onImpression : ( ) => { } ,
1580+ citations : [ ] ,
14741581 } ) ;
14751582
14761583 assert . exists ( section . activeAiSuggestion ) ;
@@ -1492,6 +1599,7 @@ color: pink !important;`;
14921599 startTime : 0 ,
14931600 clearCachedRequest : ( ) => { } ,
14941601 onImpression : ( ) => { } ,
1602+ citations : [ ] ,
14951603 } ) ;
14961604
14971605 assert . exists ( section . activeAiSuggestion ) ;
@@ -1514,6 +1622,7 @@ color: pink !important;`;
15141622 startTime : 0 ,
15151623 clearCachedRequest : ( ) => { } ,
15161624 onImpression : ( ) => { } ,
1625+ citations : [ ] ,
15171626 } ) ;
15181627
15191628 assert . isTrue ( cssPropertyPrompt . isSuggestBoxVisible ( ) ) ;
@@ -1535,6 +1644,7 @@ color: pink !important;`;
15351644 startTime : 0 ,
15361645 clearCachedRequest : ( ) => { } ,
15371646 onImpression : ( ) => { } ,
1647+ citations : [ ] ,
15381648 } ) ;
15391649
15401650 assert . strictEqual ( section . activeAiSuggestion ?. text , 'color: var(--rgb-color);' ) ;
@@ -1550,18 +1660,26 @@ color: pink !important;`;
15501660 it ( 'accepts suggestion on Tab when suggest box is hidden' , async ( ) => {
15511661 cssPropertyPrompt = new Elements . StylesSidebarPane . CSSPropertyPrompt ( mockTreeItem , true ) ;
15521662 cssPropertyPrompt . attachAndStartEditing ( attachedElement , noop ) ;
1663+ const onSuggestionAcceptedStub = aiCodeCompletionProvider . onSuggestionAccepted . returns ( ) ;
15531664
15541665 cssPropertyPrompt . aiCodeCompletionProvider ?. setAiAutoCompletion ?.( {
15551666 text : 'color: pink;' ,
15561667 from : 0 ,
15571668 startTime : 0 ,
15581669 clearCachedRequest : ( ) => { } ,
15591670 onImpression : ( ) => { } ,
1671+ citations : [ { uri : 'https://example.com' } ] ,
1672+ sampleId : 1 ,
1673+ rpcGlobalId : 1 ,
15601674 } ) ;
15611675 const tabEvent = new KeyboardEvent ( 'keydown' , { key : 'Tab' } ) ;
15621676 cssPropertyPrompt . onKeyDown ( tabEvent ) ;
1677+ // Required to make sure section.commitActiveAiSuggestion resolves
1678+ await raf ( ) ;
15631679
15641680 sinon . assert . calledOnce ( section . commitActiveAiSuggestion ) ;
1681+ sinon . assert . calledOnce ( onSuggestionAcceptedStub ) ;
1682+ assert . deepEqual ( onSuggestionAcceptedStub . firstCall . args , [ [ { uri : 'https://example.com' } ] , 1 , 1 ] ) ;
15651683 } ) ;
15661684
15671685 it ( 'accepts auto complete suggestion and re-applies ghost text on first Tab accept when suggest box is visible' ,
@@ -1577,6 +1695,7 @@ color: pink !important;`;
15771695 startTime : 0 ,
15781696 clearCachedRequest : ( ) => { } ,
15791697 onImpression : ( ) => { } ,
1698+ citations : [ ] ,
15801699 } ) ;
15811700
15821701 assert . isTrue ( cssPropertyPrompt . isSuggestBoxVisible ( ) ) ;
@@ -1601,6 +1720,7 @@ color: pink !important;`;
16011720 startTime : 0 ,
16021721 clearCachedRequest : ( ) => { } ,
16031722 onImpression : ( ) => { } ,
1723+ citations : [ ] ,
16041724 } ) ;
16051725
16061726 assert . isTrue ( cssPropertyPrompt . isSuggestBoxVisible ( ) ) ;
@@ -1628,6 +1748,7 @@ color: pink !important;`;
16281748 startTime : 0 ,
16291749 clearCachedRequest : ( ) => { } ,
16301750 onImpression : ( ) => { } ,
1751+ citations : [ ] ,
16311752 } ) ;
16321753 assert . exists ( section . activeAiSuggestion ) ;
16331754
@@ -1647,6 +1768,7 @@ color: pink !important;`;
16471768 startTime : 0 ,
16481769 clearCachedRequest : ( ) => { } ,
16491770 onImpression : ( ) => { } ,
1771+ citations : [ ] ,
16501772 } ) ;
16511773 assert . exists ( section . activeAiSuggestion ) ;
16521774
@@ -1673,6 +1795,7 @@ color: pink !important;`;
16731795 startTime : 0 ,
16741796 clearCachedRequest : ( ) => { } ,
16751797 onImpression : ( ) => { } ,
1798+ citations : [ ] ,
16761799 } ) ;
16771800
16781801 assert . exists ( section . activeAiSuggestion ) ;
@@ -1692,6 +1815,7 @@ color: pink !important;`;
16921815 startTime : 0 ,
16931816 clearCachedRequest : ( ) => { } ,
16941817 onImpression : ( ) => { } ,
1818+ citations : [ ] ,
16951819 } ) ;
16961820 assert . exists ( section . activeAiSuggestion ) ;
16971821
0 commit comments