@ds-sfdc/sfparty 1.3.3 → 1.3.5
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/README.md +7 -0
- package/package.json +2 -2
- package/src/index.js +28 -28
- package/src/lib/checkVersion.js +63 -27
- package/src/lib/fileUtils.js +1 -0
- package/src/lib/packageUtil.js +107 -77
- package/src/party/combine.js +23 -15
- package/src/party/split.js +4 -4
- package/test/checkVersion.spec.js +69 -0
- package/test/lib/git/diff.spec.js +32 -34
- package/test/lib/git/log.spec.js +18 -20
- package/test/lib/package/addMember.spec.js +49 -0
- package/test/lib/package/getPackageXML.spec.js +57 -0
- package/test/lib/package/savePackage.spec.js +57 -0
package/README.md
CHANGED
|
@@ -159,3 +159,10 @@ Optional:
|
|
|
159
159
|
```
|
|
160
160
|
sfparty combine --git=HEAD~1..HEAD --append --delta --package=deploy/package.xml --destructive=deploy/destructiveChanges/destructiveChanges.xml
|
|
161
161
|
```
|
|
162
|
+
#### Previous Commit to Current and output to different target directory
|
|
163
|
+
|
|
164
|
+
The default target is the package file specified in the `sfdx-project.json` file. You can use the `--target` parameter if you want the files to be created in a different location. sfparty will create the /main/default/* directories accordingly.
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
sfparty combine --git=HEAD~1..HEAD --append --delta --package=deploy/package.xml --destructive=deploy/destructiveChanges/destructiveChanges.xml --target=deployDir/force-app
|
|
168
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ds-sfdc/sfparty",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "Salesforce metadata XML splitter for CI/CD",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"axios": "^1.2.2",
|
|
27
27
|
"chai-as-promised": "^7.1.1",
|
|
28
|
-
"
|
|
28
|
+
"cli-color": "^2.0.3",
|
|
29
29
|
"cli-spinners": "^2.7.0",
|
|
30
30
|
"convert-hrtime": "^5.0.0",
|
|
31
31
|
"js-yaml": "^4.1.0",
|
package/src/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict'
|
|
3
|
-
import { exec } from 'child_process'
|
|
3
|
+
import { exec, spawnSync } from 'child_process'
|
|
4
4
|
import { readFileSync } from 'fs'
|
|
5
5
|
import path from 'path'
|
|
6
6
|
import yargs from 'yargs'
|
|
7
7
|
import { hideBin } from 'yargs/helpers'
|
|
8
8
|
import winston from 'winston'
|
|
9
|
-
import
|
|
9
|
+
import clc from 'cli-color'
|
|
10
10
|
import convertHrtime from 'convert-hrtime'
|
|
11
11
|
import axios from 'axios'
|
|
12
12
|
import { marked } from 'marked'
|
|
@@ -51,7 +51,7 @@ global.logger = winston.createLogger({
|
|
|
51
51
|
|
|
52
52
|
global.icons = {
|
|
53
53
|
"warn": '🔕',
|
|
54
|
-
"success":
|
|
54
|
+
"success": clc.greenBright('✔'),
|
|
55
55
|
"fail": '❗',
|
|
56
56
|
"working": '⏳',
|
|
57
57
|
"party": '🎉',
|
|
@@ -110,7 +110,7 @@ global.metaTypes = {
|
|
|
110
110
|
let types = []
|
|
111
111
|
const packageDir = getRootPath()
|
|
112
112
|
|
|
113
|
-
let errorMessage =
|
|
113
|
+
let errorMessage = clc.red('Please specify the action of ' + clc.whiteBright.bgRedBright('split') + ' or ' + clc.whiteBright.bgRedBright('combine') + '.')
|
|
114
114
|
|
|
115
115
|
displayHeader() // display header mast
|
|
116
116
|
|
|
@@ -128,7 +128,7 @@ yargs(hideBin(process.argv))
|
|
|
128
128
|
alias: 'test',
|
|
129
129
|
handler: (argv) => {
|
|
130
130
|
// THIS IS A PLACE TO TEST NEW CODE
|
|
131
|
-
global.logger.info(
|
|
131
|
+
global.logger.info(clc.magentaBright(`${global.icons.party} TEST ${global.icons.party}`))
|
|
132
132
|
}
|
|
133
133
|
})
|
|
134
134
|
.command({
|
|
@@ -139,7 +139,7 @@ yargs(hideBin(process.argv))
|
|
|
139
139
|
.check(yargCheck)
|
|
140
140
|
},
|
|
141
141
|
handler: (argv) => {
|
|
142
|
-
checkVersion(axios,
|
|
142
|
+
checkVersion(axios, spawnSync, pkgObj.default.version, true)
|
|
143
143
|
}
|
|
144
144
|
})
|
|
145
145
|
.command({
|
|
@@ -154,7 +154,7 @@ yargs(hideBin(process.argv))
|
|
|
154
154
|
.check(yargCheck)
|
|
155
155
|
},
|
|
156
156
|
handler: (argv) => {
|
|
157
|
-
checkVersion(axios,
|
|
157
|
+
checkVersion(axios, spawnSync, pkgObj.default.version)
|
|
158
158
|
global.format = argv.format
|
|
159
159
|
splitHandler(argv, processStartTime)
|
|
160
160
|
}
|
|
@@ -171,7 +171,7 @@ yargs(hideBin(process.argv))
|
|
|
171
171
|
.check(yargCheck)
|
|
172
172
|
},
|
|
173
173
|
handler: (argv) => {
|
|
174
|
-
checkVersion(axios,
|
|
174
|
+
checkVersion(axios, spawnSync, pkgObj.default.version)
|
|
175
175
|
global.format = argv.format
|
|
176
176
|
const startProm = new Promise((resolve, reject) => {
|
|
177
177
|
if (argv.git !== undefined) {
|
|
@@ -185,10 +185,10 @@ yargs(hideBin(process.argv))
|
|
|
185
185
|
global.git.latest = data.latestCommit
|
|
186
186
|
global.git.last = data.lastCommit
|
|
187
187
|
if (data.last === undefined) {
|
|
188
|
-
console.log(`${
|
|
188
|
+
console.log(`${clc.yellowBright('git mode')} ${clc.bgMagentaBright('not active:')} no prior commit - processing all`)
|
|
189
189
|
resolve(false)
|
|
190
190
|
} else {
|
|
191
|
-
console.log(`${
|
|
191
|
+
console.log(`${clc.yellowBright('git mode')} ${clc.magentaBright('active:')} ${clc.bgBlackBright(data.lastCommit) + '..' + clc.bgBlackBright(data.latestCommit)}`)
|
|
192
192
|
console.log()
|
|
193
193
|
const diff = git.diff(global.__basedir, `${data.lastCommit}..${data.latestCommit}`)
|
|
194
194
|
diff
|
|
@@ -207,7 +207,7 @@ yargs(hideBin(process.argv))
|
|
|
207
207
|
throw error
|
|
208
208
|
})
|
|
209
209
|
} else {
|
|
210
|
-
console.log(`${
|
|
210
|
+
console.log(`${clc.yellowBright('git mode')} ${clc.magentaBright('active:')} ${clc.bgBlackBright(gitRef)}`)
|
|
211
211
|
console.log()
|
|
212
212
|
const diff = git.diff(global.__basedir, gitRef)
|
|
213
213
|
diff
|
|
@@ -254,7 +254,7 @@ function yargCheck(argv, options) {
|
|
|
254
254
|
)
|
|
255
255
|
|
|
256
256
|
if (invalidKeys.length > 0) {
|
|
257
|
-
const invalidKeysWithColor = invalidKeys.map(key =>
|
|
257
|
+
const invalidKeysWithColor = invalidKeys.map(key => clc.redBright(key))
|
|
258
258
|
throw new Error(`Invalid options specified: ${invalidKeysWithColor.join(', ')}`)
|
|
259
259
|
}
|
|
260
260
|
|
|
@@ -270,13 +270,13 @@ function yargCheck(argv, options) {
|
|
|
270
270
|
if (types.length > 1) {
|
|
271
271
|
// if using multiple types you cannot specify name
|
|
272
272
|
if ((typeof name != 'undefined' && name != '')) {
|
|
273
|
-
throw new Error(
|
|
273
|
+
throw new Error(clc.redBright('You cannot specify ' + clc.whiteBright.bgRedBright('--name') + ' when using multiple types.'))
|
|
274
274
|
}
|
|
275
275
|
} else {
|
|
276
276
|
switch (argv.type) {
|
|
277
277
|
case 'label':
|
|
278
278
|
if ((typeof name != 'undefined' && name != '')) {
|
|
279
|
-
throw new Error(
|
|
279
|
+
throw new Error(clc.redBright('You cannot specify ' + clc.whiteBright.bgRedBright('--name') + ' when using label.'))
|
|
280
280
|
}
|
|
281
281
|
break
|
|
282
282
|
}
|
|
@@ -291,11 +291,11 @@ function displayMessageAndDuration(startTime, message) {
|
|
|
291
291
|
let minutes = Math.floor((executionTime.seconds + Math.round(executionTime.milliseconds / 100000)) / 60)
|
|
292
292
|
let seconds = Math.round((executionTime.seconds + Math.round(executionTime.milliseconds / 100000)) % 60)
|
|
293
293
|
if (minutes == 0 && seconds == 0) {
|
|
294
|
-
durationMessage = message +
|
|
294
|
+
durationMessage = message + clc.magentaBright(`<1s`)
|
|
295
295
|
} else if (minutes > 0) {
|
|
296
|
-
durationMessage = message +
|
|
296
|
+
durationMessage = message + clc.magentaBright(`${minutes}m ${seconds}s`)
|
|
297
297
|
} else {
|
|
298
|
-
durationMessage = message +
|
|
298
|
+
durationMessage = message + clc.magentaBright(`${seconds}s`)
|
|
299
299
|
}
|
|
300
300
|
console.log('\n' + durationMessage)
|
|
301
301
|
}
|
|
@@ -384,8 +384,8 @@ function processSplit(typeItem, argv) {
|
|
|
384
384
|
|
|
385
385
|
if (processed.total == 0) resolve(true)
|
|
386
386
|
|
|
387
|
-
console.log(`${
|
|
388
|
-
console.log(`${
|
|
387
|
+
console.log(`${clc.bgBlackBright('Source path:')} ${sourceDir}`)
|
|
388
|
+
console.log(`${clc.bgBlackBright('Target path:')} ${targetDir}`)
|
|
389
389
|
console.log()
|
|
390
390
|
console.log(`Splitting a total of ${processed.total} file(s)`)
|
|
391
391
|
console.log()
|
|
@@ -412,7 +412,7 @@ function processSplit(typeItem, argv) {
|
|
|
412
412
|
})
|
|
413
413
|
})
|
|
414
414
|
Promise.allSettled(promList).then((results) => {
|
|
415
|
-
let message = `Split ${
|
|
415
|
+
let message = `Split ${clc.bgBlackBright((processed.current > promList.length) ? promList.length : processed.current)} file(s) ${(processed.errors > 0) ? 'with ' + clc.bgBlackBright.red(processed.errors) + ' error(s) ' : ''}in `
|
|
416
416
|
displayMessageAndDuration(startTime, message)
|
|
417
417
|
resolve(true)
|
|
418
418
|
})
|
|
@@ -496,7 +496,7 @@ function processCombine(typeItem, argv) {
|
|
|
496
496
|
}
|
|
497
497
|
|
|
498
498
|
processed.total = processList.length
|
|
499
|
-
console.log(`${
|
|
499
|
+
console.log(`${clc.bgBlackBright(processed.total)} ${typeItem} file(s) to process`)
|
|
500
500
|
|
|
501
501
|
// Abort if there are no files to process
|
|
502
502
|
if (processed.total == 0) {
|
|
@@ -505,8 +505,8 @@ function processCombine(typeItem, argv) {
|
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
console.log()
|
|
508
|
-
console.log(`${
|
|
509
|
-
console.log(`${
|
|
508
|
+
console.log(`${clc.bgBlackBright('Source path:')} ${sourceDir}`)
|
|
509
|
+
console.log(`${clc.bgBlackBright('Target path:')} ${targetDir}`)
|
|
510
510
|
console.log()
|
|
511
511
|
|
|
512
512
|
const promList = []
|
|
@@ -538,7 +538,7 @@ function processCombine(typeItem, argv) {
|
|
|
538
538
|
errors++
|
|
539
539
|
}
|
|
540
540
|
})
|
|
541
|
-
let message = `Combined ${
|
|
541
|
+
let message = `Combined ${clc.bgBlackBright(successes)} file(s) ${(errors > 0) ? 'with ' + clc.bgBlackBright(errors) + 'error(s) ' : ''}in `
|
|
542
542
|
displayMessageAndDuration(startTime, message)
|
|
543
543
|
resolve(true)
|
|
544
544
|
})
|
|
@@ -603,13 +603,13 @@ function displayHeader() {
|
|
|
603
603
|
vertical: '│',
|
|
604
604
|
}
|
|
605
605
|
let versionString = `sfparty v${pkgObj.default.version}${(process.stdout.columns > pkgObj.default.description.length + 15) ? ' - ' + pkgObj.default.description : ''}`
|
|
606
|
-
let titleMessage = `${global.icons.party} ${
|
|
606
|
+
let titleMessage = `${global.icons.party} ${clc.yellowBright(versionString)} ${global.icons.party}`
|
|
607
607
|
titleMessage = titleMessage.padEnd((process.stdout.columns / 2) + versionString.length / 1.65)
|
|
608
608
|
titleMessage = titleMessage.padStart(process.stdout.columns)
|
|
609
|
-
titleMessage =
|
|
610
|
-
console.log(`${
|
|
609
|
+
titleMessage = clc.blackBright(box.vertical) + ' ' + titleMessage + ' ' + clc.blackBright(box.vertical)
|
|
610
|
+
console.log(`${clc.blackBright(box.topLeft + box.horizontal.repeat(process.stdout.columns - 2) + box.topRight)}`)
|
|
611
611
|
console.log(titleMessage)
|
|
612
|
-
console.log(`${
|
|
612
|
+
console.log(`${clc.blackBright(box.bottomLeft + box.horizontal.repeat(process.stdout.columns - 2) + box.bottomRight)}`)
|
|
613
613
|
console.log()
|
|
614
614
|
}
|
|
615
615
|
|
package/src/lib/checkVersion.js
CHANGED
|
@@ -1,42 +1,78 @@
|
|
|
1
|
-
|
|
1
|
+
import clc from 'cli-color'
|
|
2
|
+
|
|
3
|
+
class NpmNotInstalledError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message)
|
|
6
|
+
this.name = "NpmNotInstalledError"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class PackageNotFoundError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message)
|
|
13
|
+
this.name = "PackageNotFoundError"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
class UpdateError extends Error {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message)
|
|
20
|
+
this.name = "UpdateError"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function checkVersion(axios, spawnSync, currentVersion, update = false) {
|
|
2
25
|
try {
|
|
3
|
-
const { data } = await axios.get('https://registry.npmjs.org/@ds-sfdc/sfparty'
|
|
4
|
-
|
|
26
|
+
const { data } = await axios.get('https://registry.npmjs.org/@ds-sfdc/sfparty', {
|
|
27
|
+
params: {
|
|
28
|
+
field: 'dist-tags.latest'
|
|
29
|
+
}
|
|
30
|
+
})
|
|
5
31
|
if (currentVersion !== data['dist-tags'].latest) {
|
|
6
|
-
|
|
32
|
+
let icon
|
|
33
|
+
const version = clc.bgCyanBright(data['dist-tags'].latest)
|
|
34
|
+
if (update) {
|
|
35
|
+
icon = global.icons.working
|
|
36
|
+
} else {
|
|
37
|
+
icon = global.icons.fail
|
|
38
|
+
}
|
|
39
|
+
console.log(`${icon} A newer version ${version} is available.`)
|
|
7
40
|
if (!update) {
|
|
8
|
-
console.log(`Please upgrade by running ${
|
|
41
|
+
console.log(`Please upgrade by running ${clc.cyanBright('sfparty update')}`)
|
|
9
42
|
return 'A newer version'
|
|
10
43
|
} else {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
} else {
|
|
22
|
-
console.log(stdout)
|
|
23
|
-
console.log(stderr)
|
|
24
|
-
resolve(true)
|
|
25
|
-
}
|
|
26
|
-
})
|
|
44
|
+
let command = 'npm i -g @ds-sfdc/sfparty'.split(' ')
|
|
45
|
+
console.log(`Updating the application using ${clc.cyanBright(command.join(' '))}`)
|
|
46
|
+
try {
|
|
47
|
+
const npmVersion = spawnSync('npm', ['-v'])
|
|
48
|
+
if (npmVersion.stderr.toString().trim() === 'command not found') {
|
|
49
|
+
throw new NpmNotInstalledError("npm is not installed on this system. Please install npm and run the command again.")
|
|
50
|
+
}
|
|
51
|
+
const update = spawnSync(command[0], command.slice(1))
|
|
52
|
+
if (update.status !== 0) {
|
|
53
|
+
throw new UpdateError("Error updating the application.")
|
|
27
54
|
}
|
|
28
|
-
|
|
55
|
+
console.log("Application updated successfully.")
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (err instanceof NpmNotInstalledError) {
|
|
58
|
+
console.error(err)
|
|
59
|
+
} else if (err instanceof UpdateError) {
|
|
60
|
+
console.error(err)
|
|
61
|
+
}
|
|
62
|
+
throw err
|
|
63
|
+
}
|
|
29
64
|
}
|
|
30
65
|
} else {
|
|
31
66
|
if (update) {
|
|
32
67
|
console.log(`${global.icons.success} You are on the latest version.`)
|
|
33
|
-
return 'You are on the latest version'
|
|
34
68
|
}
|
|
69
|
+
return 'You are on the latest version'
|
|
35
70
|
}
|
|
36
71
|
} catch (error) {
|
|
37
|
-
|
|
72
|
+
if (error.response && error.response.status === 404) {
|
|
73
|
+
error = new PackageNotFoundError("Package not found on the npm registry")
|
|
74
|
+
}
|
|
75
|
+
console.error(error)
|
|
38
76
|
throw error
|
|
39
77
|
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
78
|
+
}
|
package/src/lib/fileUtils.js
CHANGED
package/src/lib/packageUtil.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
|
-
import chalk from 'chalk'
|
|
3
|
-
import * as xml2js from 'xml2js'
|
|
4
|
-
import * as fileUtils from './fileUtils.js'
|
|
5
2
|
import * as packageDefinition from '../meta/Package.js'
|
|
6
3
|
|
|
7
4
|
export class Package {
|
|
@@ -12,30 +9,45 @@ export class Package {
|
|
|
12
9
|
this.packageJSON = undefined
|
|
13
10
|
}
|
|
14
11
|
|
|
15
|
-
getPackageXML() {
|
|
12
|
+
getPackageXML(fileUtils) {
|
|
16
13
|
const that = this
|
|
17
14
|
return new Promise((resolve, reject) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
try {
|
|
16
|
+
if (that.xmlPath === undefined) throw new Error('Package not initialized')
|
|
17
|
+
|
|
18
|
+
let fileName = path.resolve(that.xmlPath)
|
|
19
|
+
if (fileUtils.fileExists(fileName) && global.git.append) {
|
|
20
|
+
let data = fileUtils.readFile(fileName)
|
|
21
|
+
data
|
|
22
|
+
.then((json) => {
|
|
23
|
+
try {
|
|
24
|
+
if (json === undefined || Object.keys(json).length === 0 ) json = packageDefinition.metadataDefinition.emptyPackage
|
|
25
|
+
processJSON(that, json, fileUtils)
|
|
26
|
+
resolve('existing')
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error(error)
|
|
29
|
+
reject(error)
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
.catch((error) => {
|
|
33
|
+
reject(error)
|
|
34
|
+
})
|
|
35
|
+
} else {
|
|
36
|
+
try {
|
|
37
|
+
let json = JSON.parse(JSON.stringify(packageDefinition.metadataDefinition.emptyPackage))
|
|
25
38
|
processJSON(that, json)
|
|
26
|
-
resolve('
|
|
27
|
-
})
|
|
28
|
-
.catch((error) => {
|
|
39
|
+
resolve('not found')
|
|
40
|
+
} catch (error) {
|
|
29
41
|
reject(error)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
resolve('not found')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
reject(error)
|
|
35
46
|
}
|
|
47
|
+
|
|
36
48
|
})
|
|
37
49
|
|
|
38
|
-
function processJSON(that, json) {
|
|
50
|
+
function processJSON(that, json, fileUtils) {
|
|
39
51
|
try {
|
|
40
52
|
json.Package.version = fileUtils.readFile(path.join(global.__basedir, 'sfdx-project.json')).sourceApiVersion
|
|
41
53
|
} catch (error) {
|
|
@@ -49,8 +61,9 @@ export class Package {
|
|
|
49
61
|
addMember(type, member) {
|
|
50
62
|
const that = this
|
|
51
63
|
if (that.packageJSON === undefined) throw new Error('getPackageXML must be called before adding members')
|
|
52
|
-
if (type === undefined) throw new Error('An undefined type was received when attempting to add a member')
|
|
53
|
-
if (member === undefined) throw new Error('An undefined member was received when attempting to add a member')
|
|
64
|
+
if (type === undefined || type.trim() == '') throw new Error('An undefined type was received when attempting to add a member')
|
|
65
|
+
if (member === undefined || member.trim() == '') throw new Error('An undefined member was received when attempting to add a member')
|
|
66
|
+
if (member.indexOf(`.${global.format}`) !== -1) throw new Error('Part file received as member is not allowed')
|
|
54
67
|
|
|
55
68
|
const packageJSON = that.packageJSON
|
|
56
69
|
let foundMember = false
|
|
@@ -77,82 +90,99 @@ export class Package {
|
|
|
77
90
|
})
|
|
78
91
|
}
|
|
79
92
|
} catch (error) {
|
|
80
|
-
|
|
93
|
+
throw error
|
|
81
94
|
}
|
|
82
95
|
})
|
|
83
96
|
|
|
84
97
|
// exit if member already exists
|
|
85
98
|
if (foundMember) return
|
|
86
99
|
if (foundAsterisk) {
|
|
87
|
-
// global.logger.warn(`Found ${chalk.bgBlackBright('*')} in type: ${type}.`)
|
|
88
100
|
return
|
|
89
101
|
}
|
|
90
102
|
|
|
91
|
-
|
|
92
|
-
typeJSON
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
try {
|
|
104
|
+
if (typeJSON !== undefined) {
|
|
105
|
+
typeJSON.members.push(member)
|
|
106
|
+
typeJSON.members.sort()
|
|
107
|
+
} else {
|
|
108
|
+
typeJSON = JSON.parse(JSON.stringify(packageDefinition.metadataDefinition.emptyNode))
|
|
109
|
+
typeJSON.name = type
|
|
110
|
+
typeJSON.members.push(member)
|
|
111
|
+
|
|
112
|
+
packageJSON.Package.types.push(typeJSON)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
packageJSON.Package.types.sort((a, b) => {
|
|
116
|
+
if (a.name < b.name) return -1
|
|
117
|
+
if (a.name > b.name) return 1
|
|
118
|
+
return 0
|
|
119
|
+
})
|
|
120
|
+
} catch (error) {
|
|
121
|
+
throw error
|
|
100
122
|
}
|
|
101
|
-
|
|
102
|
-
packageJSON.Package.types.sort((a, b) => {
|
|
103
|
-
if (a.name < b.name) return -1
|
|
104
|
-
if (a.name > b.name) return 1
|
|
105
|
-
return 0
|
|
106
|
-
})
|
|
107
123
|
}
|
|
108
124
|
|
|
109
|
-
savePackage() {
|
|
125
|
+
savePackage(xml2js, fileUtils) {
|
|
110
126
|
let that = this
|
|
111
127
|
let json = that.packageJSON.Package
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
try {
|
|
129
|
+
json.$.xmlns = json.$.xmlns.replace('http:', 'https:')
|
|
130
|
+
const version = json.version
|
|
131
|
+
delete json.version
|
|
132
|
+
json.version = version
|
|
133
|
+
|
|
134
|
+
const builder = new xml2js.Builder(
|
|
135
|
+
{
|
|
136
|
+
cdata: false,
|
|
137
|
+
rootName: 'Package',
|
|
138
|
+
xmldec: { 'version': '1.0', 'encoding': 'UTF-8' }
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
let fileName = that.xmlPath
|
|
142
|
+
fileUtils.createDirectory(path.dirname(fileName))
|
|
143
|
+
|
|
144
|
+
const xml = builder.buildObject(json)
|
|
145
|
+
|
|
146
|
+
fileUtils.writeFile(fileName, xml)
|
|
147
|
+
|
|
148
|
+
} catch (error) {
|
|
149
|
+
throw error
|
|
150
|
+
}
|
|
127
151
|
}
|
|
128
152
|
}
|
|
129
153
|
|
|
130
154
|
function transformJSON(json) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
155
|
+
try {
|
|
156
|
+
json.forEach(typesItem => {
|
|
157
|
+
Object.keys(typesItem).forEach(key => {
|
|
158
|
+
let jsonString = JSON.stringify(typesItem[key], (name, value) => {
|
|
159
|
+
if (key == 'members') {
|
|
160
|
+
return value
|
|
161
|
+
} else {
|
|
162
|
+
return xml2json(value)
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
typesItem[key] = JSON.parse(jsonString)
|
|
139
166
|
})
|
|
140
|
-
typesItem[key] = JSON.parse(jsonString)
|
|
141
167
|
})
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
168
|
+
|
|
169
|
+
return
|
|
170
|
+
} catch (error) {
|
|
171
|
+
throw error
|
|
172
|
+
}
|
|
146
173
|
|
|
147
|
-
function xml2json(currentValue) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
174
|
+
function xml2json(currentValue) {
|
|
175
|
+
try {
|
|
176
|
+
if (Array.isArray(currentValue)) {
|
|
177
|
+
if (currentValue.length == 1) {
|
|
178
|
+
currentValue = currentValue[0].toString().trim()
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (currentValue == 'true') currentValue = true
|
|
182
|
+
if (currentValue == 'false') currentValue = false
|
|
183
|
+
return currentValue
|
|
184
|
+
} catch (error) {
|
|
185
|
+
throw error
|
|
151
186
|
}
|
|
152
187
|
}
|
|
153
|
-
if (currentValue == 'true') currentValue = true
|
|
154
|
-
if (currentValue == 'false') currentValue = false
|
|
155
|
-
return currentValue
|
|
156
188
|
}
|
|
157
|
-
|
|
158
|
-
// Create JEST tests that cover 100% of the code
|
package/src/party/combine.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import logUpdate from 'log-update'
|
|
3
|
-
import
|
|
3
|
+
import clc from 'cli-color'
|
|
4
4
|
import convertHrtime from 'convert-hrtime'
|
|
5
5
|
import cliSpinners from 'cli-spinners'
|
|
6
6
|
import os from 'node:os'
|
|
@@ -110,8 +110,8 @@ export class Combine {
|
|
|
110
110
|
if (global.git.enabled) {
|
|
111
111
|
that.#addPkg = new packageUtil.Package(that.addManifest)
|
|
112
112
|
that.#desPkg = new packageUtil.Package(that.desManifest)
|
|
113
|
-
const prom1 = that.#addPkg.getPackageXML()
|
|
114
|
-
const prom2 = that.#desPkg.getPackageXML()
|
|
113
|
+
const prom1 = that.#addPkg.getPackageXML(fileUtils)
|
|
114
|
+
const prom2 = that.#desPkg.getPackageXML(fileUtils)
|
|
115
115
|
|
|
116
116
|
Promise.allSettled([prom1, prom2])
|
|
117
117
|
.then((results) => {
|
|
@@ -130,20 +130,24 @@ export class Combine {
|
|
|
130
130
|
function processStart(that) {
|
|
131
131
|
let success = getXML(that)
|
|
132
132
|
if (success) {
|
|
133
|
-
if (!that.metadataDefinition.packageTypeIsDirectory
|
|
133
|
+
if (!that.metadataDefinition.packageTypeIsDirectory && global.git.enabled) {
|
|
134
|
+
that.#addPkg.addMember(that.#root, that.#fileName.shortName)
|
|
135
|
+
}
|
|
134
136
|
saveXML(that)
|
|
135
137
|
if (global.git.enabled) savePackageXML(that)
|
|
136
138
|
return true
|
|
137
139
|
} else {
|
|
138
140
|
logUpdate(that.#spinnerMessage
|
|
139
141
|
.replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
|
|
140
|
-
.replace('[%2]', `. ${
|
|
142
|
+
.replace('[%2]', `. ${clc.redBright('source not found - removing XML file')}`)
|
|
141
143
|
.replace('[%3]', ``)
|
|
142
144
|
.replace('[%4]', `${global.icons.delete} `)
|
|
143
145
|
.replace('[%5]', that.#fileName.shortName)
|
|
144
146
|
)
|
|
145
147
|
logUpdate.done()
|
|
146
|
-
if (!that.metadataDefinition.packageTypeIsDirectory
|
|
148
|
+
if (!that.metadataDefinition.packageTypeIsDirectory && global.git.enabled) {
|
|
149
|
+
that.#desPkg.addMember(that.#root, that.#fileName.shortName)
|
|
150
|
+
}
|
|
147
151
|
deleteFile(that.#fileName.fullName)
|
|
148
152
|
if (global.git.enabled) savePackageXML(that)
|
|
149
153
|
return 'deleted'
|
|
@@ -151,8 +155,8 @@ export class Combine {
|
|
|
151
155
|
}
|
|
152
156
|
|
|
153
157
|
function savePackageXML(that) {
|
|
154
|
-
that.#addPkg.savePackage()
|
|
155
|
-
that.#desPkg.savePackage()
|
|
158
|
+
that.#addPkg.savePackage(xml2js, fileUtils)
|
|
159
|
+
that.#desPkg.savePackage(xml2js, fileUtils)
|
|
156
160
|
}
|
|
157
161
|
|
|
158
162
|
function getXML(that) {
|
|
@@ -163,14 +167,14 @@ export class Combine {
|
|
|
163
167
|
processed.current++
|
|
164
168
|
|
|
165
169
|
that.#startTime = process.hrtime.bigint()
|
|
166
|
-
that.#spinnerMessage = `[%1] of ${that.total} - ${that.#root}: [%4]${
|
|
170
|
+
that.#spinnerMessage = `[%1] of ${that.total} - ${that.#root}: [%4]${clc.yellowBright('[%5]')}[%2][%3]`
|
|
167
171
|
|
|
168
172
|
try {
|
|
169
173
|
that.#types.forEach(key => {
|
|
170
174
|
// display message
|
|
171
175
|
logUpdate(that.#spinnerMessage
|
|
172
176
|
.replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
|
|
173
|
-
.replace('[%2]', `\n${
|
|
177
|
+
.replace('[%2]', `\n${clc.magentaBright(nextFrame(that))} ${key}`)
|
|
174
178
|
.replace('[%3]', `${that.#errorMessage}`)
|
|
175
179
|
.replace('[%4]', `${global.icons.working} `)
|
|
176
180
|
.replace('[%5]', `${that.#fileName.shortName} `)
|
|
@@ -195,7 +199,7 @@ export class Combine {
|
|
|
195
199
|
} else if (that.metadataDefinition.directories.includes(key)) {
|
|
196
200
|
processDirectory(that, key)
|
|
197
201
|
} else {
|
|
198
|
-
global.logger.warn(`Unexpected metadata type: ${
|
|
202
|
+
global.logger.warn(`Unexpected metadata type: ${clc.redBright(key)}`)
|
|
199
203
|
}
|
|
200
204
|
})
|
|
201
205
|
return true
|
|
@@ -230,7 +234,7 @@ export class Combine {
|
|
|
230
234
|
fileList.forEach((file, index) => {
|
|
231
235
|
logUpdate(that.#spinnerMessage
|
|
232
236
|
.replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
|
|
233
|
-
.replace('[%2]', `\n${
|
|
237
|
+
.replace('[%2]', `\n${clc.magentaBright(nextFrame(that))} ${key} - ${index + 1} of ${fileList.length} - ${clc.magentaBright(file)}`)
|
|
234
238
|
.replace('[%3]', `${that.#errorMessage}`)
|
|
235
239
|
.replace('[%4]', `${global.icons.working} `)
|
|
236
240
|
.replace('[%5]', `${that.#fileName.shortName} `)
|
|
@@ -259,7 +263,7 @@ export class Combine {
|
|
|
259
263
|
fileObj.shortName === undefined ||
|
|
260
264
|
fileObj.fullName === undefined
|
|
261
265
|
) {
|
|
262
|
-
global.displayError(`${global.icons.warn} Invalid file information passed ${
|
|
266
|
+
global.displayError(`${global.icons.warn} Invalid file information passed ${clc.redBright(fileObj)}`, true)
|
|
263
267
|
}
|
|
264
268
|
|
|
265
269
|
if (!fileUtils.fileExists(fileObj.fullName)) {
|
|
@@ -270,7 +274,9 @@ export class Combine {
|
|
|
270
274
|
}
|
|
271
275
|
|
|
272
276
|
if (that.metadataDefinition.packageTypeIsDirectory) {
|
|
273
|
-
|
|
277
|
+
if (global.git.enabled) {
|
|
278
|
+
that.#desPkg.addMember(that.#root, fileObj.shortName.replace(`.${global.format}`, ''))
|
|
279
|
+
}
|
|
274
280
|
}
|
|
275
281
|
return true
|
|
276
282
|
}
|
|
@@ -313,7 +319,9 @@ export class Combine {
|
|
|
313
319
|
|
|
314
320
|
if (that.metadataDefinition.packageTypeIsDirectory) {
|
|
315
321
|
if (fileObj.fullName !== path.join(that.sourceDir, that.metaDir, `main.${global.format}`)) {
|
|
316
|
-
|
|
322
|
+
if (global.git.enabled) {
|
|
323
|
+
that.#addPkg.addMember(that.#root, fileObj.shortName.replace(`.${global.format}`, ''))
|
|
324
|
+
}
|
|
317
325
|
}
|
|
318
326
|
}
|
|
319
327
|
|
package/src/party/split.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'path'
|
|
|
4
4
|
import { readFile } from 'fs'
|
|
5
5
|
import { Parser } from 'xml2js'
|
|
6
6
|
import logUpdate from 'log-update'
|
|
7
|
-
import
|
|
7
|
+
import clc from 'cli-color'
|
|
8
8
|
import convertHrtime from 'convert-hrtime'
|
|
9
9
|
import cliSpinners from 'cli-spinners'
|
|
10
10
|
import * as fileUtils from '../lib/fileUtils.js'
|
|
@@ -128,7 +128,7 @@ export class Split {
|
|
|
128
128
|
})
|
|
129
129
|
|
|
130
130
|
function processJSON(that, json, baseDir) {
|
|
131
|
-
that.#spinnerMessage = `[%1] of ${that.total} - ${that.#root}: [%4]${
|
|
131
|
+
that.#spinnerMessage = `[%1] of ${that.total} - ${that.#root}: [%4]${clc.yellowBright(that.#fileName.shortName)}[%2][%3]`
|
|
132
132
|
|
|
133
133
|
let targetDir = baseDir
|
|
134
134
|
if (processed.type != that.#root) {
|
|
@@ -140,7 +140,7 @@ export class Split {
|
|
|
140
140
|
that.sequence = processed.current
|
|
141
141
|
logUpdate(that.#spinnerMessage
|
|
142
142
|
.replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
|
|
143
|
-
.replace('[%2]', `\n${
|
|
143
|
+
.replace('[%2]', `\n${clc.magentaBright(nextFrame(that))} ${key}`)
|
|
144
144
|
.replace('[%3]', `${that.#errorMessage}`)
|
|
145
145
|
.replace('[%4]', `${global.icons.working} `)
|
|
146
146
|
)
|
|
@@ -176,7 +176,7 @@ export class Split {
|
|
|
176
176
|
that.sequence = processed.current
|
|
177
177
|
logUpdate(that.#spinnerMessage
|
|
178
178
|
.replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
|
|
179
|
-
.replace('[%2]', `\n${
|
|
179
|
+
.replace('[%2]', `\n${clc.magentaBright(nextFrame(that))} ${key}`)
|
|
180
180
|
.replace('[%3]', `${that.#errorMessage}`)
|
|
181
181
|
.replace('[%4]', `${global.icons.working} `)
|
|
182
182
|
)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import { spawnSync } from 'child_process'
|
|
3
|
+
import clc from 'cli-color'
|
|
4
|
+
import { checkVersion } from '../src/lib/checkVersion.js'
|
|
5
|
+
|
|
6
|
+
global.icons = {
|
|
7
|
+
"success": clc.greenBright('✔'),
|
|
8
|
+
"fail": '❗',
|
|
9
|
+
"working": '⏳',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
jest.mock('axios')
|
|
13
|
+
jest.mock('child_process', () => ({ spawnSync: jest.fn() }))
|
|
14
|
+
|
|
15
|
+
describe('checkVersion', () => {
|
|
16
|
+
let spy
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
jest.clearAllMocks()
|
|
19
|
+
spy = jest.spyOn(console, 'log')
|
|
20
|
+
})
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
spy.mockRestore()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should return "A newer version" if a newer version is available', async () => {
|
|
26
|
+
axios.get.mockResolvedValue({ data: { 'dist-tags': { latest: '2.0.0' } } })
|
|
27
|
+
const result = await checkVersion(axios, spawnSync, '1.0.0')
|
|
28
|
+
expect(result).toBe('A newer version')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should return "You are on the latest version" if the current version is the latest version', async () => {
|
|
32
|
+
axios.get.mockResolvedValue({ data: { 'dist-tags': { latest: '1.0.0' } } })
|
|
33
|
+
const result = await checkVersion(axios, spawnSync, '1.0.0')
|
|
34
|
+
expect(result).toBe('You are on the latest version')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should throw a NpmNotInstalledError if npm is not installed', async () => {
|
|
38
|
+
axios.get.mockResolvedValue({ data: { 'dist-tags': { latest: '2.0.0' } } })
|
|
39
|
+
spawnSync.mockReturnValue({ status: 1, stderr: { toString: () => 'command not found' } })
|
|
40
|
+
try {
|
|
41
|
+
await checkVersion(axios, spawnSync, '1.0.0', true)
|
|
42
|
+
} catch (err) {
|
|
43
|
+
expect(err.name).toBe('NpmNotInstalledError')
|
|
44
|
+
expect(err.message).toBe('npm is not installed on this system. Please install npm and run the command again.')
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('should throw a PackageNotFoundError if the package is not found on the npm registry', async () => {
|
|
49
|
+
axios.get.mockRejectedValue({ response: { status: 404 } });
|
|
50
|
+
try {
|
|
51
|
+
await checkVersion(axios, spawnSync, '1.0.0');
|
|
52
|
+
} catch (err) {
|
|
53
|
+
expect(err.name).toBe('PackageNotFoundError');
|
|
54
|
+
expect(err.message).toBe('Package not found on the npm registry');
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should throw a UpdateError if an error occurs while updating the package', async () => {
|
|
59
|
+
axios.get.mockResolvedValue({ data: { 'dist-tags': { latest: '2.0.0' } } });
|
|
60
|
+
spawnSync.mockReturnValue({ status: 1, stderr: { toString: () => 'Update error' } });
|
|
61
|
+
try {
|
|
62
|
+
await checkVersion(axios, spawnSync, '1.0.0', true);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
expect(err.name).toBe('UpdateError');
|
|
65
|
+
expect(err.message).toBe('Error updating the application.');
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
})
|
|
@@ -10,45 +10,43 @@ jest.mock('child_process', () => ({
|
|
|
10
10
|
}))
|
|
11
11
|
const gitRef = "HEAD~1..HEAD"
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
})
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks()
|
|
15
|
+
})
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
test('rejects if directory is not a git repository', async () => {
|
|
18
|
+
existsSync.mockReturnValueOnce(false)
|
|
19
|
+
try {
|
|
20
|
+
await diff('/path/to/dir')
|
|
21
|
+
fail('Expected function to throw an error', gitRef, existsSync)
|
|
22
|
+
} catch (error) {
|
|
23
|
+
expect(error.message).toEqual('The directory "/path/to/dir" is not a git repository')
|
|
24
|
+
}
|
|
25
|
+
})
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
`A\tfile1.txt
|
|
27
|
+
test('resolves with files when git diff command is successful', async () => {
|
|
28
|
+
existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
|
|
29
|
+
execSync.mockReturnValueOnce(
|
|
30
|
+
`A\tfile1.txt
|
|
32
31
|
M\tfile2.txt
|
|
33
32
|
D\tfile3.txt`)
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
const files = await diff('/path/to/dir', gitRef, existsSync, execSync)
|
|
35
|
+
expect(files).toEqual([
|
|
36
|
+
{ type: 'add', path: 'file1.txt', action: 'add' },
|
|
37
|
+
{ type: 'modify', path: 'file2.txt', action: 'add' },
|
|
38
|
+
{ type: 'delete', path: 'file3.txt', action: 'delete' }
|
|
39
|
+
])
|
|
40
|
+
})
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
test('rejects when git diff command fails', async () => {
|
|
43
|
+
existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
|
|
44
|
+
execSync.mockImplementation(() => { throw new Error('Command failed') })
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
})
|
|
46
|
+
try {
|
|
47
|
+
await diff('/path/to/dir', gitRef, existsSync, execSync)
|
|
48
|
+
fail('Expected function to throw an error')
|
|
49
|
+
} catch (error) {
|
|
50
|
+
expect(error.message).toEqual('Command failed')
|
|
51
|
+
}
|
|
54
52
|
})
|
package/test/lib/git/log.spec.js
CHANGED
|
@@ -8,26 +8,24 @@ jest.mock('child_process', () => {
|
|
|
8
8
|
}
|
|
9
9
|
})
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
})
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
jest.resetAllMocks()
|
|
13
|
+
})
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
it('should return an array of git commit hashes', () => {
|
|
16
|
+
const commits = ['1234567890abcdef', '234567890abcdef1', '34567890abcdef12']
|
|
17
|
+
execSync.mockReturnValue(commits.join(os.EOL))
|
|
18
|
+
const dir = process.cwd()
|
|
19
|
+
const gitRef = 'HEAD~1..HEAD'
|
|
20
|
+
const result = log(dir, gitRef, execSync)
|
|
21
|
+
expect(execSync).toHaveBeenCalledWith(`git log --format=format:%H ${gitRef}`, { cwd: dir, encoding: 'utf-8' })
|
|
22
|
+
expect(result).toEqual(commits)
|
|
23
|
+
})
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
})
|
|
25
|
+
it('should throw an error if git is not installed or no entry found in path', () => {
|
|
26
|
+
const error = { message: 'ENOENT' }
|
|
27
|
+
execSync.mockImplementation(() => { throw error })
|
|
28
|
+
const dir = process.cwd()
|
|
29
|
+
const gitRef = 'HEAD~1..HEAD'
|
|
30
|
+
expect(() => log(dir, gitRef, execSync)).toThrowError('git not installed or no entry found in path')
|
|
33
31
|
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as packageDefinition from '../../../src/meta/Package.js'
|
|
2
|
+
import { Package } from '../../../src/lib/packageUtil.js'
|
|
3
|
+
|
|
4
|
+
let pkg;
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
pkg = new Package('xmlPath');
|
|
7
|
+
pkg.packageJSON = JSON.parse(JSON.stringify(packageDefinition.metadataDefinition.emptyPackage));
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
it('should add a member to the pkg JSON', () => {
|
|
16
|
+
pkg.addMember('type', 'member');
|
|
17
|
+
expect(pkg.packageJSON.Package.types[0].name).toBe('type');
|
|
18
|
+
expect(pkg.packageJSON.Package.types[0].members).toEqual(['member']);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should throw an error if packageJSON is undefined', () => {
|
|
22
|
+
pkg.packageJSON = undefined;
|
|
23
|
+
expect(() => pkg.addMember('type', 'member')).toThrowError('getPackageXML must be called before adding members');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should throw an error if type is undefined', () => {
|
|
27
|
+
expect(() => pkg.addMember(undefined, 'member')).toThrowError('An undefined type was received when attempting to add a member');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should throw an error if member is undefined', () => {
|
|
31
|
+
expect(() => pkg.addMember('type', undefined)).toThrowError('An undefined member was received when attempting to add a member');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should throw an error if member is a part file', () => {
|
|
35
|
+
global.format = "part"
|
|
36
|
+
expect(() => pkg.addMember('type', 'member.part')).toThrowError('Part file received as member is not allowed');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should not add the member if it already exists', () => {
|
|
40
|
+
pkg.packageJSON.Package.types = [{ name: "type", members: ["member"] }];
|
|
41
|
+
pkg.addMember('type', 'member');
|
|
42
|
+
expect(pkg.packageJSON.Package.types[0].members).toEqual(["member"]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should not add the member if type already has an asterisk', () => {
|
|
46
|
+
pkg.packageJSON.Package.types = [{ name: "type", members: ["*"] }];
|
|
47
|
+
pkg.addMember('type', 'member');
|
|
48
|
+
expect(pkg.packageJSON.Package.types[0].members).toEqual(["*"]);
|
|
49
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as packageDefinition from '../../../src/meta/Package.js'
|
|
2
|
+
import { Package } from '../../../src/lib/packageUtil.js'
|
|
3
|
+
|
|
4
|
+
let pkg;
|
|
5
|
+
const fileUtils = {
|
|
6
|
+
fileExists: jest.fn(),
|
|
7
|
+
readFile: jest.fn(),
|
|
8
|
+
}
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
pkg = new Package('xmlPath');
|
|
11
|
+
});
|
|
12
|
+
global.__basedir = '.'
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should default the package if the json is empty', async () => {
|
|
18
|
+
fileUtils.fileExists.mockReturnValue(true);
|
|
19
|
+
fileUtils.readFile.mockResolvedValue({});
|
|
20
|
+
global.git = { append: true }
|
|
21
|
+
const result = await pkg.getPackageXML(fileUtils);
|
|
22
|
+
expect(result).toBe('existing');
|
|
23
|
+
expect(fileUtils.fileExists).toHaveBeenCalled();
|
|
24
|
+
expect(fileUtils.readFile).toHaveBeenCalled();
|
|
25
|
+
expect(pkg.packageJSON).toEqual(packageDefinition.metadataDefinition.emptyPackage);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should read an existing file and call processJSON', async () => {
|
|
29
|
+
fileUtils.fileExists.mockReturnValue(true);
|
|
30
|
+
fileUtils.readFile.mockResolvedValue(packageDefinition.metadataDefinition.emptyPackage);
|
|
31
|
+
global.git = { append: true }
|
|
32
|
+
const result = await pkg.getPackageXML(fileUtils);
|
|
33
|
+
expect(result).toBe('existing');
|
|
34
|
+
expect(fileUtils.fileExists).toHaveBeenCalled();
|
|
35
|
+
expect(fileUtils.readFile).toHaveBeenCalled();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should create an empty pkg JSON and call processJSON', async () => {
|
|
39
|
+
fileUtils.fileExists.mockReturnValue(false);
|
|
40
|
+
const finalJSON = JSON.parse(JSON.stringify(packageDefinition.metadataDefinition.emptyPackage))
|
|
41
|
+
finalJSON.Package.version = packageDefinition.metadataDefinition.fallbackVersion
|
|
42
|
+
const result = await pkg.getPackageXML(fileUtils);
|
|
43
|
+
expect(result).toBe('not found');
|
|
44
|
+
expect(fileUtils.fileExists).toHaveBeenCalled();
|
|
45
|
+
expect(pkg.packageJSON).toEqual(finalJSON);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should throw an error if xmlPath is undefined', async () => {
|
|
49
|
+
pkg.xmlPath = undefined;
|
|
50
|
+
await expect(pkg.getPackageXML(fileUtils)).rejects.toThrowError('Package not initialized');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should throw an error if error occurs during processing', async () => {
|
|
54
|
+
fileUtils.fileExists.mockReturnValue(true);
|
|
55
|
+
fileUtils.readFile.mockRejectedValue(new Error('Error'));
|
|
56
|
+
await expect(pkg.getPackageXML(fileUtils)).rejects.toThrowError('Error');
|
|
57
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as xml2js from 'xml2js'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import { Package } from '../../../src/lib/packageUtil.js'
|
|
5
|
+
|
|
6
|
+
const fileUtils = {
|
|
7
|
+
createDirectory: jest.fn(),
|
|
8
|
+
writeFile: jest.fn()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('savePackage', () => {
|
|
12
|
+
let pkg
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
pkg = new Package('path/to/file.xml')
|
|
15
|
+
pkg.packageJSON = {
|
|
16
|
+
Package: {
|
|
17
|
+
$: { xmlns: 'http://www.example.com' },
|
|
18
|
+
version: '1.0'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
jest.resetAllMocks()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('should replace http with https in xmlns property', () => {
|
|
28
|
+
pkg.savePackage(xml2js, fileUtils)
|
|
29
|
+
expect(pkg.packageJSON.Package.$.xmlns).toBe('https://www.example.com')
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should save version property to variable, delete it from json and set it again', () => {
|
|
33
|
+
const version = pkg.packageJSON.Package.version
|
|
34
|
+
pkg.savePackage(xml2js, fileUtils)
|
|
35
|
+
expect(version).toBe('1.0')
|
|
36
|
+
expect(pkg.packageJSON.Package.version).toBe(version)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should build xml object from json and write it to a file', () => {
|
|
40
|
+
pkg.addMember('type', 'member');
|
|
41
|
+
expect(pkg.packageJSON.Package.types[0].name).toBe('type');
|
|
42
|
+
expect(pkg.packageJSON.Package.types[0].members).toEqual(['member']);
|
|
43
|
+
pkg.savePackage(xml2js, fileUtils)
|
|
44
|
+
const xml = '<?xml version="1.0" encoding="UTF-8"?>\n<Package xmlns="https://www.example.com">\n <types>\n <members>member</members>\n <name>type</name>\n </types>\n <version>1.0</version>\n</Package>'
|
|
45
|
+
expect(fileUtils.createDirectory).toHaveBeenCalledWith(path.dirname('path/to/file.xml'))
|
|
46
|
+
expect(fileUtils.writeFile).toHaveBeenCalledWith('path/to/file.xml', xml)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should throw an error if it occurs', () => {
|
|
50
|
+
fileUtils.createDirectory.mockImplementation(() => {
|
|
51
|
+
throw new Error('createDirectory error')
|
|
52
|
+
})
|
|
53
|
+
expect(() => {
|
|
54
|
+
pkg.savePackage(xml2js, fileUtils)
|
|
55
|
+
}).toThrowError('createDirectory error')
|
|
56
|
+
})
|
|
57
|
+
})
|