From c9288ffcdd6282150afb6ae70a0ea842d70529d3 Mon Sep 17 00:00:00 2001 From: mshanemc Date: Mon, 18 Sep 2023 13:02:45 -0500 Subject: [PATCH] test: more coverage and cases for typeguard and SC --- src/utils/previewOutput.ts | 4 +-- src/utils/types.ts | 17 ++++++++----- test/utils/types.test.ts | 52 ++++++++++++++++++++++++++++++-------- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/utils/previewOutput.ts b/src/utils/previewOutput.ts index 62e088cc..158957e0 100644 --- a/src/utils/previewOutput.ts +++ b/src/utils/previewOutput.ts @@ -21,7 +21,7 @@ import { import { filePathsFromMetadataComponent } from '@salesforce/source-deploy-retrieve/lib/src/utils/filePathGenerator'; import { SourceTracking } from '@salesforce/source-tracking'; -import { isSourceComponent } from './types'; +import { isSourceComponentWithXml } from './types'; Messages.importMessagesDirectory(__dirname); const messages = Messages.loadMessages('@salesforce/plugin-deploy-retrieve', 'previewMessages'); @@ -64,7 +64,7 @@ const resolvePaths = (filenames: string[]): Array ({ fullName: sc.fullName, type: sc.type.name, path: ensureAbsolutePath(sc.xml) })); // dedupe by xml path return Array.from(new Map(sourceComponents.map((sc) => [sc.path, sc])).values()); diff --git a/src/utils/types.ts b/src/utils/types.ts index 30dc13b9..0e316ad6 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -101,14 +101,19 @@ export type Formatter = { }; /** validates source component with fullname, type, and xml props */ -export const isSourceComponent = (sc: unknown): sc is SourceComponent & { xml: string } => +export const isSourceComponent = (sc: unknown): sc is SourceComponent => isObject(sc) && - 'fullName' in sc && 'type' in sc && - 'xml' in sc && - typeof sc.fullName === 'string' && - typeof sc.type === 'string' && - typeof sc.xml === 'string'; + typeof sc.type === 'object' && + sc.type !== null && + 'name' in sc.type && + typeof sc.type.name === 'string' && + 'fullName' in sc && + // (typeof sc.fullName === 'string' || typeof sc.fullName === 'function'); + typeof sc.fullName === 'string'; + +export const isSourceComponentWithXml = (sc: unknown): sc is SourceComponent & { xml: string } => + isSourceComponent(sc) && 'xml' in sc && typeof sc.xml === 'string'; export const isSdrFailure = (fileResponse: FileResponse): fileResponse is FileResponseFailure => fileResponse.state === ComponentStatus.Failed; diff --git a/test/utils/types.test.ts b/test/utils/types.test.ts index 244ab6e2..19fa572f 100644 --- a/test/utils/types.test.ts +++ b/test/utils/types.test.ts @@ -5,19 +5,28 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import { expect } from 'chai'; +import { expect, config } from 'chai'; import { SourceComponent, RegistryAccess } from '@salesforce/source-deploy-retrieve'; -import { isSourceComponent } from '../../src/utils/types'; +import { isSourceComponent, isSourceComponentWithXml } from '../../src/utils/types'; + +config.truncateThreshold = 0; + +const reg = new RegistryAccess(); +const type = reg.getTypeByName('ApexClass'); describe('isSourceComponent (type guard)', () => { describe('good', () => { it('full, correct definition', () => { - expect({ fullName: 'foo', type: 'fooType', xml: 'fooXml', content: 'fooContent' }).to.satisfy(isSourceComponent); + expect({ fullName: 'foo', type, xml: 'fooXml', content: 'fooContent' }).to.satisfy(isSourceComponent); }); it('SC constructed with xml', () => { - const reg = new RegistryAccess(); - const type = reg.getTypeByName('ApexClass'); - expect(new SourceComponent({ name: 'foo', type, xml: 'classes/foo.cls' })).to.not.satisfy(isSourceComponent); + expect(new SourceComponent({ name: 'foo', type, xml: 'classes/foo.cls' })).to.satisfy(isSourceComponent); + }); + it('SC constructed with no xml', () => { + const sc = new SourceComponent({ name: 'foo', type }); + // console.log(sc); + // console.log(typeof sc.fullName); + expect(sc).to.satisfy(isSourceComponent); }); }); describe('bad', () => { @@ -27,16 +36,37 @@ describe('isSourceComponent (type guard)', () => { it('empty object', () => { expect({}).to.not.satisfy(isSourceComponent); }); + + it('object.type is set to undefined', () => { + expect({ fullName: 'foo', type: undefined, xml: 'fooXml' }).to.not.satisfy(isSourceComponent); + }); + }); +}); + +describe('isSourceComponentWithXml (type guard)', () => { + describe('good', () => { + it('full, correct definition', () => { + expect({ fullName: 'foo', type, xml: 'fooXml', content: 'fooContent' }).to.satisfy(isSourceComponentWithXml); + }); + it('SC constructed with xml', () => { + expect(new SourceComponent({ name: 'foo', type, xml: 'classes/foo.cls' })).to.satisfy(isSourceComponentWithXml); + }); + }); + describe('bad', () => { + it('object is undefined', () => { + expect(undefined).to.not.satisfy(isSourceComponentWithXml); + }); + it('empty object', () => { + expect({}).to.not.satisfy(isSourceComponentWithXml); + }); it('object.xml is undefined', () => { - expect({ fullName: 'foo', type: 'fooType', content: 'fooContent' }).to.not.satisfy(isSourceComponent); + expect({ fullName: 'foo', type: 'fooType', content: 'fooContent' }).to.not.satisfy(isSourceComponentWithXml); }); it('object.type is set to undefined', () => { - expect({ fullName: 'foo', type: undefined, xml: 'fooXml' }).to.not.satisfy(isSourceComponent); + expect({ fullName: 'foo', type: undefined, xml: 'fooXml' }).to.not.satisfy(isSourceComponentWithXml); }); it('SC constructed with no xml', () => { - const reg = new RegistryAccess(); - const type = reg.getTypeByName('ApexClass'); - expect(new SourceComponent({ name: 'foo', type })).to.not.satisfy(isSourceComponent); + expect(new SourceComponent({ name: 'foo', type })).to.not.satisfy(isSourceComponentWithXml); }); }); });