@ds-sfdc/sfparty 1.2.6 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/cicd.yaml +38 -0
- package/babel.config.cjs +3 -0
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/applicationVisibilities.yaml +2 -2
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/fieldPermissions/Case.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/main.yaml +2 -2
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AIRecordInsight.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Account.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AppointmentInvitation.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AppointmentTopicTimeSlot.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Asset.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationForm.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormConsent.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormDataUse.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormText.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/BusinessBrand.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Campaign.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscription.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionChannelType.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionConsent.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionTiming.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Contact.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointAddress.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointConsent.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointEmail.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointPhone.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointTypeConsent.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactRequest.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContractLineItem.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Customer.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DandBCompany.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DataUseLegalBasis.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DataUsePurpose.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Document.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/EngagementChannelType.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Entitlement.yaml +2 -2
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/EntitlementContact.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Individual.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Lead.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Macro.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/OperatingHours.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Opportunity.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/PartyConsent.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Promotion.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/QuickText.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Seller.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ServiceContract.yaml +2 -2
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ServiceTerritory.yaml +2 -2
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Shift.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/SocialPersona.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Solution.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/StreamingChannel.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WebCart.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WebStore.yaml +1 -1
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkPlan.yaml +3 -3
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkPlanTemplate.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkStepTemplate.yaml +4 -4
- package/force-app-party/main/default/profiles/Customer Portal Manager Standard/tabVisibilities.yaml +29 -29
- package/force-app-party/main/default/profiles/External Identity User/applicationVisibilities.yaml +1 -1
- package/force-app-party/main/default/profiles/External Identity User/main.yaml +2 -2
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Account.yaml +2 -2
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Asset.yaml +3 -3
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/BusinessBrand.yaml +2 -2
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Contact.yaml +3 -3
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/ContactPointEmail.yaml +3 -3
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/ContactPointPhone.yaml +3 -3
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Customer.yaml +2 -2
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Document.yaml +1 -1
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Individual.yaml +3 -3
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/PushTopic.yaml +4 -4
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Seller.yaml +2 -2
- package/force-app-party/main/default/profiles/External Identity User/tabVisibilities.yaml +30 -30
- package/package.json +7 -5
- package/src/index.js +7 -45
- package/src/lib/checkVersion.js +42 -0
- package/src/lib/fileUtils.js +38 -38
- package/src/lib/gitUtils.js +87 -140
- package/test/lib/git/diff.spec.js +54 -0
- package/test/lib/git/lastCommit.spec.js +69 -0
- package/test/lib/git/log.spec.js +33 -0
- package/test/lib/git/updateLastCommit.spec.js +65 -0
- package/test/root.spec.js +7 -0
- package/test_mocha/lib/fileUtils.js +374 -0
- package/test_mocha/lib/git.js +82 -0
- package/test_mocha/lib/gitUtils.js +54 -0
- package/{tests/lib/versionCheck.spec.js → test_mocha/lib/versionCheck.js} +0 -0
- package/{tests/root.spec.js → test_mocha/root.js} +0 -0
- package/force-app-party/main/default/labels/CustomLabels/labels/Description.yaml +0 -7
- package/force-app-party/main/default/profiles/External Identity User/objectPermissions/SocialPersona.yaml +0 -8
- package/tests/lib/gitUtils.spec.js +0 -137
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { execSync } from 'child_process'
|
|
3
|
+
import * as fileUtils from '../../../src/lib/fileUtils.js'
|
|
4
|
+
import { lastCommit } from '../../../src/lib/gitUtils.js'
|
|
5
|
+
import { existsSync } from 'fs'
|
|
6
|
+
|
|
7
|
+
const dir = '/test/sfparty'
|
|
8
|
+
const fileName = 'index.yaml'
|
|
9
|
+
const folder = path.resolve(dir, '.sfdx', 'sfparty')
|
|
10
|
+
const filePath = path.resolve(folder, fileName)
|
|
11
|
+
|
|
12
|
+
jest.mock('fs', () => ({
|
|
13
|
+
existsSync: jest.fn()
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
jest.mock('../../../src/lib/fileUtils.js', () => {
|
|
17
|
+
return {
|
|
18
|
+
createDirectory: jest.fn(),
|
|
19
|
+
readFile: jest.fn()
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
jest.mock('child_process', () => {
|
|
24
|
+
return {
|
|
25
|
+
execSync: jest.fn()
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
jest.resetAllMocks()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should return the last commit and latest commit', () => {
|
|
34
|
+
const lastCommitHash = '16d69f0cf3d902a900a0609177fe5cf0fda9a961'
|
|
35
|
+
const latestCommitHash = '16d69f0cf3d902a900a0609177fe5cf0fda9a961'
|
|
36
|
+
fileUtils.createDirectory.mockReturnValue(undefined)
|
|
37
|
+
fileUtils.readFile.mockReturnValue({ git: { lastCommit: lastCommitHash } })
|
|
38
|
+
execSync.mockReturnValue(latestCommitHash)
|
|
39
|
+
existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
|
|
40
|
+
const result = lastCommit(dir, fileName, existsSync, execSync, fileUtils)
|
|
41
|
+
expect(fileUtils.createDirectory).toHaveBeenCalledWith(folder)
|
|
42
|
+
expect(fileUtils.readFile).toHaveBeenCalledWith(filePath)
|
|
43
|
+
expect(execSync).toHaveBeenCalledWith(`git log --format=format:%H -1`, { cwd: dir, encoding: 'utf-8' })
|
|
44
|
+
expect(result).toEqual({lastCommit: lastCommitHash, latestCommit: latestCommitHash })
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should return the latest commit if no lastCommit is found', () => {
|
|
48
|
+
const latestCommit = '16d69f0cf3d902a900a0609177fe5cf0fda9a961'
|
|
49
|
+
fileUtils.createDirectory.mockReturnValue(undefined)
|
|
50
|
+
fileUtils.readFile.mockReturnValue({ git: {} })
|
|
51
|
+
execSync.mockReturnValue(latestCommit)
|
|
52
|
+
existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
|
|
53
|
+
const result = lastCommit(dir, fileName, existsSync, execSync, fileUtils)
|
|
54
|
+
expect(fileUtils.createDirectory).toHaveBeenCalledWith(folder)
|
|
55
|
+
expect(fileUtils.readFile).toHaveBeenCalledWith(filePath)
|
|
56
|
+
expect(execSync).toHaveBeenCalledWith(`git log --format=format:%H -1`, { cwd: dir, encoding: 'utf-8' })
|
|
57
|
+
expect(result).toEqual({ lastCommit: undefined, latestCommit })
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should throw an error when fileUtils.readFile is called', () => {
|
|
61
|
+
const errorMessage = 'Unable to read file'
|
|
62
|
+
fileUtils.createDirectory.mockReturnValue(undefined)
|
|
63
|
+
fileUtils.readFile.mockImplementation(() => { throw new Error(errorMessage) })
|
|
64
|
+
execSync.mockReturnValue(undefined)
|
|
65
|
+
existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
|
|
66
|
+
expect(() => lastCommit(dir, fileName, existsSync, execSync, fileUtils)).toThrowError(errorMessage)
|
|
67
|
+
expect(fileUtils.createDirectory).toHaveBeenCalledWith(folder)
|
|
68
|
+
expect(fileUtils.readFile).toHaveBeenCalledWith(filePath)
|
|
69
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { execSync } from 'child_process'
|
|
2
|
+
import * as os from 'os'
|
|
3
|
+
import { log } from '../../../src/lib/gitUtils'
|
|
4
|
+
|
|
5
|
+
jest.mock('child_process', () => {
|
|
6
|
+
return {
|
|
7
|
+
execSync: jest.fn()
|
|
8
|
+
}
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
describe('log', () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.resetAllMocks()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should return an array of git commit hashes', () => {
|
|
17
|
+
const commits = ['1234567890abcdef', '234567890abcdef1', '34567890abcdef12']
|
|
18
|
+
execSync.mockReturnValue(commits.join(os.EOL))
|
|
19
|
+
const dir = process.cwd()
|
|
20
|
+
const gitRef = 'HEAD~1..HEAD'
|
|
21
|
+
const result = log(dir, gitRef, execSync)
|
|
22
|
+
expect(execSync).toHaveBeenCalledWith(`git log --format=format:%H ${gitRef}`, { cwd: dir, encoding: 'utf-8' })
|
|
23
|
+
expect(result).toEqual(commits)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should throw an error if git is not installed or no entry found in path', () => {
|
|
27
|
+
const error = { message: 'ENOENT' }
|
|
28
|
+
execSync.mockImplementation(() => { throw error })
|
|
29
|
+
const dir = process.cwd()
|
|
30
|
+
const gitRef = 'HEAD~1..HEAD'
|
|
31
|
+
expect(() => log(dir, gitRef, execSync)).toThrowError('git not installed or no entry found in path')
|
|
32
|
+
})
|
|
33
|
+
})
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import * as fileUtils from '../../../src/lib/fileUtils.js'
|
|
3
|
+
import { updateLastCommit } from '../../../src/lib/gitUtils.js'
|
|
4
|
+
|
|
5
|
+
jest.mock('../../../src/lib/fileUtils.js', () => {
|
|
6
|
+
return {
|
|
7
|
+
fileExists: jest.fn(),
|
|
8
|
+
readFile: jest.fn(),
|
|
9
|
+
saveFile: jest.fn()
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.resetModules()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
jest.clearAllMocks()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('throws error when latest is not a string or undefined', () => {
|
|
22
|
+
expect(() => updateLastCommit('dir', {})).toThrowError(/string/)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('saves file with updated last commit', () => {
|
|
26
|
+
const dir = 'dir'
|
|
27
|
+
const latest = 'latest'
|
|
28
|
+
const folder = path.join(dir, '.sfdx', 'sfparty')
|
|
29
|
+
const fileName = path.join(folder, 'index.yaml')
|
|
30
|
+
const data = { git: { lastCommit: 'old' } }
|
|
31
|
+
|
|
32
|
+
fileUtils.fileExists.mockReturnValue(true)
|
|
33
|
+
fileUtils.readFile.mockReturnValue(data)
|
|
34
|
+
|
|
35
|
+
updateLastCommit(dir, latest, fileUtils)
|
|
36
|
+
|
|
37
|
+
expect(fileUtils.fileExists).toHaveBeenCalledWith(fileName)
|
|
38
|
+
expect(fileUtils.readFile).toHaveBeenCalledWith(fileName)
|
|
39
|
+
expect(fileUtils.saveFile).toHaveBeenCalledWith({ git: { lastCommit: latest } }, fileName)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('creates file with default definition if it does not exist', () => {
|
|
43
|
+
const dir = 'dir'
|
|
44
|
+
const latest = 'latest'
|
|
45
|
+
const folder = path.join(dir, '.sfdx', 'sfparty')
|
|
46
|
+
const fileName = path.join(folder, 'index.yaml')
|
|
47
|
+
const defaultDefinition = {
|
|
48
|
+
git: {
|
|
49
|
+
lastCommit: latest,
|
|
50
|
+
latestCommit: undefined,
|
|
51
|
+
},
|
|
52
|
+
local: {
|
|
53
|
+
lastDate: undefined,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fileUtils.fileExists.mockReturnValue(false)
|
|
58
|
+
expect(fileUtils.readFile).not.toHaveBeenCalled()
|
|
59
|
+
|
|
60
|
+
updateLastCommit(dir, latest, fileUtils)
|
|
61
|
+
|
|
62
|
+
expect(fileUtils.fileExists).toHaveBeenCalledWith(fileName)
|
|
63
|
+
expect(fileUtils.readFile).not.toHaveBeenCalled()
|
|
64
|
+
expect(fileUtils.saveFile).toHaveBeenCalledWith(defaultDefinition, fileName)
|
|
65
|
+
})
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import * as yaml from 'js-yaml'
|
|
4
|
+
import { Parser } from 'xml2js'
|
|
5
|
+
import { expect } from 'chai'
|
|
6
|
+
import sinon from 'sinon'
|
|
7
|
+
import {
|
|
8
|
+
directoryExists,
|
|
9
|
+
fileExists,
|
|
10
|
+
createDirectory,
|
|
11
|
+
deleteDirectory,
|
|
12
|
+
getFiles,
|
|
13
|
+
getDirectories,
|
|
14
|
+
deleteFile,
|
|
15
|
+
fileInfo,
|
|
16
|
+
saveFile,
|
|
17
|
+
readFile,
|
|
18
|
+
writeFile
|
|
19
|
+
} from '../../src/lib/fileUtils.js'
|
|
20
|
+
|
|
21
|
+
describe('fileUtils', () => {
|
|
22
|
+
describe('directoryExists', () => {
|
|
23
|
+
let sandbox
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
sandbox = sinon.createSandbox()
|
|
26
|
+
})
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
sandbox.restore()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should return true if directory exists', () => {
|
|
32
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
33
|
+
sandbox.stub(fs, 'statSync').returns({ isDirectory: () => true })
|
|
34
|
+
expect(directoryExists('/path/to/directory', fs)).to.be.true
|
|
35
|
+
})
|
|
36
|
+
it('should return false if directory does not exist', () => {
|
|
37
|
+
sandbox.stub(fs, 'existsSync').returns(false)
|
|
38
|
+
expect(directoryExists('/path/to/directory', fs)).to.be.false
|
|
39
|
+
})
|
|
40
|
+
it('should return false if directory exists but is not a directory', () => {
|
|
41
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
42
|
+
sandbox.stub(fs, 'statSync').returns({ isDirectory: () => false })
|
|
43
|
+
expect(directoryExists('/path/to/directory', fs)).to.be.false
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe('fileExists', () => {
|
|
48
|
+
let sandbox
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
sandbox = sinon.createSandbox()
|
|
51
|
+
})
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
sandbox.restore()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should return true if file exists', () => {
|
|
57
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
58
|
+
sandbox.stub(fs, 'statSync').returns({ isFile: () => true })
|
|
59
|
+
expect(fileExists('/path/to/file', fs)).to.be.true
|
|
60
|
+
})
|
|
61
|
+
it('should return false if file does not exist', () => {
|
|
62
|
+
sandbox.stub(fs, 'existsSync').returns(false)
|
|
63
|
+
expect(fileExists('/path/to/file', fs)).to.be.false
|
|
64
|
+
})
|
|
65
|
+
it('should return false if file exists but is not a file', () => {
|
|
66
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
67
|
+
sandbox.stub(fs, 'statSync').returns({ isFile: () => false })
|
|
68
|
+
expect(fileExists('/path/to/file', fs)).to.be.false
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('createDirectory', () => {
|
|
73
|
+
let sandbox
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
sandbox = sinon.createSandbox()
|
|
76
|
+
})
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
sandbox.restore()
|
|
79
|
+
})
|
|
80
|
+
it('should create directory if it does not exist', () => {
|
|
81
|
+
sandbox.stub(fs, 'existsSync').returns(false)
|
|
82
|
+
sandbox.stub(fs, 'mkdirSync')
|
|
83
|
+
createDirectory('/path/to/directory', fs)
|
|
84
|
+
sinon.assert.calledOnce(fs.mkdirSync)
|
|
85
|
+
})
|
|
86
|
+
it('should not create directory if it already exists', () => {
|
|
87
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
88
|
+
sandbox.stub(fs, 'mkdirSync')
|
|
89
|
+
createDirectory('/path/to/directory', fs)
|
|
90
|
+
sinon.assert.notCalled(fs.mkdirSync)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe('deleteDirectory', () => {
|
|
95
|
+
let sandbox
|
|
96
|
+
beforeEach(() => {
|
|
97
|
+
sandbox = sinon.createSandbox()
|
|
98
|
+
})
|
|
99
|
+
afterEach(() => {
|
|
100
|
+
sandbox.restore()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('should delete directory if it exists', () => {
|
|
104
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
105
|
+
sandbox.stub(fs, 'readdirSync').returns([])
|
|
106
|
+
sandbox.stub(fs, 'statSync').returns({ isDirectory: () => true })
|
|
107
|
+
sandbox.stub(fs, 'rmdirSync')
|
|
108
|
+
deleteDirectory('/path/to/directory', false, fs)
|
|
109
|
+
sinon.assert.calledOnce(fs.rmdirSync)
|
|
110
|
+
})
|
|
111
|
+
it('should not delete directory if it does not exist', () => {
|
|
112
|
+
sandbox.stub(fs, 'existsSync').returns(false)
|
|
113
|
+
sandbox.stub(fs, 'statSync').returns({ isDirectory: () => false })
|
|
114
|
+
sandbox.stub(fs, 'rmdirSync')
|
|
115
|
+
deleteDirectory('/path/to/directory', false, fs)
|
|
116
|
+
sinon.assert.notCalled(fs.rmdirSync)
|
|
117
|
+
})
|
|
118
|
+
it('should delete directory and files', () => {
|
|
119
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
120
|
+
sandbox.stub(fs, 'readdirSync').returns(['file1.txt', 'file2.txt'])
|
|
121
|
+
sandbox.stub(fs, 'statSync').returns({ isDirectory: () => true })
|
|
122
|
+
sandbox.stub(fs, 'lstatSync').returns({ isDirectory: () => false })
|
|
123
|
+
sandbox.stub(fs, 'unlinkSync')
|
|
124
|
+
sandbox.stub(fs, 'rmdirSync')
|
|
125
|
+
deleteDirectory('/path/to/directory', false, fs)
|
|
126
|
+
sinon.assert.calledTwice(fs.unlinkSync)
|
|
127
|
+
sinon.assert.calledOnce(fs.rmdirSync)
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe('getFiles', () => {
|
|
132
|
+
let sandbox
|
|
133
|
+
beforeEach(() => {
|
|
134
|
+
sandbox = sinon.createSandbox()
|
|
135
|
+
})
|
|
136
|
+
afterEach(() => {
|
|
137
|
+
sandbox.restore()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('should return all files in the directory if filter is undefined', () => {
|
|
141
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
142
|
+
sandbox.stub(fs, 'statSync').returns({ isDirectory: () => true })
|
|
143
|
+
sandbox.stub(fs, 'readdirSync').returns(['file1.txt', 'file2.txt', 'file3.jpg'])
|
|
144
|
+
let files = getFiles('/path/to/directory', undefined, fs)
|
|
145
|
+
expect(files).to.deep.equal(['file1.txt', 'file2.txt', 'file3.jpg'])
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('should return filtered files in the directory if filter is provided', () => {
|
|
149
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
150
|
+
sandbox.stub(fs, 'statSync').returns({ isDirectory: () => true })
|
|
151
|
+
sandbox.stub(fs, 'readdirSync').returns(['file1.txt', 'file2.txt', 'file3.jpg', 'file4.yaml'])
|
|
152
|
+
let files = getFiles('/path/to/directory', '.yaml', fs)
|
|
153
|
+
expect(files).to.deep.equal(['file4.yaml'])
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('should return empty array if directory does not exist', () => {
|
|
157
|
+
sandbox.stub(fs, 'existsSync').returns(false)
|
|
158
|
+
sandbox.stub(fs, 'statSync').returns({ isDirectory: () => true })
|
|
159
|
+
sandbox.stub(fs, 'readdirSync').returns(['file1.txt', 'file2.txt'])
|
|
160
|
+
let files = getFiles('/path/to/directory', '.yaml', fs)
|
|
161
|
+
expect(files).to.deep.equal([])
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
describe('getDirectories', () => {
|
|
166
|
+
let sandbox
|
|
167
|
+
beforeEach(() => {
|
|
168
|
+
sandbox = sinon.createSandbox()
|
|
169
|
+
})
|
|
170
|
+
afterEach(() => {
|
|
171
|
+
sandbox.restore()
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it('should return all directories in the directory', () => {
|
|
175
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
176
|
+
sandbox.stub(fs, 'statSync').returns({ isDirectory: () => true })
|
|
177
|
+
sandbox.stub(fs, 'readdirSync').returns([
|
|
178
|
+
{ name: 'dir1', isDirectory: () => true },
|
|
179
|
+
{ name: 'file1.txt', isDirectory: () => false },
|
|
180
|
+
{ name: 'dir2', isDirectory: () => true },
|
|
181
|
+
{ name: 'file2.txt', isDirectory: () => false }
|
|
182
|
+
])
|
|
183
|
+
let dirs = getDirectories('/path/to/directory', fs)
|
|
184
|
+
expect(dirs).to.deep.equal(['dir1', 'dir2'])
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should return empty array if directory does not exist', () => {
|
|
188
|
+
sandbox.stub(fs, 'existsSync').returns(false)
|
|
189
|
+
let dirs = getDirectories('/path/to/directory', fs)
|
|
190
|
+
expect(dirs).to.deep.equal([])
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
describe('deleteFile', () => {
|
|
195
|
+
let sandbox
|
|
196
|
+
beforeEach(() => {
|
|
197
|
+
sandbox = sinon.createSandbox()
|
|
198
|
+
})
|
|
199
|
+
afterEach(() => {
|
|
200
|
+
sandbox.restore()
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('should delete the file if it exists', () => {
|
|
204
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
205
|
+
sandbox.stub(fs, 'statSync').returns({ isFile: () => true })
|
|
206
|
+
sandbox.stub(fs, 'unlinkSync')
|
|
207
|
+
deleteFile('path/to/file.txt', fs)
|
|
208
|
+
sinon.assert.calledOnce(fs.unlinkSync)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
it('should not delete the file if it does not exist', () => {
|
|
213
|
+
sandbox.stub(fs, 'existsSync').returns(false)
|
|
214
|
+
sandbox.stub(fs, 'unlinkSync')
|
|
215
|
+
deleteFile('/path/to/file.txt', fs)
|
|
216
|
+
sinon.assert.notCalled(fs.unlinkSync)
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
describe('fileInfo', () => {
|
|
221
|
+
let sandbox
|
|
222
|
+
beforeEach(() => {
|
|
223
|
+
sandbox = sinon.createSandbox()
|
|
224
|
+
})
|
|
225
|
+
afterEach(() => {
|
|
226
|
+
sandbox.restore()
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('should return file information if file exists', () => {
|
|
230
|
+
sandbox.stub(path, 'parse').returns({
|
|
231
|
+
dir: 'path/to',
|
|
232
|
+
base: 'existingfile.txt',
|
|
233
|
+
ext: '.txt',
|
|
234
|
+
name: 'existingfile'
|
|
235
|
+
})
|
|
236
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
237
|
+
sandbox.stub(fs, 'statSync').returns({
|
|
238
|
+
size: 12345,
|
|
239
|
+
birthtime: new Date()
|
|
240
|
+
})
|
|
241
|
+
let fileResult = fileInfo('path/to/existingfile.txt', fs)
|
|
242
|
+
expect(fileResult).to.have.property('dirname', 'path/to')
|
|
243
|
+
expect(fileResult).to.have.property('basename', 'existingfile')
|
|
244
|
+
expect(fileResult).to.have.property('filename', 'existingfile.txt')
|
|
245
|
+
expect(fileResult).to.have.property('extname', '.txt')
|
|
246
|
+
expect(fileResult).to.have.property('exists', true)
|
|
247
|
+
expect(fileResult).to.have.property('stats')
|
|
248
|
+
expect(fileResult.stats).to.have.property('size', 12345)
|
|
249
|
+
expect(fileResult.stats.birthtime).to.be.a('date')
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
describe('saveFile', () => {
|
|
254
|
+
let sandbox
|
|
255
|
+
beforeEach(() => {
|
|
256
|
+
sandbox = sinon.createSandbox()
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
afterEach(() => {
|
|
260
|
+
sandbox.restore()
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
it('should write json file correctly', () => {
|
|
264
|
+
const json = { key: 'value' }
|
|
265
|
+
const fileName = 'test.json'
|
|
266
|
+
const format = 'json'
|
|
267
|
+
const fs = { writeFileSync: sandbox.stub() }
|
|
268
|
+
|
|
269
|
+
expect(saveFile(json, fileName, format, fs)).to.be.true
|
|
270
|
+
sinon.assert.calledWith(fs.writeFileSync, fileName, JSON.stringify(json, null, '\t'))
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it('should write yaml file correctly', () => {
|
|
274
|
+
const json = { key: 'value' }
|
|
275
|
+
const fileName = 'test.yaml'
|
|
276
|
+
const format = 'yaml'
|
|
277
|
+
const fs = { writeFileSync: sandbox.stub() }
|
|
278
|
+
|
|
279
|
+
expect(saveFile(json, fileName, format, fs)).to.be.true
|
|
280
|
+
sinon.assert.calledWith(fs.writeFileSync, fileName, yaml.dump(json))
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
it('should return true if format is not json or yaml', () => {
|
|
284
|
+
const json = { key: 'value' }
|
|
285
|
+
const fileName = 'test.yaml'
|
|
286
|
+
const format = 'txt'
|
|
287
|
+
const fs = { writeFileSync: sandbox.stub() }
|
|
288
|
+
|
|
289
|
+
expect(saveFile(json, fileName, format, fs)).to.be.true
|
|
290
|
+
sinon.assert.notCalled(fs.writeFileSync)
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
describe('readFile', () => {
|
|
295
|
+
let sandbox
|
|
296
|
+
beforeEach(() => {
|
|
297
|
+
sandbox = sinon.createSandbox()
|
|
298
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
299
|
+
sandbox.stub(fs, 'statSync').returns({ isFile: () => true })
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
afterEach(() => {
|
|
303
|
+
sandbox.restore()
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it('should read json file correctly with convert as true', () => {
|
|
307
|
+
const fileName = 'test.json'
|
|
308
|
+
const json = { key: 'value' }
|
|
309
|
+
sandbox.stub(fs, 'readFileSync').returns(JSON.stringify(json))
|
|
310
|
+
|
|
311
|
+
expect(readFile(fileName, true, fs)).to.deep.equal(json)
|
|
312
|
+
sinon.assert.calledWith(fs.readFileSync, fileName)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
it('should read yaml file correctly with convert as true', () => {
|
|
316
|
+
const fileName = 'test.yaml'
|
|
317
|
+
const json = { key: 'value' }
|
|
318
|
+
sandbox.stub(fs, 'readFileSync').returns(yaml.dump(json))
|
|
319
|
+
|
|
320
|
+
expect(readFile(fileName, true, fs)).to.deep.equal(json)
|
|
321
|
+
sinon.assert.calledWith(fs.readFileSync, fileName)
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
it('should return string content of file with convert as false', () => {
|
|
325
|
+
const fileName = 'test.txt'
|
|
326
|
+
const content = 'test content'
|
|
327
|
+
sandbox.stub(fs, 'readFileSync').returns(content)
|
|
328
|
+
|
|
329
|
+
expect(readFile(fileName, false, fs)).to.equal(content)
|
|
330
|
+
sinon.assert.calledWith(fs.readFileSync, fileName)
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('should return string content of file if file extension is not json, yaml, or xml', () => {
|
|
334
|
+
const fileName = 'test.txt'
|
|
335
|
+
const json = { key: 'value' }
|
|
336
|
+
sandbox.stub(fs, 'readFileSync').returns(JSON.stringify(json))
|
|
337
|
+
|
|
338
|
+
expect(readFile(fileName, true, fs)).to.equal(JSON.stringify(json))
|
|
339
|
+
sinon.assert.calledWith(fs.readFileSync, fileName)
|
|
340
|
+
})
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
describe('writeFile', () => {
|
|
344
|
+
let sandbox;
|
|
345
|
+
beforeEach(() => {
|
|
346
|
+
sandbox = sinon.createSandbox();
|
|
347
|
+
sandbox.stub(fs, 'writeFileSync');
|
|
348
|
+
sandbox.stub(fs, 'utimesSync');
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
afterEach(() => {
|
|
352
|
+
sandbox.restore();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should write json file correctly', async () => {
|
|
356
|
+
const fileName = 'test.json';
|
|
357
|
+
const json = { key: 'value' };
|
|
358
|
+
|
|
359
|
+
writeFile(fileName, JSON.stringify(json, null, '\t'), null, null, fs);
|
|
360
|
+
sinon.assert.calledWith(fs.writeFileSync, fileName, JSON.stringify(json, null, '\t'));
|
|
361
|
+
sinon.assert.calledOnce(fs.utimesSync);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('should write yaml file correctly', async () => {
|
|
365
|
+
const fileName = 'test.yaml';
|
|
366
|
+
const json = { key: 'value' };
|
|
367
|
+
|
|
368
|
+
writeFile(fileName, yaml.dump(json), null, null, fs);
|
|
369
|
+
sinon.assert.calledWith(fs.writeFileSync, fileName, yaml.dump(json));
|
|
370
|
+
sinon.assert.calledOnce(fs.utimesSync);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
})
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { expect } from 'chai'
|
|
2
|
+
import sinon from 'sinon'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import child_process from 'child_process'
|
|
5
|
+
import { diff, log } from '../../src/lib/gitUtils.js'
|
|
6
|
+
|
|
7
|
+
describe('git', () => {
|
|
8
|
+
|
|
9
|
+
describe('diff function', () => {
|
|
10
|
+
let sandbox
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
sandbox = sinon.createSandbox()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
sandbox.restore()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('should return files array if dir and gitRef are provided', async () => {
|
|
20
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
21
|
+
sandbox.stub(child_process, 'execSync').returns('A\tfile1\nM\tfile2\nD\tfile3')
|
|
22
|
+
|
|
23
|
+
const result = await diff('/path/to/dir', 'HEAD', fs.existsSync, child_process.execSync)
|
|
24
|
+
expect(result).to.deep.equal([
|
|
25
|
+
{ type: 'add', path: 'file1', action: 'add' },
|
|
26
|
+
{ type: 'modify', path: 'file2', action: 'add' },
|
|
27
|
+
{ type: 'delete', path: 'file3', action: 'delete' }
|
|
28
|
+
])
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should throw an error if dir is not a git repository', async () => {
|
|
32
|
+
sandbox.stub(fs, 'existsSync').returns(false)
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
await diff('/path/to/dir', 'HEAD', fs.existsSync, child_process.execSync)
|
|
36
|
+
throw new Error('Unexpected behavior')
|
|
37
|
+
} catch (error) {
|
|
38
|
+
expect(error.message).to.equal(`The directory "/path/to/dir" is not a git repository`)
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should throw an error if gitRef not exist', async () => {
|
|
43
|
+
sandbox.stub(fs, 'existsSync').returns(true)
|
|
44
|
+
sandbox.stub(child_process, 'execSync').throws(new Error('gitRef not exist'))
|
|
45
|
+
try {
|
|
46
|
+
await diff('/path/to/dir', 'HEAD', fs.existsSync, child_process.execSync)
|
|
47
|
+
throw new Error('Unexpected behavior')
|
|
48
|
+
} catch (error) {
|
|
49
|
+
expect(error.message).to.equal(`gitRef not exist`)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe('log', () => {
|
|
55
|
+
let sandbox
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
sandbox = sinon.createSandbox()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
sandbox.restore()
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should return an array of commits', () => {
|
|
65
|
+
// Stub execSync to return a string of commits
|
|
66
|
+
sandbox.stub(child_process, 'execSync').returns('abc123\ndef456\nghi789')
|
|
67
|
+
|
|
68
|
+
const commits = log('/path/to/repo', 'master', child_process.execSync)
|
|
69
|
+
|
|
70
|
+
expect(commits).to.deep.equal(['abc123', 'def456', 'ghi789'])
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('should throw an error if git is not installed', () => {
|
|
74
|
+
// Stub execSync to throw an ENOENT error
|
|
75
|
+
sandbox.stub(child_process, 'execSync').throws(new Error('ENOENT'))
|
|
76
|
+
|
|
77
|
+
expect(() => log('/path/to/repo', 'master', child_process.execSync)).to.throw('git not installed or no entry found in path')
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as git from '../../src/lib/gitUtils.js'
|
|
2
|
+
import chai from 'chai'
|
|
3
|
+
import chaiAsPromised from 'chai-as-promised'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
import path from 'path'
|
|
6
|
+
import * as yaml from 'js-yaml'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
chai.use(chaiAsPromised)
|
|
10
|
+
const expect = chai.expect
|
|
11
|
+
|
|
12
|
+
describe('git Util', () => {
|
|
13
|
+
describe('lastCommit function', () => {
|
|
14
|
+
it('should return an error if the file is not in the repository', async () => {
|
|
15
|
+
try {
|
|
16
|
+
await git.lastCommit(process.cwd(), 'invalid-index.yaml')
|
|
17
|
+
} catch (err) {
|
|
18
|
+
expect(err.message).to.equal('file not found in repository')
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should return an error if git is not installed or not in PATH', async () => {
|
|
23
|
+
try {
|
|
24
|
+
await git.lastCommit(process.cwd())
|
|
25
|
+
} catch (err) {
|
|
26
|
+
expect(err.message).to.equal('git not installed or no entry found in path')
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('should return the last commit hash for a given file', async () => {
|
|
31
|
+
const commitHash = await git.lastCommit(process.cwd())
|
|
32
|
+
expect(commitHash).to.be.an('object')
|
|
33
|
+
expect(commitHash).to.have.all.keys('lastCommit', 'latestCommit')
|
|
34
|
+
expect(commitHash.lastCommit).to.satisfy(val => val === undefined || (val.length === 40 && /^[a-f0-9]+$/.test(val)))
|
|
35
|
+
expect(commitHash.latestCommit).to.satisfy(val =>val === undefined || (val.length === 40 && /^[a-f0-9]+$/.test(val)))
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('updateLastCommit function', () => {
|
|
40
|
+
it('should update the last commit hash for a file', async () => {
|
|
41
|
+
const dir = process.cwd()
|
|
42
|
+
const commitHash = await git.lastCommit(dir)
|
|
43
|
+
const lastCommitHash = '16d69f0cf3d902a900a0609177fe5cf0fda9a965'
|
|
44
|
+
git.updateLastCommit(dir, lastCommitHash)
|
|
45
|
+
const folder = path.join(dir, '.sfdx', 'sfparty')
|
|
46
|
+
const fileName = path.join(folder, 'index.yaml')
|
|
47
|
+
const fileData1 = yaml.load(fs.readFileSync(fileName))
|
|
48
|
+
expect(fileData1.git.lastCommit).to.equal(lastCommitHash)
|
|
49
|
+
git.updateLastCommit(dir, commitHash.lastCommit)
|
|
50
|
+
const fileData2 = yaml.load(fs.readFileSync(fileName))
|
|
51
|
+
expect(fileData2.git.lastCommit).to.satisfy((val) => val === commitHash.lastCommit)
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
})
|