mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-15 17:22:25 -05:00
Adding Move, Exclude, and Rename support for folders (#22867)
* Adding exclude folder and base for move folder * checkpoint * rename * Fixing up tests * Adding exclude test to projController * Adding tests * fixing order of service.moveX() calls * Updating move() order in sqlproj service * PR feedback * unskipping * reskipping test * Fixing folder move conditional * updating comments
This commit is contained in:
@@ -349,8 +349,7 @@ describe('Project: sdk style project content operations', function (): void {
|
||||
should(project.files.length).equal(0, 'There should not be any SQL object scripts after the excludes');
|
||||
});
|
||||
|
||||
// skipped because exclude folder not yet supported
|
||||
it.skip('Should handle excluding glob included folders', async function (): Promise<void> {
|
||||
it('Should handle excluding glob included folders', async function (): Promise<void> {
|
||||
const testFolderPath = await testUtils.generateTestFolderPath(this.test);
|
||||
const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.openSdkStyleSqlProjectBaseline, testFolderPath);
|
||||
await testUtils.createDummyFileStructureWithPrePostDeployScripts(this.test, false, undefined, path.dirname(projFilePath));
|
||||
@@ -362,23 +361,16 @@ describe('Project: sdk style project content operations', function (): void {
|
||||
should(project.noneDeployScripts.length).equal(2);
|
||||
|
||||
// try to exclude a glob included folder
|
||||
//await project.excludeFolder('folder1\\');
|
||||
await project.excludeFolder('folder1');
|
||||
|
||||
// verify folder and contents are excluded
|
||||
should(project.folders.length).equal(1);
|
||||
should(project.files.length).equal(6);
|
||||
should(project.noneDeployScripts.length).equal(1, 'Script.PostDeployment2.sql should have been excluded');
|
||||
should(project.files.find(f => f.relativePath === 'folder1\\')).equal(undefined);
|
||||
|
||||
// verify sqlproj has glob exclude for folder, but not for files and inner folder
|
||||
const projFileText = (await fs.readFile(projFilePath)).toString();
|
||||
should(projFileText.includes('<Build Remove="folder1\\**" />')).equal(true, projFileText);
|
||||
should(projFileText.includes('<Build Remove="folder1\\file1.sql" />')).equal(false, projFileText);
|
||||
should(projFileText.includes('<Build Remove="folder1\\nestedFolder\\**" />')).equal(false, projFileText);
|
||||
should(project.folders.find(f => f.relativePath === 'folder1')).equal(undefined);
|
||||
});
|
||||
|
||||
// skipped because exclude folder not yet supported
|
||||
it.skip('Should handle excluding nested glob included folders', async function (): Promise<void> {
|
||||
it('Should handle excluding folders', async function (): Promise<void> {
|
||||
const testFolderPath = await testUtils.generateTestFolderPath(this.test,);
|
||||
const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.openSdkStyleSqlProjectBaseline, testFolderPath);
|
||||
await testUtils.createDummyFileStructureWithPrePostDeployScripts(this.test, false, undefined, path.dirname(projFilePath));
|
||||
@@ -389,21 +381,16 @@ describe('Project: sdk style project content operations', function (): void {
|
||||
should(project.folders.length).equal(3);
|
||||
|
||||
// try to exclude a glob included folder
|
||||
//await project.excludeFolder('folder1\\nestedFolder\\');
|
||||
await project.excludeFolder('folder1\\nestedFolder');
|
||||
|
||||
// verify folder and contents are excluded
|
||||
should(project.folders.length).equal(2);
|
||||
should(project.files.length).equal(11);
|
||||
should(project.files.find(f => f.relativePath === 'folder1\\nestedFolder\\')).equal(undefined);
|
||||
|
||||
// verify sqlproj has glob exclude for folder, but not for files
|
||||
const projFileText = (await fs.readFile(projFilePath)).toString();
|
||||
should(projFileText.includes('<Build Remove="folder1\\nestedFolder\\**" />')).equal(true, projFileText);
|
||||
should(projFileText.includes('<Build Remove="folder1\\nestedFolder\\otherFile1.sql" />')).equal(false, projFileText);
|
||||
should(project.folders.find(f => f.relativePath === 'folder1\\nestedFolder')).equal(undefined);
|
||||
});
|
||||
|
||||
// skipped because exclude folder not yet supported
|
||||
it.skip('Should handle excluding explicitly included folders', async function (): Promise<void> {
|
||||
it('Should handle excluding explicitly included folders', async function (): Promise<void> {
|
||||
const testFolderPath = await testUtils.generateTestFolderPath(this.test,);
|
||||
const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.openSdkStyleSqlProjectWithFilesSpecifiedBaseline, testFolderPath);
|
||||
await testUtils.createDummyFileStructure(this.test, false, undefined, path.dirname(projFilePath));
|
||||
@@ -412,36 +399,27 @@ describe('Project: sdk style project content operations', function (): void {
|
||||
|
||||
should(project.files.length).equal(11);
|
||||
should(project.folders.length).equal(2);
|
||||
should(project.files.find(f => f.relativePath === 'folder1\\')!).not.equal(undefined);
|
||||
should(project.files.find(f => f.relativePath === 'folder2\\')!).not.equal(undefined);
|
||||
should(project.folders.find(f => f.relativePath === 'folder1')!).not.equal(undefined);
|
||||
should(project.folders.find(f => f.relativePath === 'folder2')!).not.equal(undefined);
|
||||
|
||||
// try to exclude an explicitly included folder without trailing \ in sqlproj
|
||||
//await project.excludeFolder('folder1\\');
|
||||
await project.excludeFolder('folder1');
|
||||
|
||||
// verify folder and contents are excluded
|
||||
should(project.folders.length).equal(1);
|
||||
should(project.files.length).equal(6);
|
||||
should(project.files.find(f => f.relativePath === 'folder1\\')).equal(undefined);
|
||||
should(project.folders.find(f => f.relativePath === 'folder1')).equal(undefined);
|
||||
|
||||
// try to exclude an explicitly included folder with trailing \ in sqlproj
|
||||
//await project.excludeFolder('folder2\\');
|
||||
await project.excludeFolder('folder2');
|
||||
|
||||
// verify folder and contents are excluded
|
||||
should(project.folders.length).equal(0);
|
||||
should(project.files.length).equal(1);
|
||||
should(project.files.find(f => f.relativePath === 'folder2\\')).equal(undefined);
|
||||
|
||||
// make sure both folders are removed from sqlproj and remove entry is added
|
||||
const projFileText = (await fs.readFile(projFilePath)).toString();
|
||||
should(projFileText.includes('<Folder Include="folder1" />')).equal(false, projFileText);
|
||||
should(projFileText.includes('<Folder Include="folder2\\" />')).equal(false, projFileText);
|
||||
|
||||
should(projFileText.includes('<Build Remove="folder1\\**" />')).equal(true, projFileText);
|
||||
should(projFileText.includes('<Build Remove="folder2\\**" />')).equal(true, projFileText);
|
||||
should(project.folders.find(f => f.relativePath === 'folder2')).equal(undefined);
|
||||
});
|
||||
|
||||
// TODO: skipped until fix for folder trailing slashes comes in from DacFx
|
||||
it.skip('Should handle deleting explicitly included folders', async function (): Promise<void> {
|
||||
it('Should handle deleting explicitly included folders', async function (): Promise<void> {
|
||||
const testFolderPath = await testUtils.generateTestFolderPath(this.test,);
|
||||
const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.openSdkStyleSqlProjectWithFilesSpecifiedBaseline, testFolderPath);
|
||||
await testUtils.createDummyFileStructureWithPrePostDeployScripts(this.test, false, undefined, path.dirname(projFilePath));
|
||||
@@ -450,32 +428,24 @@ describe('Project: sdk style project content operations', function (): void {
|
||||
|
||||
should(project.files.length).equal(13);
|
||||
should(project.folders.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);
|
||||
should(project.folders.find(f => f.relativePath === 'folder1')!).not.equal(undefined);
|
||||
should(project.folders.find(f => f.relativePath === 'folder2')!).not.equal(undefined);
|
||||
|
||||
// try to delete an explicitly included folder with the trailing \ in sqlproj
|
||||
await project.deleteFolder('folder2\\');
|
||||
await project.deleteFolder('folder2');
|
||||
|
||||
// verify the project not longer has folder2 and its contents
|
||||
should(project.folders.length).equal(2);
|
||||
should(project.files.length).equal(8);
|
||||
should(project.files.find(f => f.relativePath === 'folder2\\')).equal(undefined);
|
||||
should(project.folders.find(f => f.relativePath === 'folder2')).equal(undefined);
|
||||
|
||||
// try to delete an explicitly included folder without trailing \ in sqlproj
|
||||
await project.deleteFolder('folder1\\');
|
||||
await project.deleteFolder('folder1');
|
||||
|
||||
// verify the project not longer has folder1 and its contents
|
||||
should(project.folders.length).equal(0);
|
||||
should(project.files.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('<Folder Include="folder1" />')).equal(false, projFileText);
|
||||
should(projFileText.includes('<Folder Include="folder2\\" />')).equal(false, projFileText);
|
||||
|
||||
should(projFileText.includes('<Build Remove="folder1\\**" />')).equal(false, projFileText);
|
||||
should(projFileText.includes('<Build Remove="folder2\\**" />')).equal(false, projFileText);
|
||||
should(project.folders.find(f => f.relativePath === 'folder1')).equal(undefined);
|
||||
});
|
||||
|
||||
// TODO: remove once DacFx exposes both absolute and relative outputPath
|
||||
@@ -890,7 +860,7 @@ describe('Project: database references', function (): void {
|
||||
should(project.databaseReferences.length).equal(1, 'There should be one database reference after trying to add a reference to testProject again');
|
||||
});
|
||||
|
||||
it.skip('Should update sqlcmd variable values if value changes', async function (): Promise<void> {
|
||||
it('Should update sqlcmd variable values if value changes', async function (): Promise<void> {
|
||||
const projFilePath = await testUtils.createTestSqlProjFile(this.test, baselines.newProjectFileBaseline);
|
||||
const project = await Project.openProject(projFilePath);
|
||||
const databaseVariable = 'test3Db';
|
||||
@@ -933,12 +903,6 @@ describe('Project: database references', function (): void {
|
||||
should(project.databaseReferences.length).equal(1, 'There should be a database reference after adding a reference to test3');
|
||||
should(project.databaseReferences[0].referenceName).equal('test3', 'The database reference should be test3');
|
||||
should(project.sqlCmdVariables.size).equal(2, 'There should still be 2 sqlcmdvars after adding the dacpac reference again with different sqlcmdvar values');
|
||||
|
||||
projFileText = (await fs.readFile(projFilePath)).toString();
|
||||
should(projFileText).containEql('<SqlCmdVariable Include="test3Db">');
|
||||
should(projFileText).containEql('<DefaultValue>newDbName</DefaultValue>');
|
||||
should(projFileText).containEql('<SqlCmdVariable Include="otherServer">');
|
||||
should(projFileText).containEql('<DefaultValue>newServerName</DefaultValue>');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1140,7 +1104,6 @@ describe('Project: round trip updates', function (): void {
|
||||
await testUpdateInRoundTrip(this.test, baselines.SSDTProjectFileBaseline);
|
||||
});
|
||||
|
||||
// skipped until https://mssqltools.visualstudio.com/SQL%20Tools%20Semester%20Work%20Tracking/_workitems/edit/15749 is fixed
|
||||
it.skip('Should update SSDT project with new system database references', async function (): Promise<void> {
|
||||
await testUpdateInRoundTrip(this.test, baselines.SSDTUpdatedProjectBaseline);
|
||||
});
|
||||
|
||||
@@ -318,7 +318,7 @@ describe('ProjectsController', function (): void {
|
||||
proj = await Project.openProject(proj.projectFilePath); // reload edited sqlproj from disk
|
||||
|
||||
// confirm result
|
||||
should(proj.files.length).equal(2, 'number of file entries'); // LowerFolder and the contained scripts should be deleted
|
||||
should(proj.files.length).equal(0, 'number of file entries'); // LowerFolder and the contained scripts should be excluded
|
||||
should(proj.folders.find(f => f.relativePath === 'UpperFolder')).not.equal(undefined, 'UpperFolder should still be there');
|
||||
should(proj.preDeployScripts.length).equal(0, 'Pre deployment scripts');
|
||||
should(proj.postDeployScripts.length).equal(0, 'Post deployment scripts');
|
||||
@@ -330,6 +330,29 @@ describe('ProjectsController', function (): void {
|
||||
should(await utils.exists(noneEntry.fsUri.fsPath)).equal(true, 'none entry pre-deployment script is supposed to still exist on disk');
|
||||
});
|
||||
|
||||
it('Should exclude a folder', async function (): Promise<void> {
|
||||
let proj = await testUtils.createTestSqlProject(this.test);
|
||||
await proj.addScriptItem('SomeFolder\\MyTable.sql', 'CREATE TABLE [NotARealTable]');
|
||||
|
||||
const projController = new ProjectsController(testContext.outputChannel);
|
||||
const projTreeRoot = new ProjectRootTreeItem(proj);
|
||||
|
||||
should(await utils.exists(path.join(proj.projectFolderPath, 'SomeFolder\\MyTable.sql'))).be.true('File should exist in original location');
|
||||
(proj.files.length).should.equal(1, 'Starting number of files');
|
||||
(proj.folders.length).should.equal(1, 'Starting number of folders');
|
||||
|
||||
// exclude folder
|
||||
const folderNode = projTreeRoot.children.find(f => f.friendlyName === 'SomeFolder');
|
||||
await projController.exclude(createWorkspaceTreeItem(folderNode!));
|
||||
|
||||
// reload project and verify files were renamed
|
||||
proj = await Project.openProject(proj.projectFilePath);
|
||||
|
||||
should(await utils.exists(path.join(proj.projectFolderPath, 'SomeFolder\\MyTable.sql'))).be.true('File should still exist on disk');
|
||||
(proj.files.length).should.equal(0, 'Number of files should not have changed');
|
||||
(proj.folders.length).should.equal(0, 'Number of folders should not have changed');
|
||||
});
|
||||
|
||||
// TODO: move test to DacFx and fix delete
|
||||
it.skip('Should delete folders with excluded items', async function (): Promise<void> {
|
||||
let proj = await testUtils.createTestProject(this.test, templates.newSqlProjectTemplate);
|
||||
@@ -879,7 +902,7 @@ describe('ProjectsController', function (): void {
|
||||
await projController.moveFile(vscode.Uri.file(proj.projectFilePath), sqlcmdVarNode, projectRootWorkspaceTreeItem);
|
||||
|
||||
should(spy.calledOnce).be.true('showErrorMessage should have been called exactly once when trying to move a sqlcmd variable');
|
||||
should(spy.calledWith(constants.onlyMoveSqlFilesSupported)).be.true(`showErrorMessage not called with expected message '${constants.onlyMoveSqlFilesSupported}' Actual '${spy.getCall(0).args[0]}'`);
|
||||
should(spy.calledWith(constants.onlyMoveFilesFoldersSupported)).be.true(`showErrorMessage not called with expected message '${constants.onlyMoveFilesFoldersSupported}' Actual '${spy.getCall(0).args[0]}'`);
|
||||
spy.restore();
|
||||
|
||||
// try moving a database reference
|
||||
@@ -887,7 +910,7 @@ describe('ProjectsController', function (): void {
|
||||
await projController.moveFile(vscode.Uri.file(proj.projectFilePath), dbRefNode, projectRootWorkspaceTreeItem);
|
||||
|
||||
should(spy.calledOnce).be.true('showErrorMessage should have been called exactly once when trying to move a database reference');
|
||||
should(spy.calledWith(constants.onlyMoveSqlFilesSupported)).be.true(`showErrorMessage not called with expected message '${constants.onlyMoveSqlFilesSupported}' Actual '${spy.getCall(0).args[0]}'`);
|
||||
should(spy.calledWith(constants.onlyMoveFilesFoldersSupported)).be.true(`showErrorMessage not called with expected message '${constants.onlyMoveFilesFoldersSupported}' Actual '${spy.getCall(0).args[0]}'`);
|
||||
spy.restore();
|
||||
|
||||
// try moving a folder
|
||||
@@ -895,7 +918,7 @@ describe('ProjectsController', function (): void {
|
||||
await projController.moveFile(vscode.Uri.file(proj.projectFilePath), folderNode, projectRootWorkspaceTreeItem);
|
||||
|
||||
should(spy.calledOnce).be.true('showErrorMessage should have been called exactly once when trying to move a folder');
|
||||
should(spy.calledWith(constants.onlyMoveSqlFilesSupported)).be.true(`showErrorMessage not called with expected message '${constants.onlyMoveSqlFilesSupported}' Actual '${spy.getCall(0).args[0]}'`);
|
||||
should(spy.calledWith(constants.onlyMoveFilesFoldersSupported)).be.true(`showErrorMessage not called with expected message '${constants.onlyMoveFilesFoldersSupported}' Actual '${spy.getCall(0).args[0]}'`);
|
||||
spy.restore();
|
||||
});
|
||||
|
||||
@@ -942,7 +965,7 @@ describe('ProjectsController', function (): void {
|
||||
});
|
||||
|
||||
it('Should rename a sql object file', async function (): Promise<void> {
|
||||
sinon.stub(vscode.window, 'showInputBox').resolves('newName');
|
||||
sinon.stub(vscode.window, 'showInputBox').resolves('newName.sql');
|
||||
let proj = await testUtils.createTestProject(this.test, baselines.openSdkStyleSqlProjectBaseline);
|
||||
const projTreeRoot = await setupMoveTest(proj);
|
||||
const projController = new ProjectsController(testContext.outputChannel);
|
||||
@@ -966,12 +989,12 @@ describe('ProjectsController', function (): void {
|
||||
const projTreeRoot = new ProjectRootTreeItem(proj);
|
||||
|
||||
// try to rename a file from the root folder
|
||||
sinon.stub(vscode.window, 'showInputBox').resolves('predeployNewName');
|
||||
sinon.stub(vscode.window, 'showInputBox').resolves('predeployNewName.sql');
|
||||
const preDeployScriptNode = projTreeRoot.children.find(x => x.friendlyName === 'Script.PreDeployment1.sql');
|
||||
await projController.rename(createWorkspaceTreeItem(preDeployScriptNode!));
|
||||
|
||||
sinon.restore();
|
||||
sinon.stub(vscode.window, 'showInputBox').resolves('postdeployNewName');
|
||||
sinon.stub(vscode.window, 'showInputBox').resolves('postdeployNewName.sql');
|
||||
const postDeployScriptNode = projTreeRoot.children.find(x => x.friendlyName === 'Script.PostDeployment1.sql');
|
||||
await projController.rename(createWorkspaceTreeItem(postDeployScriptNode!));
|
||||
|
||||
@@ -984,8 +1007,31 @@ describe('ProjectsController', function (): void {
|
||||
should(await utils.exists(path.join(proj.projectFolderPath, 'postdeployNewName.sql'))).be.true('The moved post deploy script file should exist');
|
||||
});
|
||||
|
||||
it('Should rename a folder', async function (): Promise<void> {
|
||||
let proj = await testUtils.createTestSqlProject(this.test);
|
||||
await proj.addScriptItem('SomeFolder\\MyTable.sql', 'CREATE TABLE [NotARealTable]');
|
||||
|
||||
// TODO: add test for renaming a file in a folder after fix from DacFx for slashes is brought over
|
||||
const projController = new ProjectsController(testContext.outputChannel);
|
||||
const projTreeRoot = new ProjectRootTreeItem(proj);
|
||||
|
||||
sinon.stub(vscode.window, 'showInputBox').resolves('RenamedFolder');
|
||||
should(await utils.exists(path.join(proj.projectFolderPath, 'SomeFolder\\MyTable.sql'))).be.true('File should exist in original location');
|
||||
(proj.files.length).should.equal(1, 'Starting number of files');
|
||||
(proj.folders.length).should.equal(1, 'Starting number of folders');
|
||||
|
||||
// rename folder
|
||||
const folderNode = projTreeRoot.children.find(f => f.friendlyName === 'SomeFolder');
|
||||
await projController.rename(createWorkspaceTreeItem(folderNode!));
|
||||
|
||||
// reload project and verify files were renamed
|
||||
proj = await Project.openProject(proj.projectFilePath);
|
||||
|
||||
should(await utils.exists(path.join(proj.projectFolderPath, 'RenamedFolder\\MyTable.sql'))).be.true('File should exist in new location');
|
||||
(proj.files.length).should.equal(1, 'Number of files should not have changed');
|
||||
(proj.folders.length).should.equal(1, 'Number of folders should not have changed');
|
||||
should(proj.folders.find(f => f.relativePath === 'RenamedFolder') !== undefined).be.true('The folder path should have been updated');
|
||||
should(proj.files.find(f => f.relativePath === 'RenamedFolder\\MyTable.sql') !== undefined).be.true('Path of the script in the folder should have been updated');
|
||||
});
|
||||
});
|
||||
|
||||
describe('SqlCmd Variables', function (): void {
|
||||
@@ -1000,7 +1046,7 @@ describe('ProjectsController', function (): void {
|
||||
should(project.sqlCmdVariables.size).equal(2, 'The project should start with 2 sqlcmd variables');
|
||||
|
||||
sinon.stub(vscode.window, 'showWarningMessage').returns(<any>Promise.resolve('Cancel'));
|
||||
await projController.delete(createWorkspaceTreeItem(projRoot.children.find(x => x.friendlyName === constants.sqlcmdVariablesNodeName)!.children[0]));
|
||||
await projController.delete(createWorkspaceTreeItem(projRoot.children.find(x => x.friendlyName === constants.sqlcmdVariablesNodeName)!.children[0] /* LowerFolder */));
|
||||
|
||||
// reload project
|
||||
project = await Project.openProject(project.projectFilePath);
|
||||
|
||||
Reference in New Issue
Block a user