@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.
Files changed (89) hide show
  1. package/.github/workflows/cicd.yaml +38 -0
  2. package/babel.config.cjs +3 -0
  3. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/applicationVisibilities.yaml +2 -2
  4. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/fieldPermissions/Case.yaml +1 -1
  5. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/main.yaml +2 -2
  6. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AIRecordInsight.yaml +3 -3
  7. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Account.yaml +3 -3
  8. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AppointmentInvitation.yaml +3 -3
  9. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AppointmentTopicTimeSlot.yaml +3 -3
  10. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Asset.yaml +1 -1
  11. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationForm.yaml +4 -4
  12. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormConsent.yaml +4 -4
  13. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormDataUse.yaml +4 -4
  14. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/AuthorizationFormText.yaml +4 -4
  15. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/BusinessBrand.yaml +4 -4
  16. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Campaign.yaml +1 -1
  17. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscription.yaml +3 -3
  18. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionChannelType.yaml +3 -3
  19. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionConsent.yaml +4 -4
  20. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/CommSubscriptionTiming.yaml +4 -4
  21. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Contact.yaml +3 -3
  22. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointAddress.yaml +4 -4
  23. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointConsent.yaml +4 -4
  24. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointEmail.yaml +4 -4
  25. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointPhone.yaml +4 -4
  26. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactPointTypeConsent.yaml +4 -4
  27. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContactRequest.yaml +3 -3
  28. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ContractLineItem.yaml +1 -1
  29. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Customer.yaml +4 -4
  30. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DandBCompany.yaml +1 -1
  31. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DataUseLegalBasis.yaml +4 -4
  32. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/DataUsePurpose.yaml +3 -3
  33. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Document.yaml +3 -3
  34. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/EngagementChannelType.yaml +3 -3
  35. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Entitlement.yaml +2 -2
  36. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/EntitlementContact.yaml +3 -3
  37. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Individual.yaml +3 -3
  38. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Lead.yaml +4 -4
  39. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Macro.yaml +1 -1
  40. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/OperatingHours.yaml +3 -3
  41. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Opportunity.yaml +4 -4
  42. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/PartyConsent.yaml +4 -4
  43. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Promotion.yaml +4 -4
  44. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/QuickText.yaml +1 -1
  45. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Seller.yaml +4 -4
  46. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ServiceContract.yaml +2 -2
  47. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/ServiceTerritory.yaml +2 -2
  48. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Shift.yaml +3 -3
  49. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/SocialPersona.yaml +3 -3
  50. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/Solution.yaml +1 -1
  51. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/StreamingChannel.yaml +4 -4
  52. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WebCart.yaml +1 -1
  53. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WebStore.yaml +1 -1
  54. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkPlan.yaml +3 -3
  55. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkPlanTemplate.yaml +4 -4
  56. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/objectPermissions/WorkStepTemplate.yaml +4 -4
  57. package/force-app-party/main/default/profiles/Customer Portal Manager Standard/tabVisibilities.yaml +29 -29
  58. package/force-app-party/main/default/profiles/External Identity User/applicationVisibilities.yaml +1 -1
  59. package/force-app-party/main/default/profiles/External Identity User/main.yaml +2 -2
  60. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Account.yaml +2 -2
  61. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Asset.yaml +3 -3
  62. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/BusinessBrand.yaml +2 -2
  63. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Contact.yaml +3 -3
  64. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/ContactPointEmail.yaml +3 -3
  65. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/ContactPointPhone.yaml +3 -3
  66. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Customer.yaml +2 -2
  67. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Document.yaml +1 -1
  68. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Individual.yaml +3 -3
  69. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/PushTopic.yaml +4 -4
  70. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/Seller.yaml +2 -2
  71. package/force-app-party/main/default/profiles/External Identity User/tabVisibilities.yaml +30 -30
  72. package/package.json +7 -5
  73. package/src/index.js +7 -45
  74. package/src/lib/checkVersion.js +42 -0
  75. package/src/lib/fileUtils.js +38 -38
  76. package/src/lib/gitUtils.js +87 -140
  77. package/test/lib/git/diff.spec.js +54 -0
  78. package/test/lib/git/lastCommit.spec.js +69 -0
  79. package/test/lib/git/log.spec.js +33 -0
  80. package/test/lib/git/updateLastCommit.spec.js +65 -0
  81. package/test/root.spec.js +7 -0
  82. package/test_mocha/lib/fileUtils.js +374 -0
  83. package/test_mocha/lib/git.js +82 -0
  84. package/test_mocha/lib/gitUtils.js +54 -0
  85. package/{tests/lib/versionCheck.spec.js → test_mocha/lib/versionCheck.js} +0 -0
  86. package/{tests/root.spec.js → test_mocha/root.js} +0 -0
  87. package/force-app-party/main/default/labels/CustomLabels/labels/Description.yaml +0 -7
  88. package/force-app-party/main/default/profiles/External Identity User/objectPermissions/SocialPersona.yaml +0 -8
  89. 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,7 @@
1
+ const sum = (a, b) => {
2
+ return a + b
3
+ }
4
+
5
+ test('adds 1 + 2 to equal 3', () => {
6
+ expect(sum(1, 2)).toBe(3)
7
+ })
@@ -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
+ })