@ds-sfdc/sfparty 1.3.4 → 1.3.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ds-sfdc/sfparty
2
2
 
3
- [![NPM](https://img.shields.io/npm/v/@ds-sfdc/sfparty.svg?label=@ds-sfdc/sfparty)](https://www.npmjs.com/package/@ds-sfdc/sfparty) [![Downloads/week](https://img.shields.io/npm/dw/@ds-sfdc/sfparty.svg)](https://npmjs.org/package/@ds-sfdc/sfparty) [![License](https://img.shields.io/badge/License-BSD%203--Clause-brightgreen.svg)](https://github.com/TimPaulaskasDS/sfparty/blob/main/LICENSE.md)
3
+ [![NPM](https://img.shields.io/npm/v/@ds-sfdc/sfparty.svg?label=@ds-sfdc/sfparty)](https://www.npmjs.com/package/@ds-sfdc/sfparty) [![Downloads/week](https://img.shields.io/npm/dw/@ds-sfdc/sfparty.svg)](https://npmjs.org/package/@ds-sfdc/sfparty) [![License](https://img.shields.io/badge/License-BSD%203--Clause-brightgreen.svg)](https://github.com/TimPaulaskasDS/sfparty/blob/main/LICENSE.md) ![CI/CD](https://github.com/TimPaulaskasDS/sfparty/actions/workflows/cicd.yaml/badge.svg)
4
4
 
5
5
  ## Why use sfparty?
6
6
 
@@ -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.4",
3
+ "version": "1.3.6",
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
- "chalk": "^5.2.0",
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",
@@ -33,6 +33,7 @@
33
33
  "marked": "^4.2.12",
34
34
  "marked-terminal": "^5.1.1",
35
35
  "pinst": "^3.0.0",
36
+ "semver": "^7.3.8",
36
37
  "util": "^0.10.3",
37
38
  "winston": "^3.8.2",
38
39
  "xml2js": "^0.4.23",
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 chalk from 'chalk'
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": chalk.greenBright('✔'),
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 = chalk.red('Please specify the action of ' + chalk.whiteBright.bgRedBright('split') + ' or ' + chalk.whiteBright.bgRedBright('combine') + '.')
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(chalk.magentaBright(`${global.icons.party} TEST ${global.icons.party}`))
131
+ global.logger.info(clc.magentaBright(`${global.icons.party} TEST ${global.icons.party}`))
132
132
  }
133
133
  })
134
134
  .command({
@@ -139,9 +139,17 @@ yargs(hideBin(process.argv))
139
139
  .check(yargCheck)
140
140
  },
141
141
  handler: (argv) => {
142
- checkVersion(axios, exec, pkgObj.default.version, true)
142
+ checkVersion({axios, spawnSync, currentVersion: pkgObj.default.version, update: true})
143
143
  }
144
144
  })
145
+ .command({
146
+ command: '[version]',
147
+ alias: 'version',
148
+ builder: (yargs) => {
149
+ yargs
150
+ .check(yargCheck)
151
+ },
152
+ })
145
153
  .command({
146
154
  command: '[split]',
147
155
  alias: 'split',
@@ -154,7 +162,7 @@ yargs(hideBin(process.argv))
154
162
  .check(yargCheck)
155
163
  },
156
164
  handler: (argv) => {
157
- checkVersion(axios, exec, pkgObj.default.version)
165
+ checkVersion({axios, spawnSync, currentVersion: pkgObj.default.version})
158
166
  global.format = argv.format
159
167
  splitHandler(argv, processStartTime)
160
168
  }
@@ -171,7 +179,7 @@ yargs(hideBin(process.argv))
171
179
  .check(yargCheck)
172
180
  },
