11/**********************************************************************
2- * Copyright (c) 2024 Red Hat, Inc.
2+ * Copyright (c) 2024-2025 Red Hat, Inc.
33 *
44 * This program and the accompanying materials are made
55 * available under the terms of the Eclipse Public License 2.0
@@ -42,26 +42,50 @@ const CONFIGMAP_SETTINGS_DATA = {
4242 'settings.json' : SETTINGS_CONTENT ,
4343} ;
4444
45- const EXTENSIONS_CONTENT =
45+ const WORKSPACE_FILE_EXTENSIONS_CONTENT =
4646 '{\n' +
4747 ' "recommendations": [\n' +
4848 ' "dbaeumer.vscode-eslint",\n' +
4949 ' "github.vscode-pull-request-github"\n' +
5050 ' ]\n' +
5151 '}\n' ;
52- const EXTENSIONS_JSON = JSON . parse ( EXTENSIONS_CONTENT ) ;
53- const WORKSPACE_CONFIG_JSON = JSON . parse ( WORKSPACE_FILE_CONTENT ) ;
54- WORKSPACE_CONFIG_JSON [ 'extensions' ] = EXTENSIONS_JSON ;
55- const WORKSPACE_CONFIG_WITH_EXTENSIONS_TO_FILE = JSON . stringify ( WORKSPACE_CONFIG_JSON , null , '\t' ) ;
52+
53+ // prettier-ignore
54+ const CONFIGMAP_EXTENSIONS_CONTENT =
55+ '{\n' +
56+ ' "recommendations": [\n' +
57+ ' "redhat.vscode-yaml",\n' +
58+ ' "redhat.java"\n' +
59+ ' ]\n' +
60+ '}\n' ;
61+
62+ const MERGED_EXTENSIONS_CONTENT =
63+ '{\n' +
64+ ' "recommendations": [\n' +
65+ ' "redhat.vscode-yaml",\n' +
66+ ' "redhat.java",\n' +
67+ ' "dbaeumer.vscode-eslint",\n' +
68+ ' "github.vscode-pull-request-github"\n' +
69+ ' ]\n' +
70+ '}\n' ;
71+ const WORKSPACE_EXTENSIONS_JSON = JSON . parse ( WORKSPACE_FILE_EXTENSIONS_CONTENT ) ;
72+ const WORKSPACE_CONFIG_WITHOUT_EXTENSIONS_JSON = JSON . parse ( WORKSPACE_FILE_CONTENT ) ;
73+ const CONFIGMAP_EXTENSIONS_JSON = JSON . parse ( CONFIGMAP_EXTENSIONS_CONTENT ) ;
74+ const MERGED_EXTENSIONS_JSON = JSON . parse ( MERGED_EXTENSIONS_CONTENT ) ;
5675const CONFIGMAP_EXTENSIOSN_DATA = {
57- 'extensions.json' : EXTENSIONS_CONTENT ,
76+ 'extensions.json' : CONFIGMAP_EXTENSIONS_CONTENT ,
5877} ;
5978
6079const CONFIGMAP_INCORRECT_DATA = {
6180 'extensions.json' : '//some incorrect data' ,
6281 'settings.json' : '//some incorrect data' ,
6382} ;
6483
84+ const EMPTY_RECOMMENDATIONS_CONTENT = '{\n' + ' "recommendations": []\n' + '}\n' ;
85+ const EMPTY_RECOMMENDATIONS_DATA = {
86+ 'extensions.json' : EMPTY_RECOMMENDATIONS_CONTENT ,
87+ } ;
88+
6589jest . mock ( '@kubernetes/client-node' , ( ) => {
6690 const actual = jest . requireActual ( '@kubernetes/client-node' ) ;
6791 return {
@@ -106,7 +130,7 @@ describe('Test applying editor configurations:', () => {
106130 } ) ) ;
107131 } ) ;
108132
109- it ( 'should skip applying editor congis if there is no DEVWORKSPACE_NAMESPACE' , async ( ) => {
133+ it ( 'should skip applying editor configs if there is no DEVWORKSPACE_NAMESPACE' , async ( ) => {
110134 await new EditorConfigurations ( ) . configure ( ) ;
111135
112136 expect ( mockMakeApiClient ) . not . toHaveBeenCalled ( ) ;
@@ -214,6 +238,27 @@ describe('Test applying editor configurations:', () => {
214238 expect ( writeFileMock ) . not . toHaveBeenCalled ( ) ;
215239 } ) ;
216240
241+ it ( 'should skip applying extensions when empty list of extensions in a configmap' , async ( ) => {
242+ env . DEVWORKSPACE_NAMESPACE = DEVWORKSPACE_NAMESPACE ;
243+ const mockResponse = {
244+ response : { } as IncomingMessage ,
245+ body : { data : EMPTY_RECOMMENDATIONS_DATA } as V1ConfigMap ,
246+ } ;
247+ mockCoreV1Api . readNamespacedConfigMap . mockResolvedValue ( mockResponse ) ;
248+ fileExistsMock . mockResolvedValue ( false ) ;
249+
250+ await new EditorConfigurations ( WORKSPACE_FILE_PATH ) . configure ( ) ;
251+
252+ expect ( mockMakeApiClient ) . toHaveBeenCalledWith ( CoreV1Api ) ;
253+ expect ( mockCoreV1Api . readNamespacedConfigMap ) . toHaveBeenCalledWith (
254+ 'vscode-editor-configurations' ,
255+ DEVWORKSPACE_NAMESPACE
256+ ) ;
257+ expect ( fileExistsMock ) . not . toHaveBeenCalled ( ) ;
258+ expect ( readFileMock ) . not . toHaveBeenCalled ( ) ;
259+ expect ( writeFileMock ) . not . toHaveBeenCalled ( ) ;
260+ } ) ;
261+
217262 it ( 'should skip applying extensions when the workspace file is not found' , async ( ) => {
218263 env . DEVWORKSPACE_NAMESPACE = DEVWORKSPACE_NAMESPACE ;
219264 const mockResponse = {
@@ -233,8 +278,13 @@ describe('Test applying editor configurations:', () => {
233278 expect ( writeFileMock ) . not . toHaveBeenCalled ( ) ;
234279 } ) ;
235280
236- it ( 'should apply extensions from a configmap' , async ( ) => {
281+ it ( 'should apply extensions from a configmap when workspace file does not contain extensions ' , async ( ) => {
237282 env . DEVWORKSPACE_NAMESPACE = DEVWORKSPACE_NAMESPACE ;
283+ const workspaceConfigWithConfigMapExtensionsJson = {
284+ ...WORKSPACE_CONFIG_WITHOUT_EXTENSIONS_JSON ,
285+ extensions : CONFIGMAP_EXTENSIONS_JSON ,
286+ } ;
287+ const workspaceConfigWithExtensionsToFile = JSON . stringify ( workspaceConfigWithConfigMapExtensionsJson , null , '\t' ) ;
238288 const mockResponse = {
239289 response : { } as IncomingMessage ,
240290 body : { data : CONFIGMAP_EXTENSIOSN_DATA } as V1ConfigMap ,
@@ -251,7 +301,42 @@ describe('Test applying editor configurations:', () => {
251301 DEVWORKSPACE_NAMESPACE
252302 ) ;
253303 expect ( writeFileMock ) . toBeCalledTimes ( 1 ) ; // only extensions were applied
254- expect ( writeFileMock ) . toBeCalledWith ( WORKSPACE_FILE_PATH , WORKSPACE_CONFIG_WITH_EXTENSIONS_TO_FILE ) ;
304+ expect ( writeFileMock ) . toBeCalledWith ( WORKSPACE_FILE_PATH , workspaceConfigWithExtensionsToFile ) ;
305+ } ) ;
306+
307+ it ( 'should merge extensions from the configmap and workspace file extensions' , async ( ) => {
308+ env . DEVWORKSPACE_NAMESPACE = DEVWORKSPACE_NAMESPACE ;
309+ const workspaceConfigWithExtensionsJson = {
310+ ...WORKSPACE_CONFIG_WITHOUT_EXTENSIONS_JSON ,
311+ extensions : WORKSPACE_EXTENSIONS_JSON ,
312+ } ;
313+ const workspaceConfigWithMergedExtensionsJson = {
314+ ...WORKSPACE_CONFIG_WITHOUT_EXTENSIONS_JSON ,
315+ extensions : MERGED_EXTENSIONS_JSON ,
316+ } ;
317+ const workspaceConfigWithExtensionsToFile = JSON . stringify ( workspaceConfigWithExtensionsJson , null , '\t' ) ;
318+ const workspaceConfigWithMergedExtensionsToFile = JSON . stringify (
319+ workspaceConfigWithMergedExtensionsJson ,
320+ null ,
321+ '\t'
322+ ) ;
323+ const mockResponse = {
324+ response : { } as IncomingMessage ,
325+ body : { data : CONFIGMAP_EXTENSIOSN_DATA } as V1ConfigMap ,
326+ } ;
327+ mockCoreV1Api . readNamespacedConfigMap . mockResolvedValue ( mockResponse ) ;
328+ fileExistsMock . mockResolvedValue ( true ) ;
329+ readFileMock . mockResolvedValue ( workspaceConfigWithExtensionsToFile ) ;
330+
331+ await new EditorConfigurations ( WORKSPACE_FILE_PATH ) . configure ( ) ;
332+
333+ expect ( mockMakeApiClient ) . toHaveBeenCalledWith ( CoreV1Api ) ;
334+ expect ( mockCoreV1Api . readNamespacedConfigMap ) . toHaveBeenCalledWith (
335+ 'vscode-editor-configurations' ,
336+ DEVWORKSPACE_NAMESPACE
337+ ) ;
338+ expect ( writeFileMock ) . toBeCalledTimes ( 1 ) ; // only extensions were applied
339+ expect ( writeFileMock ) . toBeCalledWith ( WORKSPACE_FILE_PATH , workspaceConfigWithMergedExtensionsToFile ) ;
255340 } ) ;
256341
257342 it ( 'should merge product.json with a provided config map' , async ( ) => {
0 commit comments