Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions messages/assess.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
"operationCancelled": "Operation cancelled.",
"invalidYesNoResponse": "Invalid response. Please answer y or n.",
"notSfdxProjectFolderPath": "Provided folder is not a valid Salesforce DX project. Please select a folder containing sfdx-project.json",
"restrictedFolderName": "Restricted folder name: %s. Do not use 'labels', 'messageChannels', or 'lwc'. Try again with a different name.",
"errorRunningAssess": "Assessment process failed reason : %s",
"enableVerboseOutput": "Enable verbose output",
"apexFileChangesIdentifiedNotApplied": "Changes were identified but not applied for the %s Apex class when the assessment mode was running.",
Expand Down
1 change: 1 addition & 0 deletions messages/migrate.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"notEmptyProjectFolderPath": "Provided project folder is not empty. Please provide a valid empty project folder name and path",
"invalidYesNoResponse": "Invalid response. Please answer y or n.",
"notSfdxProjectFolderPath": "Provided folder is not a valid Salesforce DX project. Please select a folder containing sfdx-project.json",
"restrictedFolderName": "Restricted folder name: %s. Do not use 'labels', 'messageChannels', or 'lwc'. Try again with a different name.",
"operationCancelled": "Operation cancelled.",
"errorRunningMigrate": "Migration process failed reason : %s",
"exceptionSettingDesignersToStandardDataModel": "We've encountered an exception while configuring the Omnistudio standard designers to use the standard data model. Try again later.",
Expand Down
8 changes: 8 additions & 0 deletions src/utils/projectPathUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ export class ProjectPathUtil {
return false;
}

// Check if folder path ends with restricted names (case insensitive)
const restrictedFolderNames = ['labels', 'messagechannels', 'lwc'];
const folderName = path.basename(folderPath);
if (restrictedFolderNames.includes(folderName.toLowerCase())) {
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
return false;
}

if (mode === EMPTY_MODE && fs.readdirSync(folderPath).length > 0) {
Logger.error(messages.getMessage('notEmptyProjectFolderPath'));
return false;
Expand Down
184 changes: 184 additions & 0 deletions test/utils/projectPathUtil.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import * as fs from 'fs';
import * as path from 'path';
import { expect } from 'chai';
import * as sinon from 'sinon';
import { Messages } from '@salesforce/core';
import { Logger } from '../../src/utils/logger';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-omnistudio-migration-tool', 'assess');

describe('ProjectPathUtil - Restricted Folder Name Validation', () => {
let loggerErrorStub: sinon.SinonStub;
let fsExistsSyncStub: sinon.SinonStub;
let fsLstatSyncStub: sinon.SinonStub;

beforeEach(() => {
loggerErrorStub = sinon.stub(Logger, 'error');
fsExistsSyncStub = sinon.stub(fs, 'existsSync');
fsLstatSyncStub = sinon.stub(fs, 'lstatSync');
});

afterEach(() => {
loggerErrorStub.restore();
fsExistsSyncStub.restore();
fsLstatSyncStub.restore();
});

/**
* Helper function that simulates the isValidFolderPath restricted name check
*/
function isRestrictedFolderName(folderPath: string): boolean {
const restrictedFolderNames = ['labels', 'messagechannels', 'lwc'];
const folderName = path.basename(folderPath);
return restrictedFolderNames.includes(folderName.toLowerCase());
}

describe('Restricted folder names', () => {
it('should reject folder path ending with "lwc"', () => {
const folderPath = '/some/path/lwc';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.calledOnce).to.be.true;
expect(loggerErrorStub.firstCall.args[0]).to.include('Restricted folder name: lwc');
expect(loggerErrorStub.firstCall.args[0]).to.include('Try again with a different name');
});

it('should reject folder path ending with "labels"', () => {
const folderPath = '/some/path/labels';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.calledOnce).to.be.true;
expect(loggerErrorStub.firstCall.args[0]).to.include('Restricted folder name: labels');
expect(loggerErrorStub.firstCall.args[0]).to.include('Try again with a different name');
});

it('should reject folder path ending with "messageChannels"', () => {
const folderPath = '/some/path/messageChannels';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.calledOnce).to.be.true;
expect(loggerErrorStub.firstCall.args[0]).to.include('Restricted folder name: messageChannels');
expect(loggerErrorStub.firstCall.args[0]).to.include('Try again with a different name');
});

it('should reject folder path with uppercase "LWC" (case insensitive)', () => {
const folderPath = '/some/path/LWC';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.calledOnce).to.be.true;
expect(loggerErrorStub.firstCall.args[0]).to.include('Restricted folder name: LWC');
expect(loggerErrorStub.firstCall.args[0]).to.include('Try again with a different name');
});

it('should reject folder path with mixed case "Labels" (case insensitive)', () => {
const folderPath = '/some/path/Labels';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.calledOnce).to.be.true;
expect(loggerErrorStub.firstCall.args[0]).to.include('Restricted folder name: Labels');
expect(loggerErrorStub.firstCall.args[0]).to.include('Try again with a different name');
});

it('should reject folder path with uppercase "MESSAGECHANNELS" (case insensitive)', () => {
const folderPath = '/some/path/MESSAGECHANNELS';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.calledOnce).to.be.true;
expect(loggerErrorStub.firstCall.args[0]).to.include('Restricted folder name: MESSAGECHANNELS');
expect(loggerErrorStub.firstCall.args[0]).to.include('Try again with a different name');
});
});

describe('Valid folder names', () => {
it('should allow folder path ending with "myproject"', () => {
const folderPath = '/some/path/myproject';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.called).to.be.false;
});

it('should allow folder path ending with "src"', () => {
const folderPath = '/some/path/src';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.called).to.be.false;
});

it('should allow folder path containing "lwc" but not ending with it', () => {
const folderPath = '/some/lwc/myproject';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.called).to.be.false;
});

it('should allow folder path containing "labels" but not ending with it', () => {
const folderPath = '/some/labels/customfolder';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.called).to.be.false;
});

it('should allow folder name that contains restricted name as substring', () => {
const folderPath = '/some/path/mylwcproject';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.called).to.be.false;
});

it('should allow folder name "labelsbackup"', () => {
const folderPath = '/some/path/labelsbackup';

if (isRestrictedFolderName(folderPath)) {
const folderName = path.basename(folderPath);
Logger.error(messages.getMessage('restrictedFolderName', [folderName]));
}

expect(loggerErrorStub.called).to.be.false;
});
});
});