173
181
  handler: (argv) => {
174
- checkVersion(axios, exec, pkgObj.default.version)
182
+ checkVersion({axios, spawnSync, currentVersion: pkgObj.default.version})
175
183
  global.format = argv.format
176
184
  const startProm = new Promise((resolve, reject) => {
177
185
  if (argv.git !== undefined) {
@@ -185,10 +193,10 @@ yargs(hideBin(process.argv))
185
193
  global.git.latest = data.latestCommit
186
194
  global.git.last = data.lastCommit
187
195
  if (data.last === undefined) {
188
- console.log(`${chalk.yellowBright('git mode')} ${chalk.bgMagentaBright('not active:')} no prior commit - processing all`)
196
+ console.log(`${clc.yellowBright('git mode')} ${clc.bgMagentaBright('not active:')} no prior commit - processing all`)
189
197
  resolve(false)
190
198
  } else {
191
- console.log(`${chalk.yellowBright('git mode')} ${chalk.magentaBright('active:')} ${chalk.bgBlackBright(data.lastCommit) + '..' + chalk.bgBlackBright(data.latestCommit)}`)
199
+ console.log(`${clc.yellowBright('git mode')} ${clc.magentaBright('active:')} ${clc.bgBlackBright(data.lastCommit) + '..' + clc.bgBlackBright(data.latestCommit)}`)
192
200
  console.log()
193
201
  const diff = git.diff(global.__basedir, `${data.lastCommit}..${data.latestCommit}`)
194
202
  diff
@@ -207,7 +215,7 @@ yargs(hideBin(process.argv))
207
215
  throw error
208
216
  })
209
217
  } else {
210
- console.log(`${chalk.yellowBright('git mode')} ${chalk.magentaBright('active:')} ${chalk.bgBlackBright(gitRef)}`)
218
+ console.log(`${clc.yellowBright('git mode')} ${clc.magentaBright('active:')} ${clc.bgBlackBright(gitRef)}`)
211
219
  console.log()
212
220
  const diff = git.diff(global.__basedir, gitRef)
213
221
  diff
@@ -253,8 +261,12 @@ function yargCheck(argv, options) {
253
261
  !options.array.includes(key)
254
262
  )
255
263
 
264
+ if (!argv._.includes('update')) {
265
+ checkVersion({axios, spawnSync, currentVersion: pkgObj.default.version, update: false})
266
+ }
267
+
256
268
  if (invalidKeys.length > 0) {
257
- const invalidKeysWithColor = invalidKeys.map(key => chalk.redBright(key))
269
+ const invalidKeysWithColor = invalidKeys.map(key => clc.redBright(key))
258
270
  throw new Error(`Invalid options specified: ${invalidKeysWithColor.join(', ')}`)
259
271
  }
260
272
 
@@ -270,13 +282,13 @@ function yargCheck(argv, options) {
270
282
  if (types.length > 1) {
271
283
  // if using multiple types you cannot specify name
272
284
  if ((typeof name != 'undefined' && name != '')) {
273
- throw new Error(chalk.redBright('You cannot specify ' + chalk.whiteBright.bgRedBright('--name') + ' when using multiple types.'))
285
+ throw new Error(clc.redBright('You cannot specify ' + clc.whiteBright.bgRedBright('--name') + ' when using multiple types.'))
274
286
  }
275
287
  } else {
276
288
  switch (argv.type) {
277
289
  case 'label':
278
290
  if ((typeof name != 'undefined' && name != '')) {
279
- throw new Error(chalk.redBright('You cannot specify ' + chalk.whiteBright.bgRedBright('--name') + ' when using label.'))
291
+ throw new Error(clc.redBright('You cannot specify ' + clc.whiteBright.bgRedBright('--name') + ' when using label.'))
280
292
  }
281
293
  break
282
294
  }
@@ -291,11 +303,11 @@ function displayMessageAndDuration(startTime, message) {
291
303
  let minutes = Math.floor((executionTime.seconds + Math.round(executionTime.milliseconds / 100000)) / 60)
292
304
  let seconds = Math.round((executionTime.seconds + Math.round(executionTime.milliseconds / 100000)) % 60)
293
305
  if (minutes == 0 && seconds == 0) {
294
- durationMessage = message + chalk.magentaBright(`<1s`)
306
+ durationMessage = message + clc.magentaBright(`<1s`)
295
307
  } else if (minutes > 0) {
296
- durationMessage = message + chalk.magentaBright(`${minutes}m ${seconds}s`)
308
+ durationMessage = message + clc.magentaBright(`${minutes}m ${seconds}s`)
297
309
  } else {
298
- durationMessage = message + chalk.magentaBright(`${seconds}s`)
310
+ durationMessage = message + clc.magentaBright(`${seconds}s`)
299
311
  }
300
312
  console.log('\n' + durationMessage)
301
313
  }
@@ -384,8 +396,8 @@ function processSplit(typeItem, argv) {
384
396
 
385
397
  if (processed.total == 0) resolve(true)
386
398
 
387
- console.log(`${chalk.bgBlackBright('Source path:')} ${sourceDir}`)
388
- console.log(`${chalk.bgBlackBright('Target path:')} ${targetDir}`)
399
+ console.log(`${clc.bgBlackBright('Source path:')} ${sourceDir}`)
400
+ console.log(`${clc.bgBlackBright('Target path:')} ${targetDir}`)
389
401
  console.log()
390
402
  console.log(`Splitting a total of ${processed.total} file(s)`)
391
403
  console.log()
@@ -412,7 +424,7 @@ function processSplit(typeItem, argv) {
412
424
  })
413
425
  })
414
426
  Promise.allSettled(promList).then((results) => {
415
- let message = `Split ${chalk.bgBlackBright((processed.current > promList.length) ? promList.length : processed.current)} file(s) ${(processed.errors > 0) ? 'with ' + chalk.bgBlackBright.red(processed.errors) + ' error(s) ' : ''}in `
427
+ 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
428
  displayMessageAndDuration(startTime, message)
417
429
  resolve(true)
418
430
  })
@@ -496,7 +508,7 @@ function processCombine(typeItem, argv) {
496
508
  }
497
509
 
498
510
  processed.total = processList.length
499
- console.log(`${chalk.bgBlackBright(processed.total)} ${typeItem} file(s) to process`)
511
+ console.log(`${clc.bgBlackBright(processed.total)} ${typeItem} file(s) to process`)
500
512
 
501
513
  // Abort if there are no files to process
502
514
  if (processed.total == 0) {
@@ -505,8 +517,8 @@ function processCombine(typeItem, argv) {
505
517
  }
506
518
 
507
519
  console.log()
508
- console.log(`${chalk.bgBlackBright('Source path:')} ${sourceDir}`)
509
- console.log(`${chalk.bgBlackBright('Target path:')} ${targetDir}`)
520
+ console.log(`${clc.bgBlackBright('Source path:')} ${sourceDir}`)
521
+ console.log(`${clc.bgBlackBright('Target path:')} ${targetDir}`)
510
522
  console.log()
511
523
 
512
524
  const promList = []
@@ -538,7 +550,7 @@ function processCombine(typeItem, argv) {
538
550
  errors++
539
551
  }
540
552
  })
541
- let message = `Combined ${chalk.bgBlackBright(successes)} file(s) ${(errors > 0) ? 'with ' + chalk.bgBlackBright(errors) + 'error(s) ' : ''}in `
553
+ let message = `Combined ${clc.bgBlackBright(successes)} file(s) ${(errors > 0) ? 'with ' + clc.bgBlackBright(errors) + 'error(s) ' : ''}in `
542
554
  displayMessageAndDuration(startTime, message)
543
555
  resolve(true)
544
556
  })
