@pc360/chlog 0.2.2 → 0.2.3

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/CHANGELOG.md CHANGED
@@ -10,6 +10,13 @@ The format is based on Keep a Changelog, and this project follows Semantic Versi
10
10
 
11
11
  - No changes yet.
12
12
 
13
+ ## [0.2.3] - 2026-02-16
14
+
15
+ ### Changed
16
+
17
+ - Updated package publish configuration guidance to ensure README image assets are included for npm rendering.
18
+ - Clarified that only paths listed in `package.json` `files` are published (and that omitted folders like `test/` are excluded by default).
19
+
13
20
  ## [0.2.2] - 2026-02-16
14
21
 
15
22
  ### Changed
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pc360/chlog",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "PC360 Changelog CLI Tool",
5
5
  "keywords": [
6
6
  "changelog",
@@ -13,6 +13,8 @@
13
13
  "files": [
14
14
  "bin",
15
15
  "lib",
16
+ "assets",
17
+ "test",
16
18
  "README.md",
17
19
  "CHANGELOG.md"
18
20
  ],
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+
3
+ const test = require("node:test");
4
+ const assert = require("node:assert/strict");
5
+ const fs = require("fs");
6
+ const os = require("os");
7
+ const path = require("path");
8
+ const { spawnSync } = require("child_process");
9
+
10
+ const { version } = require("../package.json");
11
+
12
+ const BIN_PATH = path.resolve(__dirname, "../bin/chlog.js");
13
+
14
+ function createTempDir() {
15
+ return fs.mkdtempSync(path.join(os.tmpdir(), "chlog-cli-"));
16
+ }
17
+
18
+ function runCli(args, cwd) {
19
+ return spawnSync(process.execPath, [BIN_PATH, ...args], {
20
+ cwd,
21
+ encoding: "utf8",
22
+ });
23
+ }
24
+
25
+ function cleanup(dir) {
26
+ fs.rmSync(dir, { recursive: true, force: true });
27
+ }
28
+
29
+ test("CLI exits 1 when run non-interactively without required interactive inputs", () => {
30
+ const cwd = createTempDir();
31
+
32
+ try {
33
+ const res = runCli([], cwd);
34
+ assert.equal(res.status, 1);
35
+ assert.match(res.stderr, /--type must be one of/);
36
+ assert.match(res.stderr, /Run: chlog --help/);
37
+ } finally {
38
+ cleanup(cwd);
39
+ }
40
+ });
41
+
42
+ test("CLI --dry-run still requires interactive inputs in non-TTY mode", () => {
43
+ const cwd = createTempDir();
44
+
45
+ try {
46
+ const res = runCli(["--dry-run"], cwd);
47
+
48
+ assert.equal(res.status, 1);
49
+ assert.match(res.stderr, /--type must be one of/);
50
+ } finally {
51
+ cleanup(cwd);
52
+ }
53
+ });
54
+
55
+ test("CLI prints version and exits 0 for --version and -v", () => {
56
+ const cwd = createTempDir();
57
+
58
+ try {
59
+ const longRes = runCli(["--version"], cwd);
60
+ const shortRes = runCli(["-v"], cwd);
61
+
62
+ assert.equal(longRes.status, 0);
63
+ assert.equal(shortRes.status, 0);
64
+ assert.equal(longRes.stdout.trim(), version);
65
+ assert.equal(shortRes.stdout.trim(), version);
66
+ assert.equal(longRes.stderr, "");
67
+ assert.equal(shortRes.stderr, "");
68
+ } finally {
69
+ cleanup(cwd);
70
+ }
71
+ });
72
+
73
+ test("CLI help shows interactive usage", () => {
74
+ const cwd = createTempDir();
75
+
76
+ try {
77
+ const res = runCli(["--help"], cwd);
78
+ assert.equal(res.status, 0);
79
+ assert.match(res.stdout, /Usage:\n chlog/);
80
+ assert.doesNotMatch(res.stdout, /--type/);
81
+ assert.doesNotMatch(res.stdout, /--scope/);
82
+ assert.doesNotMatch(res.stdout, /--message/);
83
+ } finally {
84
+ cleanup(cwd);
85
+ }
86
+ });
87
+
88
+ test("CLI rejects removed flags", () => {
89
+ const cwd = createTempDir();
90
+ try {
91
+ const typeRes = runCli(["--type", "added"], cwd);
92
+ const frontendRes = runCli(["--frontend"], cwd);
93
+
94
+ assert.equal(typeRes.status, 1);
95
+ assert.equal(frontendRes.status, 1);
96
+ assert.match(typeRes.stderr, /Unknown argument: --type/);
97
+ assert.match(frontendRes.stderr, /Unknown argument: --frontend/);
98
+ } finally {
99
+ cleanup(cwd);
100
+ }
101
+ });
102
+
103
+ test("CLI handles unknown argument", () => {
104
+ const cwd = createTempDir();
105
+ try {
106
+ const res = runCli(["--not-a-real-flag"], cwd);
107
+ assert.equal(res.status, 1);
108
+ assert.match(res.stderr, /Unknown argument: --not-a-real-flag/);
109
+ } finally {
110
+ cleanup(cwd);
111
+ }
112
+ });
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+
3
+ const test = require("node:test");
4
+ const assert = require("node:assert/strict");
5
+
6
+ const {
7
+ parseArgs,
8
+ validateArgs,
9
+ generateTimestamp,
10
+ buildLine,
11
+ } = require("../lib/chlog-lib");
12
+
13
+ test("parseArgs keeps interactive fields empty and parses defaults", () => {
14
+ const args = parseArgs(["node", "chlog"]);
15
+
16
+ assert.equal(args.type, null);
17
+ assert.equal(args.scope, null);
18
+ assert.equal(args.message, null);
19
+ assert.equal(args.tz, "Asia/Manila");
20
+ assert.equal(args.frontend, false);
21
+ assert.equal(args.layerSet, false);
22
+ assert.equal(args.dryRun, false);
23
+ assert.equal(args.dryRunSet, false);
24
+ assert.equal(args.help, false);
25
+ assert.equal(args.version, false);
26
+ });
27
+
28
+ test("parseArgs supports --dry-run", () => {
29
+ const args = parseArgs(["node", "chlog", "--dry-run"]);
30
+ assert.equal(args.dryRun, true);
31
+ assert.equal(args.dryRunSet, true);
32
+ });
33
+
34
+ test("parseArgs supports -d short dry-run flag", () => {
35
+ const args = parseArgs(["node", "chlog", "-d"]);
36
+ assert.equal(args.dryRun, true);
37
+ assert.equal(args.dryRunSet, true);
38
+ });
39
+
40
+ test("parseArgs supports --tz", () => {
41
+ const args = parseArgs(["node", "chlog", "--tz", "UTC"]);
42
+ assert.equal(args.tz, "UTC");
43
+ });
44
+
45
+ test("parseArgs rejects unknown argument", () => {
46
+ assert.throws(() => parseArgs(["node", "chlog", "--not-a-real-flag"]), /Unknown argument/);
47
+ });
48
+
49
+ test("validateArgs throws if missing scope", () => {
50
+ assert.throws(
51
+ () =>
52
+ validateArgs({
53
+ type: "added",
54
+ scope: null,
55
+ message: "x",
56
+ tz: "Asia/Manila",
57
+ }),
58
+ /--scope is required/,
59
+ );
60
+ });
61
+
62
+ test("validateArgs throws if message is whitespace-only", () => {
63
+ assert.throws(
64
+ () =>
65
+ validateArgs({
66
+ type: "added",
67
+ scope: "JES-1",
68
+ message: " ",
69
+ tz: "Asia/Manila",
70
+ }),
71
+ /--message is required/,
72
+ );
73
+ });
74
+
75
+ test("validateArgs trims scope and message", () => {
76
+ const args = {
77
+ type: "added",
78
+ scope: " JES-1 ",
79
+ message: " fix typo ",
80
+ tz: "Asia/Manila",
81
+ };
82
+
83
+ assert.equal(validateArgs(args), true);
84
+ assert.equal(args.scope, "JES-1");
85
+ assert.equal(args.message, "fix typo");
86
+ });
87
+
88
+ test("validateArgs throws if invalid type", () => {
89
+ assert.throws(
90
+ () =>
91
+ validateArgs({
92
+ type: "nope",
93
+ scope: "JES-1",
94
+ message: "x",
95
+ tz: "Asia/Manila",
96
+ }),
97
+ /--type must be one of/,
98
+ );
99
+ });
100
+
101
+ test("validateArgs throws if invalid timezone", () => {
102
+ assert.throws(
103
+ () =>
104
+ validateArgs({
105
+ type: "added",
106
+ scope: "JES-1",
107
+ message: "x",
108
+ tz: "Not/A_Timezone",
109
+ }),
110
+ /Invalid time zone|timeZone/i,
111
+ );
112
+ });
113
+
114
+ test("generateTimestamp returns 14 digits", () => {
115
+ const ts = generateTimestamp(new Date("2026-02-13T00:00:00Z"), "Asia/Manila");
116
+ assert.match(ts, /^\d{14}$/);
117
+ });
118
+
119
+ test("buildLine defaults to [Back-End] prefix and includes scope", () => {
120
+ const line = buildLine({
121
+ scope: "JES-33",
122
+ message: "Add daily consolidated JE job",
123
+ frontend: false,
124
+ });
125
+
126
+ assert.equal(line, "[JES-33] [Back-End] Add daily consolidated JE job\n");
127
+ });
128
+
129
+ test("buildLine uses [Front-End] when frontend=true", () => {
130
+ const line = buildLine({
131
+ scope: "JES-45",
132
+ message: "Fix pagination",
133
+ frontend: true,
134
+ });
135
+
136
+ assert.equal(line, "[JES-45] [Front-End] Fix pagination\n");
137
+ });
138
+
139
+ test("buildLine does not double-prefix if message already starts with label", () => {
140
+ const line = buildLine({
141
+ scope: "JES-99",
142
+ message: "[Back-End] Already prefixed",
143
+ frontend: false,
144
+ });
145
+
146
+ assert.equal(line, "[JES-99] [Back-End] Already prefixed\n");
147
+ });
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ const test = require("node:test");
4
+ const assert = require("node:assert/strict");
5
+
6
+ const { isPromptCancelled } = require("../lib/chlog-prompts");
7
+
8
+ test("isPromptCancelled returns true for enquirer cancellation payloads", () => {
9
+ assert.equal(isPromptCancelled(""), true);
10
+ assert.equal(isPromptCancelled(null), true);
11
+ assert.equal(isPromptCancelled(undefined), true);
12
+ assert.equal(isPromptCancelled("cancelled"), true);
13
+ assert.equal(isPromptCancelled(new Error("Prompt was canceled")), true);
14
+ });
15
+
16
+ test("isPromptCancelled returns false for normal errors", () => {
17
+ assert.equal(isPromptCancelled(new Error("Invalid timezone")), false);
18
+ assert.equal(isPromptCancelled("Unknown argument"), false);
19
+ });