diff --git a/extensions/sql-database-projects/src/models/project.ts b/extensions/sql-database-projects/src/models/project.ts index d5d7be147b..b8843946d5 100644 --- a/extensions/sql-database-projects/src/models/project.ts +++ b/extensions/sql-database-projects/src/models/project.ts @@ -1052,9 +1052,8 @@ export class Project implements ISqlProject { // again when they convert to an SDK-style project if (this.isSdkStyleProject) { // update sqlproj if a node was deleted and load files and folders again - if (deleted) { - await this.writeToSqlProjAndUpdateFilesFolders(); - } + await this.writeToSqlProjAndUpdateFilesFolders(); + // get latest folders to see if it still exists const currentFolders = await this.readFolders(); diff --git a/extensions/sql-database-projects/src/test/baselines/openSdkStyleSqlProjectWithFilesSpecifiedBaseline.xml b/extensions/sql-database-projects/src/test/baselines/openSdkStyleSqlProjectWithFilesSpecifiedBaseline.xml index 8c6f83a0aa..d7eecc0273 100644 --- a/extensions/sql-database-projects/src/test/baselines/openSdkStyleSqlProjectWithFilesSpecifiedBaseline.xml +++ b/extensions/sql-database-projects/src/test/baselines/openSdkStyleSqlProjectWithFilesSpecifiedBaseline.xml @@ -35,7 +35,7 @@ - + diff --git a/extensions/sql-database-projects/src/test/project.test.ts b/extensions/sql-database-projects/src/test/project.test.ts index 50310f8e7d..92de05b087 100644 --- a/extensions/sql-database-projects/src/test/project.test.ts +++ b/extensions/sql-database-projects/src/test/project.test.ts @@ -1253,6 +1253,97 @@ describe('Project: sdk style project content operations', function (): void { should(project.files.find(f => f.relativePath === 'folder3\\')!).not.equal(undefined, 'folder3\\ should be loaded'); should(project.files.find(f => f.relativePath === 'folder3\\innerFolder\\')!).not.equal(undefined, 'folder3\\innerFolder\\ should be loaded'); }); + + it('Should handle deleting empty folders', async function (): Promise { + const testFolderPath = await testUtils.generateTestFolderPath(); + projFilePath = await testUtils.createTestSqlProjFile(baselines.newSdkStyleProjectSdkNodeBaseline, testFolderPath); + + const project: Project = await Project.openProject(projFilePath); + const beforeProjFileText = (await fs.readFile(projFilePath)).toString(); + + should(project.files.filter(f => f.type === EntryType.File).length).equal(0); + should(project.files.filter(f => f.type === EntryType.Folder).length).equal(0); + + // add an empty folder + await project.addFolderItem('folder1'); + + // verify folder was added + should(project.files.filter(f => f.type === EntryType.Folder).length).equal(1); + should(project.files.filter(f => f.type === EntryType.File).length).equal(0); + should(project.files.find(f => f.relativePath === 'folder1\\')).not.equal(undefined, 'folder1 should have been added'); + + // verify entry was added for the new empty folder in the sqlproj + let projFileText = (await fs.readFile(projFilePath)).toString(); + should(projFileText.includes('')).equal(true, projFileText); + + // delete the empty folder + await project.deleteFileFolder(project.files.find(f => f.relativePath === 'folder1\\')!); + + should(project.files.filter(f => f.type === EntryType.Folder).length).equal(0, 'folder1 should have been deleted'); + + // verify the folder entry was removed from the sqlproj and a Build Remove was not added + projFileText = (await fs.readFile(projFilePath)).toString(); + should(projFileText.trimEnd() === beforeProjFileText.trimEnd()).equal(true, 'The sqlproj should not have changed after deleting folder1'); + }); + + it('Should handle deleting not empty glob included folders', async function (): Promise { + const testFolderPath = await testUtils.generateTestFolderPath(); + projFilePath = await testUtils.createTestSqlProjFile(baselines.openSdkStyleSqlProjectBaseline, testFolderPath); + await testUtils.createDummyFileStructureWithPrePostDeployScripts(false, undefined, path.dirname(projFilePath)); + + const project: Project = await Project.openProject(projFilePath); + const beforeProjFileText = (await fs.readFile(projFilePath)).toString(); + + should(project.files.filter(f => f.type === EntryType.File).length).equal(13); + should(project.files.filter(f => f.type === EntryType.Folder).length).equal(3); + + // delete a folder with contents + await project.deleteFileFolder(project.files.find(f => f.relativePath === 'folder2\\')!); + + should(project.files.filter(f => f.type === EntryType.File).length).equal(8); + should(project.files.filter(f => f.type === EntryType.Folder).length).equal(2); + + // verify the folder entry was removed from the sqlproj and a Build Remove was not added + const projFileText = (await fs.readFile(projFilePath)).toString(); + should(projFileText.trimEnd() === beforeProjFileText.trimEnd()).equal(true, 'The sqlproj should not have changed after deleting folder2'); + }); + + it('Should handle deleting explicitly included folders', async function (): Promise { + const testFolderPath = await testUtils.generateTestFolderPath(); + projFilePath = await testUtils.createTestSqlProjFile(baselines.openSdkStyleSqlProjectWithFilesSpecifiedBaseline, testFolderPath); + await testUtils.createDummyFileStructureWithPrePostDeployScripts(false, undefined, path.dirname(projFilePath)); + + const project: Project = await Project.openProject(projFilePath); + + should(project.files.filter(f => f.type === EntryType.File).length).equal(13); + should(project.files.filter(f => f.type === EntryType.Folder).length).equal(3); + should(project.files.find(f => f.relativePath === 'folder1\\')!).not.equal(undefined); + should(project.files.find(f => f.relativePath === 'folder2\\')!).not.equal(undefined); + + // try to delete an explicitly included folder with the trailing \ in sqlproj + await project.deleteFileFolder(project.files.find(f => f.relativePath === 'folder2\\')!); + + // verify the project not longer has folder2 and its contents + should(project.files.filter(f => f.type === EntryType.Folder).length).equal(2); + should(project.files.filter(f => f.type === EntryType.File).length).equal(8); + should(project.files.find(f => f.relativePath === 'folder2\\')).equal(undefined); + + // try to delete an explicitly included folder without trailing \ in sqlproj + await project.deleteFileFolder(project.files.find(f => f.relativePath === 'folder1\\')!); + + // verify the project not longer has folder1 and its contents + should(project.files.filter(f => f.type === EntryType.Folder).length).equal(0); + should(project.files.filter(f => f.type === EntryType.File).length).equal(1); + should(project.files.find(f => f.relativePath === 'folder1\\')).equal(undefined); + + // make sure both folders are removed from sqlproj and Build Remove entries were not added + const projFileText = (await fs.readFile(projFilePath)).toString(); + should(projFileText.includes('')).equal(false, projFileText); + should(projFileText.includes('')).equal(false, projFileText); + + should(projFileText.includes('')).equal(false, projFileText); + should(projFileText.includes('')).equal(false, projFileText); + }); }); describe('Project: add SQLCMD Variables', function (): void {