Skip to content

Commit

Permalink
feat: support split custom labels (#62)
Browse files Browse the repository at this point in the history
* feat: support split custom labels

* test: fix tests
  • Loading branch information
mdonnalley authored Apr 13, 2021
1 parent 782a099 commit ab290a7
Show file tree
Hide file tree
Showing 11 changed files with 388 additions and 60 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@oclif/config": "^1",
"@salesforce/command": "^3.1.0",
"@salesforce/core": "^2.20.8",
"@salesforce/source-deploy-retrieve": "1.1.21",
"@salesforce/source-deploy-retrieve": "^2",
"chalk": "^4.1.0",
"cli-ux": "^5.5.1",
"tslib": "^2"
Expand Down Expand Up @@ -120,7 +120,7 @@
"test": "sf-test",
"test:command-reference": "./bin/run commandreference:generate --erroronwarnings",
"test:deprecation-policy": "./bin/run snapshot:compare",
"test:nuts": "ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 1",
"test:nuts": "ts-node ./test/nuts/generateNuts.ts && nyc mocha \"**/*.nut.ts\" --slow 3000 --timeout 600000 --parallel --retries 0",
"version": "oclif-dev readme"
},
"husky": {
Expand Down
13 changes: 5 additions & 8 deletions src/commands/force/source/retrieve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { Lifecycle, Messages, SfdxError } from '@salesforce/core';
import { Duration } from '@salesforce/kit';
import { asArray, asString } from '@salesforce/ts-types';
import { blue } from 'chalk';
import { MetadataApiRetrieveStatus, RetrieveResult } from '@salesforce/source-deploy-retrieve';
import { FileProperties } from '@salesforce/source-deploy-retrieve/lib/src/client/types';
import { MetadataApiRetrieveStatus } from '@salesforce/source-deploy-retrieve';
import { FileProperties, FileResponse } from '@salesforce/source-deploy-retrieve/lib/src/client/types';
import { SourceCommand } from '../../../sourceCommand';

Messages.importMessagesDirectory(__dirname);
Expand Down Expand Up @@ -52,8 +52,7 @@ export class Retrieve extends SourceCommand {
};
protected readonly lifecycleEventNames = ['preretrieve', 'postretrieve'];

// eslint-disable-next-line @typescript-eslint/no-explicit-any
public async run(): Promise<RetrieveResult> {
public async run(): Promise<FileResponse[]> {
const hookEmitter = Lifecycle.getInstance();

const defaultPackagePath = this.project.getDefaultPackage().fullPath;
Expand Down Expand Up @@ -85,13 +84,11 @@ export class Retrieve extends SourceCommand {
throw new SfdxError(messages.getMessage('retrieveTimeout', [(this.flags.wait as Duration).minutes]));
}

if (this.flags.json) {
this.ux.logJson(mdapiResult.getFileResponses());
} else {
if (!this.flags.json) {
this.printTable(results, true);
}

return mdapiResult;
return mdapiResult.getFileResponses();
}

/**
Expand Down
21 changes: 13 additions & 8 deletions src/sourceCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as path from 'path';
import { SfdxCommand } from '@salesforce/command';
import { ComponentSet } from '@salesforce/source-deploy-retrieve';
import { fs, SfdxError, Logger, ConfigFile } from '@salesforce/core';
import { ComponentLike } from '@salesforce/source-deploy-retrieve/lib/src/common';
import { ComponentLike } from '@salesforce/source-deploy-retrieve/lib/src/resolve';

export type FlagOptions = {
packagenames?: string[];
Expand Down Expand Up @@ -59,11 +59,12 @@ export abstract class SourceCommand extends SfdxCommand {
if (options.manifest) {
logger.debug(`Building ComponentSet from manifest: ${options.manifest}`);
const packageDirs = this.project.getUniquePackageDirectories().map((pDir) => pDir.fullPath);
for (const packageDir of packageDirs) {
logger.debug(`Searching in packageDir: ${packageDir} for matching metadata`);
const compSet = await ComponentSet.fromManifestFile(options.manifest, { resolve: packageDir });
setAggregator.push(...compSet);
}
logger.debug(`Searching in packageDir: ${packageDirs.join(', ')} for matching metadata`);
const compSet = await ComponentSet.fromManifest({
manifestPath: options.manifest,
resolveSourcePaths: packageDirs,
});
setAggregator.push(...compSet);
}

// Resolve metadata entries with source in package directories.
Expand All @@ -82,8 +83,12 @@ export abstract class SourceCommand extends SfdxCommand {

// Search the packages directories for matching metadata
const packageDirs = this.project.getUniquePackageDirectories().map((pDir) => pDir.fullPath);
logger.debug(`Searching for matching metadata in packageDirs: ${packageDirs.toString()}`);
setAggregator.push(...ComponentSet.fromSource({ inclusiveFilter: filter, fsPaths: packageDirs }));
logger.debug(`Searching for matching metadata in packageDirs: ${packageDirs.join(', ')}`);

const fromSource = ComponentSet.fromSource({ fsPaths: packageDirs, include: filter });
// If no matching metadata is found, default to the original component set
const finalized = fromSource.size > 0 ? fromSource : filter;
setAggregator.push(...finalized);
}

const componentSet = new ComponentSet(setAggregator);
Expand Down
10 changes: 5 additions & 5 deletions test/commands/source/retrieve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ describe('force:source:retrieve', () => {
it('should pass along sourcepath', async () => {
const sourcepath = ['somepath'];
const result = await run({ sourcepath, json: true });
expect(result).to.deep.equal(stubbedResults);
expect(result).to.deep.equal(stubbedResults.getFileResponses());
ensureCreateComponentSetArgs({ sourcepath });
ensureRetrieveArgs();
ensureHookArgs();
Expand All @@ -122,7 +122,7 @@ describe('force:source:retrieve', () => {
it('should pass along metadata', async () => {
const metadata = ['ApexClass:MyClass'];
const result = await run({ metadata, json: true });
expect(result).to.deep.equal(stubbedResults);
expect(result).to.deep.equal(stubbedResults.getFileResponses());
ensureCreateComponentSetArgs({ metadata });
ensureRetrieveArgs();
ensureHookArgs();
Expand All @@ -131,7 +131,7 @@ describe('force:source:retrieve', () => {
it('should pass along manifest', async () => {
const manifest = 'package.xml';
const result = await run({ manifest, json: true });
expect(result).to.deep.equal(stubbedResults);
expect(result).to.deep.equal(stubbedResults.getFileResponses());
ensureCreateComponentSetArgs({ manifest });
ensureRetrieveArgs();
ensureHookArgs();
Expand All @@ -141,7 +141,7 @@ describe('force:source:retrieve', () => {
const manifest = 'package.xml';
const apiversion = '50.0';
const result = await run({ manifest, apiversion, json: true });
expect(result).to.deep.equal(stubbedResults);
expect(result).to.deep.equal(stubbedResults.getFileResponses());
ensureCreateComponentSetArgs({ apiversion, manifest });
ensureRetrieveArgs();
ensureHookArgs();
Expand All @@ -151,7 +151,7 @@ describe('force:source:retrieve', () => {
const manifest = 'package.xml';
const packagenames = ['package1'];
const result = await run({ manifest, packagenames, json: true });
expect(result).to.deep.equal(stubbedResults);
expect(result).to.deep.equal(stubbedResults.getFileResponses());
ensureCreateComponentSetArgs({ manifest, packagenames });
ensureRetrieveArgs({ packageNames: packagenames });
ensureHookArgs();
Expand Down
30 changes: 15 additions & 15 deletions test/commands/source/sourceCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('SourceCommand', () => {
beforeEach(() => {
fileExistsSyncStub = stubMethod(sandbox, fsCore, 'fileExistsSync');
fromSourceStub = stubMethod(sandbox, ComponentSet, 'fromSource');
fromManifestStub = stubMethod(sandbox, ComponentSet, 'fromManifestFile');
fromManifestStub = stubMethod(sandbox, ComponentSet, 'fromManifest');
getUniquePackageDirectoriesStub = stubMethod(sandbox, SfdxProject.prototype, 'getUniquePackageDirectories');
componentSet = new ComponentSet();
});
Expand Down Expand Up @@ -173,7 +173,7 @@ describe('SourceCommand', () => {
expect(fromSourceArgs).to.have.deep.property('fsPaths', [packageDir1]);
const filter = new ComponentSet();
filter.add({ type: 'ApexClass', fullName: '*' });
expect(fromSourceArgs).to.have.deep.property('inclusiveFilter', filter);
expect(fromSourceArgs).to.have.deep.property('include', filter);
expect(compSet.size).to.equal(1);
expect(compSet.has(apexClassComponent)).to.equal(true);
});
Expand All @@ -195,7 +195,7 @@ describe('SourceCommand', () => {
expect(fromSourceArgs).to.have.deep.property('fsPaths', [packageDir1]);
const filter = new ComponentSet();
filter.add({ type: 'ApexClass', fullName: 'MyClass' });
expect(fromSourceArgs).to.have.deep.property('inclusiveFilter', filter);
expect(fromSourceArgs).to.have.deep.property('include', filter);
expect(compSet.size).to.equal(1);
expect(compSet.has(apexClassComponent)).to.equal(true);
});
Expand All @@ -219,7 +219,7 @@ describe('SourceCommand', () => {
const filter = new ComponentSet();
filter.add({ type: 'ApexClass', fullName: 'MyClass' });
filter.add({ type: 'CustomObject', fullName: '*' });
expect(fromSourceArgs).to.have.deep.property('inclusiveFilter', filter);
expect(fromSourceArgs).to.have.deep.property('include', filter);
expect(compSet.size).to.equal(2);
expect(compSet.has(apexClassComponent)).to.equal(true);
expect(compSet.has(customObjectComponent)).to.equal(true);
Expand All @@ -245,7 +245,7 @@ describe('SourceCommand', () => {
expect(fromSourceArgs).to.have.deep.property('fsPaths', [packageDir1, packageDir2]);
const filter = new ComponentSet();
filter.add({ type: 'ApexClass', fullName: '*' });
expect(fromSourceArgs).to.have.deep.property('inclusiveFilter', filter);
expect(fromSourceArgs).to.have.deep.property('include', filter);
expect(compSet.size).to.equal(2);
expect(compSet.has(apexClassComponent)).to.equal(true);
expect(compSet.has(apexClassComponent2)).to.equal(true);
Expand All @@ -264,19 +264,19 @@ describe('SourceCommand', () => {

const compSet = await command.callCreateComponentSet(options);
expect(fromManifestStub.calledOnce).to.equal(true);
expect(fromManifestStub.firstCall.args[0]).to.equal(options.manifest);
expect(fromManifestStub.firstCall.args[1]).to.deep.equal({ resolve: packageDir1 });
expect(fromManifestStub.firstCall.args[0]).to.deep.equal({
manifestPath: options.manifest,
resolveSourcePaths: [packageDir1],
});
expect(compSet.size).to.equal(1);
expect(compSet.has(apexClassComponent)).to.equal(true);
});

it('should create ComponentSet from manifest and multiple package', async () => {
componentSet.add(apexClassComponent);
const componentSet2 = new ComponentSet();
const apexClassComponent2 = { type: 'ApexClass', fullName: 'MyClass2' };
componentSet2.add(apexClassComponent2);
componentSet.add(apexClassComponent2);
fromManifestStub.onFirstCall().resolves(componentSet);
fromManifestStub.onSecondCall().resolves(componentSet2);
const packageDir1 = path.resolve('force-app');
const packageDir2 = path.resolve('my-app');
getUniquePackageDirectoriesStub.returns([{ fullPath: packageDir1 }, { fullPath: packageDir2 }]);
Expand All @@ -287,11 +287,11 @@ describe('SourceCommand', () => {
};

const compSet = await command.callCreateComponentSet(options);
expect(fromManifestStub.callCount).to.equal(2);
expect(fromManifestStub.firstCall.args[0]).to.equal(options.manifest);
expect(fromManifestStub.firstCall.args[1]).to.deep.equal({ resolve: packageDir1 });
expect(fromManifestStub.secondCall.args[0]).to.equal(options.manifest);
expect(fromManifestStub.secondCall.args[1]).to.deep.equal({ resolve: packageDir2 });
expect(fromManifestStub.callCount).to.equal(1);
expect(fromManifestStub.firstCall.args[0]).to.deep.equal({
manifestPath: options.manifest,
resolveSourcePaths: [packageDir1, packageDir2],
});
expect(compSet.size).to.equal(2);
expect(compSet.has(apexClassComponent)).to.equal(true);
expect(compSet.has(apexClassComponent2)).to.equal(true);
Expand Down
36 changes: 35 additions & 1 deletion test/nuts/assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ export class Assertions {
expect(fileHistory.changedFromPrevious, 'File to be changed').to.be.true;
}

/**
* Expect given file to NOT be changed according to the file history provided by FileTracker
*/
public fileToNotBeChanged(file: string): void {
const fileHistory = this.fileTracker.getLatest(file);
expect(fileHistory.changedFromPrevious, 'File to NOT be changed').to.be.false;
}

/**
* Expect all files found by globs to be changed according to the file history provided by FileTracker
*/
Expand All @@ -61,6 +69,19 @@ export class Assertions {
expect(allChanged, 'all files to be changed').to.be.true;
}

/**
* Expect all files found by globs to NOT be changed according to the file history provided by FileTracker
*/
public async filesToNotBeChanged(globs: string[]): Promise<void> {
const files = await this.doGlob(globs);
const fileHistories = files
.filter((f) => !f.endsWith('.resource-meta.xml'))
.map((f) => this.fileTracker.getLatest(f))
.filter((f) => !!f);
const allChanged = fileHistories.every((f) => f.changedFromPrevious);
expect(allChanged, 'all files to NOT be changed').to.be.false;
}

/**
* Finds all files in project based on the provided globs and expects them to be updated on the server
*/
Expand Down Expand Up @@ -122,7 +143,7 @@ export class Assertions {
}

/**
* Expects given globs to return files
* Expects files found by glob to not contain any of the provided strings
*/
public async filesToNotContainString(glob: string, ...strings: string[]): Promise<void> {
const files = await this.doGlob([glob]);
Expand All @@ -134,6 +155,19 @@ export class Assertions {
}
}

/**
* Expects files found by glob to contain the provided strings
*/
public async filesToContainString(glob: string, ...strings: string[]): Promise<void> {
const files = await this.doGlob([glob]);
for (const file of files) {
const contents = await fs.readFile(file, 'UTF-8');
for (const str of strings) {
expect(contents, `expect ${file} to not include ${str}`).to.include(str);
}
}
}

/**
* Expect the retrieved package to exist and contain some files
*/
Expand Down
3 changes: 2 additions & 1 deletion test/nuts/executionLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ export class ExecutionLog {
* Return the most recent entry for a command
*/
public getLatest(cmd: string): ExecutionLog.Details {
return this.log.get(cmd).reverse()[0];
const sorted = this.log.get(cmd).sort((a, b) => (a.timestamp < b.timestamp ? 1 : -1));
return sorted[0];
}

private async querySourceMembers(): Promise<SourceMember[]> {
Expand Down
47 changes: 46 additions & 1 deletion test/nuts/nutshell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import * as fg from 'fast-glob';
import { exec } from 'shelljs';
import { TestSession, execCmd } from '@salesforce/cli-plugins-testkit';
import { Env } from '@salesforce/kit';
import { AnyJson, ensureString, JsonMap, Nullable } from '@salesforce/ts-types';
import { AnyJson, Dictionary, ensureString, JsonMap, Nullable } from '@salesforce/ts-types';
import { AuthInfo, Connection, fs, NamedPackageDir, SfdxProject } from '@salesforce/core';
import { AsyncCreatable } from '@salesforce/kit';
import { debug, Debugger } from 'debug';
Expand Down Expand Up @@ -176,6 +176,18 @@ export class Nutshell extends AsyncCreatable<Nutshell.Options> {
}
}

/**
* Read files found by globs
*/
public async readGlobs(globs: string[]): Promise<Dictionary<string>> {
const files = await this.doGlob(globs);
const returnValue = {};
for (const file of files) {
returnValue[file] = await fs.readFile(file, 'UTF-8');
}
return returnValue;
}

/**
* Read the org's sourcePathInfos.json
*/
Expand Down Expand Up @@ -206,6 +218,28 @@ export class Nutshell extends AsyncCreatable<Nutshell.Options> {
return fs.writeJson(maxRevisionPath, contents);
}

/**
* Write file
*/
public async writeFile(filename: string, contents: string): Promise<void> {
return fs.writeFile(filename, contents);
}

/**
* Create a package.xml
*/
public async createPackageXml(xml: string): Promise<string> {
const packageXml = `<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
${xml}
<version>51.0</version>
</Package>
`;
const packageXmlPath = path.join(this.session.project.dir, 'package.xml');
await fs.writeFile(packageXmlPath, packageXml);
return packageXmlPath;
}

/**
* Delete the org's sourcePathInfos.json
*/
Expand All @@ -228,6 +262,16 @@ export class Nutshell extends AsyncCreatable<Nutshell.Options> {
return fs.unlink(maxRevisionPath);
}

/**
* Delete the files found by the given globs
*/
public async deleteGlobs(globs: string[]): Promise<void> {
const files = await this.doGlob(globs);
for (const file of files) {
await fs.unlink(file);
}
}

/**
* Delete all source files in the project directory
*/
Expand Down Expand Up @@ -405,6 +449,7 @@ export class Nutshell extends AsyncCreatable<Nutshell.Options> {
: [
// TODO: remove this config:set call
'sfdx config:set apiVersion=50.0 --global',
'sfdx config:set restDeploy=false --global',
'sfdx force:org:create -d 1 -s -f config/project-scratch-def.json',
];
return await TestSession.create({
Expand Down
Loading

0 comments on commit ab290a7

Please sign in to comment.