@@ -547,8 +559,8 @@ function processCombine(typeItem, argv) {
547
559
 
548
560
  function gitFiles(data) {
549
561
  data.forEach(item => {
550
- if (item.path.indexOf(packageDir + '-party' + path.sep) == 0) {
551
- const pathArray = item.path.split(path.sep)
562
+ if (item.path.indexOf(packageDir + '-party/') == 0) {
563
+ const pathArray = item.path.split('/')
552
564
  if (pathArray.length > 3) {
553
565
  if (getDirectories().includes(pathArray[3])) {
554
566
  switch (item.action) {
@@ -603,13 +615,13 @@ function displayHeader() {
603
615
  vertical: '│',
604
616
  }
605
617
  let versionString = `sfparty v${pkgObj.default.version}${(process.stdout.columns > pkgObj.default.description.length + 15) ? ' - ' + pkgObj.default.description : ''}`
606
- let titleMessage = `${global.icons.party} ${chalk.yellowBright(versionString)} ${global.icons.party}`
618
+ let titleMessage = `${global.icons.party} ${clc.yellowBright(versionString)} ${global.icons.party}`
607
619
  titleMessage = titleMessage.padEnd((process.stdout.columns / 2) + versionString.length / 1.65)
608
620
  titleMessage = titleMessage.padStart(process.stdout.columns)
609
- titleMessage = chalk.blackBright(box.vertical) + ' ' + titleMessage + ' ' + chalk.blackBright(box.vertical)
610
- console.log(`${chalk.blackBright(box.topLeft + box.horizontal.repeat(process.stdout.columns - 2) + box.topRight)}`)
621
+ titleMessage = clc.blackBright(box.vertical) + ' ' + titleMessage + ' ' + clc.blackBright(box.vertical)
622
+ console.log(`${clc.blackBright(box.topLeft + box.horizontal.repeat(process.stdout.columns - 2) + box.topRight)}`)
611
623
  console.log(titleMessage)
612
- console.log(`${chalk.blackBright(box.bottomLeft + box.horizontal.repeat(process.stdout.columns - 2) + box.bottomRight)}`)
624
+ console.log(`${clc.blackBright(box.bottomLeft + box.horizontal.repeat(process.stdout.columns - 2) + box.bottomRight)}`)
613
625
  console.log()
614
626
  }
615
627
 
@@ -1,42 +1,74 @@
1
- export async function checkVersion(axios, exec, currentVersion, update = false) {
1
+ import clc from 'cli-color'
2
+ import semver from 'semver'
3
+
4
+ class NpmNotInstalledError extends Error {
5
+ constructor(message) {
6
+ super(message)
7
+ this.name = "NpmNotInstalledError"
8
+ }
9
+ }
10
+
11
+ class PackageNotFoundError extends Error {
12
+ constructor(message) {
13
+ super(message)
14
+ this.name = "PackageNotFoundError"
15
+ }
16
+ }
17
+
18
+ class UpdateError extends Error {
19
+ constructor(message) {
20
+ super(message)
21
+ this.name = "UpdateError"
22
+ }
23
+ }
24
+
25
+ export async function checkVersion({axios, spawnSync, currentVersion, update = false}) {
2
26
  try {
3
- const { data } = await axios.get('https://registry.npmjs.org/@ds-sfdc/sfparty')
4
- const command = 'npm i -g @ds-sfdc/sfparty@latest'
5
- if (currentVersion !== data['dist-tags'].latest) {
6
- console.log(`${(update) ? global.icons.working : global.icons.fail} A newer version ${chalk.bgCyanBright(data['dist-tags'].latest)} is available.`)
27
+ const { data } = await axios.get('https://registry.npmjs.org/@ds-sfdc/sfparty', {
28
+ params: {
29
+ field: 'dist-tags.latest'
30
+ }
31
+ })
32
+ const latestVersion = data['dist-tags'].latest
33
+ if (semver.gt(latestVersion, currentVersion)) {
34
+ let icon
35
+ const version = clc.bgCyanBright(data['dist-tags'].latest)
36
+ if (update) {
37
+ icon = global.icons.working
38
+ } else {
39
+ icon = global.icons.fail
40
+ }
41
+ console.log(`${icon} A newer version ${version} is available.`)
7
42
  if (!update) {
8
- console.log(`Please upgrade by running ${chalk.cyanBright('sfparty update')}`)
43
+ console.log(`Please upgrade by running ${clc.cyanBright('sfparty update')}`)
9
44
  return 'A newer version'
10
45
  } else {
11
- console.log(`Updating the application using ${chalk.cyanBright(command)}`)
12
- exec('npm -v', (error, stdout, stderr) => {
13
- if (error) {
14
- global.logger.error("npm is not installed on this system. Please install npm and run the command again.")
15
- return 'npm is not installed'
16
- } else {
17
- exec(command, (error, stdout, stderr) => {
18
- if (error) {
19
- global.logger.error(error)
20
- reject(error)
21
- } else {
22
- console.log(stdout)
23
- console.log(stderr)
24
- resolve(true)
25
- }
26
- })
46
+ let command = 'npm i -g @ds-sfdc/sfparty@latest'.split(' ')
47
+ console.log(`Updating the application using ${clc.cyanBright(command.join(' '))}`)
48
+ try {
49
+ const npmVersion = spawnSync('npm', ['-v'])
50
+ if (npmVersion.stderr && npmVersion.stderr.toString().trim() === 'command not found') {
51
+ throw new NpmNotInstalledError("npm is not installed on this system. Please install npm and run the command again.")
52
+ }
53
+ const update = spawnSync(command[0], command.slice(1))
54
+ if (update.status !== 0) {
55
+ throw new UpdateError("Error updating the application.")
27
56
  }
28
- })
57
+ console.log("Application updated successfully.")
58
+ } catch (err) {
59
+ throw err
60
+ }
29
61
  }
30
62
  } else {
31
63
  if (update) {
32
64
  console.log(`${global.icons.success} You are on the latest version.`)
33
- return 'You are on the latest version'
34
65
  }
66
+ return 'You are on the latest version'
35
67
  }
36
68
  } catch (error) {
37
- global.logger.error(error)
69
+ if (error.response && error.response.status === 404) {
70
+ error = new PackageNotFoundError("Package not found on the npm registry")
71
+ }
38
72
  throw error
39
73
  }
40
- }
41
-
42
-
74
+ }
@@ -133,6 +133,7 @@ async function convertXML(data) {
133
133
  try {
134
134
  let parser = new Parser()
135
135
  parser.parseString(data, function (err, result) {
136
+ if (err) throw err
136
137
  resolve(result)
137
138
  })
138
139
  } catch (error) {
@@ -57,7 +57,7 @@ export function diff(dir, gitRef = 'HEAD', existsSyncStub = existsSync, execSync
57
57
 
58
58
  let data = ''
59
59
  try {
60
- data = execSyncStub(`git diff --name-status --oneline --relative ${gitRef}`, { cwd: dir, maxBuffer: 1024 * 1024 * 10 }).toString()
60
+ data = execSyncStub(`git diff --name-status --oneline --relative ${gitRef} -- *-party/*`, { cwd: dir, maxBuffer: 1024 * 1024 * 10 }).toString()
61
61
  } catch (error) {
62
62
  reject(error)
63
63
  }
@@ -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
- if (that.xmlPath === undefined) throw new Error('Package not initialized')
19
-
20
- let fileName = path.resolve(that.xmlPath)
21
- if (fileUtils.fileExists(fileName) && global.git.append) {
22
- let data = fileUtils.readFile(fileName)
23
- data
24
- .then((json) => {
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('existing')
27
- })
28
- .catch((error) => {
39
+ resolve('not found')
40
+ } catch (error) {
29
41
  reject(error)
30
- })
31
- } else {
32
- let json = JSON.parse(JSON.stringify(packageDefinition.metadataDefinition.emptyPackage))
33
- processJSON(that, json)
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
- global.displayError(error, true)
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
- if (typeJSON !== undefined) {
92
- typeJSON.members.push(member)
93
- typeJSON.members.sort()
94
- } else {
95
- typeJSON = JSON.parse(JSON.stringify(packageDefinition.metadataDefinition.emptyNode))
96
- typeJSON.name = type
97
- typeJSON.members.push(member)
98
-
99
- packageJSON.Package.types.push(typeJSON)
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
- json.$.xmlns = json.$.xmlns.replace('http:', 'https:')
113
-
114
- const builder = new xml2js.Builder(
115
- {
116
- cdata: false,
117
- rootName: 'Package',
118
- xmldec: { 'version': '1.0', 'encoding': 'UTF-8' }
119
- }
120
- )
121
- let fileName = that.xmlPath
122
- fileUtils.createDirectory(path.dirname(fileName))
123
-
124
- const xml = builder.buildObject(json)
125
-
126
- fileUtils.writeFile(fileName, xml)
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
- json.forEach(typesItem => {
132
- Object.keys(typesItem).forEach(key => {
133
- let jsonString = JSON.stringify(typesItem[key], (name, value) => {
134
- if (key == 'members') {
135
- return value
136
- } else {
137
- return xml2json(value)
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
- return
145
- }
168
+
169
+ return
170
+ } catch (error) {
171
+ throw error
172
+ }
146
173
 
147
- function xml2json(currentValue) {
148
- if (Array.isArray(currentValue)) {
149
- if (currentValue.length == 1) {
150
- currentValue = currentValue[0].toString().trim()
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
@@ -1,6 +1,6 @@
1
1
  import path from 'path'
2
2
  import logUpdate from 'log-update'
3
- import chalk from 'chalk'
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) => {
@@ -139,7 +139,7 @@ export class Combine {
139
139
  } else {
140
140
  logUpdate(that.#spinnerMessage
141
141
  .replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
142
- .replace('[%2]', `. ${chalk.redBright('source not found - removing XML file')}`)
142
+ .replace('[%2]', `. ${clc.redBright('source not found - removing XML file')}`)
143
143
  .replace('[%3]', ``)
144
144
  .replace('[%4]', `${global.icons.delete} `)
145
145
  .replace('[%5]', that.#fileName.shortName)
@@ -155,8 +155,8 @@ export class Combine {
155
155
  }
156
156
 
157
157
  function savePackageXML(that) {
158
- that.#addPkg.savePackage()
159
- that.#desPkg.savePackage()
158
+ that.#addPkg.savePackage(xml2js, fileUtils)
159
+ that.#desPkg.savePackage(xml2js, fileUtils)
160
160
  }
161
161
 
162
162
  function getXML(that) {
@@ -167,14 +167,14 @@ export class Combine {
167
167
  processed.current++
168
168
 
169
169
  that.#startTime = process.hrtime.bigint()
170
- that.#spinnerMessage = `[%1] of ${that.total} - ${that.#root}: [%4]${chalk.yellowBright('[%5]')}[%2][%3]`
170
+ that.#spinnerMessage = `[%1] of ${that.total} - ${that.#root}: [%4]${clc.yellowBright('[%5]')}[%2][%3]`
171
171
 
172
172
  try {
173
173
  that.#types.forEach(key => {
174
174
  // display message
175
175
  logUpdate(that.#spinnerMessage
176
176
  .replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
177
- .replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key}`)
177
+ .replace('[%2]', `\n${clc.magentaBright(nextFrame(that))} ${key}`)
178
178
  .replace('[%3]', `${that.#errorMessage}`)
179
179
  .replace('[%4]', `${global.icons.working} `)
180
180
  .replace('[%5]', `${that.#fileName.shortName} `)
@@ -199,7 +199,7 @@ export class Combine {
199
199
  } else if (that.metadataDefinition.directories.includes(key)) {
200
200
  processDirectory(that, key)
201
201
  } else {
202
- global.logger.warn(`Unexpected metadata type: ${chalk.redBright(key)}`)
202
+ global.logger.warn(`Unexpected metadata type: ${clc.redBright(key)}`)
203
203
  }
204
204
  })
205
205
  return true
@@ -234,7 +234,7 @@ export class Combine {
234
234
  fileList.forEach((file, index) => {
235
235
  logUpdate(that.#spinnerMessage
236
236
  .replace('[%1]', that.sequence.toString().padStart(that.total.toString().length, ' '))
237
- .replace('[%2]', `\n${chalk.magentaBright(nextFrame(that))} ${key} - ${index + 1} of ${fileList.length} - ${chalk.magentaBright(file)}`)
237
+ .replace('[%2]', `\n${clc.magentaBright(nextFrame(that))} ${key} - ${index + 1} of ${fileList.length} - ${clc.magentaBright(file)}`)
238
238
  .replace('[%3]', `${that.#errorMessage}`)
239
239
  .replace('[%4]', `${global.icons.working} `)
240
240
  .replace('[%5]', `${that.#fileName.shortName} `)
@@ -263,7 +263,7 @@ export class Combine {
263
263
  fileObj.shortName === undefined ||
264
264
  fileObj.fullName === undefined
265
265
  ) {
266
- global.displayError(`${global.icons.warn} Invalid file information passed ${chalk.redBright(fileObj)}`, true)
266
+ global.displayError(`${global.icons.warn} Invalid file information passed ${clc.redBright(fileObj)}`, true)
267
267
  }
268
268
 
269
269
  if (!fileUtils.fileExists(fileObj.fullName)) {
@@ -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 chalk from 'chalk'
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]${chalk.yellowBright(that.#fileName.shortName)}[%2][%3]`
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${chalk.magentaBright(nextFrame(that))} ${key}`)
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${chalk.magentaBright(nextFrame(that))} ${key}`)
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,95 @@
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, currentVersion: '1.0.0'})
28
+ expect(result).toBe('A newer version')
29
+ }, { silent: true })
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, currentVersion: '1.0.0'})
34
+ expect(result).toBe('You are on the latest version')
35
+ }, { silent: true })
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, currentVersion: '1.0.0', update: 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
+ }, { silent: true })
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, currentVersion: '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
+ }, { silent: true });
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, currentVersion: '1.0.0', update: true});
63
+ } catch (err) {
64
+ expect(err.name).toBe('UpdateError');
65
+ expect(err.message).toBe('Error updating the application.');
66
+ }
67
+ }, { silent: true });
68
+
69
+ it('should throw a UpdateError if update.status !== 0', async () => {
70
+ axios.get.mockResolvedValue({ data: { 'dist-tags': { latest: '2.0.0' } } });
71
+ spawnSync.mockImplementationOnce(() => ({ status: 0 }));
72
+ spawnSync.mockImplementationOnce(() => ({ status: 1, stderr: { toString: () => 'Update error' } }));
73
+ try {
74
+ await checkVersion({axios, spawnSync, currentVersion: '1.0.0', update: true});
75
+ } catch (err) {
76
+ expect(err.name).toBe('UpdateError');
77
+ expect(err.message).toBe('Error updating the application.');
78
+ }
79
+ }, { silent: true });
80
+
81
+
82
+ it('should log "You are on the latest version" if update flag is true and the current version is the latest version', async () => {
83
+ axios.get.mockResolvedValue({ data: { 'dist-tags': { latest: '1.0.0' } } });
84
+ await checkVersion({axios, spawnSync, currentVersion: '1.0.0', update: true});
85
+ expect(console.log).toHaveBeenCalledWith(`${global.icons.success} You are on the latest version.`);
86
+ }, { silent: true });
87
+
88
+ it('should log "Application updated successfully." after successful update', async () => {
89
+ axios.get.mockResolvedValue({ data: { 'dist-tags': { latest: '2.0.0' } } });
90
+ spawnSync.mockReturnValue({ status: 0 });
91
+ await checkVersion({axios, spawnSync, currentVersion: '1.0.0', update: true});
92
+ expect(console.log).toHaveBeenCalledWith("Application updated successfully.");
93
+ }, { silent: true });
94
+
95
+ })
@@ -10,45 +10,43 @@ jest.mock('child_process', () => ({
10
10
  }))
11
11
  const gitRef = "HEAD~1..HEAD"
12
12
 
13
- describe('diff', () => {
14
- beforeEach(() => {
15
- jest.clearAllMocks()
16
- })
13
+ beforeEach(() => {
14
+ jest.clearAllMocks()
15
+ })
17
16
 
18
- test('rejects if directory is not a git repository', async () => {
19
- existsSync.mockReturnValueOnce(false)
20
- try {
21
- await diff('/path/to/dir')
22
- fail('Expected function to throw an error', gitRef, existsSync)
23
- } catch (error) {
24
- expect(error.message).toEqual('The directory "/path/to/dir" is not a git repository')
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
- test('resolves with files when git diff command is successful', async () => {
29
- existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
30
- execSync.mockReturnValueOnce(
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
- const files = await diff('/path/to/dir', gitRef, existsSync, execSync)
36
- expect(files).toEqual([
37
- { type: 'add', path: 'file1.txt', action: 'add' },
38
- { type: 'modify', path: 'file2.txt', action: 'add' },
39
- { type: 'delete', path: 'file3.txt', action: 'delete' }
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
- test('rejects when git diff command fails', async () => {
44
- existsSync.mockReturnValueOnce(true).mockReturnValueOnce(true)
45
- execSync.mockImplementation(() => { throw new Error('Command failed') })
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
- try {
48
- await diff('/path/to/dir', gitRef, existsSync, execSync)
49
- fail('Expected function to throw an error')
50
- } catch (error) {
51
- expect(error.message).toEqual('Command failed')
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
  })
@@ -8,26 +8,24 @@ jest.mock('child_process', () => {
8
8
  }
9
9
  })
10
10
 
11
- describe('log', () => {
12
- beforeEach(() => {
13
- jest.resetAllMocks()
14
- })
11
+ beforeEach(() => {
12
+ jest.resetAllMocks()
13
+ })
15
14
 
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
- })
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
- 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
- })
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
+ })